1
28 package demo.layout.module;
29
30 import y.module.LayoutModule;
31 import y.module.YModule;
32
33 import y.algo.GraphConnectivity;
34 import y.base.DataProvider;
35 import y.base.Edge;
36 import y.base.EdgeCursor;
37 import y.base.EdgeMap;
38 import y.base.Node;
39 import y.base.NodeCursor;
40 import y.base.NodeMap;
41 import y.geom.YPoint;
42 import y.geom.YPointCursor;
43 import y.geom.YPointPath;
44 import y.layout.AbstractLayoutStage;
45 import y.layout.LayoutGraph;
46 import y.layout.Layouter;
47 import y.layout.router.BusDescriptor;
48 import y.layout.router.BusRouter;
49 import y.option.ConstraintManager;
50 import y.option.DoubleOptionItem;
51 import y.option.IntOptionItem;
52 import y.option.OptionGroup;
53 import y.option.OptionHandler;
54 import y.option.OptionItem;
55 import y.util.DataProviderAdapter;
56 import y.util.GraphHider;
57 import y.util.Maps;
58 import y.view.Graph2D;
59
60 import java.util.HashSet;
61 import java.util.Set;
62
63
87 public class BusRouterModule extends LayoutModule {
88 protected static final String MODULE_BUS_ROUTER = "BUS_ROUTER";
90
91 protected static final String TITLE_LAYOUT = "GROUP_LAYOUT";
93 protected static final String ITEM_SCOPE = "SCOPE";
94 protected static final String VALUE_SCOPE_ALL = "ALL";
95 protected static final String VALUE_SCOPE_SUBSET = "SUBSET";
96 protected static final String VALUE_SCOPE_EDGES_AT_SELECTED_NODES = "EDGES_AT_SELECTED_NODES";
97 protected static final String VALUE_SCOPE_SUBSET_BUS = "SUBSET_BUS";
98 protected static final String VALUE_SCOPE_PARTIAL = "PARTIAL";
99 protected static final String ITEM_BUSES = "BUSES";
100 protected static final String VALUE_SINGLE = "SINGLE";
101 protected static final String VALUE_COLOR = "COLOR";
102 protected static final String VALUE_CONNECTED_COMPONENT = "CONNECTED_COMPONENT";
103 protected static final String ITEM_GRID_ENABLED = "GRID_ENABLED";
104 protected static final String ITEM_GRID_SPACING = "GRID_SPACING";
105 protected static final String ITEM_MIN_DISTANCE_TO_NODES = "MIN_DISTANCE_TO_NODES";
106 protected static final String ITEM_MIN_DISTANCE_TO_EDGES = "MIN_DISTANCE_TO_EDGES";
107 protected static final String TITLE_SELECTION = "GROUP_SELECTION";
108 protected static final String ITEM_PREFERRED_BACKBONE_COUNT = "PREFERRED_BACKBONE_COUNT";
109 protected static final String ITEM_MINIMUM_BACKBONE_LENGTH = "MINIMUM_BACKBONE_LENGTH";
110 protected static final String TITLE_ROUTING = "GROUP_ROUTING";
111 protected static final String ITEM_CROSSING_COST = "CROSSING_COST";
112 protected static final String ITEM_CROSSING_REROUTING = "CROSSING_REROUTING";
113 protected static final String ITEM_MINIMUM_CONNECTIONS_COUNT = "MINIMUM_CONNECTIONS_COUNT";
114
115
118 protected boolean optionsLayout;
119
122 protected boolean optionsSelection;
123
126 protected boolean optionsRouting;
127
128
131 public BusRouterModule() {
132 super(MODULE_BUS_ROUTER);
133 optionsLayout = true;
134 optionsSelection = true;
135 optionsRouting = true;
136 }
137
138
142 protected OptionHandler createOptionHandler() {
143 final OptionHandler options = new OptionHandler(getModuleName());
144 addOptionItems(new BusRouter(), options);
145 return options;
146 }
147
148
153 protected void addOptionItems(final BusRouter defaults, final OptionHandler options) {
154 final ConstraintManager optionConstraints = new ConstraintManager(options);
155
156 if (optionsLayout) {
157 final OptionGroup layoutGroup = new OptionGroup();
159 layoutGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_LAYOUT);
160 layoutGroup.addItem(options.addEnum(ITEM_SCOPE, new String[]{
162 VALUE_SCOPE_ALL,
163 VALUE_SCOPE_SUBSET,
164 VALUE_SCOPE_EDGES_AT_SELECTED_NODES,
165 VALUE_SCOPE_SUBSET_BUS,
166 VALUE_SCOPE_PARTIAL
167 }, (int) defaults.getScope()));
168 layoutGroup.addItem(options.addEnum(ITEM_BUSES, new String[]{
169 VALUE_SINGLE,
170 VALUE_COLOR,
171 VALUE_CONNECTED_COMPONENT
172 }, 0));
173 layoutGroup.addItem(options.addBool(ITEM_GRID_ENABLED, defaults.isGridRoutingEnabled()));
174 final OptionItem itemGridSpacing = layoutGroup.addItem(
175 options.addInt(ITEM_GRID_SPACING, defaults.getGridSpacing()));
176 itemGridSpacing.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
177 final OptionItem itemMinDistanceNodes = layoutGroup.addItem(
178 options.addInt(ITEM_MIN_DISTANCE_TO_NODES, defaults.getMinimumDistanceToNode()));
179 itemMinDistanceNodes.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
180 final OptionItem itemMinDistanceEdges = layoutGroup.addItem(
181 options.addInt(ITEM_MIN_DISTANCE_TO_EDGES, defaults.getMinimumDistanceToEdge()));
182 itemMinDistanceEdges.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
183 optionConstraints.setEnabledOnValueEquals(ITEM_GRID_ENABLED, Boolean.TRUE, ITEM_GRID_SPACING);
185 }
186
187 if (optionsSelection) {
188 final OptionGroup selectionGroup = new OptionGroup();
190 selectionGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_SELECTION);
191 final OptionItem itemPreferredBackboneCount = selectionGroup.addItem(
193 options.addInt(ITEM_PREFERRED_BACKBONE_COUNT, defaults.getPreferredBackboneSegmentCount()));
194 itemPreferredBackboneCount.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
195 final OptionItem itemMinBackboneLength = selectionGroup.addItem(
196 options.addDouble(ITEM_MINIMUM_BACKBONE_LENGTH, defaults.getMinimumBackboneSegmentLength()));
197 itemMinBackboneLength.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(1.0));
198 }
199
200 if (optionsRouting) {
201 final OptionGroup routingGroup = new OptionGroup();
203 routingGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_ROUTING);
204 final OptionItem itemCrossingCost = routingGroup.addItem(
206 options.addDouble(ITEM_CROSSING_COST, defaults.getCrossingCost()));
207 itemCrossingCost.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(0.0));
208 final OptionItem itemCrossingRerouting = routingGroup.addItem(
209 options.addBool(ITEM_CROSSING_REROUTING, defaults.isReroutingEnabled()));
210 itemCrossingRerouting.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(0.0));
211 final OptionItem itemMinimumConnectionCount = routingGroup.addItem(
212 options.addInt(ITEM_MINIMUM_CONNECTIONS_COUNT, defaults.getMinimumBusConnectionsCount()));
213 itemMinimumConnectionCount.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
214 }
215 }
216
217
221 protected void mainrun() {
222 final BusRouter bus = new BusRouter();
223
224 final OptionHandler options = getOptionHandler();
225 configure(bus, options);
226
227 final Graph2D graph = getGraph2D();
228 prepareGraph(graph, options);
229 try {
230 launchLayouter(new EdgeHidingStage(bus));
231 } finally {
232 restoreGraph(graph, options);
233 }
234 }
235
236
246 protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
247 if (!optionsLayout) {
248 return;
250 }
251
252
253
257 backupDataProvider(graph, BusRouter.EDGE_DESCRIPTOR_DPKEY);
259
260 final String busType = options.getString(ITEM_BUSES);
262 final Object scope = options.get(ITEM_SCOPE);
263
264 final EdgeMap descriptorMap = Maps.createHashedEdgeMap();
267 graph.addDataProvider(BusRouter.EDGE_DESCRIPTOR_DPKEY, descriptorMap);
268 final NodeMap node2CompId = Maps.createHashedNodeMap();
269 GraphConnectivity.connectedComponents(graph, node2CompId);
270 if (VALUE_SCOPE_SUBSET.equals(scope)) {
273 for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
274 final Edge edge = ec.edge();
275 descriptorMap.set(edge, new BusDescriptor(getBusId(graph, edge, node2CompId, busType), false));
276 }
277 } else if (VALUE_SCOPE_EDGES_AT_SELECTED_NODES.equals(scope)) {
278 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
279 final Edge edge = ec.edge();
280 if (graph.isSelected(edge.source()) || graph.isSelected(edge.target())) {
281 descriptorMap.set(edge, new BusDescriptor(getBusId(graph, edge, node2CompId, busType), false));
282 }
283 }
284 } else if (VALUE_SCOPE_PARTIAL.equals(scope)) {
285 EdgeMap nonOrthogonalFixedEdges = null;
286 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
287 final Edge edge = ec.edge();
288 final boolean fixed = !graph.isSelected(edge.source()) && !graph.isSelected(edge.target());
289 if (fixed && !isOrthogonal(edge, graph)) {
290 if (nonOrthogonalFixedEdges == null) {
291 nonOrthogonalFixedEdges = Maps.createHashedEdgeMap();
292 }
293 nonOrthogonalFixedEdges.setBool(edge, true);
295 }
296 if (nonOrthogonalFixedEdges != null) {
297 graph.addDataProvider(EdgeHidingStage.EDGE_HIDE_DPKEY, nonOrthogonalFixedEdges);
298 }
299 descriptorMap.set(edge, new BusDescriptor(getBusId(graph, edge, node2CompId, busType), fixed));
300 }
301 } else {
302 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
304 final Edge edge = ec.edge();
305 descriptorMap.set(edge, new BusDescriptor(getBusId(graph, edge, node2CompId, busType), false));
306 }
307 }
308
309
310
314 backupDataProvider(graph, BusRouter.EDGE_SUBSET_DPKEY);
316
317 if (VALUE_SCOPE_SUBSET.equals(scope)) {
318 graph.addDataProvider(BusRouter.EDGE_SUBSET_DPKEY, new DataProviderAdapter() {
320 public boolean getBool(Object dataHolder) {
321 return dataHolder instanceof Edge && graph.isSelected((Edge) dataHolder);
322 }
323 });
324 } else if (VALUE_SCOPE_EDGES_AT_SELECTED_NODES.equals(scope)) {
325 graph.addDataProvider(BusRouter.EDGE_SUBSET_DPKEY, new DataProviderAdapter() {
326 public boolean getBool(Object dataHolder) {
327 if (dataHolder instanceof Edge) {
328 final Edge edge = (Edge) dataHolder;
329 return graph.isSelected(edge.source()) || graph.isSelected(edge.target());
330 }
331 return false;
332 }
333 });
334 } else if (VALUE_SCOPE_SUBSET_BUS.equals(scope)) {
335 final Set selectedIDs = new HashSet();
337 for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
338 selectedIDs.add(((BusDescriptor) descriptorMap.get(ec.edge())).getID());
339 }
340 graph.addDataProvider(BusRouter.EDGE_SUBSET_DPKEY, new DataProviderAdapter() {
341 public boolean getBool(Object dataHolder) {
342 return selectedIDs.contains(((BusDescriptor) descriptorMap.get(dataHolder)).getID());
343 }
344 });
345 } else if (VALUE_SCOPE_PARTIAL.equals(scope)) {
346 final Set selectedIDs = new HashSet();
349 for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
350 final Node node = nc.node();
351 for (EdgeCursor ec = node.edges(); ec.ok(); ec.next()) {
352 selectedIDs.add(((BusDescriptor) descriptorMap.get(ec.edge())).getID());
353 }
354 }
355 graph.addDataProvider(BusRouter.EDGE_SUBSET_DPKEY, new DataProviderAdapter() {
356 public boolean getBool(Object dataHolder) {
357 return selectedIDs.contains(((BusDescriptor) descriptorMap.get(dataHolder)).getID());
358 }
359 });
360 }
361 }
362
363
369 protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
370 if (optionsLayout) {
371 restoreDataProvider(graph, BusRouter.EDGE_DESCRIPTOR_DPKEY);
373 restoreDataProvider(graph, BusRouter.EDGE_SUBSET_DPKEY);
374 graph.removeDataProvider(EdgeHidingStage.EDGE_HIDE_DPKEY);
375 }
376 }
377
378
383 protected void configure(final BusRouter bus, final OptionHandler options) {
384
385 if (optionsLayout) {
386 bus.setScope(toBusRouterScope(options.get(ITEM_SCOPE)));
387 bus.setGridRoutingEnabled(options.getBool(ITEM_GRID_ENABLED));
388 bus.setGridSpacing(options.getInt(ITEM_GRID_SPACING));
389 bus.setMinimumDistanceToNode(options.getInt(ITEM_MIN_DISTANCE_TO_NODES));
390 bus.setMinimumDistanceToEdge(options.getInt(ITEM_MIN_DISTANCE_TO_EDGES));
391 }
392
393 if (optionsSelection) {
394 bus.setPreferredBackboneSegmentCount(options.getInt(ITEM_PREFERRED_BACKBONE_COUNT));
395 bus.setMinimumBackboneSegmentLength(options.getDouble(ITEM_MINIMUM_BACKBONE_LENGTH));
396 }
397
398 if (optionsRouting) {
399 bus.setCrossingCost(options.getDouble(ITEM_CROSSING_COST));
400 bus.setReroutingEnabled(options.getBool(ITEM_CROSSING_REROUTING));
401 bus.setMinimumBusConnectionsCount(options.getInt(ITEM_MINIMUM_CONNECTIONS_COUNT));
402 }
403 }
404
405 private static Object getBusId(final Graph2D graph, final Edge edge, final DataProvider component, final String busType) {
406 if (VALUE_COLOR.equals(busType)) {
407 return graph.getRealizer(edge).getLineColor();
408 } else if (VALUE_CONNECTED_COMPONENT.equals(busType)) {
409 return component.get(edge.source());
410 }
411 return VALUE_SINGLE;
413 }
414
415 private static byte toBusRouterScope(final Object scopeName) {
416 if (VALUE_SCOPE_ALL.equals(scopeName)) {
417 return BusRouter.SCOPE_ALL;
418 } else if (VALUE_SCOPE_SUBSET.equals(scopeName) ||
419 VALUE_SCOPE_SUBSET_BUS.equals(scopeName) ||
420 VALUE_SCOPE_PARTIAL.equals(scopeName) ||
421 VALUE_SCOPE_EDGES_AT_SELECTED_NODES.equals(scopeName)) {
422 return BusRouter.SCOPE_SUBSET;
423 } else {
424 return BusRouter.SCOPE_ALL;
425 }
426 }
427
428
436 private static boolean isOrthogonal(final Edge edge, final LayoutGraph graph) {
437 final YPointPath path = graph.getPath(edge);
438 YPoint last = null;
439 for (YPointCursor cur = path.points(); cur.ok(); cur.next()) {
440 final YPoint current = cur.point();
441 if (last != null && Math.abs(last.x - current.x) > 0.00001 && Math.abs(last.y - current.y) > 0.00001) {
442 return false;
443 }
444 last = current;
445 }
446 return true;
447 }
448
449
453 private static class EdgeHidingStage extends AbstractLayoutStage {
454
455 private static final Object EDGE_HIDE_DPKEY = "y.module.BusRouterModule.EdgeHiderStage.EDGE_HIDE_DPKEY";
456
457 EdgeHidingStage(Layouter core) {
458 super(core);
459 }
460
461 public boolean canLayout(LayoutGraph graph) {
462 return canLayoutCore(graph);
463 }
464
465 public void doLayout(LayoutGraph graph) {
466 GraphHider hider = null;
467 final DataProvider edge2ShouldHide = graph.getDataProvider(EDGE_HIDE_DPKEY);
468 if (edge2ShouldHide != null) {
469 hider = new GraphHider(graph);
470 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
471 final Edge edge = ec.edge();
472 if (edge2ShouldHide.getBool(edge)) {
473 hider.hide(edge);
474 }
475 }
476 }
477
478 doLayoutCore(graph);
479
480 if (hider != null) {
481 hider.unhideEdges();
482 }
483 }
484 }
485 }
486