Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into rectpacking-order…
Browse files Browse the repository at this point in the history
…bysize
  • Loading branch information
Eddykasp committed Dec 12, 2024
2 parents f5f0c71 + 37c7ded commit 0020c12
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 40 deletions.
8 changes: 8 additions & 0 deletions docs/content/blog/2024.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "2024"
menu:
main:
identifier: "2024"
parent: "Blog"
weight: 20
---
37 changes: 37 additions & 0 deletions docs/content/blog/posts/2024/24-12-06-layer-unzipping.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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." >}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ private LayoutProcessorConfiguration<LayeredPhases, LGraph> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -97,7 +97,7 @@ public enum IntermediateProcessorStrategy implements ILayoutProcessorFactory<LGr
/** Position self loops after phase 3. */
SELF_LOOP_PORT_RESTORER,
/** Unzips layers for compaction. */
LAYER_UNZIPPER,
ALTERNATING_LAYER_UNZIPPER,
/** Wraps graphs such that they better fit a given drawing area, allowing only a single edge per cut. */
SINGLE_EDGE_GRAPH_WRAPPER,
/** Makes sure that in-layer constraints are handled. */
Expand Down Expand Up @@ -331,8 +331,8 @@ public ILayoutProcessor<LGraph> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* </dl>
*
*/
public class GeneralLayerUnzipper implements ILayoutProcessor<LGraph> {
public class AlternatingLayerUnzipper implements ILayoutProcessor<LGraph> {

@Override
public void process (LGraph graph, IElkProgressMonitor progressMonitor) {
Expand All @@ -58,8 +58,8 @@ public void process (LGraph graph, IElkProgressMonitor progressMonitor) {
List<Pair<Layer, Integer>> 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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
11 changes: 11 additions & 0 deletions plugins/org.eclipse.elk.core/src/org/eclipse/elk/core/Core.melk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ protected List<ElkEdge> 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(
childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH),
childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_WIDTH) /
childNode.getProperty(CoreOptions.TOPDOWN_HIERARCHICAL_NODE_ASPECT_RATIO)
);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 0020c12

Please sign in to comment.