1
14 package demo.module;
15
16 import java.awt.Color;
17
18 import y.base.Edge;
19 import y.base.EdgeCursor;
20 import y.base.EdgeMap;
21 import y.base.NodeCursor;
22 import y.base.Node;
23 import y.base.DataProvider;
24 import y.layout.LabelLayoutConstants;
25 import y.layout.LabelLayoutTranslator;
26 import y.layout.LabelRanking;
27 import y.layout.OrientationLayouter;
28 import y.layout.LayoutGraph;
29 import y.layout.PortConstraintKeys;
30 import y.layout.labeling.GreedyMISLabeling;
31 import y.layout.orthogonal.DirectedOrthogonalLayouter;
32 import y.option.OptionHandler;
33 import y.option.ConstraintManager;
34 import y.option.OptionGroup;
35 import y.option.OptionItem;
36 import y.option.EnumOptionItem;
37 import y.util.DataProviderAdapter;
38 import y.util.pq.BHeapIntNodePQ;
39 import y.view.EdgeLabel;
40 import y.view.EdgeRealizer;
41 import y.view.Graph2D;
42 import y.view.Arrow;
43 import y.view.LineType;
44
45 import y.option.ArrowCellRenderer;
46 import y.option.StrokeCellRenderer;
47 import y.module.LayoutModule;
48
49
53 public class DirectedOrthogonalLayoutModule extends LayoutModule
54 {
55 private static final String DIRECTED_ORTHOGONAL_LAYOUTER = "DIRECTED_ORTHOGONAL_LAYOUTER";
56
57 private static final String LAYOUT = "LAYOUT";
58
59 private static final String USE_EXISTING_DRAWING_AS_SKETCH = "USE_EXISTING_DRAWING_AS_SKETCH";
60 private static final String GRID = "GRID";
61
62 private static final String RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
63 private static final String BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
64 private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
65 private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
66
67 private static final String ORIENTATION = "ORIENTATION";
68
69 private static final String[] orientEnum = {
70 TOP_TO_BOTTOM,
71 LEFT_TO_RIGHT,
72 BOTTOM_TO_TOP,
73 RIGHT_TO_LEFT
74 };
75
76 private static final String AUTO_GROUP_DIRECTED_EDGES = "AUTO_GROUP_DIRECTED_EDGES";
77
78 private static final String EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
79 private static final String EDGE_LABELING = "EDGE_LABELING";
80 private static final String LABELING = "LABELING";
81 private static final String GENERIC = "GENERIC";
82 private static final String NONE = "NONE";
83 private static final String INTEGRATED = "INTEGRATED";
84 private static final String FREE = "FREE";
85 private static final String SIDE_SLIDER = "SIDE_SLIDER";
86 private static final String CENTER_SLIDER = "CENTER_SLIDER";
87 private static final String AS_IS = "AS_IS";
88 private static final String BEST = "BEST";
89
90 private static final String IDENTIFY_DIRECTED_EDGES = "IDENTIFY_DIRECTED_EDGES";
91 private static final String USE_AS_CRITERIA = "USE_AS_CRITERIA";
92 private static final String LINE_COLOR = "LINE_COLOR";
93 private static final String TARGET_ARROW = "TARGET_ARROW";
94 private static final String LINE_TYPE = "LINE_TYPE";
95
96 private static final String[] edgeLabeling = {
97 NONE,
98 INTEGRATED,
99 GENERIC
100 };
101
102 private static final String[] edgeLabelModel = {
103 BEST,
104 AS_IS,
105 CENTER_SLIDER,
106 SIDE_SLIDER,
107 FREE,
108 };
109
110 public DirectedOrthogonalLayoutModule()
111 {
112 super(DIRECTED_ORTHOGONAL_LAYOUTER, "yFiles Layout Team",
113 "Directed Orthogonal Layouter");
114 setPortIntersectionCalculatorEnabled(true);
115 }
116
117 public OptionHandler createOptionHandler()
118 {
119 OptionHandler op = new OptionHandler(getModuleName());
120 op.useSection(LAYOUT);
121 op.addInt(GRID, 25);
122 op.addEnum(ORIENTATION, orientEnum, 0);
123
124 op.addBool(USE_EXISTING_DRAWING_AS_SKETCH, false);
125
126 OptionGroup og = new OptionGroup();
127 og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, IDENTIFY_DIRECTED_EDGES);
128 OptionItem oi = op.addEnum(USE_AS_CRITERIA, new String[]{LINE_COLOR, TARGET_ARROW, LINE_TYPE}, 0);
129 og.addItem(oi);
130 oi = op.addColor(LINE_COLOR, Color.red, true);
131 og.addItem(oi);
132 EnumOptionItem eoi;
133 eoi = new EnumOptionItem(TARGET_ARROW,
134 Arrow.availableArrows().toArray(),
135 Arrow.STANDARD);
136 eoi.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER,
137 new ArrowCellRenderer());
138 eoi.setUsingIntegers(true);
139 op.addItem(eoi);
140 og.addItem(eoi);
141 eoi = new EnumOptionItem(LINE_TYPE,
142 LineType.availableLineTypes().toArray(),
143 LineType.LINE_2);
144 eoi.setAttribute(EnumOptionItem.ATTRIBUTE_RENDERER,
145 new StrokeCellRenderer());
146 eoi.setUsingIntegers(true);
147 op.addItem(eoi);
148 og.addItem(eoi);
149
150 ConstraintManager cm = new ConstraintManager(op);
151
152 cm.setEnabledOnValueEquals(USE_AS_CRITERIA, LINE_COLOR, LINE_COLOR);
153 cm.setEnabledOnValueEquals(USE_AS_CRITERIA, TARGET_ARROW, TARGET_ARROW);
154 cm.setEnabledOnValueEquals(USE_AS_CRITERIA, LINE_TYPE, LINE_TYPE);
155
156 op.addBool(AUTO_GROUP_DIRECTED_EDGES, true);
157
158 cm.setEnabledOnValueEquals(USE_EXISTING_DRAWING_AS_SKETCH, Boolean.FALSE,
159 AUTO_GROUP_DIRECTED_EDGES);
160
161 op.useSection(LABELING);
162 og = new OptionGroup();
163 cm.setEnabledOnValueEquals(op.addEnum(EDGE_LABELING, edgeLabeling, 0), NONE,
164 og.addItem(op.addEnum(EDGE_LABEL_MODEL, edgeLabelModel, 0)),
165 true);
166
167
168 return op;
169 }
170
171 public void mainrun()
172 {
173 final OptionHandler op = getOptionHandler();
174
175 final Graph2D graph = getGraph2D();
176
177 DataProvider upwardDP = null;
178 if (graph.getDataProvider(DirectedOrthogonalLayouter.DIRECTED_EDGE_DPKEY) == null) {
179 upwardDP = new DataProviderAdapter() {
181 public boolean getBool(Object o) {
182 EdgeRealizer er = graph.getRealizer((Edge) o);
183 if (op.get(USE_AS_CRITERIA).equals(LINE_COLOR)) {
184 Color c1 = (Color) op.get(LINE_COLOR);
185 Color c2 = er.getLineColor();
186 return c1 != null && c1.equals(c2);
187 }
188 else if(op.get(USE_AS_CRITERIA).equals(TARGET_ARROW)) {
189 Arrow a1 = (Arrow) op.get(TARGET_ARROW);
190 Arrow a2 = er.getTargetArrow();
191 return a1 != null && a1.equals(a2);
192 }
193 else if (op.get(USE_AS_CRITERIA).equals(LINE_TYPE)) {
194 LineType l1 = (LineType) op.get(LINE_TYPE);
195 LineType l2 = er.getLineType();
196 return l1 != null && l1.equals(l2);
197 }
198 return false;
199 }
200 };
201 graph.addDataProvider(DirectedOrthogonalLayouter.DIRECTED_EDGE_DPKEY, upwardDP);
202 }
203
204 DataProvider sgDPOrig = null, tgDPOrig = null;
205 EdgeMap sgMap = null, tgMap = null;
206 if (op.getBool(AUTO_GROUP_DIRECTED_EDGES)) {
207 sgDPOrig = graph.getDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY);
208 tgDPOrig = graph.getDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY);
209 sgMap = graph.createEdgeMap();
210 tgMap = graph.createEdgeMap();
211 graph.addDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, sgMap);
212 graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, tgMap);
213 autoGroupEdges(graph, sgMap, tgMap, upwardDP);
214 }
215
216 DirectedOrthogonalLayouter orthogonal = new DirectedOrthogonalLayouter();
217
218 orthogonal.setGrid(op.getInt(GRID));
219 orthogonal.setUseSketchDrawing(op.getBool(USE_EXISTING_DRAWING_AS_SKETCH));
220
221 final OrientationLayouter ol = (OrientationLayouter) orthogonal.getOrientationLayouter();
222 if (op.get(ORIENTATION).equals(TOP_TO_BOTTOM))
223 ol.setOrientation(OrientationLayouter.TOP_TO_BOTTOM);
224 else if (op.get(ORIENTATION).equals(LEFT_TO_RIGHT))
225 ol.setOrientation(OrientationLayouter.LEFT_TO_RIGHT);
226 else if (op.get(ORIENTATION).equals(BOTTOM_TO_TOP))
227 ol.setOrientation(OrientationLayouter.BOTTOM_TO_TOP);
228 else if (op.get(ORIENTATION).equals(RIGHT_TO_LEFT))
229 ol.setOrientation(OrientationLayouter.RIGHT_TO_LEFT);
230
231
235 String el = op.getString(EDGE_LABELING);
236 if (!el.equals(NONE)) {
237 setupEdgeLabelModel(el, op.getString(EDGE_LABEL_MODEL));
238 if (el.equals(GENERIC)) {
239 GreedyMISLabeling la = new GreedyMISLabeling();
240 la.setPlaceNodeLabels(false);
241 la.setPlaceEdgeLabels(true);
242 la.setProfitModel(new LabelRanking());
243 orthogonal.setLabelLayouter(la);
244 orthogonal.setLabelLayouterEnabled(true);
245 } else if (el.equals(INTEGRATED)) {
246 orthogonal.setLabelLayouter(new LabelLayoutTranslator());
247 orthogonal.setLabelLayouterEnabled(true);
248 }
249 } else {
250 orthogonal.setLabelLayouterEnabled(false);
251 }
252
253 try {
254 launchLayouter(orthogonal, true);
255 }
256 finally {
257 if(op.getBool(AUTO_GROUP_DIRECTED_EDGES))
258 {
259 graph.removeDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY);
260 graph.removeDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY);
261 if (sgDPOrig != null)
262 graph.addDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, sgDPOrig);
263 if (tgDPOrig != null)
264 graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, tgDPOrig);
265 if (sgMap != null)
266 graph.disposeEdgeMap(sgMap);
267 if (tgMap != null)
268 graph.disposeEdgeMap(tgMap);
269 }
270 if (upwardDP != null) {
271 graph.removeDataProvider(DirectedOrthogonalLayouter.DIRECTED_EDGE_DPKEY);
272 }
273 }
274 }
275
276 void setupEdgeLabelModel(String edgeLabeling, String edgeLabelModel) {
277 if (edgeLabeling.equals(NONE) || edgeLabelModel.equals(AS_IS)) {
278 return; }
280
281 if (edgeLabelModel.equals(BEST)) {
282 if (edgeLabeling.equals(GENERIC))
283 edgeLabelModel = SIDE_SLIDER;
284 else if (edgeLabeling.equals(INTEGRATED))
285 edgeLabelModel = FREE;
286 }
287
288 byte model = EdgeLabel.SIDE_SLIDER;
289 int preferredSide = LabelLayoutConstants.PLACE_RIGHT_OF_EDGE;
290 if (edgeLabelModel.equals(CENTER_SLIDER)) {
291 model = EdgeLabel.CENTER_SLIDER;
292 preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
293 } else if (edgeLabelModel.equals(FREE)) {
294 model = EdgeLabel.FREE;
295 preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
296 }
297
298 Graph2D graph = getGraph2D();
299 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
300 Edge e = ec.edge();
301 EdgeRealizer er = graph.getRealizer(e);
302 for (int i = 0; i < er.labelCount(); i++) {
303 EdgeLabel el = er.getLabel(i);
304 el.setModel(model);
305 int prefAlongEdge = el.getPreferredPlacement() & LabelLayoutConstants.PLACEMENT_ALONG_EDGE_MASK;
306 el.setPreferredPlacement((byte) (preferredSide | prefAlongEdge));
307 }
308 }
309 }
310
311
318 void autoGroupEdges(LayoutGraph graph, EdgeMap sgMap, EdgeMap tgMap, DataProvider positiveDP) {
319 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
320 sgMap.set(ec.edge(), null);
321 tgMap.set(ec.edge(), null);
322 }
323
324 BHeapIntNodePQ sourceGroupPQ = new BHeapIntNodePQ(graph);
325 BHeapIntNodePQ targetGroupPQ = new BHeapIntNodePQ(graph);
326 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
327 Node n = nc.node();
328 int outDegree = 0;
329 for (EdgeCursor ec = n.outEdges(); ec.ok(); ec.next()) {
330 if (positiveDP.getBool(ec.edge()) && !ec.edge().isSelfLoop())
331 outDegree++;
332 }
333 sourceGroupPQ.add(n, -outDegree);
334 int inDegree = 0;
335 for (EdgeCursor ec = n.inEdges(); ec.ok(); ec.next()) {
336 if (positiveDP.getBool(ec.edge()) && !ec.edge().isSelfLoop())
337 inDegree++;
338 }
339 targetGroupPQ.add(n, -inDegree);
340 }
341
342 while (!sourceGroupPQ.isEmpty() && !targetGroupPQ.isEmpty()) {
343 int bestIn = 0, bestOut = 0;
344 if (!sourceGroupPQ.isEmpty()) {
345 bestOut = -sourceGroupPQ.getMinPriority();
346 }
347 if (!targetGroupPQ.isEmpty()) {
348 bestIn = -targetGroupPQ.getMinPriority();
349 }
350 if (bestIn > bestOut) {
351 Node n = targetGroupPQ.removeMin();
352 for (EdgeCursor ec = n.inEdges(); ec.ok(); ec.next()) {
353 Edge e = ec.edge();
354 if (sgMap.get(e) == null && positiveDP.getBool(e) && !e.isSelfLoop()) {
355 tgMap.set(e, n);
356 sourceGroupPQ.changePriority(e.source(), sourceGroupPQ.getPriority(e.source()) + 1);
357 }
358 }
359 } else {
360 Node n = sourceGroupPQ.removeMin();
361 for (EdgeCursor ec = n.outEdges(); ec.ok(); ec.next()) {
362 Edge e = ec.edge();
363 if (tgMap.get(e) == null && positiveDP.getBool(e) && !e.isSelfLoop()) {
364 sgMap.set(e, n);
365 targetGroupPQ.increasePriority(e.target(), targetGroupPQ.getPriority(e.target()) + 1);
366 }
367 }
368 }
369 }
370 }
371 }
372