/
ComponentGroupModelOrderGraphPlacer.java
172 lines (150 loc) · 7.4 KB
/
ComponentGroupModelOrderGraphPlacer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*******************************************************************************
* Copyright (c) 2022 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.layered.components;
import java.util.List;
import java.util.Set;
import org.eclipse.elk.alg.layered.graph.LGraph;
import org.eclipse.elk.alg.layered.options.LayeredOptions;
import org.eclipse.elk.core.math.KVector;
import org.eclipse.elk.core.options.CoreOptions;
import org.eclipse.elk.core.options.EdgeRouting;
import org.eclipse.elk.core.options.PortSide;
/**
* A graph placer that tries to place the components of a graph with taking the model order and the
* connections to external ports into account. This graph placer should only be used if the constraints applying to the
* external ports are either {@code FREE} or {@code FIXED_SIDES}.
*/
public class ComponentGroupModelOrderGraphPlacer extends ComponentGroupGraphPlacer {
///////////////////////////////////////////////////////////////////////////////
// AbstractGraphPlacer
@Override
public void combine(final List<LGraph> components, final LGraph target) {
componentGroups.clear();
assert !components.contains(target);
target.getLayerlessNodes().clear();
// Check if there are any components to be placed
if (components.isEmpty()) {
target.getSize().x = 0;
target.getSize().y = 0;
return;
}
// Set the graph properties
LGraph firstComponent = components.get(0);
target.copyProperties(firstComponent);
// Construct component groups
for (LGraph component : components) {
addComponent(component);
}
// Place components in each group
KVector offset = new KVector();
KVector maxSize = new KVector();
KVector maxGroupSize = new KVector();
double componentSpacing = firstComponent.getProperty(LayeredOptions.SPACING_COMPONENT_COMPONENT);
// The current free y has to be calculated based on that constraints the current graph has.
// The offset has to be readded if a NORTH edges is present.
// All previous component that might have different sizes have to be taken into account here.
for (ComponentGroup group : componentGroups) {
// Place the components
for (Set<PortSide> side : group.getPortSides()) {
if (target.getProperty(CoreOptions.DIRECTION).isHorizontal()) {
if (side.contains(PortSide.NORTH)) {
offset.x = maxGroupSize.x;
break;
}
} else if (target.getProperty(CoreOptions.DIRECTION).isVertical()) {
if (side.contains(PortSide.EAST)) {
offset.y = maxGroupSize.y;
break;
}
}
}
KVector groupSize = this.placeComponents((ModelOrderComponentGroup) group, componentSpacing);
offsetGraphs(group.getComponents(), offset.x, offset.y);
maxSize.x = Math.max(maxSize.x, groupSize.x + offset.x);
maxSize.y = Math.max(maxSize.y, groupSize.y + offset.y);
// Compute the new offset. The previous component might not be in vertical or horizontal conflict.
// Normally this cannot occur but here graphs with the same port sides may be in different groups.
maxGroupSize = new KVector(Math.max(maxGroupSize.x, groupSize.x + offset.x),
Math.max(maxGroupSize.y, groupSize.y + offset.y));
for (Set<PortSide> side : group.getPortSides()) {
if (target.getProperty(CoreOptions.DIRECTION).isHorizontal()) {
if (side.contains(PortSide.SOUTH)) {
offset.x += groupSize.x;
break;
}
} else if (target.getProperty(CoreOptions.DIRECTION).isVertical()) {
if (side.contains(PortSide.WEST)) {
offset.y += groupSize.y;
break;
}
}
}
for (Set<PortSide> side : group.getPortSides()) {
if (target.getProperty(CoreOptions.DIRECTION).isHorizontal()) {
if (side.contains(PortSide.WEST)) {
offset.y += groupSize.y;
break;
}
} else if (target.getProperty(CoreOptions.DIRECTION).isVertical()) {
if (side.contains(PortSide.NORTH)) {
offset.x += groupSize.x;
break;
}
}
}
}
// Set the graph's new size (the component group sizes include additional spacing
// on the right and bottom sides which we need to subtract at this point)
target.getSize().x = maxSize.x - componentSpacing;
target.getSize().y = maxSize.y - componentSpacing;
// if compaction is desired, do so!cing;
if (firstComponent.getProperty(LayeredOptions.COMPACTION_CONNECTED_COMPONENTS)
// the compaction only supports orthogonally routed edges
&& firstComponent.getProperty(LayeredOptions.EDGE_ROUTING) == EdgeRouting.ORTHOGONAL) {
// apply graph offsets (which we reset later on)
// since the compaction works in a common coordinate system
for (LGraph h : components) {
offsetGraph(h, h.getOffset().x, h.getOffset().y);
}
ComponentsCompactor compactor = new ComponentsCompactor();
compactor.compact(components, target.getSize(), componentSpacing);
// the compaction algorithm places components absolutely,
// therefore we have to use the final drawing's offset
for (LGraph h : components) {
h.getOffset().reset().add(compactor.getOffset());
}
// set the new (compacted) graph size
target.getSize().reset().add(compactor.getGraphSize());
}
// finally move the components to the combined graph
for (ComponentGroup group : componentGroups) {
moveGraphs(target, group.getComponents(), 0, 0);
}
}
///////////////////////////////////////////////////////////////////////////////
// Component Group Building
/**
* Adds the given component to the first component group that has place for it.
*
* @param component the component to be placed.
*/
protected void addComponent(final LGraph component) {
// Check if one of the existing component groups has some place left
if (this.componentGroups.size() > 0) {
ModelOrderComponentGroup group =
(ModelOrderComponentGroup) this.componentGroups.get(componentGroups.size() - 1);
if (group.add(component)) {
return;
}
}
// Create a new component group for the component
componentGroups.add(new ModelOrderComponentGroup(component));
}
}