1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.withoutview;
29  
30  import java.util.*;
31  
32  import y.base.Node;
33  import y.base.Edge;
34  import y.base.EdgeCursor;
35  import y.base.EdgeList;
36  
37  import y.layout.EdgeLayout;
38  import y.layout.LayoutGraph;
39  import y.layout.CanonicMultiStageLayouter;
40  
41  import y.geom.YPoint;
42  
43  /**
44   * This class demonstrates how to write a 
45   * custom layouter for the yFiles framework.
46   * <br>
47   * This class lays out a graph in the following style:
48   * <br>
49   * The nodes of each graph component will be placed on a 
50   * diagonal line.
51   * Edges will be routed with exactly one bend, so that no 
52   * edges that share a common terminal node will cross.
53   * <br>
54   * See {@link demo.layout.module.DiagonalLayoutModule} for a module wrapper
55   * and {@link demo.layout.module.LayoutModuleDemo} for the diagonal layouter in action. 
56   */
57  public class DiagonalLayouter extends CanonicMultiStageLayouter
58  {
59    double minimalNodeDistance = 40;
60    
61    /**
62     * Creates a new instance of DiagonalLayouter
63     */
64    public DiagonalLayouter()
65    {
66      //do not use defualt behaviour. we handle parallel edge routing ourselves.  
67      setParallelEdgeLayouterEnabled(false);
68    }
69    
70    /**
71     * Sets the minimal distance between nodes.
72     */
73    public void setMinimalNodeDistance(double d)
74    {
75      minimalNodeDistance = d;
76    }
77    
78    /**
79     * Returns the minimal distance between nodes.
80     */
81    public double getMinimalNodeDistance()
82    {
83      return minimalNodeDistance;
84    }
85    
86    /**
87     * Returns always <code>true</code>, because every graph can be
88     * laid out.
89     */
90    protected boolean canLayoutCore(LayoutGraph graph)
91    {
92      return true;
93    }
94    
95    /**
96     * Perform the layout.
97     */
98    protected void doLayoutCore(LayoutGraph graph)
99    {
100 
101     //place the nodes on a diagonal line
102     Node[] nodes = graph.getNodeArray();
103     double offset = 0.0;
104     for(int i = 0; i < nodes.length; i++)
105     {
106       Node v = nodes[i];
107       graph.setLocation(v,offset,offset);
108       offset += minimalNodeDistance + Math.max(graph.getWidth(v),graph.getHeight(v));
109     }
110     
111     //comparator used to sort edges by the
112     //index of their target node
113     Comparator outComp = new Comparator() {
114       public int compare(Object a, Object b) {
115         Node va = ((Edge)a).target();
116         Node vb = ((Edge)b).target();
117         if(va != vb) 
118           return va.index() - vb.index();
119         else
120           return ((Edge)a).index() - ((Edge)b).index();
121       }
122     };
123     
124     //comparator used to sort edges by the
125     //index of their source node.
126     Comparator inComp = new Comparator() {
127       public int compare(Object a, Object b) {
128         Node va = ((Edge)a).source();
129         Node vb = ((Edge)b).source();
130         if(va != vb) 
131           return va.index() - vb.index();
132         else
133           return ((Edge)b).index() - ((Edge)a).index();
134       }
135     };
136     
137     //prepare edge layout. use exactly one bend per edge
138     for(EdgeCursor ec = graph.edges(); ec.ok(); ec.next())
139     {
140       EdgeLayout el = graph.getLayout(ec.edge());
141       el.clearPoints();
142       el.addPoint(0,0);
143     }
144     
145     //route the edges
146     for(int i = 0; i < nodes.length; i++)
147     {
148       Node v = nodes[i];
149 
150       
151       EdgeList rightSide  = new EdgeList();
152       EdgeList leftSide   = new EdgeList();
153       
154       //assign x coodinates to all outgoing edges of v
155       v.sortOutEdges(outComp);
156       for(EdgeCursor ec = v.outEdges(); ec.ok(); ec.next())
157       {
158         Edge e = ec.edge();
159         Node w = e.target();
160         
161         if(w.index() < v.index())
162           rightSide.addLast(e);
163         else
164           leftSide.addLast(e);
165       }
166       
167       if(!rightSide.isEmpty())
168       {
169         double space  = graph.getWidth(v)/rightSide.size();
170         double xcoord = graph.getX(v) + graph.getWidth(v) - space/2.0;
171         for(EdgeCursor ec = rightSide.edges(); ec.ok(); ec.next())
172         {
173           Edge e = ec.edge();
174           EdgeLayout el = graph.getLayout(e);
175           YPoint p = el.getPoint(0);
176           el.setPoint(0, xcoord, p.getY());
177           graph.setSourcePointAbs(e, new YPoint(xcoord, graph.getCenterY(v)));
178           xcoord -= space;
179         }
180       }
181       
182       if(!leftSide.isEmpty())
183       {
184         double space  = graph.getWidth(v)/leftSide.size();
185         double xcoord = graph.getX(v) + graph.getWidth(v) - space/2.0;
186         for(EdgeCursor ec = leftSide.edges(); ec.ok(); ec.next())
187         {
188           Edge e = ec.edge();
189           EdgeLayout el = graph.getLayout(e);
190           YPoint p = el.getPoint(0);
191           el.setPoint(0, xcoord, p.getY());
192           graph.setSourcePointAbs(e, new YPoint(xcoord,graph.getCenterY(v)));
193           xcoord -= space;
194         }
195       }
196       
197       //assign y coodinates to all ingoing edges of v
198       rightSide.clear();
199       leftSide.clear();
200       v.sortInEdges(inComp);
201       for(EdgeCursor ec = v.inEdges(); ec.ok(); ec.next())
202       {
203         Edge e = ec.edge();
204         Node w = e.source();
205         
206         if(w.index() < v.index())
207           leftSide.addLast(e);
208         else
209           rightSide.addLast(e);
210       }
211       
212       if(!rightSide.isEmpty())
213       {
214         double space  = graph.getHeight(v)/rightSide.size();
215         double ycoord = graph.getY(v) + graph.getHeight(v) - space/2.0;
216         for(EdgeCursor ec = rightSide.edges(); ec.ok(); ec.next())
217         {
218           Edge e = ec.edge();
219           EdgeLayout el = graph.getLayout(e);
220           YPoint p = el.getPoint(0);
221           el.setPoint(0, p.getX(), ycoord);
222           graph.setTargetPointAbs(e, new YPoint(graph.getCenterX(v), ycoord));
223           ycoord -= space;
224         }
225       }
226       
227       if(!leftSide.isEmpty())
228       {
229         double space  = graph.getHeight(v)/leftSide.size();
230         double ycoord = graph.getY(v) + graph.getHeight(v) - space/2.0;
231         for(EdgeCursor ec = leftSide.edges(); ec.ok(); ec.next())
232         {
233           Edge e = ec.edge();
234           EdgeLayout el = graph.getLayout(e);
235           YPoint p = el.getPoint(0);
236           el.setPoint(0, p.getX(), ycoord);
237           graph.setTargetPointAbs(e, new YPoint(graph.getCenterX(v), ycoord));
238           ycoord -= space;
239         }
240       }
241     }
242   }
243 }
244