Skip to content

Commit 55debcb

Browse files
committed
Merge branch 'master' of [email protected]:eclipse/elk.git
2 parents 281d684 + 5efade6 commit 55debcb

File tree

10 files changed

+514
-24
lines changed

10 files changed

+514
-24
lines changed

plugins/org.eclipse.elk.alg.layered/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Export-Package: org.eclipse.elk.alg.layered,
1616
org.eclipse.elk.alg.layered.compaction.oned;x-friends:="org.eclipse.elk.alg.layered.test",
1717
org.eclipse.elk.alg.layered.compaction.oned.algs;x-friends:="org.eclipse.elk.alg.layered.test",
1818
org.eclipse.elk.alg.layered.compaction.recthull;x-friends:="org.eclipse.elk.alg.layered.test",
19-
org.eclipse.elk.alg.layered.components;x-friends:="org.eclipse.elk.alg.layered.test",
19+
org.eclipse.elk.alg.layered.components,
2020
org.eclipse.elk.alg.layered.compound;x-friends:="org.eclipse.elk.alg.layered.test",
2121
org.eclipse.elk.alg.layered.graph,
2222
org.eclipse.elk.alg.layered.graph.transform;x-friends:="org.eclipse.elk.alg.layered.test",

plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/Layered.melk

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package org.eclipse.elk.alg.layered
1111

1212
import java.util.List
1313
import org.eclipse.elk.alg.layered.LayeredLayoutProvider
14+
import org.eclipse.elk.alg.layered.components.ComponentOrderingStrategy
1415
import org.eclipse.elk.core.math.ElkPadding
1516
import org.eclipse.elk.core.options.Direction
1617
import org.eclipse.elk.core.options.EdgeRouting
@@ -948,12 +949,15 @@ group considerModelOrder {
948949
default = false
949950
targets nodes
950951
}
951-
option components: boolean {
952+
option components: ComponentOrderingStrategy {
952953
label "Consider Model Order for Components"
953954
description
954-
"If enabled orders components by their minimal node model order."
955+
"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used.
956+
INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side.
957+
FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub
958+
optimal drawings in terms of used area since the ordering should be respected."
955959
targets parents
956-
default = false
960+
default = ComponentOrderingStrategy.NONE
957961
requires org.eclipse.elk.separateConnectedComponents
958962
}
959963
option longEdgeStrategy: LongEdgeOrderingStrategy {

plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/components/ComponentGroup.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
*
7272
* <p>This class is not supposed to be public, but needs to be for JUnit tests to find it.</p>
7373
*/
74-
public final class ComponentGroup {
74+
public class ComponentGroup {
7575

7676
///////////////////////////////////////////////////////////////////////////////
7777
// Constants
@@ -88,7 +88,7 @@ public final class ComponentGroup {
8888
* port sides to a list of port side sets that must not already exist in this group for a
8989
* component to be added.</p>
9090
*/
91-
private static final Multimap<Set<PortSide>, Set<PortSide>> CONSTRAINTS = HashMultimap.create();
91+
protected static final Multimap<Set<PortSide>, Set<PortSide>> CONSTRAINTS = HashMultimap.create();
9292

9393
static {
9494
// Setup constraints
@@ -174,7 +174,7 @@ public final class ComponentGroup {
174174
/**
175175
* A map mapping external port side combinations to components in this group.
176176
*/
177-
private Multimap<Set<PortSide>, LGraph> components = ArrayListMultimap.create();
177+
protected Multimap<Set<PortSide>, LGraph> components = ArrayListMultimap.create();
178178

179179

180180
///////////////////////////////////////////////////////////////////////////////
@@ -212,8 +212,8 @@ public ComponentGroup(final LGraph component) {
212212
public boolean add(final LGraph component) {
213213
if (canAdd(component)) {
214214
components.put(
215-
component.getProperty(InternalProperties.EXT_PORT_CONNECTIONS),
216-
component);
215+
component.getProperty(InternalProperties.EXT_PORT_CONNECTIONS),
216+
component);
217217
return true;
218218
} else {
219219
return false;
@@ -227,7 +227,7 @@ public boolean add(final LGraph component) {
227227
* @return {@code true} if the group has enough space left to add the component, {@code false}
228228
* otherwise.
229229
*/
230-
private boolean canAdd(final LGraph component) {
230+
protected boolean canAdd(final LGraph component) {
231231
// Check if we have a component with incompatible external port sides
232232
Set<PortSide> candidateSides = component.getProperty(InternalProperties.EXT_PORT_CONNECTIONS);
233233
Collection<Set<PortSide>> constraints = CONSTRAINTS.get(candidateSides);
@@ -243,6 +243,15 @@ private boolean canAdd(final LGraph component) {
243243
return true;
244244
}
245245

246+
/**
247+
* Returns all port sides in this component group.
248+
*
249+
* @return all port sides in this component group.
250+
*/
251+
public Collection<Set<PortSide>> getPortSides() {
252+
return components.keys();
253+
}
254+
246255
/**
247256
* Returns all components in this component group.
248257
*

plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/components/ComponentGroupGraphPlacer.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@
4949
*
5050
* <p>The target graph must not be contained in the list of components.</p>
5151
*/
52-
final class ComponentGroupGraphPlacer extends AbstractGraphPlacer {
52+
class ComponentGroupGraphPlacer extends AbstractGraphPlacer {
5353

5454
///////////////////////////////////////////////////////////////////////////////
5555
// Variables
5656

5757
/**
5858
* List of component groups holding the different components.
5959
*/
60-
private final List<ComponentGroup> componentGroups = Lists.newArrayList();
60+
protected final List<ComponentGroup> componentGroups = Lists.newArrayList();
6161

6262

6363
///////////////////////////////////////////////////////////////////////////////
@@ -142,7 +142,7 @@ public void combine(final List<LGraph> components, final LGraph target) {
142142
*
143143
* @param component the component to be placed.
144144
*/
145-
private void addComponent(final LGraph component) {
145+
protected void addComponent(final LGraph component) {
146146
// Check if one of the existing component groups has some place left
147147
for (ComponentGroup group : componentGroups) {
148148
if (group.add(component)) {
@@ -176,7 +176,7 @@ private void addComponent(final LGraph component) {
176176
* @param spacing the amount of space to leave between two components.
177177
* @return the group's size.
178178
*/
179-
private KVector placeComponents(final ComponentGroup group, final double spacing) {
179+
protected KVector placeComponents(final ComponentGroup group, final double spacing) {
180180

181181
// Determine the spacing between two components
182182
// Place the different sector components and remember the amount of space their placement uses.
@@ -293,7 +293,7 @@ private KVector placeComponents(final ComponentGroup group, final double spacing
293293
* @return the space used by the component placement, including spacing to the right and to the
294294
* bottom of the components.
295295
*/
296-
private KVector placeComponentsHorizontally(final Collection<LGraph> components,
296+
protected KVector placeComponentsHorizontally(final Collection<LGraph> components,
297297
final double spacing) {
298298

299299
KVector size = new KVector();
@@ -321,7 +321,7 @@ private KVector placeComponentsHorizontally(final Collection<LGraph> components,
321321
* @param spacing the amount of space to leave between two components.
322322
* @return the space used by the component placement.
323323
*/
324-
private KVector placeComponentsVertically(final Collection<LGraph> components,
324+
protected KVector placeComponentsVertically(final Collection<LGraph> components,
325325
final double spacing) {
326326

327327
KVector size = new KVector();
@@ -349,7 +349,7 @@ private KVector placeComponentsVertically(final Collection<LGraph> components,
349349
* @param spacing the amount of space to leave between two components.
350350
* @return the space used by the component placement.
351351
*/
352-
private KVector placeComponentsInRows(final Collection<LGraph> components,
352+
protected KVector placeComponentsInRows(final Collection<LGraph> components,
353353
final double spacing) {
354354

355355
/* This code is basically taken from the SimpleRowGraphPlacer. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Kiel University and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*******************************************************************************/
10+
package org.eclipse.elk.alg.layered.components;
11+
12+
import java.util.List;
13+
import java.util.Set;
14+
15+
import org.eclipse.elk.alg.layered.graph.LGraph;
16+
import org.eclipse.elk.alg.layered.options.LayeredOptions;
17+
import org.eclipse.elk.core.math.KVector;
18+
import org.eclipse.elk.core.options.CoreOptions;
19+
import org.eclipse.elk.core.options.EdgeRouting;
20+
import org.eclipse.elk.core.options.PortSide;
21+
22+
/**
23+
* A graph placer that tries to place the components of a graph with taking the model order and the
24+
* connections to external ports into account. This graph placer should only be used if the constraints applying to the
25+
* external ports are either {@code FREE} or {@code FIXED_SIDES}.
26+
*/
27+
public class ComponentGroupModelOrderGraphPlacer extends ComponentGroupGraphPlacer {
28+
29+
///////////////////////////////////////////////////////////////////////////////
30+
// AbstractGraphPlacer
31+
32+
@Override
33+
public void combine(final List<LGraph> components, final LGraph target) {
34+
componentGroups.clear();
35+
assert !components.contains(target);
36+
target.getLayerlessNodes().clear();
37+
38+
// Check if there are any components to be placed
39+
if (components.isEmpty()) {
40+
target.getSize().x = 0;
41+
target.getSize().y = 0;
42+
return;
43+
}
44+
45+
// Set the graph properties
46+
LGraph firstComponent = components.get(0);
47+
target.copyProperties(firstComponent);
48+
49+
// Construct component groups
50+
for (LGraph component : components) {
51+
addComponent(component);
52+
}
53+
54+
// Place components in each group
55+
KVector spaceBlockedBySouthEdges = new KVector();
56+
KVector spaceBlockedByComponents = new KVector();
57+
KVector offset = new KVector();
58+
KVector maxSize = new KVector();
59+
double componentSpacing = firstComponent.getProperty(LayeredOptions.SPACING_COMPONENT_COMPONENT);
60+
61+
// In horizontal mode the next component is placed below the last one.
62+
// If it has a connection to NORTH, it has to be placed at an x-coordinate to not hit the other components.
63+
// Otherwise will be placed as far WEST as possible without potentially hitting SOUTH edges.
64+
for (ComponentGroup group : componentGroups) {
65+
// Place the components
66+
if (target.getProperty(CoreOptions.DIRECTION).isHorizontal()) {
67+
offset.x = spaceBlockedBySouthEdges.x;
68+
for (Set<PortSide> side : group.getPortSides()) {
69+
if (side.contains(PortSide.NORTH)) {
70+
offset.x = spaceBlockedByComponents.x;
71+
break;
72+
}
73+
}
74+
} else if (target.getProperty(CoreOptions.DIRECTION).isVertical()) {
75+
offset.y = spaceBlockedBySouthEdges.y;
76+
for (Set<PortSide> side : group.getPortSides()) {
77+
if (side.contains(PortSide.WEST)) {
78+
offset.y = spaceBlockedByComponents.y;
79+
break;
80+
}
81+
}
82+
}
83+
KVector groupSize = this.placeComponents((ModelOrderComponentGroup) group, componentSpacing);
84+
offsetGraphs(group.getComponents(), offset.x, offset.y);
85+
86+
if (target.getProperty(CoreOptions.DIRECTION).isHorizontal()) {
87+
spaceBlockedByComponents.x = offset.x + groupSize.x;
88+
maxSize.x = Math.max(maxSize.x, spaceBlockedByComponents.x);
89+
for (Set<PortSide> side : group.getPortSides()) {
90+
if (side.contains(PortSide.SOUTH)) {
91+
spaceBlockedBySouthEdges.x = offset.x + groupSize.x;
92+
break;
93+
}
94+
}
95+
spaceBlockedByComponents.y = offset.y + groupSize.y;
96+
offset.y = spaceBlockedByComponents.y;
97+
maxSize.y = Math.max(maxSize.y, offset.y);
98+
} else if (target.getProperty(CoreOptions.DIRECTION).isVertical()) {
99+
spaceBlockedByComponents.y = offset.y + groupSize.y;
100+
maxSize.y = Math.max(maxSize.y, spaceBlockedByComponents.y);
101+
for (Set<PortSide> side : group.getPortSides()) {
102+
if (side.contains(PortSide.EAST)) {
103+
spaceBlockedBySouthEdges.y = offset.y + groupSize.y;
104+
break;
105+
}
106+
}
107+
spaceBlockedByComponents.x = offset.x + groupSize.x;
108+
offset.x = spaceBlockedByComponents.x;
109+
maxSize.x = Math.max(maxSize.x, offset.x);
110+
}
111+
}
112+
113+
// Set the graph's new size (the component group sizes include additional spacing
114+
// on the right and bottom sides which we need to subtract at this point)
115+
target.getSize().x = maxSize.x - componentSpacing;
116+
target.getSize().y = maxSize.y - componentSpacing;
117+
118+
// if compaction is desired, do so!cing;
119+
if (firstComponent.getProperty(LayeredOptions.COMPACTION_CONNECTED_COMPONENTS)
120+
// the compaction only supports orthogonally routed edges
121+
&& firstComponent.getProperty(LayeredOptions.EDGE_ROUTING) == EdgeRouting.ORTHOGONAL) {
122+
123+
// apply graph offsets (which we reset later on)
124+
// since the compaction works in a common coordinate system
125+
for (LGraph h : components) {
126+
offsetGraph(h, h.getOffset().x, h.getOffset().y);
127+
}
128+
129+
ComponentsCompactor compactor = new ComponentsCompactor();
130+
compactor.compact(components, target.getSize(), componentSpacing);
131+
132+
// the compaction algorithm places components absolutely,
133+
// therefore we have to use the final drawing's offset
134+
for (LGraph h : components) {
135+
h.getOffset().reset().add(compactor.getOffset());
136+
}
137+
138+
// set the new (compacted) graph size
139+
target.getSize().reset().add(compactor.getGraphSize());
140+
}
141+
142+
// finally move the components to the combined graph
143+
for (ComponentGroup group : componentGroups) {
144+
moveGraphs(target, group.getComponents(), 0, 0);
145+
}
146+
}
147+
148+
149+
///////////////////////////////////////////////////////////////////////////////
150+
// Component Group Building
151+
152+
/**
153+
* Adds the given component to the first component group that has place for it.
154+
*
155+
* @param component the component to be placed.
156+
*/
157+
protected void addComponent(final LGraph component) {
158+
// Check if one of the existing component groups has some place left
159+
if (this.componentGroups.size() > 0) {
160+
ModelOrderComponentGroup group =
161+
(ModelOrderComponentGroup) this.componentGroups.get(componentGroups.size() - 1);
162+
if (group.add(component)) {
163+
return;
164+
}
165+
}
166+
167+
// Create a new component group for the component
168+
componentGroups.add(new ModelOrderComponentGroup(component));
169+
}
170+
171+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Kiel University and others.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*******************************************************************************/
10+
package org.eclipse.elk.alg.layered.components;
11+
12+
/**
13+
* Strategy to order components by model order.
14+
*/
15+
public enum ComponentOrderingStrategy {
16+
/**
17+
* Components are ordered by priority or size.
18+
*/
19+
NONE,
20+
/**
21+
* Components are ordered only inside their port groups by their minimal node model order
22+
* to prevent ONO alignment cases.
23+
* This usually results in a smaller drawing.
24+
*/
25+
INSIDE_PORT_SIDE_GROUPS,
26+
/**
27+
* Components are ordered by their minimal node model order. This may still have some bugs and may create worse
28+
* drawings in terms of size compared to INSIDE_PORT_SIDE_GROUPS.
29+
*/
30+
FORCE_MODEL_ORDER;
31+
32+
}

plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/components/ComponentsProcessor.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public final class ComponentsProcessor {
6565

6666
/** Cached instance of a {@link ComponentGroupGraphPlacer}. */
6767
private final ComponentGroupGraphPlacer componentGroupGraphPlacer = new ComponentGroupGraphPlacer();
68+
/** Cached instance of a {@link ComponentGroupGraphPlacer}. */
69+
private final ComponentGroupModelOrderGraphPlacer componentGroupModelOrderGraphPlacer =
70+
new ComponentGroupModelOrderGraphPlacer();
6871
/** Cached instance of a {@link SimpleRowGraphPlacer}. */
6972
private final SimpleRowGraphPlacer simpleRowGraphPlacer = new SimpleRowGraphPlacer();
7073
/** Graph placer to be used to combine the different components back into a single graph. */
@@ -135,14 +138,19 @@ public List<LGraph> split(final LGraph graph) {
135138
if (extPorts) {
136139
// With external port connections, we want to use the more complex components
137140
// placement algorithm
138-
graphPlacer = componentGroupGraphPlacer;
141+
if (graph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_COMPONENTS)
142+
== ComponentOrderingStrategy.FORCE_MODEL_ORDER) {
143+
graphPlacer = componentGroupModelOrderGraphPlacer;
144+
} else {
145+
graphPlacer = componentGroupGraphPlacer;
146+
}
139147
}
140148
} else {
141149
result = Arrays.asList(graph);
142150
}
143151
// If model order should be preserved the connected components should be ordered by their elements.
144152
// The component with the node with the smallest order should be first.
145-
if (graph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_COMPONENTS)) {
153+
if (graph.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_COMPONENTS) != ComponentOrderingStrategy.NONE) {
146154
Collections.sort(result, (g1, g2) -> {
147155
int g1Order = LGraphUtil.getMinimalModelOrder(g1);
148156
int g2Order = LGraphUtil.getMinimalModelOrder(g2);

0 commit comments

Comments
 (0)