From c85c8b62ae543af3fbb9f642319684c59d87d03c Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Wed, 23 Oct 2024 13:31:00 +0200 Subject: [PATCH 1/7] add fuzziness property for label softwrapping (#1082) --- .../src/org/eclipse/elk/core/Core.melk | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk index d5b059bab..8d56fcc12 100644 --- a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk @@ -716,6 +716,17 @@ programmatic option labelManager: ILabelManager { targets parents, labels } +option softwrappingFuzziness: double { + label "Softwrapping Fuzziness" + description + "Determines the amount of fuzziness to be used when performing softwrapping on labels. + The value expresses the percent of overhang that is permitted for each line. + If the next line would take up less space than this threshold, it is appended to the + current line instead of being placed in a new line." + default = 0.0 + targets labels +} + option margins: ElkMargin { label "Margins" description From 099fd0d8c21b81f630b6eb0595ebcc3214af3a88 Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Wed, 6 Nov 2024 13:33:42 +0100 Subject: [PATCH 2/7] Fixes Self-loop label bug when using compaction (#1081) Self-loop labels were previously omitted during compaction. This PR moves them together with everything else. --- .../compaction/LGraphToCGraphTransformer.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/compaction/LGraphToCGraphTransformer.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/compaction/LGraphToCGraphTransformer.java index 4ce09c400..5b3076edf 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/compaction/LGraphToCGraphTransformer.java +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/compaction/LGraphToCGraphTransformer.java @@ -479,6 +479,19 @@ public void applyLayout() { } + // offset selfloop labels + nodesMap.keySet().stream() + .flatMap(n -> StreamSupport.stream(n.getOutgoingEdges().spliterator(), false)) + .filter(e -> e.isSelfLoop()) + .forEach(sl -> { + LNode lNode = sl.getSource().getNode(); + CNode cNode = nodesMap.get(lNode); + double deltaX = cNode.hitbox.x - cNode.hitboxPreCompaction.x; + sl.getLabels().forEach(l -> { + l.getPosition().x += deltaX; + }); + }); + // calculating new graph size and offset KVector topLeft = new KVector(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); KVector bottomRight = new KVector(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); From 7ba490bd1983124fa3e116a143ae7f14daa2a2c0 Mon Sep 17 00:00:00 2001 From: Greg Zanchelli Date: Tue, 19 Nov 2024 03:19:57 -0500 Subject: [PATCH 3/7] Fix typo in meta model section (#1088) --- docs/content/documentation/tooldevelopers/graphdatastructure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/documentation/tooldevelopers/graphdatastructure.md b/docs/content/documentation/tooldevelopers/graphdatastructure.md index b47d6f569..4c2f175e6 100644 --- a/docs/content/documentation/tooldevelopers/graphdatastructure.md +++ b/docs/content/documentation/tooldevelopers/graphdatastructure.md @@ -66,7 +66,7 @@ Inclusion trees capture the hierarchical structure of a graph. See below for the ### The Meta Model -The ELK Graph meta modelh looks like this: +The ELK Graph meta model looks like this: {{< image src="graph_metamodel.png" alt="The ELK Graph meta model." >}} From 95417f0a3b88f4dc070b05e95569864a79914086 Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Mon, 9 Dec 2024 10:36:50 +0100 Subject: [PATCH 4/7] Layer Splitting Improvements (#1094) * Layer Splitter Update rename property properties can now be set on any node in a layer lifted unnecessary restriction in the combination of certain properties * add blogpost explaining layer splitting --- docs/content/blog/2024.md | 8 ++++ .../posts/2024/24-12-06-layer-unzipping.md | 37 +++++++++++++++ .../elk/alg/layered/GraphConfigurator.java | 4 +- .../org/eclipse/elk/alg/layered/Layered.melk | 17 +++---- .../IntermediateProcessorStrategy.java | 8 ++-- ...per.java => AlternatingLayerUnzipper.java} | 45 +++++++++++++++++-- .../options/LayerUnzippingStrategy.java | 4 +- ...java => AlternatingLayerUnzipperTest.java} | 28 ++++++------ 8 files changed, 118 insertions(+), 33 deletions(-) create mode 100644 docs/content/blog/2024.md create mode 100644 docs/content/blog/posts/2024/24-12-06-layer-unzipping.md rename plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/{GeneralLayerUnzipper.java => AlternatingLayerUnzipper.java} (84%) rename test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/{GeneralLayerUnzipperTest.java => AlternatingLayerUnzipperTest.java} (97%) diff --git a/docs/content/blog/2024.md b/docs/content/blog/2024.md new file mode 100644 index 000000000..e7a3249f9 --- /dev/null +++ b/docs/content/blog/2024.md @@ -0,0 +1,8 @@ +--- +title: "2024" +menu: + main: + identifier: "2024" + parent: "Blog" + weight: 20 +--- \ No newline at end of file diff --git a/docs/content/blog/posts/2024/24-12-06-layer-unzipping.md b/docs/content/blog/posts/2024/24-12-06-layer-unzipping.md new file mode 100644 index 000000000..b8445b8b5 --- /dev/null +++ b/docs/content/blog/posts/2024/24-12-06-layer-unzipping.md @@ -0,0 +1,37 @@ +--- +title: "Layer Unzipping" +menu: + main: + identifier: "24-12-06-layer-unzipping" + parent: "2024" + weight: 10 +--- + +_By Maximilian Kasperowski, December 6, 2024_ + +The coming update (ELK 0.9.2) introduces a new feature to control the positioning of nodes in a layer for further compaction. +Layer Unzipping can split up the nodes of a single layer into multiple layers. +This reduces the total height and can be helpful in certain situations. + +The update introduces three new properties: +- `org.eclipse.elk.layerUnzipping.strategy` +- `org.eclipse.elk.layerUnzipping.layerSplit` +- `org.eclipse.elk.layerUnizpping.resetOnLongEdges` + +This [elklive example](https://rtsys.informatik.uni-kiel.de/elklive/elkgraph.html?compressedContent=MYewdgzglgJgpgJwLIngGwPIPggdBAFwQEMC4BzATwC4ACABQCUBRAMWcYH1mARAcWYBlAFBpilRAFUwALygAHeVDDl8RUhRq0AggBkAKhwBy2-QEkjfYcLCo4tMAEZa1uDHL2ntALQA+BwBMLm4eDs5+DgDMwiGeQRFgACwx7p4ArD7+Sa6pYZkOybF5CWkpoV4JAGzWAPQ1tABmUAiEtGIIoWISCDZ2gbQA3sK0I23iUrIKSiq4CHAQcAQYYLrg5MypEHQNxGgLtHW0MFAQxABGaPbwOwCuaAS0Z3AAFsQAblAgNwi0xGAwtCIcFIbTWtFiEF+kLm5DuxB+tngEGEAF9evAooNhqMuhM5IplKpcQhBPI0FACHQAqj0Z5Ei5EekGX0wJUXMJDgtQP8xh17MTaYFnENRmNutJ8dMieMSWSKXRomjGVFnIKkqrlWA0hqWZVVWU6fl1cFcqyjXqcuUAuEsgB2E3lSI2hz2g0FZ1gV1FLUer2mvVG11q21AA) demonstrates the properties and the exact behaviour of the properties is explained in the sections below. + +## Layer Unzipping Strategy +For now there is only the strategy `LayerUnzippingStrategy.ALTERNATING`. +It evenly distributes the nodes into several sub-layers. +The first node goes into the first sub-layer, the second goes into the second and so on. +The default configuration is to use two sub-layers. + +## Configuring the Number of Sub-layers +The number of sub-layers is two by default and can be changed with the `layerSplit` property. +The property applies to an entire layer. +To use it, it must be set on any node of that layer. +If multiple values are set, then the lowest value is used. + +## Long Edge Treatment +Under the hood long edges are implemented using invisible dummy nodes. +The default behaviour is begin the alternatinon anew after a long edge, but this behaviour can be disabled for a layer by setting `resetOnLongEdges` to `false` for any node in the layer. \ No newline at end of file diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/GraphConfigurator.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/GraphConfigurator.java index 3f861f8d4..b92432813 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/GraphConfigurator.java +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/GraphConfigurator.java @@ -299,8 +299,8 @@ private LayoutProcessorConfiguration getPhaseIndependentL } switch (lgraph.getProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY)) { - case N_LAYERS: - configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.LAYER_UNZIPPER); + case ALTERNATING: + configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.ALTERNATING_LAYER_UNZIPPER); break; default: break; diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/Layered.melk b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/Layered.melk index 2a62ff398..b3d9d094b 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/Layered.melk +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/Layered.melk @@ -960,25 +960,26 @@ group layerUnzipping { advanced option layerSplit: Integer { label "Unzipping Layer Split" description - "Defines the number of sublayers to split a layer into when using the N_LAYERS strategy. - The property can be set to the first node in a layer, which then applies the property - for the layer the node belongs to." + "Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer, + which then applies the property for the layer. If multiple nodes set the value to different values, + then the lowest value is chosen." default = 2 targets nodes lowerBound = 1 - requires layerUnzipping.strategy == LayerUnzippingStrategy.N_LAYERS } option resetOnLongEdges: Boolean { label "Reset Alternation on Long Edges" description - "If set to true, nodes will always be placed in the first sublayer after a long edge. + "If set to true, nodes will always be placed in the first sublayer after a long edge when using the + ALTERNATING strategy. Otherwise long edge dummies are treated the same as regular nodes. The default value is true. - The property can be set to the first node in a layer, which then applies the property - for the layer the node belongs to." + The property can be set to the nodes in a layer, which then applies the property + for the layer. If any node sets the value to false, then the value is set to false for the entire + layer." default = true targets nodes - requires layerUnzipping.strategy == LayerUnzippingStrategy.N_LAYERS + requires layerUnzipping.strategy == LayerUnzippingStrategy.ALTERNATING } } diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/IntermediateProcessorStrategy.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/IntermediateProcessorStrategy.java index e4947be28..1cba2967c 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/IntermediateProcessorStrategy.java +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/IntermediateProcessorStrategy.java @@ -11,7 +11,7 @@ import org.eclipse.elk.alg.layered.graph.LGraph; import org.eclipse.elk.alg.layered.intermediate.compaction.HorizontalGraphCompactor; -import org.eclipse.elk.alg.layered.intermediate.unzipping.GeneralLayerUnzipper; +import org.eclipse.elk.alg.layered.intermediate.unzipping.AlternatingLayerUnzipper; import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointInserter; import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointProcessor; import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointRemover; @@ -97,7 +97,7 @@ public enum IntermediateProcessorStrategy implements ILayoutProcessorFactory create() { case SELF_LOOP_PORT_RESTORER: return new SelfLoopPortRestorer(); - case LAYER_UNZIPPER: - return new GeneralLayerUnzipper(); + case ALTERNATING_LAYER_UNZIPPER: + return new AlternatingLayerUnzipper(); case SELF_LOOP_POSTPROCESSOR: return new SelfLoopPostProcessor(); diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/GeneralLayerUnzipper.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/AlternatingLayerUnzipper.java similarity index 84% rename from plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/GeneralLayerUnzipper.java rename to plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/AlternatingLayerUnzipper.java index a05415371..64096d9ca 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/GeneralLayerUnzipper.java +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/intermediate/unzipping/AlternatingLayerUnzipper.java @@ -47,7 +47,7 @@ * * */ -public class GeneralLayerUnzipper implements ILayoutProcessor { +public class AlternatingLayerUnzipper implements ILayoutProcessor { @Override public void process (LGraph graph, IElkProgressMonitor progressMonitor) { @@ -58,8 +58,8 @@ public void process (LGraph graph, IElkProgressMonitor progressMonitor) { List> newLayers = new ArrayList<>(); for (int i = 0; i < graph.getLayers().size(); i++) { - int N = graph.getLayers().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT); - boolean resetOnLongEdges = graph.getLayers().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES); + int N = getLayerSplitProperty(graph.getLayers().get(i)); + boolean resetOnLongEdges = getResetOnLongEdgesProperty(graph.getLayers().get(i)); // only split if there are more nodes than the resulting sub-layers // an alternative would be to reduce N for this layer, this may or may @@ -110,6 +110,45 @@ public void process (LGraph graph, IElkProgressMonitor progressMonitor) { } + + /** + * Checks all nodes of a layer for the layerSplit property and returns the lowest set value. + * + * @param layer The layer to determine the layerSplit property for + * @return the layerSplit value + */ + private int getLayerSplitProperty(Layer layer) { + int layerSplit = Integer.MAX_VALUE; + boolean propertyUnset = true; + for (int i = 0; i < layer.getNodes().size(); i++) { + if (layer.getNodes().get(i).hasProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT)) { + propertyUnset = false; + int nodeValue = layer.getNodes().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT); + layerSplit = layerSplit < nodeValue ? layerSplit : nodeValue; + } + } + if (propertyUnset) { + layerSplit = LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT.getDefault(); + } + return layerSplit; + } + + /** + * Checks all nodes of a layer for the resetOnLongEdges property and if any sets the value to false, returns false. + * + * @param layer The layer to determine the resetOnLongEdges property for. + * @return the resetOnLongEdges value + */ + private boolean getResetOnLongEdgesProperty(Layer layer) { + for (int i = 0; i < layer.getNodes().size(); i++) { + if (layer.getNodes().get(i).hasProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES)) { + if (!layer.getNodes().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES)) { + return false; + } + } + } + return true; + } /** * checks the layer split property of the first node in a layer and copies the property to the layer diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/options/LayerUnzippingStrategy.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/options/LayerUnzippingStrategy.java index ab5c14809..24ee4a50c 100644 --- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/options/LayerUnzippingStrategy.java +++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/options/LayerUnzippingStrategy.java @@ -17,7 +17,7 @@ public enum LayerUnzippingStrategy { NONE, - /** Splits all layers with more than two nodes into two layers. */ - N_LAYERS; + /** Splits all layers with more than two nodes into several layers in an alternating pattern. */ + ALTERNATING; } diff --git a/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/GeneralLayerUnzipperTest.java b/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/AlternatingLayerUnzipperTest.java similarity index 97% rename from test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/GeneralLayerUnzipperTest.java rename to test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/AlternatingLayerUnzipperTest.java index 15f893c65..0dd9d3ebe 100644 --- a/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/GeneralLayerUnzipperTest.java +++ b/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/intermediate/AlternatingLayerUnzipperTest.java @@ -22,10 +22,10 @@ import org.junit.Test; /** - * Tests the general n-way layer unzipper. + * Tests the general n-way alternating layer unzipper. * */ -public class GeneralLayerUnzipperTest { +public class AlternatingLayerUnzipperTest { LayeredLayoutProvider layeredLayout; @@ -71,7 +71,7 @@ public void simpleTwoSplit() { ElkGraphUtil.createSimpleEdge(node4, nodeFinal); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -125,7 +125,7 @@ public void simpleThreeSplit() { ElkGraphUtil.createSimpleEdge(node4, nodeFinal); ElkGraphUtil.createSimpleEdge(node5, nodeFinal); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 3); @@ -173,7 +173,7 @@ public void danglingOutgoing() { ElkGraphUtil.createSimpleEdge(node1, node4); ElkGraphUtil.createSimpleEdge(node1, node5); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -225,7 +225,7 @@ public void danglingIncoming() { ElkGraphUtil.createSimpleEdge(node4, nodeFinal); ElkGraphUtil.createSimpleEdge(node5, nodeFinal); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -290,7 +290,7 @@ public void multipleLayersSplit() { ElkGraphUtil.createSimpleEdge(node4, node22); ElkGraphUtil.createSimpleEdge(node4, node23); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node1.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); node21.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -354,7 +354,7 @@ public void multipleIncomingEdges() { ElkGraphUtil.createSimpleEdge(node4, nodeFinal); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -409,7 +409,7 @@ public void multipleOutgoingEdges() { ElkGraphUtil.createSimpleEdge(node4, nodeFinal); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -466,7 +466,7 @@ public void mixedDanglingIncoming() { ElkGraphUtil.createSimpleEdge(node3, node6); ElkGraphUtil.createSimpleEdge(node4, node7); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); node5.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -530,7 +530,7 @@ public void mixedDanglingOutgoing() { ElkGraphUtil.createSimpleEdge(node3, node6); ElkGraphUtil.createSimpleEdge(node4, node7); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); node5.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -598,7 +598,7 @@ public void mixedTwoThreeLayerSplit() { ElkGraphUtil.createSimpleEdge(node4, node22); ElkGraphUtil.createSimpleEdge(node4, node23); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node1.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 3); node21.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); @@ -652,7 +652,7 @@ public void resetOnLongEdges() { ElkGraphUtil.createSimpleEdge(node4, node5); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES, true); @@ -707,7 +707,7 @@ public void noResetOnLongEdges() { ElkGraphUtil.createSimpleEdge(node4, node5); ElkGraphUtil.createSimpleEdge(node1, node6); - graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS); + graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING); graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2); node2.setProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES, false); From 83f94c801c29275dd46dee645919597a6d504f1a Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Mon, 9 Dec 2024 10:37:09 +0100 Subject: [PATCH 5/7] Add support for handling top-down layout when no approximator is set (#1089) * Add support for handling top-down layout when no approximator is set * Update topdown layout test to handle the case when no approximators are set correctly. * Remove commented old code --- .../elk/core/RecursiveGraphLayoutEngine.java | 9 +++++++++ .../elk/alg/topdown/test/TopdownLayoutTest.java | 16 ++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java index 1a8b7844e..04cd98992 100644 --- a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java @@ -255,6 +255,15 @@ protected List layoutRecursively(final ElkNode layoutNode, final TestCo ElkPadding padding = childNode.getProperty(CoreOptions.PADDING); childNode.setDimensions(Math.max(childNode.getWidth(), size.x + padding.left + padding.right), Math.max(childNode.getHeight(), size.y + padding.top + padding.bottom)); + } else { + // If no approximator is set, use the set sizes for atomic nodes and use the properties + // that have been set for nodes containing further children + if (childNode.getChildren().size() != 0) { + childNode.setDimensions( + layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH), + layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH) / + layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO)); + } } } } diff --git a/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java b/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java index 5a0081047..7aa5f959e 100644 --- a/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java +++ b/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Kiel University and others. + * Copyright (c) 2022-2024 Kiel University and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -51,8 +51,9 @@ public void testTwoLevelLayoutHorizontalScaling() { toplevel.setProperty(CoreOptions.ALGORITHM, "org.eclipse.elk.fixed"); toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); - // set size of node so that children will be scaled down - toplevel.setDimensions(20, 50); + // set size of node so that children will be scaled down + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 20.0); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 0.4); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -105,7 +106,8 @@ public void testTwoLevelLayoutVerticalScaling() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - toplevel.setDimensions(40, 30); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 40.0); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.33333); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -159,7 +161,8 @@ public void testScaleCap() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - toplevel.setDimensions(300, 300); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -212,7 +215,8 @@ public void testScaleCapBounded() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - toplevel.setDimensions(300, 300); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); + graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); From 5999901e375ab610347f8599c62f9cbe6ea638ea Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Wed, 11 Dec 2024 16:16:09 +0100 Subject: [PATCH 6/7] Nodes define their own width and height even in case no approximator is set(#1097) --- .../elk/core/RecursiveGraphLayoutEngine.java | 7 ++++--- .../elk/alg/topdown/test/TopdownLayoutTest.java | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java index 04cd98992..f06465932 100644 --- a/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java +++ b/plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/RecursiveGraphLayoutEngine.java @@ -260,9 +260,10 @@ protected List layoutRecursively(final ElkNode layoutNode, final TestCo // that have been set for nodes containing further children if (childNode.getChildren().size() != 0) { childNode.setDimensions( - layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH), - layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH) / - layoutNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO)); + childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH), + childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH) / + childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO) + ); } } } diff --git a/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java b/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java index 7aa5f959e..f5e181fa7 100644 --- a/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java +++ b/test/org.eclipse.elk.alg.topdown.test/src/org/eclipse/elk/alg/topdown/test/TopdownLayoutTest.java @@ -52,8 +52,8 @@ public void testTwoLevelLayoutHorizontalScaling() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 20.0); - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 0.4); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 20.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 0.4); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -106,8 +106,8 @@ public void testTwoLevelLayoutVerticalScaling() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 40.0); - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.33333); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 40.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.33333); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -161,8 +161,8 @@ public void testScaleCap() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); @@ -215,8 +215,8 @@ public void testScaleCapBounded() { toplevel.setProperty(CoreOptions.PADDING, new ElkPadding()); toplevel.setProperty(CoreOptions.SPACING_NODE_NODE, 0.0); // set size of node so that children will be scaled down - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); - graph.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH, 300.0); + toplevel.setProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO, 1.0); ElkNode child1 = ElkGraphUtil.createNode(toplevel); child1.setProperty(CoreOptions.TOPDOWN_LAYOUT, true); From 37c7deddacd451790ad8b41eee1978223c562e0f Mon Sep 17 00:00:00 2001 From: Max Kasperowski Date: Wed, 11 Dec 2024 16:16:30 +0100 Subject: [PATCH 7/7] Add OrderBySize Rectpacking preprocessor (#1095) * Add OrderBySize Rectpacking preprocessor Setting OrderBySize to true will order nodes by the sizes of their areas while preserving any prior ordering in case of ties. * put node size orderer before interactive orderer --- .../elk/alg/rectpacking/RectPacking.melk | 10 +++++ .../RectPackingLayoutProvider.java | 5 +++ .../IntermediateProcessorStrategy.java | 3 ++ .../intermediate/NodeSizeComparator.java | 33 +++++++++++++++ .../intermediate/NodeSizeReorderer.java | 41 +++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeComparator.java create mode 100644 plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeReorderer.java diff --git a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPacking.melk b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPacking.melk index e6139546c..cde949cc2 100644 --- a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPacking.melk +++ b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPacking.melk @@ -59,6 +59,7 @@ algorithm rectpacking(RectPackingLayoutProvider) { supports currentPosition supports inNewRow supports trybox + supports orderBySize } @@ -203,3 +204,12 @@ option inNewRow: boolean { targets nodes default = false } + +option orderBySize: boolean { + label "Order nodes by size" + description + "If set to true the nodes will be sorted by the size of their area before computing the layout. The largest + node will be in the first position." + default = false + targets parents +} diff --git a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPackingLayoutProvider.java b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPackingLayoutProvider.java index 2fb18b557..45ca526fc 100644 --- a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPackingLayoutProvider.java +++ b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/RectPackingLayoutProvider.java @@ -217,6 +217,11 @@ private LayoutProcessorConfiguration getPhaseI configuration.addBefore(RectPackingLayoutPhases.P2_PACKING, IntermediateProcessorStrategy.MIN_SIZE_POST_PROCESSOR); + if (layoutGraph.getProperty(RectPackingOptions.ORDER_BY_SIZE)) { + configuration.addBefore(RectPackingLayoutPhases.P1_WIDTH_APPROXIMATION, + IntermediateProcessorStrategy.NODE_SIZE_REORDERER); + } + if (layoutGraph.getProperty(RectPackingOptions.INTERACTIVE)) { configuration.addBefore(RectPackingLayoutPhases.P1_WIDTH_APPROXIMATION, IntermediateProcessorStrategy.INTERACTIVE_NODE_REORDERER); diff --git a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/IntermediateProcessorStrategy.java b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/IntermediateProcessorStrategy.java index 8164c56b3..4afa4dbbe 100644 --- a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/IntermediateProcessorStrategy.java +++ b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/IntermediateProcessorStrategy.java @@ -29,6 +29,7 @@ public enum IntermediateProcessorStrategy implements ILayoutProcessorFactory create() { switch (this) { + case NODE_SIZE_REORDERER: + return new NodeSizeReorderer(); case INTERACTIVE_NODE_REORDERER: return new InteractiveNodeReorderer(); case MIN_SIZE_PRE_PROCESSOR: diff --git a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeComparator.java b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeComparator.java new file mode 100644 index 000000000..4466f4aaa --- /dev/null +++ b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeComparator.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2024 Kiel University and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.elk.alg.rectpacking.intermediate; + +import java.util.Comparator; + +import org.eclipse.elk.graph.ElkNode; + +/** + * Node size comparator to compare nodes by their size + * + */ +public class NodeSizeComparator implements Comparator { + + /* (non-Javadoc) + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + @Override + public int compare(ElkNode node0, ElkNode node1) { + double area0 = node0.getWidth() * node0.getHeight(); + double area1 = node1.getWidth() * node1.getHeight(); + + return Double.compare(area1, area0); + } + +} diff --git a/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeReorderer.java b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeReorderer.java new file mode 100644 index 000000000..e131e1979 --- /dev/null +++ b/plugins/org.eclipse.elk.alg.rectpacking/src/org/eclipse/elk/alg/rectpacking/intermediate/NodeSizeReorderer.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2024 Kiel University and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.elk.alg.rectpacking.intermediate; + +import org.eclipse.elk.core.alg.ILayoutProcessor; +import org.eclipse.elk.core.util.IElkProgressMonitor; +import org.eclipse.elk.graph.ElkNode; +import org.eclipse.emf.common.util.ECollections; + +/** + * Sorts all child nodes by their sizes from largest to smallest while preserving any existing ordering. + * + *
+ *
Precondition:
+ *
Postcondition:
+ *
Children are sorted in descending order from largest to smallest.
+ *
Slots:
+ *
Before phase 1.
+ *
Same-slot dependencies:
+ *
Before Interactive Node Reorderer
+ *
+ */ +public class NodeSizeReorderer implements ILayoutProcessor { + + /* (non-Javadoc) + * @see org.eclipse.elk.core.alg.ILayoutProcessor#process(java.lang.Object, org.eclipse.elk.core.util.IElkProgressMonitor) + */ + @Override + public void process(ElkNode graph, IElkProgressMonitor progressMonitor) { + + ECollections.sort(graph.getChildren(), new NodeSizeComparator()); + } + +}