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