1
28 package demo.layout.module;
29
30 import y.module.LayoutModule;
31 import y.module.YModule;
32
33 import y.base.EdgeCursor;
34 import y.base.NodeCursor;
35 import y.layout.ComponentLayouter;
36 import y.layout.EdgeBundleDescriptor;
37 import y.layout.EdgeBundling;
38 import y.layout.EdgeLabelModel;
39 import y.layout.FreeEdgeLabelModel;
40 import y.layout.FreeNodeLabelModel;
41 import y.layout.NodeLabelModel;
42 import y.layout.router.OrganicEdgeRouter;
43 import y.layout.router.polyline.EdgeRouter;
44 import y.layout.tree.BalloonLayouter;
45 import y.layout.tree.TreeReductionStage;
46 import y.option.ConstraintManager;
47 import y.option.EnumOptionItem;
48 import y.option.OptionHandler;
49 import y.option.OptionItem;
50 import y.view.EdgeLabel;
51 import y.view.EdgeRealizer;
52 import y.view.Graph2D;
53 import y.view.NodeLabel;
54 import y.view.NodeRealizer;
55 import y.view.Selections;
56 import y.view.SmartEdgeLabelModel;
57 import y.view.SmartNodeLabelModel;
58
59
65 public class BalloonLayoutModule extends LayoutModule {
66 protected static final String MODULE_BALLOON = "BALLOON";
68
69 protected static final String SECTION_GENERAL = "GENERAL";
71 protected static final String ITEM_ROOT_NODE_POLICY = "ROOT_NODE_POLICY";
73 protected static final String VALUE_DIRECTED_ROOT = "DIRECTED_ROOT";
74 protected static final String VALUE_CENTER_ROOT = "CENTER_ROOT";
75 protected static final String VALUE_SELECTED_ROOT = "SELECTED_ROOT";
76 protected static final String VALUE_WEIGHTED_CENTER_ROOT = "WEIGHTED_CENTER_ROOT";
77 protected static final String ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES = "ROUTING_STYLE_FOR_NON_TREE_EDGES";
78 protected static final String VALUE_ROUTE_ORGANIC = "ROUTE_ORGANIC";
79 protected static final String VALUE_ROUTE_ORTHOGONAL = "ROUTE_ORTHOGONAL";
80 protected static final String VALUE_ROUTE_STRAIGHTLINE = "ROUTE_STRAIGHTLINE";
81 protected static final String VALUE_ROUTE_BUNDLED = "ROUTE_BUNDLED";
82 protected static final String ITEM_ACT_ON_SELECTION_ONLY = "ACT_ON_SELECTION_ONLY";
83 protected static final String ITEM_PREFERRED_CHILD_WEDGE = "PREFERRED_CHILD_WEDGE";
84 protected static final String ITEM_PREFERRED_ROOT_WEDGE = "PREFERRED_ROOT_WEDGE";
85 protected static final String ITEM_MINIMAL_EDGE_LENGTH = "MINIMAL_EDGE_LENGTH";
86 protected static final String ITEM_COMPACTNESS_FACTOR = "COMPACTNESS_FACTOR";
87 protected static final String ITEM_ALLOW_OVERLAPS = "ALLOW_OVERLAPS";
88 protected static final String ITEM_BALLOON_FROM_SKETCH = "FROM_SKETCH";
89 protected static final String ITEM_PLACE_CHILDREN_INTERLEAVED = "PLACE_CHILDREN_INTERLEAVED";
90 protected static final String ITEM_STRAIGHTEN_CHAINS = "STRAIGHTEN_CHAINS";
91 protected static final String EDGE_BUNDLING_STRENGTH = "EDGE_BUNDLING_STRENGTH";
92 protected static final String SECTION_LABELING = "LABELING";
94 protected static final String ITEM_NODE_LABELING_STYLE = "NODE_LABELING_STYLE";
96 protected static final String VALUE_NODE_LABELING_STYLE_NONE = "NODE_LABELING_STYLE_NONE";
97 protected static final String VALUE_NODE_LABELING_STYLE_HORIZONTAL = "NODE_LABELING_STYLE_HORIZONTAL";
98 protected static final String VALUE_NODE_LABELING_STYLE_RAYLIKE_LEAVES = "NODE_LABELING_STYLE_RAYLIKE_LEAVES";
99 protected static final String VALUE_NODE_LABELING_STYLE_CONSIDER_CURRENT_POSITION = "NODE_LABELING_STYLE_CONSIDER_CURRENT_POSITION";
100 protected static final String ITEM_INTEGRATED_EDGE_LABELING = "INTEGRATED_EDGE_LABELING";
101
104 public BalloonLayoutModule() {
105 super(MODULE_BALLOON);
106 setPortIntersectionCalculatorEnabled(true);
107 }
108
109
113 protected OptionHandler createOptionHandler() {
114 final OptionHandler options = new OptionHandler(getModuleName());
115 final BalloonLayouter defaults = new BalloonLayouter();
117
118 options.useSection(SECTION_GENERAL);
120 options.addEnum(ITEM_ROOT_NODE_POLICY, new String[]{
122 VALUE_DIRECTED_ROOT,
123 VALUE_CENTER_ROOT,
124 VALUE_WEIGHTED_CENTER_ROOT,
125 VALUE_SELECTED_ROOT
126 }, 0);
127 final EnumOptionItem itemNonTreeEdgeRouting = options.addEnum(ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES, new String[]{
128 VALUE_ROUTE_ORGANIC,
129 VALUE_ROUTE_ORTHOGONAL,
130 VALUE_ROUTE_STRAIGHTLINE,
131 VALUE_ROUTE_BUNDLED
132 }, 0);
133
134 final OptionItem itemBundlingStrength = options.addDouble(EDGE_BUNDLING_STRENGTH, 0.99, 0.0, 1.0);
135
136 final ConstraintManager optionConstraints = new ConstraintManager(options);
137 final ConstraintManager.Condition bundlingCondition =
138 optionConstraints.createConditionValueEquals(itemNonTreeEdgeRouting, VALUE_ROUTE_BUNDLED);
139 optionConstraints.setEnabledOnCondition(bundlingCondition, itemBundlingStrength);
140
141 options.addBool(ITEM_ACT_ON_SELECTION_ONLY, false);
142 options.addInt(ITEM_PREFERRED_CHILD_WEDGE, defaults.getPreferredChildWedge(), 1, 359);
143 options.addInt(ITEM_PREFERRED_ROOT_WEDGE, defaults.getPreferredRootWedge(), 1, 360);
144 options.addInt(ITEM_MINIMAL_EDGE_LENGTH, defaults.getMinimalEdgeLength(), 10, 400);
145 options.addDouble(ITEM_COMPACTNESS_FACTOR, defaults.getCompactnessFactor(), 0.1, 0.9);
146 options.addBool(ITEM_ALLOW_OVERLAPS, defaults.getAllowOverlaps());
147 options.addBool(ITEM_BALLOON_FROM_SKETCH, defaults.isFromSketchModeEnabled());
148 options.addBool(ITEM_PLACE_CHILDREN_INTERLEAVED,
149 defaults.getInterleavedMode() == BalloonLayouter.INTERLEAVED_MODE_ALL_NODES);
150 options.addBool(ITEM_STRAIGHTEN_CHAINS, defaults.isChainStraighteningModeEnabled());
151
152 options.useSection(SECTION_LABELING);
154 options.addBool(ITEM_INTEGRATED_EDGE_LABELING, true);
156 options.addEnum(ITEM_NODE_LABELING_STYLE, new String[]{
157 VALUE_NODE_LABELING_STYLE_NONE,
158 VALUE_NODE_LABELING_STYLE_HORIZONTAL,
159 VALUE_NODE_LABELING_STYLE_RAYLIKE_LEAVES,
160 VALUE_NODE_LABELING_STYLE_CONSIDER_CURRENT_POSITION
161 }, 3);
162
163 return options;
164 }
165
166
170 protected void mainrun() {
171 final BalloonLayouter balloon = new BalloonLayouter();
172
173 final OptionHandler options = getOptionHandler();
174 configure(balloon, options);
175
176 final Graph2D graph = getGraph2D();
177 prepareGraph(graph, options);
178 launchLayouter(balloon);
179 restoreGraph(graph, options);
180 }
181
182
191 private void prepareGraph(final Graph2D graph, final OptionHandler options) {
192 if (options.getBool(ITEM_INTEGRATED_EDGE_LABELING)) {
193 setupEdgeLabelModel(graph);
194 }
195 if (VALUE_NODE_LABELING_STYLE_RAYLIKE_LEAVES.equals(options.getString(ITEM_NODE_LABELING_STYLE))) {
196 setupNodeLabelModel(graph);
197 }
198 if (options.get(ITEM_ROOT_NODE_POLICY).equals(VALUE_SELECTED_ROOT)){
199 backupDataProvider(graph, BalloonLayouter.SELECTED_ROOT_DPKEY);
201 graph.addDataProvider(BalloonLayouter.SELECTED_ROOT_DPKEY, Selections.createSelectionNodeMap(graph));
202 }
203 }
204
205
211 protected void restoreGraph(Graph2D graph, OptionHandler options) {
212 if (options.get(ITEM_ROOT_NODE_POLICY).equals(VALUE_SELECTED_ROOT)){
213 restoreDataProvider(graph, BalloonLayouter.SELECTED_ROOT_DPKEY);
214 }
215 }
216
217
222 protected void configure(final BalloonLayouter balloon, final OptionHandler options) {
223 ((ComponentLayouter) balloon.getComponentLayouter()).setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
224
225 if (options.get(ITEM_ROOT_NODE_POLICY).equals(VALUE_DIRECTED_ROOT)) {
226 balloon.setRootNodePolicy(BalloonLayouter.DIRECTED_ROOT);
227 } else if (options.get(ITEM_ROOT_NODE_POLICY).equals(VALUE_CENTER_ROOT)) {
228 balloon.setRootNodePolicy(BalloonLayouter.CENTER_ROOT);
229 } else if (options.get(ITEM_ROOT_NODE_POLICY).equals(VALUE_SELECTED_ROOT)){
230 balloon.setRootNodePolicy(BalloonLayouter.SELECTED_ROOT);
231 } else {
232 balloon.setRootNodePolicy(BalloonLayouter.WEIGHTED_CENTER_ROOT);
233 }
234
235 balloon.setPreferredChildWedge(options.getInt(ITEM_PREFERRED_CHILD_WEDGE));
236 balloon.setPreferredRootWedge(options.getInt(ITEM_PREFERRED_ROOT_WEDGE));
237 balloon.setMinimalEdgeLength(options.getInt(ITEM_MINIMAL_EDGE_LENGTH));
238 balloon.setCompactnessFactor(options.getDouble(ITEM_COMPACTNESS_FACTOR));
239 balloon.setAllowOverlaps(options.getBool(ITEM_ALLOW_OVERLAPS));
240 balloon.setFromSketchModeEnabled(options.getBool(ITEM_BALLOON_FROM_SKETCH));
241
242 if (options.getBool(ITEM_INTEGRATED_EDGE_LABELING)) {
243 balloon.setIntegratedEdgeLabelingEnabled(true);
244 } else {
245 balloon.setIntegratedEdgeLabelingEnabled(false);
246 }
247
248 balloon.setChainStraighteningModeEnabled(options.getBool(ITEM_STRAIGHTEN_CHAINS));
249 balloon.setInterleavedMode(options.getBool(ITEM_PLACE_CHILDREN_INTERLEAVED)
250 ? BalloonLayouter.INTERLEAVED_MODE_ALL_NODES : BalloonLayouter.INTERLEAVED_MODE_OFF);
251 balloon.setIntegratedNodeLabelingEnabled(false);
252 balloon.setConsiderNodeLabelsEnabled(false);
253
254 final String nodeLabelingStyle = options.getString(ITEM_NODE_LABELING_STYLE);
255 if (VALUE_NODE_LABELING_STYLE_RAYLIKE_LEAVES.equals(nodeLabelingStyle)) {
256 balloon.setIntegratedNodeLabelingEnabled(true);
257 balloon.setNodeLabelingPolicy(BalloonLayouter.NODE_LABELING_MIXED);
258 } else if (VALUE_NODE_LABELING_STYLE_CONSIDER_CURRENT_POSITION.equals(nodeLabelingStyle)) {
259 balloon.setConsiderNodeLabelsEnabled(true);
260 } else if (VALUE_NODE_LABELING_STYLE_HORIZONTAL.equals(nodeLabelingStyle)) {
261 balloon.setIntegratedNodeLabelingEnabled(true);
262 balloon.setNodeLabelingPolicy(BalloonLayouter.NODE_LABELING_HORIZONTAL);
263 }
264
265 balloon.setSubgraphLayouterEnabled(options.getBool(ITEM_ACT_ON_SELECTION_ONLY));
266
267 final TreeReductionStage trs = new TreeReductionStage();
269 balloon.appendStage(trs);
270 if (VALUE_ROUTE_ORGANIC.equals(options.get(ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
271 trs.setNonTreeEdgeRouter(new OrganicEdgeRouter());
272 trs.setNonTreeEdgeSelectionKey(OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
273 } else if (VALUE_ROUTE_ORTHOGONAL.equals(options.get(ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
274 final EdgeRouter orthogonal = new EdgeRouter();
275 orthogonal.setReroutingEnabled(true);
276 orthogonal.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
277 trs.setNonTreeEdgeSelectionKey(orthogonal.getSelectedEdgesDpKey());
278 trs.setNonTreeEdgeRouter(orthogonal);
279 } else if (VALUE_ROUTE_STRAIGHTLINE.equals(options.get(ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
280 trs.setNonTreeEdgeRouter(trs.createStraightlineRouter());
281 } else if (VALUE_ROUTE_BUNDLED.equals(options.get(ITEM_ROUTING_STYLE_FOR_NON_TREE_EDGES))){
282 final EdgeBundling ebc = trs.getEdgeBundling();
284 final EdgeBundleDescriptor descriptor = new EdgeBundleDescriptor();
285 descriptor.setBundled(true);
286 ebc.setDefaultBundleDescriptor(descriptor);
287 ebc.setBundlingStrength(options.getDouble(EDGE_BUNDLING_STRENGTH));
288
289 trs.setNonTreeEdgeRouter(trs.createStraightlineRouter());
291 }
292 }
293
294
297 private void setupEdgeLabelModel(final Graph2D graph) {
298 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
299 final EdgeRealizer er = graph.getRealizer(ec.edge());
300 for (int i = 0; i < er.labelCount(); i++) {
301 final EdgeLabel el = er.getLabel(i);
302 final EdgeLabelModel labelModel = el.getLabelModel();
303 if (!isFreeModel(labelModel)) {
304 final SmartEdgeLabelModel defaultLabelModel = new SmartEdgeLabelModel();
306 el.setLabelModel(defaultLabelModel, defaultLabelModel.getDefaultParameter());
307 }
308 }
309 }
310 }
311
312 private static boolean isFreeModel(final EdgeLabelModel model) {
313 return (model instanceof FreeEdgeLabelModel) || (model instanceof SmartEdgeLabelModel);
314 }
315
316
317 private void setupNodeLabelModel(final Graph2D graph) {
318 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
319 final NodeRealizer nr = graph.getRealizer(nc.node());
320 for (int i = 0; i < nr.labelCount(); i++) {
321 final NodeLabel nl = nr.getLabel(i);
322 final NodeLabelModel labelModel = nl.getLabelModel();
323 if (!isFreeModel(labelModel)) {
324 final SmartNodeLabelModel defaultLabelModel = new SmartNodeLabelModel();
326 nl.setLabelModel(defaultLabelModel, defaultLabelModel.getDefaultParameter());
327 }
328 }
329 }
330 }
331
332 private static boolean isFreeModel(final NodeLabelModel model) {
333 return (model instanceof FreeNodeLabelModel) || (model instanceof SmartNodeLabelModel);
334 }
335 }
336