1
14 package demo.layout.module;
15
16 import y.module.LayoutModule;
17 import y.module.YModule;
18
19 import y.layout.organic.SmartOrganicLayouter;
20 import y.layout.organic.OutputRestriction;
21 import y.layout.ComponentLayouter;
22 import y.option.ConstraintManager;
23 import y.option.DefaultEditorFactory;
24 import y.option.DoubleOptionItem;
25 import y.option.IntOptionItem;
26 import y.option.OptionHandler;
27 import y.option.OptionGroup;
28 import y.option.OptionItem;
29 import y.view.Graph2D;
30 import y.view.Selections;
31 import y.view.hierarchy.HierarchyManager;
32 import y.base.NodeCursor;
33 import y.base.Node;
34 import y.base.NodeMap;
35 import y.util.Maps;
36
37 import java.awt.Rectangle;
38
39
47 public class SmartOrganicLayoutModule extends LayoutModule
48 {
49 private static final String ACTIVATE_DETERMINISTIC_MODE = "ACTIVATE_DETERMINISTIC_MODE";
50 private static final String VISUAL = "VISUAL";
51 private static final String ALGORITHM = "ALGORITHM";
52 private static final String COMPACTNESS = "COMPACTNESS";
53 private static final String USE_AUTOMATIC_GROUP_NODE_COMPACTION = "USE_AUTOMATIC_GROUP_NODE_COMPACTION";
54 private static final String GROUP_COMPACTNESS = "GROUP_COMPACTNESS";
55 private static final String MAXIMAL_DURATION = "MAXIMAL_DURATION";
56 private static final String OBEY_NODE_SIZES = "OBEY_NODE_SIZES";
57 private static final String CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
58 private static final String ALLOW_NODE_OVERLAPS = "ALLOW_NODE_OVERLAPS";
59 private static final String MINIMAL_NODE_DISTANCE = "MINIMAL_NODE_DISTANCE";
60 private static final String SCOPE = "SCOPE";
61 private static final String PREFERRED_EDGE_LENGTH = "PREFERRED_EDGE_LENGTH";
62 private static final String SMARTORGANIC = "SMARTORGANIC";
63 private static final String SCOPE_SUBSET = "SUBSET";
64 private static final String SCOPE_MAINLY_SUBSET = "MAINLY_SUBSET";
65 private static final String SCOPE_ALL = "ALL";
66 private static final String QUALITY_TIME_RATIO = "QUALITY_TIME_RATIO";
67 private static final String RESTRICT_OUTPUT = "RESTRICT_OUTPUT";
68 private static final String NONE = "NONE";
69 private static final String OUTPUT_CAGE = "OUTPUT_CAGE";
70 private static final String OUTPUT_CIRCULAR_CAGE = "OUTPUT_CIRCULAR_CAGE";
71 private static final String OUTPUT_ELLIPTICAL_CAGE = "OUTPUT_ELLIPTICAL_CAGE";
72 private static final String OUTPUT_AR = "OUTPUT_AR";
73 private static final String CAGE_X = "CAGE_X";
74 private static final String CAGE_Y = "CAGE_Y";
75 private static final String CAGE_WIDTH = "CAGE_WIDTH";
76 private static final String CAGE_HEIGHT = "CAGE_HEIGHT";
77 private static final String ELLIPTICAL_CAGE_X = "ELLIPTICAL_CAGE_X";
78 private static final String ELLIPTICAL_CAGE_Y = "ELLIPTICAL_CAGE_Y";
79 private static final String ELLIPTICAL_CAGE_WIDTH = "ELLIPTICAL_CAGE_WIDTH";
80 private static final String ELLIPTICAL_CAGE_HEIGHT = "ELLIPTICAL_CAGE_HEIGHT";
81 private static final String CAGE_CENTER_X = "CAGE_CENTER_X";
82 private static final String CAGE_CENTER_Y = "CAGE_CENTER_Y";
83 private static final String CAGE_RADIUS = "CAGE_RADIUS";
84 private static final String CAGE_RATIO = "CAGE_RATIO";
85 private static final String AR_CAGE_USE_VIEW = "AR_CAGE_USE_VIEW";
86 private static final String RECT_CAGE_USE_VIEW = "RECT_CAGE_USE_VIEW";
87 private static final String CIRC_CAGE_USE_VIEW = "CIRC_CAGE_USE_VIEW";
88 private static final String ELL_CAGE_USE_VIEW = "ELL_CAGE_USE_VIEW";
89 private static final String RESTRICTIONS = "RESTRICTIONS";
90 private static final String AVOID_NODE_EDGE_OVERLAPS = "AVOID_NODE_EDGE_OVERLAPS";
91 private static final String GROUPING = "GROUPING";
92 private static final String GROUP_LAYOUT_POLICY = "GROUP_LAYOUT_POLICY";
93 private static final String IGNORE_GROUPS = "IGNORE_GROUPS";
94 private static final String LAYOUT_GROUPS = "LAYOUT_GROUPS";
95 private static final String FIX_GROUP_BOUNDS = "FIX_GROUP_BOUNDS";
96 private static final String FIX_GROUP_CONTENTS = "FIX_GROUP_CONTENTS";
97 private static final String USE_AUTO_CLUSTERING = "USE_AUTO_CLUSTERING";
98 private static final String AUTO_CLUSTERING_QUALITY = "AUTO_CLUSTERING_QUALITY";
99
100 private final static String[] SCOPES =
102 {
103 SCOPE_ALL,
104 SCOPE_MAINLY_SUBSET,
105 SCOPE_SUBSET,
106 };
107
108 private final String[] GROUPING_POLICIES = new String[]{
110 LAYOUT_GROUPS,
111 FIX_GROUP_CONTENTS,
112 FIX_GROUP_BOUNDS,
113 IGNORE_GROUPS,
114 };
115
116 private final static String[] OUTPUT_RESTRICTIONS =
117 {
118 NONE,
119 OUTPUT_CAGE,
120 OUTPUT_CIRCULAR_CAGE,
121 OUTPUT_AR,
122 OUTPUT_ELLIPTICAL_CAGE,
123 };
124
125 private SmartOrganicLayouter organic;
126
127 public SmartOrganicLayoutModule()
128 {
129 super (SMARTORGANIC,
130 "yWorks Graph Layout Team",
131 "Wrapper for SmartOrganicLayouter");
132 setPortIntersectionCalculatorEnabled(true);
133 }
134
135
139 protected OptionHandler createOptionHandler()
140 {
141 createOrganic();
142
143 OptionHandler op = new OptionHandler(getModuleName());
144 ConstraintManager cm = new ConstraintManager(op);
145
146 op.useSection(VISUAL);
147 op.addEnum(SCOPE,SCOPES,
148 organic.getScope());
149 op.addInt(PREFERRED_EDGE_LENGTH, (int)organic.getPreferredEdgeLength(), 5, 500);
150 op.addBool(CONSIDER_NODE_LABELS,organic.isConsiderNodeLabelsEnabled());
151 op.addBool(ALLOW_NODE_OVERLAPS,organic.isNodeOverlapsAllowed());
152 ConstraintManager.Condition condition = cm.createConditionValueEquals(ALLOW_NODE_OVERLAPS, Boolean.FALSE).or(
153 cm.createConditionValueEquals(CONSIDER_NODE_LABELS, Boolean.TRUE));
154 cm.setEnabledOnCondition(condition, op.addDouble(MINIMAL_NODE_DISTANCE,organic.getMinimalNodeDistance(),0,100,0));
155 op.addBool(AVOID_NODE_EDGE_OVERLAPS, false);
156 cm.setEnabledOnValueEquals(CONSIDER_NODE_LABELS, Boolean.FALSE, ALLOW_NODE_OVERLAPS);
157
158 op.addDouble(COMPACTNESS,organic.getCompactness(),0,1);
159
160 op.addBool(USE_AUTO_CLUSTERING, organic.isAutoClusteringEnabled());
161 op.addDouble(AUTO_CLUSTERING_QUALITY, organic.getAutoClusteringQuality(), 0, 1);
162 cm.setEnabledOnValueEquals(USE_AUTO_CLUSTERING, Boolean.TRUE, AUTO_CLUSTERING_QUALITY);
163
164 op.useSection(RESTRICTIONS);
165
166 final Object ctrId = new Object();
167 op.addEnum(RESTRICT_OUTPUT, OUTPUT_RESTRICTIONS, 0).setAttribute( DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID, ctrId );
168
169 OptionGroup og;
170 og = new OptionGroup();
171 cm.setEnabledOnValueEquals( RESTRICT_OUTPUT, OUTPUT_CAGE, og );
172 og.setAttribute( OptionGroup.ATTRIBUTE_TITLE, OUTPUT_CAGE );
173 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID, ctrId );
174 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CARD_ID, OUTPUT_CAGE );
175 og.addItem( op.addBool(RECT_CAGE_USE_VIEW, true));
176
177 condition = cm.createConditionValueEquals(RESTRICT_OUTPUT, OUTPUT_CAGE).and(
178 cm.createConditionValueEquals(RECT_CAGE_USE_VIEW, Boolean.FALSE));
179
180 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_X, 0.0d) ));
181 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_Y, 0.0d) ));
182 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_WIDTH, 1000.0d) ));
183 op.getItem(RESTRICTIONS, CAGE_WIDTH)
184 .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(Double.MIN_VALUE));
185 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_HEIGHT, 1000.0d) ));
186 op.getItem(RESTRICTIONS, CAGE_HEIGHT)
187 .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(Double.MIN_VALUE));
188
189 og = new OptionGroup();
190 cm.setEnabledOnValueEquals( RESTRICT_OUTPUT, OUTPUT_CIRCULAR_CAGE, og );
191 og.setAttribute( OptionGroup.ATTRIBUTE_TITLE, OUTPUT_CIRCULAR_CAGE );
192 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID, ctrId );
193 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CARD_ID, OUTPUT_CIRCULAR_CAGE );
194 og.addItem( op.addBool(CIRC_CAGE_USE_VIEW, true));
195 condition = cm.createConditionValueEquals(RESTRICT_OUTPUT, OUTPUT_CIRCULAR_CAGE).and(
196 cm.createConditionValueEquals(CIRC_CAGE_USE_VIEW, Boolean.FALSE));
197 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_CENTER_X, 0.0d) ));
198 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_CENTER_Y, 0.0d) ));
199 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_RADIUS, 1000.0d) ));
200 op.getItem(RESTRICTIONS,CAGE_RADIUS)
201 .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(Double.MIN_VALUE));
202
203 og = new OptionGroup();
204 cm.setEnabledOnValueEquals( RESTRICT_OUTPUT, OUTPUT_ELLIPTICAL_CAGE, og );
205 og.setAttribute( OptionGroup.ATTRIBUTE_TITLE, OUTPUT_ELLIPTICAL_CAGE );
206 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID, ctrId );
207 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CARD_ID, OUTPUT_ELLIPTICAL_CAGE );
208 og.addItem( op.addBool(ELL_CAGE_USE_VIEW, true));
209 condition = cm.createConditionValueEquals(RESTRICT_OUTPUT, OUTPUT_ELLIPTICAL_CAGE).and(
210 cm.createConditionValueEquals(ELL_CAGE_USE_VIEW, Boolean.FALSE));
211 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(ELLIPTICAL_CAGE_X, 0.0d) ));
212 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(ELLIPTICAL_CAGE_Y, 0.0d) ));
213 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(ELLIPTICAL_CAGE_WIDTH, 1000.0d) ));
214 op.getItem(RESTRICTIONS, ELLIPTICAL_CAGE_WIDTH)
215 .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(Double.MIN_VALUE));
216 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(ELLIPTICAL_CAGE_HEIGHT, 1000.0d) ));
217 op.getItem(RESTRICTIONS, ELLIPTICAL_CAGE_HEIGHT)
218 .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(Double.MIN_VALUE));
219
220 og = new OptionGroup();
221 cm.setEnabledOnValueEquals( RESTRICT_OUTPUT, OUTPUT_AR, og );
222 og.setAttribute( OptionGroup.ATTRIBUTE_TITLE, OUTPUT_AR );
223 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CONTROLLER_ID, ctrId );
224 og.setAttribute( DefaultEditorFactory.ATTRIBUTE_CARD_ID, OUTPUT_AR );
225 og.addItem( op.addBool(AR_CAGE_USE_VIEW, true));
226 condition = cm.createConditionValueEquals(RESTRICT_OUTPUT, OUTPUT_AR).and(
227 cm.createConditionValueEquals(AR_CAGE_USE_VIEW, Boolean.FALSE));
228 cm.setEnabledOnCondition(condition, og.addItem( op.addDouble(CAGE_RATIO, 1.0d) ));
229
230 op.useSection(GROUPING);
231 op.addEnum(GROUP_LAYOUT_POLICY, GROUPING_POLICIES, 0);
232 op.addBool(USE_AUTOMATIC_GROUP_NODE_COMPACTION, organic.isAutomaticGroupNodeCompactionEnabled());
233 op.addDouble(GROUP_COMPACTNESS,organic.getGroupNodeCompactness(),0,1);
234 cm.setEnabledOnValueEquals(USE_AUTOMATIC_GROUP_NODE_COMPACTION, Boolean.FALSE, GROUP_COMPACTNESS);
235
237
238 op.useSection(ALGORITHM);
239 OptionItem qualityItem = op.addDouble( QUALITY_TIME_RATIO, organic.getQualityTimeRatio(), 0, 1 );
240 qualityItem.setAttribute( DefaultEditorFactory.ATTRIBUTE_MIN_VALUE_LABEL_TEXT, "SPEED" );
241 qualityItem.setAttribute( DefaultEditorFactory.ATTRIBUTE_MAX_VALUE_LABEL_TEXT, "QUALITY" );
242
243 op.addInt(MAXIMAL_DURATION,(int)(organic.getMaximumDuration()/1000))
244 .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(0));
245 op.addBool(ACTIVATE_DETERMINISTIC_MODE,organic.isDeterministic());
246 return op;
247 }
248
249
254 protected void init()
255 {
256 createOrganic();
257
258 OptionHandler op = getOptionHandler();
259 organic.setPreferredEdgeLength(op.getInt(VISUAL, PREFERRED_EDGE_LENGTH));
260 boolean considerNodeLabels = op.getBool(VISUAL,CONSIDER_NODE_LABELS);
261 organic.setConsiderNodeLabelsEnabled(considerNodeLabels);
262 organic.setNodeOverlapsAllowed(op.getBool(VISUAL, ALLOW_NODE_OVERLAPS) && !considerNodeLabels);
263 organic.setMinimalNodeDistance(op.getDouble(VISUAL, MINIMAL_NODE_DISTANCE));
264 organic.setScope(OptionHandler.getIndex(SCOPES, op.getString(VISUAL,SCOPE)));
265 organic.setCompactness(op.getDouble(VISUAL,COMPACTNESS));
266 organic.setAutomaticGroupNodeCompactionEnabled(op.getBool(USE_AUTOMATIC_GROUP_NODE_COMPACTION));
267 organic.setGroupNodeCompactness(op.getDouble(GROUP_COMPACTNESS));
268 organic.setNodeSizeAware(true);
271 organic.setAutoClusteringEnabled(op.getBool(USE_AUTO_CLUSTERING));
272 organic.setAutoClusteringQuality(op.getDouble(AUTO_CLUSTERING_QUALITY));
273 organic.setNodeEdgeOverlapAvoided(op.getBool(AVOID_NODE_EDGE_OVERLAPS));
274 organic.setDeterministic(op.getBool(ALGORITHM, ACTIVATE_DETERMINISTIC_MODE));
275 organic.setMaximumDuration(1000*op.getInt(ALGORITHM, MAXIMAL_DURATION));
276 organic.setQualityTimeRatio(op.getDouble(ALGORITHM,QUALITY_TIME_RATIO));
277 switch (op.getEnum(RESTRICT_OUTPUT)){
278 case 0:
279 organic.setComponentLayouterEnabled(true);
280 organic.setOutputRestriction( OutputRestriction.NONE);
281 break;
282 case 1: {
283 double x;
284 double y;
285 double w;
286 double h;
287 if (op.getBool(RECT_CAGE_USE_VIEW) && getGraph2DView() != null) {
288 Rectangle visibleRect = getGraph2DView().getVisibleRect();
289 x = visibleRect.x;
290 y = visibleRect.y;
291 w = visibleRect.width;
292 h = visibleRect.height;
293 } else {
294 x = op.getDouble(CAGE_X);
295 y = op.getDouble(CAGE_Y);
296 w = op.getDouble(CAGE_WIDTH);
297 h = op.getDouble(CAGE_HEIGHT);
298 }
299 organic.setOutputRestriction(
300 OutputRestriction.createRectangularCageRestriction(x, y, w, h));
301 organic.setComponentLayouterEnabled(false);
302 break;
303 }
304 case 2:
305 {
306 double x;
307 double y;
308 double radius;
309 if (op.getBool(CIRC_CAGE_USE_VIEW) && getGraph2DView() != null) {
310 Rectangle visibleRect = getGraph2DView().getVisibleRect();
311 x = visibleRect.getCenterX();
312 y = visibleRect.getCenterY();
313 radius = Math.min(visibleRect.width, visibleRect.height) * 0.5d;
314 } else {
315 x = op.getDouble(CAGE_CENTER_X);
316 y = op.getDouble(CAGE_CENTER_Y);
317 radius = op.getDouble(CAGE_RADIUS);
318 }
319 organic.setOutputRestriction( OutputRestriction.createCircularCageRestriction(x, y, radius));
320 organic.setComponentLayouterEnabled(false);
321 break;
322 }
323 case 3:
324 {
325 double ratio;
326 if (op.getBool(AR_CAGE_USE_VIEW) && getGraph2DView() != null) {
327 Rectangle visibleRect = getGraph2DView().getVisibleRect();
328 ratio = visibleRect.getWidth()/visibleRect.getHeight();
329 } else {
330 ratio = op.getDouble(CAGE_RATIO);
331 }
332 organic.setOutputRestriction( OutputRestriction.createAspectRatioRestriction(ratio));
333 organic.setComponentLayouterEnabled(true);
334 ((ComponentLayouter) organic.getComponentLayouter()).setPreferredLayoutSize(ratio * 100, 100);
335 break;
336 }
337 case 4:
338 {
339 double x;
340 double y;
341 double w;
342 double h;
343 if (op.getBool(ELL_CAGE_USE_VIEW) && getGraph2DView() != null) {
344 Rectangle visibleRect = getGraph2DView().getVisibleRect();
345 x = visibleRect.x;
346 y = visibleRect.y;
347 w = visibleRect.width;
348 h = visibleRect.height;
349 } else {
350 x = op.getDouble(ELLIPTICAL_CAGE_X);
351 y = op.getDouble(ELLIPTICAL_CAGE_Y);
352 w = op.getDouble(ELLIPTICAL_CAGE_WIDTH);
353 h = op.getDouble(ELLIPTICAL_CAGE_HEIGHT);
354 }
355 organic.setOutputRestriction(
356 OutputRestriction.createEllipticalCageRestriction(x, y, w, h));
357 organic.setComponentLayouterEnabled(false);
358 break;
359 }
360 }
361 }
362
363
366 protected void mainrun()
367 {
368 final OptionHandler handler = getOptionHandler();
369 final int policy = handler.getEnum(GROUP_LAYOUT_POLICY);
370 createOrganic();
371 final Graph2D graph = getGraph2D();
372 try {
373 final boolean grouping = policy != 3 && HierarchyManager.containsGroupNodes(graph);
374 getLayoutExecutor().setConfiguringGrouping(grouping);
375 if (policy == 2 && grouping) {
376 NodeMap nodeMap = Maps.createHashedNodeMap();
377 for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
378 Node node = nodeCursor.node();
379 if (HierarchyManager.getInstance(graph).isGroupNode(node)){
380 nodeMap.set(node, SmartOrganicLayouter.GROUP_NODE_MODE_FIX_BOUNDS);
381 }
382 }
383 graph.addDataProvider(SmartOrganicLayouter.GROUP_NODE_MODE_DATA, nodeMap);
384 } else if (policy == 1 && grouping) {
385 NodeMap nodeMap = Maps.createHashedNodeMap();
386 for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
387 Node node = nodeCursor.node();
388 if (HierarchyManager.getInstance(graph).isGroupNode(node)){
389 nodeMap.set(node, SmartOrganicLayouter.GROUP_NODE_MODE_FIX_CONTENTS);
390 }
391 }
392 graph.addDataProvider(SmartOrganicLayouter.GROUP_NODE_MODE_DATA, nodeMap);
393 }
394 graph.addDataProvider(SmartOrganicLayouter.NODE_SUBSET_DATA,
395 Selections.createSelectionNodeMap(graph));
396 launchLayouter(organic);
397 } finally {
398 graph.removeDataProvider(SmartOrganicLayouter.NODE_SUBSET_DATA);
399 if (policy == 2 || policy == 1) {
400 graph.removeDataProvider(SmartOrganicLayouter.GROUP_NODE_MODE_DATA);
401 }
402 getLayoutExecutor().setConfiguringGrouping(true);
403 }
404 }
405
406
410 protected void dispose()
411 {
412 organic = null;
413 }
414
415 private void createOrganic()
416 {
417 if(organic == null)
418 {
419 organic = new SmartOrganicLayouter();
420 }
421 }
422 }
423