diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java index 69383fe..b35f88a 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -15,7 +15,10 @@ import models.Node; import models.controlFlowModel.*; import models.dataConstraintModel.ResourcePath; -import models.dataFlowModel.*; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.ResourceNode; import java.awt.event.MouseListener; import java.util.ArrayList; @@ -27,11 +30,11 @@ public final int PORT_DIAMETER = 8; public final int PORT_RADIUS = PORT_DIAMETER / 2; - public static final int RESOURCE_NODE_WIDTH = 240; - public static final int RESOURCE_NODE_HEIGHT = 120; + public static final int MIN_RESOURCE_NODE_WIDTH = 100; + public static final int MIN_RESOURCE_NODE_HEIGHT = 50; - public static final int EVENT_CHANNEL_NODE_WIDTH = 120; - public static final int EVENT_CHANNEL_NODE_HEIGHT = 60; + public static final int MIN_EVENT_CHANNEL_NODE_WIDTH = 100; + public static final int MIN_EVENT_CHANNEL_NODE_HEIGHT = 50; private ControlFlowGraph controlFlowGraph = null; @@ -172,18 +175,66 @@ for (ResourceNode rootNode : controlFlowGraph.getDataFlowGraph().getRootResourceNodes()) { ResourcePath rootResourcePath = rootNode.getPrimaryResourcePath(); - int width = RESOURCE_NODE_WIDTH; - int height = RESOURCE_NODE_HEIGHT; - mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, rootResourcePath.getLeafResourceName(), 20, 20, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + int width = calculateRequiredWidth(rootNode); + int height = calculateRequiredHeight(rootNode); + + String name = rootResourcePath.getLeafResourceName(); + if (rootResourcePath.endsWithParam()) { + name = "{" + rootResourcePath.getLastParam().toString() + "}"; + } + + mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, name, 20, 20, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex resourceNodeCells.put(rootNode, insertedCell); - createChildResourceNodeCell(graph, insertedCell, rootNode, resourceNodeCells, (int) (width * 0.6), (int) (height * 0.6), callGraph); + createChildResourceNodeCell(graph, insertedCell, rootNode, resourceNodeCells, (int) (width * getScale(rootNode.getChildren().size())), (int) (height * getScale(rootNode.getChildren().size())), callGraph); } return resourceNodeCells; } /** + * Calculate the scaling factor for child nodes based on their count. + * + * @param n The number of child nodes + * @return The scaling factor + */ + private double getScale(int n) { + if (n <= 1) return 0.6; + double val = (0.8 * Math.sin(Math.PI / n)) / (1.0 + 0.8 * Math.sin(Math.PI / n)); + return val; + } + + /** + * Calculate the required width of the resource node to fit all its descendants. + * + * @param node The resource node + * @return The required width + */ + private int calculateRequiredWidth(ResourceNode node) { + if (node.getChildren().isEmpty()) return MIN_RESOURCE_NODE_WIDTH; + int maxChildWidth = 0; + for (ResourceNode child : node.getChildren()) { + maxChildWidth = Math.max(maxChildWidth, calculateRequiredWidth(child)); + } + return (int) Math.ceil(maxChildWidth / getScale(node.getChildren().size())); + } + + /** + * Calculate the required height of the resource node to fit all its descendants. + * + * @param node The resource node + * @return The required height + */ + private int calculateRequiredHeight(ResourceNode node) { + if (node.getChildren().isEmpty()) return MIN_RESOURCE_NODE_HEIGHT; + int maxChildHeight = 0; + for (ResourceNode child : node.getChildren()) { + maxChildHeight = Math.max(maxChildHeight, calculateRequiredHeight(child)); + } + return (int) Math.ceil(maxChildHeight / getScale(node.getChildren().size())); + } + + /** * Create and register a new graph cell for a child resource node. * * @param graph {@link mxGraph} instance to be modified. @@ -193,21 +244,36 @@ * @param callGraph The call graph to check for node existence. */ private void createChildResourceNodeCell(final mxGraph graph, final mxCell parentCell, final ResourceNode parentNode, final Map resourceNodeCells, int width, int height, final CallGraph callGraph) { + int i = 0; + int n = parentNode.getChildren().size(); for (ResourceNode childNode : parentNode.getChildren()) { ResourcePath resourcePath = childNode.getPrimaryResourcePath(); + String name = resourcePath.getLeafResourceName(); + if (resourcePath.endsWithParam()) { + name = "{" + resourcePath.getLastParam().toString() + "}"; + } + mxGeometry parentGeo = parentCell.getGeometry(); double x = 0; double y = 0; if (parentGeo != null) { x = (parentGeo.getWidth() - width) / 2; y = (parentGeo.getHeight() - height) / 2; + if (n > 1) { + double rX = (parentGeo.getWidth() - width) / 2 * 0.8; + double rY = (parentGeo.getHeight() - height) / 2 * 0.8; + double theta = 2 * Math.PI / n * i; + x += rX * Math.cos(theta); + y += rY * Math.sin(theta); + } } - mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, resourcePath.getName(), x, y, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, name, x, y, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex resourceNodeCells.put(childNode, insertedCell); - createChildResourceNodeCell(graph, insertedCell, childNode, resourceNodeCells, (int) (width * 0.6), (int) (height * 0.6), callGraph); + createChildResourceNodeCell(graph, insertedCell, childNode, resourceNodeCells, (int) (width * getScale(childNode.getChildren().size())), (int) (height * getScale(childNode.getChildren().size())), callGraph); + i++; } } @@ -226,7 +292,7 @@ final Map outputChannelNodeCells = new HashMap<>(); for (ChannelNode rootChannelNode : controlFlowGraph.getDataFlowGraph().getRootChannelNodes()) { - createEventChannelNodeCell(graph, layerCell, rootChannelNode, channelNodeCells, outputChannelNodeCells, EVENT_CHANNEL_NODE_WIDTH, EVENT_CHANNEL_NODE_HEIGHT); + createEventChannelNodeCell(graph, layerCell, rootChannelNode, channelNodeCells, outputChannelNodeCells, MIN_EVENT_CHANNEL_NODE_WIDTH, MIN_EVENT_CHANNEL_NODE_HEIGHT); } return channelNodeCells; } @@ -366,29 +432,4 @@ } return path; } - - /** - * Add a stateless object node to the control-flow graph - * - * @param insertObjNode An object node to be added - * @param selectedOption Data-tranfer method - */ - private void addObjectNodeToCallGraph(final ObjectNode insertObjNode, final PushPullValue selectedOption) { - switch (selectedOption) { - case PUSH: { - if (controlFlowGraph.getPushCallGraph().getNodes().contains(insertObjNode)) { - return; - } - controlFlowGraph.getPushCallGraph().addNode(insertObjNode); - break; - } - case PULL: - case PUSHorPULL: - if (controlFlowGraph.getPullCallGraph().getNodes().contains(insertObjNode)) { - return; - } - controlFlowGraph.getPullCallGraph().addNode(insertObjNode); - break; - } - } }