| DiagonalLayouter.java |
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