Mercurial > projects > dwt-addons
comparison dwtx/draw2d/graph/HorizontalPlacement.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
96:b492ba44e44d | 98:95307ad235d9 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2003, 2007 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.draw2d.graph.HorizontalPlacement; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 import dwtx.dwtxhelper.Collection; | |
17 import dwtx.draw2d.graph.SpanningTreeVisitor; | |
18 import dwtx.draw2d.graph.NodeCluster; | |
19 import dwtx.draw2d.graph.NodePair; | |
20 import dwtx.draw2d.graph.Node; | |
21 import dwtx.draw2d.graph.Edge; | |
22 import dwtx.draw2d.graph.DirectedGraph; | |
23 import dwtx.draw2d.graph.RankList; | |
24 import dwtx.draw2d.graph.CollapsedEdges; | |
25 import dwtx.draw2d.graph.Rank; | |
26 import dwtx.draw2d.graph.EdgeList; | |
27 import dwtx.draw2d.graph.InitialRankSolver; | |
28 import dwtx.draw2d.graph.TightSpanningTreeSolver; | |
29 import dwtx.draw2d.graph.RankAssignmentSolver; | |
30 | |
31 /** | |
32 * Assigns the X and width values for nodes in a directed graph. | |
33 * @author Randy Hudson | |
34 * @since 2.1.2 | |
35 */ | |
36 class HorizontalPlacement : SpanningTreeVisitor { | |
37 | |
38 class ClusterSet { | |
39 int freedom = Integer.MAX_VALUE; | |
40 bool isRight; | |
41 public List members; | |
42 int pullWeight = 0; | |
43 int rawPull = 0; | |
44 | |
45 public this(){ | |
46 members = new ArrayList(); | |
47 } | |
48 | |
49 bool addCluster (NodeCluster seed) { | |
50 members.add(seed); | |
51 seed.isSetMember = true; | |
52 | |
53 rawPull += seed.weightedTotal; | |
54 pullWeight += seed.weightedDivisor; | |
55 if (isRight) { | |
56 freedom = Math.min(freedom, seed.rightNonzero); | |
57 if (freedom is 0 || rawPull <= 0) | |
58 return true; | |
59 addIncomingClusters(seed); | |
60 if (addOutgoingClusters(seed)) | |
61 return true; | |
62 } else { | |
63 freedom = Math.min(freedom, seed.leftNonzero); | |
64 if (freedom is 0 || rawPull >= 0) | |
65 return true; | |
66 addOutgoingClusters(seed); | |
67 if (addIncomingClusters(seed)) | |
68 return true; | |
69 } | |
70 return false; | |
71 } | |
72 | |
73 bool addIncomingClusters(NodeCluster seed) { | |
74 for (int i = 0; i < seed.leftCount; i++) { | |
75 NodeCluster neighbor = seed.leftNeighbors[i]; | |
76 if (neighbor.isSetMember) | |
77 continue; | |
78 CollapsedEdges edges = seed.leftLinks[i]; | |
79 if (!edges.isTight()) | |
80 continue; | |
81 if ((!isRight || neighbor.getPull() > 0) && addCluster (neighbor)) | |
82 return true; | |
83 } | |
84 return false; | |
85 } | |
86 | |
87 bool addOutgoingClusters(NodeCluster seed) { | |
88 for (int i = 0; i < seed.rightCount; i++) { | |
89 NodeCluster neighbor = seed.rightNeighbors[i]; | |
90 if (neighbor.isSetMember) | |
91 continue; | |
92 CollapsedEdges edges = seed.rightLinks[i]; | |
93 if (!edges.isTight()) | |
94 continue; | |
95 if ((isRight || neighbor.getPull() < 0) && addCluster (neighbor)) | |
96 return true; | |
97 } | |
98 return false; | |
99 } | |
100 | |
101 bool build(NodeCluster seed) { | |
102 isRight = seed.weightedTotal > 0; | |
103 if (!addCluster(seed)) { | |
104 int delta = rawPull / pullWeight; | |
105 if (delta < 0) | |
106 delta = Math.max(delta, -freedom); | |
107 else | |
108 delta = Math.min(delta, freedom); | |
109 if (delta !is 0) { | |
110 for (int i = 0; i < members.size(); i++) { | |
111 NodeCluster c = cast(NodeCluster)members.get(i); | |
112 c.adjustRank(delta, dirtyClusters); | |
113 } | |
114 refreshDirtyClusters(); | |
115 reset(); | |
116 return true; | |
117 } | |
118 } | |
119 reset(); | |
120 return false; | |
121 } | |
122 | |
123 void reset() { | |
124 rawPull = pullWeight = 0; | |
125 for (int i = 0; i < members.size(); i++) | |
126 (cast(NodeCluster)members.get(i)).isSetMember = false; | |
127 members.clear(); | |
128 freedom = Integer.MAX_VALUE; | |
129 } | |
130 } | |
131 | |
132 static int step; | |
133 private List allClusters; | |
134 private Map clusterMap; | |
135 ClusterSet clusterset; | |
136 Collection dirtyClusters; | |
137 DirectedGraph graph; | |
138 Map map_; | |
139 DirectedGraph prime; | |
140 Node graphRight; | |
141 Node graphLeft; | |
142 | |
143 public this(){ | |
144 clusterMap = new HashMap(); | |
145 clusterset = new ClusterSet(); | |
146 dirtyClusters = new HashSet(); | |
147 map_ = new HashMap(); | |
148 } | |
149 | |
150 | |
151 /** | |
152 * Inset the corresponding parts for the given 2 nodes along an edge E. The weight value | |
153 * is a value by which to scale the edges specified weighting factor. | |
154 * @param u the source | |
155 * @param v the target | |
156 * @param e the edge along which u and v exist | |
157 * @param weight a scaling for the weight | |
158 */ | |
159 void addEdge(Node u, Node v, Edge e, int weight) { | |
160 Node ne = new Node(new NodePair(u, v)); | |
161 prime.nodes.add(ne); | |
162 | |
163 ne.y = (u.y + u.height + v.y) / 2; | |
164 Node uPrime = get(u); | |
165 Node vPrime = get(v); | |
166 | |
167 int uOffset = e.getSourceOffset(); | |
168 | |
169 int vOffset = e.getTargetOffset(); | |
170 | |
171 Edge eu = new Edge(ne, uPrime, 0, e.weight * weight); | |
172 Edge ev = new Edge(ne, vPrime, 0, e.weight * weight); | |
173 | |
174 int dw = uOffset - vOffset; | |
175 if (dw < 0) | |
176 eu.delta = -dw; | |
177 else | |
178 ev.delta = dw; | |
179 | |
180 prime.edges.add(eu); | |
181 prime.edges.add(ev); | |
182 } | |
183 | |
184 /** | |
185 * Adds all of the incoming edges to the graph. | |
186 * @param n the original node | |
187 * @param nPrime its corresponding node in the auxilary graph | |
188 */ | |
189 void addEdges(Node n) { | |
190 for (int i = 0; i < n.incoming.size(); i++) { | |
191 Edge e = n.incoming.getEdge(i); | |
192 addEdge(e.source, n, e, 1); | |
193 } | |
194 } | |
195 | |
196 void applyGPrime() { | |
197 Node node; | |
198 for (int n = 0; n < prime.nodes.size(); n++) { | |
199 node = prime.nodes.getNode(n); | |
200 if (null !is cast(Node)node.data ) | |
201 (cast(Node)node.data).x = node.rank; | |
202 } | |
203 } | |
204 | |
205 private void balanceClusters() { | |
206 findAllClusters(); | |
207 | |
208 step = 0; | |
209 bool somethingMoved = false; | |
210 | |
211 for (int i = 0; i < allClusters.size();) { | |
212 | |
213 NodeCluster c = cast(NodeCluster)allClusters.get(i); | |
214 int delta = c.getPull(); | |
215 if (delta < 0) { | |
216 if (c.leftFreedom > 0) { | |
217 c.adjustRank(Math.max(delta, -c.leftFreedom), dirtyClusters); | |
218 refreshDirtyClusters(); | |
219 moveClusterForward(i, c); | |
220 somethingMoved = true; | |
221 step++; | |
222 } else if (clusterset.build(c)) { | |
223 step++; | |
224 moveClusterForward(i, c); | |
225 somethingMoved = true; | |
226 } | |
227 } else if (delta > 0) { | |
228 if (c.rightFreedom > 0) { | |
229 c.adjustRank(Math.min(delta, c.rightFreedom), dirtyClusters); | |
230 refreshDirtyClusters(); | |
231 moveClusterForward(i, c); | |
232 somethingMoved = true; | |
233 step++; | |
234 } else if (clusterset.build(c)) { | |
235 step++; | |
236 moveClusterForward(i, c); | |
237 somethingMoved = true; | |
238 } | |
239 } | |
240 i++; | |
241 if (i is allClusters.size() && somethingMoved) { | |
242 i = 0; | |
243 somethingMoved = false; | |
244 } | |
245 } | |
246 } | |
247 | |
248 //bool balanceClusterSets() { | |
249 // for (int i = 0; i < allClusters.size(); i++) { | |
250 // NodeCluster c = (NodeCluster)allClusters.get(i); | |
251 // if (c.weightedTotal < 0 && c.leftFreedom is 0) { | |
252 // if (clusterset.build(c)) { | |
253 // moveClusterForward(i, c); | |
254 // return true; | |
255 // } | |
256 // } else if (c.weightedTotal > 0 && c.rightFreedom is 0) { | |
257 // if (clusterset.build(c)) { | |
258 // moveClusterForward(i, c); | |
259 // return true; | |
260 // } | |
261 // } | |
262 // } | |
263 // return false; | |
264 //} | |
265 | |
266 void buildGPrime() { | |
267 RankList ranks = graph.ranks; | |
268 buildRankSeparators(ranks); | |
269 | |
270 Rank rank; | |
271 Node n; | |
272 for (int r = 1; r < ranks.size(); r++) { | |
273 rank = ranks.getRank(r); | |
274 for (int i = 0; i < rank.count(); i++) { | |
275 n = rank.getNode(i); | |
276 addEdges(n); | |
277 } | |
278 } | |
279 } | |
280 | |
281 void buildRankSeparators(RankList ranks) { | |
282 Rank rank; | |
283 Node n, nPrime, prevNPrime; | |
284 Edge e; | |
285 for (int r = 0; r < ranks.size(); r++) { | |
286 rank = ranks.getRank(r); | |
287 prevNPrime = null; | |
288 for (int i = 0; i < rank.count(); i++) { | |
289 n = rank.getNode(i); | |
290 nPrime = new Node(n); | |
291 if (i is 0) { | |
292 e = new Edge(graphLeft, nPrime, 0, 0); | |
293 prime.edges.add(e); | |
294 e.delta = graph.getPadding(n).left + graph.getMargin().left; | |
295 } else { | |
296 e = new Edge(prevNPrime, nPrime); | |
297 e.weight = 0; | |
298 prime.edges.add(e); | |
299 rowSeparation(e); | |
300 } | |
301 prevNPrime = nPrime; | |
302 prime.nodes.add(nPrime); | |
303 map(n, nPrime); | |
304 if (i is rank.count() - 1) { | |
305 e = new Edge(nPrime, graphRight, 0, 0); | |
306 e.delta = n.width + graph.getPadding(n).right + graph.getMargin().right; | |
307 prime.edges.add(e); | |
308 } | |
309 } | |
310 } | |
311 } | |
312 | |
313 private void calculateCellLocations() { | |
314 graph.cellLocations = new int[][](graph.ranks.size() + 1); | |
315 for (int row = 0; row < graph.ranks.size(); row++) { | |
316 Rank rank = graph.ranks.getRank(row); | |
317 int locations[] = graph.cellLocations[row] = new int[rank.size() + 1]; | |
318 int cell; | |
319 Node node = null; | |
320 for (cell = 0; cell < rank.size(); cell++) { | |
321 node = rank.getNode(cell); | |
322 locations[cell] = node.x - graph.getPadding(node).left; | |
323 } | |
324 locations[cell] = node.x + node.width + graph.getPadding(node).right; | |
325 } | |
326 } | |
327 | |
328 private void findAllClusters() { | |
329 Node root = prime.nodes.getNode(0); | |
330 NodeCluster cluster = new NodeCluster(); | |
331 allClusters = new ArrayList(); | |
332 allClusters.add(cluster); | |
333 growCluster(root, cluster); | |
334 | |
335 for (int i = 0; i < prime.edges.size(); i++) { | |
336 Edge e = prime.edges.getEdge(i); | |
337 NodeCluster sourceCluster = cast(NodeCluster)clusterMap.get(e.source); | |
338 NodeCluster targetCluster = cast(NodeCluster)clusterMap.get(e.target); | |
339 | |
340 //Ignore cluster internal edges | |
341 if (targetCluster is sourceCluster) | |
342 continue; | |
343 | |
344 CollapsedEdges link = sourceCluster.getRightNeighbor(targetCluster); | |
345 if (link is null) { | |
346 link = new CollapsedEdges(e); | |
347 sourceCluster.addRightNeighbor(targetCluster, link); | |
348 targetCluster.addLeftNeighbor(sourceCluster, link); | |
349 } else { | |
350 prime.removeEdge(link.processEdge(e)); | |
351 i--; | |
352 } | |
353 } | |
354 for (int i = 0; i < allClusters.size(); i++) | |
355 (cast(NodeCluster)allClusters.get(i)).initValues(); | |
356 } | |
357 | |
358 Node get(Node key) { | |
359 return cast(Node)map_.get(key); | |
360 } | |
361 | |
362 void growCluster(Node root, NodeCluster cluster) { | |
363 cluster.add(root); | |
364 clusterMap.put(root, cluster); | |
365 EdgeList treeChildren = getSpanningTreeChildren(root); | |
366 for (int i = 0; i < treeChildren.size(); i++) { | |
367 Edge e = treeChildren.getEdge(i); | |
368 if (e.cut !is 0) | |
369 growCluster(getTreeTail(e), cluster); | |
370 else { | |
371 NodeCluster newCluster = new NodeCluster(); | |
372 allClusters.add(newCluster); | |
373 growCluster(getTreeTail(e), newCluster); | |
374 } | |
375 } | |
376 } | |
377 | |
378 void map(Node key, Node value) { | |
379 map_.put(key, value); | |
380 } | |
381 | |
382 private void moveClusterForward(int i, NodeCluster c) { | |
383 if (i is 0) | |
384 return; | |
385 int swapIndex = i / 2; | |
386 Object temp = allClusters.get(swapIndex); | |
387 allClusters.set(swapIndex, c); | |
388 allClusters.set(i, temp); | |
389 } | |
390 | |
391 void refreshDirtyClusters() { | |
392 for (Iterator iter = dirtyClusters.iterator(); iter.hasNext();) | |
393 (cast(NodeCluster)iter.next()).refreshValues(); | |
394 dirtyClusters.clear(); | |
395 } | |
396 | |
397 void rowSeparation(Edge e) { | |
398 Node source = cast(Node)e.source.data; | |
399 Node target = cast(Node)e.target.data; | |
400 e.delta = source.width | |
401 + graph.getPadding(source).right | |
402 + graph.getPadding(target).left; | |
403 } | |
404 | |
405 public void visit(DirectedGraph g) { | |
406 graph = g; | |
407 prime = new DirectedGraph(); | |
408 prime.nodes.add(graphLeft = new Node(cast(Object)null)); | |
409 prime.nodes.add(graphRight = new Node(cast(Object)null)); | |
410 if (g.tensorStrength !is 0) | |
411 prime.edges.add(new Edge(graphLeft, graphRight, g.tensorSize, g.tensorStrength)); | |
412 buildGPrime(); | |
413 (new InitialRankSolver()) | |
414 .visit(prime); | |
415 (new TightSpanningTreeSolver()) | |
416 .visit(prime); | |
417 | |
418 RankAssignmentSolver solver = new RankAssignmentSolver(); | |
419 solver.visit(prime); | |
420 graph.size.width = graphRight.rank; | |
421 balanceClusters(); | |
422 | |
423 prime.nodes.adjustRank(-graphLeft.rank); | |
424 applyGPrime(); | |
425 calculateCellLocations(); | |
426 } | |
427 | |
428 } |