1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.layout;
15  
16  import java.util.*;
17  
18  import y.base.Node;
19  import y.base.Edge;
20  import y.base.EdgeCursor;
21  import y.base.EdgeList;
22  
23  import y.layout.EdgeLayout;
24  import y.layout.LayoutGraph;
25  import y.layout.CanonicMultiStageLayouter;
26  
27  import y.geom.YPoint;
28  
29  /**
30   * This class demonstrates how to write a 
31   * custom layouter for the yFiles framework.
32   * <br>
33   * This class lays out a graph in the following style:
34   * <br>
35   * The nodes of each graph component will be placed on a 
36   * diagonal line.
37   * Edges will be routed with exactly one bend, so that no 
38   * edges that share a common terminal node will cross.
39   * <br>
40   * See {@link demo.module.DiagonalLayoutModule} for a module wrapper
41   * and {@link demo.module.LayoutModuleDemo} for the diagonal layouter in action. 
42   */
43  public class DiagonalLayouter extends CanonicMultiStageLayouter
44  {
45    double minimalNodeDistance = 40;
46    
47    /**
48     * Creates a new instance of DiagonalLayouter
49     */
50    public DiagonalLayouter()
51    {
52      //do not use defualt behaviour. we handle parallel edge routing ourselves.  
53      setParallelEdgeLayouterEnabled(false);
54    }
55    
56    /**
57     * Sets the minimal distance between nodes.
58     */
59    public void setMinimalNodeDistance(double d)
60    {
61      minimalNodeDistance = d;
62    }
63    
64    /**
65     * Returns the minimal distance between nodes.
66     */
67    public double getMinimalNodeDistance()
68    {
69      return minimalNodeDistance;
70    }
71    
72    /**
73     * Returns always <code>true</code>, because every graph can be
74     * layed out.
75     */
76    public boolean canLayoutCore(LayoutGraph graph)
77    {
78      return true;
79    }
80    
81    /**
82     * Perform the layout.
83     */
84    protected void doLayoutCore(LayoutGraph graph)
85    {
86  
87      //place the nodes on a diagonal line
88      Node nodes[] = graph.getNodeArray();
89      double offset = 0.0;
90      for(int i = 0; i < nodes.length; i++)
91      {
92        Node v = nodes[i];
93        graph.setLocation(v,offset,offset);
94        offset += minimalNodeDistance + Math.max(graph.getWidth(v),graph.getHeight(v));
95      }
96      
97      //comparator used to sort edges by the
98      //index of their target node
99      Comparator outComp = new Comparator() {
100       public int compare(Object a, Object b) {
101         Node va = ((Edge)a).target();
102         Node vb = ((Edge)b).target();
103         if(va != vb) 
104           return va.index() - vb.index();
105         else
106           return ((Edge)a).index() - ((Edge)b).index();
107       }
108     };
109     
110     //comparator used to sort edges by the
111     //index of their source node.
112     Comparator inComp = new Comparator() {
113       public int compare(Object a, Object b) {
114         Node va = ((Edge)a).source();
115         Node vb = ((Edge)b).source();
116         if(va != vb) 
117           return va.index() - vb.index();
118         else
119           return ((Edge)b).index() - ((Edge)a).index();
120       }
121     };
122     
123     //prepare edge layout. use exactly one bend per edge
124     for(EdgeCursor ec = graph.edges(); ec.ok(); ec.next())
125     {
126       EdgeLayout el = graph.getLayout(ec.edge());
127       el.clearPoints();
128       el.addPoint(0,0);
129     }
130     
131     //route the edges
132     for(int i = 0; i < nodes.length; i++)
133     {
134       Node v = nodes[i];
135 
136       
137       EdgeList rightSide  = new EdgeList();
138       EdgeList leftSide   = new EdgeList();
139       
140       //assign x coodinates to all outgoing edges of v
141       v.sortOutEdges(outComp);
142       for(EdgeCursor ec = v.outEdges(); ec.ok(); ec.next())
143       {
144         Edge e = ec.edge();
145         Node w = e.target();
146         
147         if(w.index() < v.index())
148           rightSide.addLast(e);
149         else
150           leftSide.addLast(e);
151       }
152       
153       if(!rightSide.isEmpty())
154       {
155         double space  = graph.getWidth(v)/rightSide.size();
156         double xcoord = graph.getX(v) + graph.getWidth(v) - space/2.0;
157         for(EdgeCursor ec = rightSide.edges(); ec.ok(); ec.next())
158         {
159           Edge e = ec.edge();
160           EdgeLayout el = graph.getLayout(e);
161           YPoint p = el.getPoint(0);
162           el.setPoint(0, xcoord, p.getY());
163           graph.setSourcePointAbs(e, new YPoint(xcoord, graph.getCenterY(v)));
164           xcoord -= space;
165         }
166       }
167       
168       if(!leftSide.isEmpty())
169       {
170         double space  = graph.getWidth(v)/leftSide.size();
171         double xcoord = graph.getX(v) + graph.getWidth(v) - space/2.0;
172         for(EdgeCursor ec = leftSide.edges(); ec.ok(); ec.next())
173         {
174           Edge e = ec.edge();
175           EdgeLayout el = graph.getLayout(e);
176           YPoint p = el.getPoint(0);
177           el.setPoint(0, xcoord, p.getY());
178           graph.setSourcePointAbs(e, new YPoint(xcoord,graph.getCenterY(v)));
179           xcoord -= space;
180         }
181       }
182       
183       //assign y coodinates to all ingoing edges of v
184       rightSide.clear();
185       leftSide.clear();
186       v.sortInEdges(inComp);
187       for(EdgeCursor ec = v.inEdges(); ec.ok(); ec.next())
188       {
189         Edge e = ec.edge();
190         Node w = e.source();
191         
192         if(w.index() < v.index())
193           leftSide.addLast(e);
194         else
195           rightSide.addLast(e);
196       }
197       
198       if(!rightSide.isEmpty())
199       {
200         double space  = graph.getHeight(v)/rightSide.size();
201         double ycoord = graph.getY(v) + graph.getHeight(v) - space/2.0;
202         for(EdgeCursor ec = rightSide.edges(); ec.ok(); ec.next())
203         {
204           Edge e = ec.edge();
205           EdgeLayout el = graph.getLayout(e);
206           YPoint p = el.getPoint(0);
207           el.setPoint(0, p.getX(), ycoord);
208           graph.setTargetPointAbs(e, new YPoint(graph.getCenterX(v), ycoord));
209           ycoord -= space;
210         }
211       }
212       
213       if(!leftSide.isEmpty())
214       {
215         double space  = graph.getHeight(v)/leftSide.size();
216         double ycoord = graph.getY(v) + graph.getHeight(v) - space/2.0;
217         for(EdgeCursor ec = leftSide.edges(); ec.ok(); ec.next())
218         {
219           Edge e = ec.edge();
220           EdgeLayout el = graph.getLayout(e);
221           YPoint p = el.getPoint(0);
222           el.setPoint(0, p.getX(), ycoord);
223           graph.setTargetPointAbs(e, new YPoint(graph.getCenterX(v), ycoord));
224           ycoord -= space;
225         }
226       }
227     }
228   }
229 }
230