1   
28  package demo.view.mindmap;
29  
30  import y.base.DataProvider;
31  import y.base.Edge;
32  import y.base.EdgeCursor;
33  import y.base.EdgeMap;
34  import y.base.Node;
35  import y.base.NodeCursor;
36  import y.base.NodeList;
37  import y.base.NodeMap;
38  import y.geom.YPoint;
39  import y.layout.AbstractLayoutStage;
40  import y.layout.FixNodeLayoutStage;
41  import y.layout.LayoutGraph;
42  import y.layout.Layouter;
43  import y.layout.PortConstraint;
44  import y.layout.PortConstraintKeys;
45  import y.layout.tree.AbstractRotatableNodePlacer;
46  import y.layout.tree.DefaultPortAssignment;
47  import y.layout.tree.DelegatingNodePlacer;
48  import y.layout.tree.GenericTreeLayouter;
49  import y.layout.tree.LayeredNodePlacer;
50  import y.layout.tree.TreeReductionStage;
51  import y.util.DataProviderAdapter;
52  import y.util.GraphHider;
53  import y.view.Graph2D;
54  import y.view.Graph2DLayoutExecutor;
55  import y.view.Graph2DView;
56  import y.view.NodeRealizer;
57  
58  import java.util.Comparator;
59  
60  
63  class LayoutUtil {
64    
67    private LayoutUtil() {
68    }
69  
70    
75    static void addPlacersAndComparators( final Graph2D graph ) {
76      final DataProvider nodePlacers = new NodePlacerProvider();
77      graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacers);
78      graph.addDataProvider(
79              GenericTreeLayouter.CHILD_COMPARATOR_DPKEY,
80              new ChildComparatorProvider());
81    }
82  
83    
89    static void addLeftRightMap( final Graph2D graph, final NodeMap map ) {
90      graph.addDataProvider(DelegatingNodePlacer.LEFT_RIGHT_DPKEY, map);
91    }
92  
93    
98    static NodeMap getLeftRightMap( final Graph2D graph ) {
99      return (NodeMap) graph.getDataProvider(DelegatingNodePlacer.LEFT_RIGHT_DPKEY);
100   }
101 
102   
108   static void addCrossReferencesMap( final Graph2D graph, final EdgeMap map ) {
109     graph.addDataProvider(TreeReductionStage.NON_TREE_EDGES_DPKEY, map);
110   }
111 
112   
117   static EdgeMap getCrossReferencesMap( final Graph2D graph ) {
118     return (EdgeMap) graph.getDataProvider(TreeReductionStage.NON_TREE_EDGES_DPKEY);
119   }
120 
121   
125   static void layout( final Graph2D graph ) {
126     layout(graph, null);
127   }
128 
129   
134   static void layoutSubtree( final Graph2D graph, final Node node ) {
135     layout(graph, node);
136     graph.updateViews();
137   }
138 
139 
140   
146   private static void layout( final Graph2D graph, final Node node ) {
147             final ViewModel model = ViewModel.instance;
150     for (EdgeCursor edgeCursor = graph.edges(); edgeCursor.ok(); edgeCursor.next()) {
151       final Edge edge = edgeCursor.edge();
152             if (model.isCrossReference(edge)) {
154         graph.setSourcePointRel(edge, new YPoint(0, 0));
155         graph.setTargetPointRel(edge, new YPoint(0, 0));
156       } else {
157         final Node source = edge.source();
158         final Node target = edge.target();
159         final NodeRealizer sourceRealizer = graph.getRealizer(source);
160         final NodeRealizer targetRealizer = graph.getRealizer(target);
161         final YPoint p;
162         final YPoint p2;
163                         if (model.isLeft(target)) {
166           p = new YPoint(-sourceRealizer.getWidth() * 0.5, sourceRealizer.getHeight() * 0.5);
167           p2 = new YPoint(targetRealizer.getWidth() * 0.5, targetRealizer.getHeight() * 0.5);
168         } else {
169           p = new YPoint(sourceRealizer.getWidth() * 0.5, sourceRealizer.getHeight() * 0.5);
170           p2 = new YPoint(-targetRealizer.getWidth() * 0.5, targetRealizer.getHeight() * 0.5);
171         }
172                         if (!model.isRoot(source)) {
175           graph.setSourcePointRel(edge, p);
176         } else {
177           graph.setSourcePointRel(edge, YPoint.ORIGIN);
178         }
179         graph.setTargetPointRel(edge, p2);
180       }
181     }
182 
183         graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, new DataProviderAdapter() {
185       public Object get( Object dataHolder ) {
186         if (dataHolder instanceof Edge) {
187                     if (ViewModel.instance.isCrossReference((Edge) dataHolder)) {
189             return PortConstraint.create(PortConstraint.ANY_SIDE, true);
190                       } else {
192             final boolean isLeftSide = ViewModel.instance.isLeft(((Edge) dataHolder).target());
193             if (isLeftSide) {
194               return PortConstraint.create(PortConstraint.WEST, true);
195             } else {
196               return PortConstraint.create(PortConstraint.EAST, true);
197             }
198           }
199         }
200         return null;
201       }
202     });
203         graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, new DataProviderAdapter() {
205       public Object get( Object dataHolder ) {
206         if (dataHolder instanceof Edge) {
207           if (ViewModel.instance.isCrossReference((Edge) dataHolder)) {
208             return PortConstraint.create(PortConstraint.ANY_SIDE, true);
209           } else {
210             boolean isLeftSide = ViewModel.instance.isLeft(((Edge) dataHolder).target());
211             if (isLeftSide) {
212               return PortConstraint.create(PortConstraint.EAST, true);
213             } else {
214               return PortConstraint.create(PortConstraint.WEST, true);
215             }
216           }
217         }
218         return null;
219       }
220     });
221     GenericTreeLayouter treeLayouter = new GenericTreeLayouter();
222     treeLayouter.setDefaultPortAssignment(new DefaultPortAssignment(DefaultPortAssignment.MODE_PORT_CONSTRAINTS));
223 
224         TreeReductionStage trs = new TreeReductionStage();
226     treeLayouter.appendStage(trs);
227 
228     Layouter layouter = treeLayouter;
229 
230     final DataProviderAdapter rootDp = new DataProviderAdapter() {
231       final ViewModel model = ViewModel.instance;
232       public boolean getBool( final Object dataHolder ) {
233         return model.isRoot((Node) dataHolder);
234       }
235     };
236 
237     final Graph2DLayoutExecutor layoutExecutor = new Graph2DLayoutExecutor();
238     layoutExecutor.getLayoutMorpher().setKeepZoomFactor(true);
239     layoutExecutor.getLayoutMorpher().setPreferredDuration(300);
240     layoutExecutor.getLayoutMorpher().setEasedExecution(true);
241 
242     if (node != null) {
243       layouter = new SubtreeLayoutStage(layouter);
244       layoutExecutor.setMode(Graph2DLayoutExecutor.BUFFERED);
245       graph.addDataProvider(SubtreeLayoutStage.GLOBAL_ROOT_DPKEY, rootDp);
246       graph.addDataProvider(SubtreeLayoutStage.SUBTREE_ROOT_DPKEY, new DataProviderAdapter() {
247         public boolean getBool( final Object dataHolder ) {
248           return dataHolder == node;
249         }
250       });
251     } else {
252       layouter = new FixNodeLayoutStage(treeLayouter);
253       layoutExecutor.setMode(Graph2DLayoutExecutor.ANIMATED);
254       graph.addDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY, rootDp);
255     }
256 
257     final Object view = graph.getCurrentView();
258     if (view instanceof Graph2DView) {
259       layoutExecutor.doLayout((Graph2DView) view, layouter);
260     } else {
261       layoutExecutor.doLayout(graph, layouter);
262     }
263 
264     if (node != null) {
265       graph.removeDataProvider(SubtreeLayoutStage.SUBTREE_ROOT_DPKEY);
266       graph.removeDataProvider(SubtreeLayoutStage.GLOBAL_ROOT_DPKEY);
267     } else {
268       graph.removeDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY);
269     }
270   }
271 
272 
273   
279   private static class SubtreeLayoutStage extends AbstractLayoutStage {
280     
284     static final Object GLOBAL_ROOT_DPKEY =
285             "demo.view.mindmap.MindMapUtil.GLOBAL_ROOT_DPKEY";
286     
290     static final Object SUBTREE_ROOT_DPKEY =
291             "demo.view.mindmap.MindMapUtil.SUBTREE_ROOT_DPKEY";
292 
293     
299     SubtreeLayoutStage( final Layouter core ) {
300       super(new FixNodeLayoutStage(core));
301     }
302 
303     public boolean canLayout( final LayoutGraph graph ) {
304       return canLayoutCore(graph);
305     }
306 
307     public void doLayout( final LayoutGraph graph ) {
308       GraphHider hider = new GraphHider(graph);
309 
310             final DataProvider crossEdges = graph.getDataProvider(TreeReductionStage.NON_TREE_EDGES_DPKEY);
312       if (crossEdges != null) {
313         for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
314           final Edge edge = ec.edge();
315           if (crossEdges.getBool(edge)) {
316             hider.hide(edge);
317           }
318         }
319       }
320 
321             final DataProvider isRootNodeProvider = graph.getDataProvider(GLOBAL_ROOT_DPKEY);
323       Node root = null;
324       for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
325         final Node node = nc.node();
326         if (isRootNodeProvider.getBool(node)) {
327           root = node;
328           break;
329         }
330       }
331 
332                   NodeList stack = new NodeList(root);
335       final DataProvider subtreeRootProvider = graph.getDataProvider(SUBTREE_ROOT_DPKEY);
336       while (!stack.isEmpty()) {
337         Node currentNode = stack.popNode();
338         if (!subtreeRootProvider.getBool(currentNode)) {
339           for (EdgeCursor ec = currentNode.outEdges(); ec.ok(); ec.next()) {
340             stack.push(ec.edge().target());
341           }
342           hider.hide(currentNode);
343         }
344       }
345 
346 
347             graph.addDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY, subtreeRootProvider);
349 
350             doLayoutCore(graph);
352 
353       graph.removeDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY);
354 
355 
356             hider.unhideAll();
358     }
359   }
360 
361   
368   private static final class NodePlacerProvider extends DataProviderAdapter {
369     private final DelegatingNodePlacer rootPlacer;
370 
371     NodePlacerProvider() {
372       rootPlacer = new DelegatingNodePlacer(
373               AbstractRotatableNodePlacer.Matrix.DEFAULT,
374               newPlacer(AbstractRotatableNodePlacer.Matrix.ROT270),
375               newPlacer(AbstractRotatableNodePlacer.Matrix.ROT90));
376       rootPlacer.setSpacing(1);
377     }
378 
379     public Object get( final Object dataHolder ) {
380       final Node node = (Node) dataHolder;
381       if (ViewModel.instance.isRoot(node)) {
382         return rootPlacer;
383       } else {
384         if (ViewModel.instance.isLeft(node)) {
385           return rootPlacer.getPlacerUpperLeft();
386         } else {
387           return rootPlacer.getPlacerLowerRight();
388         }
389       }
390     }
391 
392     private static LayeredNodePlacer newPlacer(
393             final AbstractRotatableNodePlacer.Matrix matrix
394     ) {
395       final LayeredNodePlacer placer = new LayeredNodePlacer(matrix, matrix);
396       placer.setRoutingStyle(LayeredNodePlacer.ORTHOGONAL_STYLE);
397       placer.setRootAlignment(AbstractRotatableNodePlacer.RootAlignment.CENTER);
398       placer.setSpacing(10);
399       placer.setLayerSpacing(45);
400       placer.setVerticalAlignment(0);
401       return placer;
402     }
403   }
404 
405   
410   private static final class ChildComparatorProvider extends DataProviderAdapter {
411     public Object get( final Object dataHolder ) {
412       return new YCoordComparator(true);
413     }
414   }
415   
416   
424   static final class YCoordComparator implements Comparator {
425     private final boolean considerSides;
426 
427     public YCoordComparator() {
428       this(false);
429     }
430 
431     public YCoordComparator(boolean considerSides) {
432       this.considerSides = considerSides;
433     }
434 
435     public int compare( final Object o1, final Object o2 ) {
436       final Edge edge1 = (Edge) o1;
437       final Edge edge2 = (Edge) o2;
438       final double y1 = getY(edge1);
439       final double y2 = getY(edge2);
440 
441       if (!considerSides || isLeft(edge1.target())) {
442         return Double.compare(y1, y2);
443       } else {
444         return Double.compare(y2, y1);
445       }
446     }
447 
448     private boolean isLeft(Node node) {
449       return Boolean.TRUE.equals(node.getGraph().getDataProvider(DelegatingNodePlacer.LEFT_RIGHT_DPKEY).get(node));
450     }
451 
452     private double getY( final Edge edge ) {
453       return ((LayoutGraph) edge.getGraph()).getY(edge.target());
454     }
455   }
456 }
457