Mercurial > projects > dwt-addons
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/graph/HorizontalPlacement.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,428 @@ +/******************************************************************************* + * Copyright (c) 2003, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.graph.HorizontalPlacement; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.SpanningTreeVisitor; +import dwtx.draw2d.graph.NodeCluster; +import dwtx.draw2d.graph.NodePair; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.RankList; +import dwtx.draw2d.graph.CollapsedEdges; +import dwtx.draw2d.graph.Rank; +import dwtx.draw2d.graph.EdgeList; +import dwtx.draw2d.graph.InitialRankSolver; +import dwtx.draw2d.graph.TightSpanningTreeSolver; +import dwtx.draw2d.graph.RankAssignmentSolver; + +/** + * Assigns the X and width values for nodes in a directed graph. + * @author Randy Hudson + * @since 2.1.2 + */ +class HorizontalPlacement : SpanningTreeVisitor { + +class ClusterSet { + int freedom = Integer.MAX_VALUE; + bool isRight; + public List members; + int pullWeight = 0; + int rawPull = 0; + + public this(){ + members = new ArrayList(); + } + + bool addCluster (NodeCluster seed) { + members.add(seed); + seed.isSetMember = true; + + rawPull += seed.weightedTotal; + pullWeight += seed.weightedDivisor; + if (isRight) { + freedom = Math.min(freedom, seed.rightNonzero); + if (freedom is 0 || rawPull <= 0) + return true; + addIncomingClusters(seed); + if (addOutgoingClusters(seed)) + return true; + } else { + freedom = Math.min(freedom, seed.leftNonzero); + if (freedom is 0 || rawPull >= 0) + return true; + addOutgoingClusters(seed); + if (addIncomingClusters(seed)) + return true; + } + return false; + } + + bool addIncomingClusters(NodeCluster seed) { + for (int i = 0; i < seed.leftCount; i++) { + NodeCluster neighbor = seed.leftNeighbors[i]; + if (neighbor.isSetMember) + continue; + CollapsedEdges edges = seed.leftLinks[i]; + if (!edges.isTight()) + continue; + if ((!isRight || neighbor.getPull() > 0) && addCluster (neighbor)) + return true; + } + return false; + } + + bool addOutgoingClusters(NodeCluster seed) { + for (int i = 0; i < seed.rightCount; i++) { + NodeCluster neighbor = seed.rightNeighbors[i]; + if (neighbor.isSetMember) + continue; + CollapsedEdges edges = seed.rightLinks[i]; + if (!edges.isTight()) + continue; + if ((isRight || neighbor.getPull() < 0) && addCluster (neighbor)) + return true; + } + return false; + } + + bool build(NodeCluster seed) { + isRight = seed.weightedTotal > 0; + if (!addCluster(seed)) { + int delta = rawPull / pullWeight; + if (delta < 0) + delta = Math.max(delta, -freedom); + else + delta = Math.min(delta, freedom); + if (delta !is 0) { + for (int i = 0; i < members.size(); i++) { + NodeCluster c = cast(NodeCluster)members.get(i); + c.adjustRank(delta, dirtyClusters); + } + refreshDirtyClusters(); + reset(); + return true; + } + } + reset(); + return false; + } + + void reset() { + rawPull = pullWeight = 0; + for (int i = 0; i < members.size(); i++) + (cast(NodeCluster)members.get(i)).isSetMember = false; + members.clear(); + freedom = Integer.MAX_VALUE; + } +} + +static int step; +private List allClusters; +private Map clusterMap; +ClusterSet clusterset; +Collection dirtyClusters; +DirectedGraph graph; +Map map_; +DirectedGraph prime; +Node graphRight; +Node graphLeft; + +public this(){ + clusterMap = new HashMap(); + clusterset = new ClusterSet(); + dirtyClusters = new HashSet(); + map_ = new HashMap(); +} + + +/** + * Inset the corresponding parts for the given 2 nodes along an edge E. The weight value + * is a value by which to scale the edges specified weighting factor. + * @param u the source + * @param v the target + * @param e the edge along which u and v exist + * @param weight a scaling for the weight + */ +void addEdge(Node u, Node v, Edge e, int weight) { + Node ne = new Node(new NodePair(u, v)); + prime.nodes.add(ne); + + ne.y = (u.y + u.height + v.y) / 2; + Node uPrime = get(u); + Node vPrime = get(v); + + int uOffset = e.getSourceOffset(); + + int vOffset = e.getTargetOffset(); + + Edge eu = new Edge(ne, uPrime, 0, e.weight * weight); + Edge ev = new Edge(ne, vPrime, 0, e.weight * weight); + + int dw = uOffset - vOffset; + if (dw < 0) + eu.delta = -dw; + else + ev.delta = dw; + + prime.edges.add(eu); + prime.edges.add(ev); +} + +/** + * Adds all of the incoming edges to the graph. + * @param n the original node + * @param nPrime its corresponding node in the auxilary graph + */ +void addEdges(Node n) { + for (int i = 0; i < n.incoming.size(); i++) { + Edge e = n.incoming.getEdge(i); + addEdge(e.source, n, e, 1); + } +} + +void applyGPrime() { + Node node; + for (int n = 0; n < prime.nodes.size(); n++) { + node = prime.nodes.getNode(n); + if (null !is cast(Node)node.data ) + (cast(Node)node.data).x = node.rank; + } +} + +private void balanceClusters() { + findAllClusters(); + + step = 0; + bool somethingMoved = false; + + for (int i = 0; i < allClusters.size();) { + + NodeCluster c = cast(NodeCluster)allClusters.get(i); + int delta = c.getPull(); + if (delta < 0) { + if (c.leftFreedom > 0) { + c.adjustRank(Math.max(delta, -c.leftFreedom), dirtyClusters); + refreshDirtyClusters(); + moveClusterForward(i, c); + somethingMoved = true; + step++; + } else if (clusterset.build(c)) { + step++; + moveClusterForward(i, c); + somethingMoved = true; + } + } else if (delta > 0) { + if (c.rightFreedom > 0) { + c.adjustRank(Math.min(delta, c.rightFreedom), dirtyClusters); + refreshDirtyClusters(); + moveClusterForward(i, c); + somethingMoved = true; + step++; + } else if (clusterset.build(c)) { + step++; + moveClusterForward(i, c); + somethingMoved = true; + } + } + i++; + if (i is allClusters.size() && somethingMoved) { + i = 0; + somethingMoved = false; + } + } +} + +//bool balanceClusterSets() { +// for (int i = 0; i < allClusters.size(); i++) { +// NodeCluster c = (NodeCluster)allClusters.get(i); +// if (c.weightedTotal < 0 && c.leftFreedom is 0) { +// if (clusterset.build(c)) { +// moveClusterForward(i, c); +// return true; +// } +// } else if (c.weightedTotal > 0 && c.rightFreedom is 0) { +// if (clusterset.build(c)) { +// moveClusterForward(i, c); +// return true; +// } +// } +// } +// return false; +//} + +void buildGPrime() { + RankList ranks = graph.ranks; + buildRankSeparators(ranks); + + Rank rank; + Node n; + for (int r = 1; r < ranks.size(); r++) { + rank = ranks.getRank(r); + for (int i = 0; i < rank.count(); i++) { + n = rank.getNode(i); + addEdges(n); + } + } +} + +void buildRankSeparators(RankList ranks) { + Rank rank; + Node n, nPrime, prevNPrime; + Edge e; + for (int r = 0; r < ranks.size(); r++) { + rank = ranks.getRank(r); + prevNPrime = null; + for (int i = 0; i < rank.count(); i++) { + n = rank.getNode(i); + nPrime = new Node(n); + if (i is 0) { + e = new Edge(graphLeft, nPrime, 0, 0); + prime.edges.add(e); + e.delta = graph.getPadding(n).left + graph.getMargin().left; + } else { + e = new Edge(prevNPrime, nPrime); + e.weight = 0; + prime.edges.add(e); + rowSeparation(e); + } + prevNPrime = nPrime; + prime.nodes.add(nPrime); + map(n, nPrime); + if (i is rank.count() - 1) { + e = new Edge(nPrime, graphRight, 0, 0); + e.delta = n.width + graph.getPadding(n).right + graph.getMargin().right; + prime.edges.add(e); + } + } + } +} + +private void calculateCellLocations() { + graph.cellLocations = new int[][](graph.ranks.size() + 1); + for (int row = 0; row < graph.ranks.size(); row++) { + Rank rank = graph.ranks.getRank(row); + int locations[] = graph.cellLocations[row] = new int[rank.size() + 1]; + int cell; + Node node = null; + for (cell = 0; cell < rank.size(); cell++) { + node = rank.getNode(cell); + locations[cell] = node.x - graph.getPadding(node).left; + } + locations[cell] = node.x + node.width + graph.getPadding(node).right; + } +} + +private void findAllClusters() { + Node root = prime.nodes.getNode(0); + NodeCluster cluster = new NodeCluster(); + allClusters = new ArrayList(); + allClusters.add(cluster); + growCluster(root, cluster); + + for (int i = 0; i < prime.edges.size(); i++) { + Edge e = prime.edges.getEdge(i); + NodeCluster sourceCluster = cast(NodeCluster)clusterMap.get(e.source); + NodeCluster targetCluster = cast(NodeCluster)clusterMap.get(e.target); + + //Ignore cluster internal edges + if (targetCluster is sourceCluster) + continue; + + CollapsedEdges link = sourceCluster.getRightNeighbor(targetCluster); + if (link is null) { + link = new CollapsedEdges(e); + sourceCluster.addRightNeighbor(targetCluster, link); + targetCluster.addLeftNeighbor(sourceCluster, link); + } else { + prime.removeEdge(link.processEdge(e)); + i--; + } + } + for (int i = 0; i < allClusters.size(); i++) + (cast(NodeCluster)allClusters.get(i)).initValues(); +} + +Node get(Node key) { + return cast(Node)map_.get(key); +} + +void growCluster(Node root, NodeCluster cluster) { + cluster.add(root); + clusterMap.put(root, cluster); + EdgeList treeChildren = getSpanningTreeChildren(root); + for (int i = 0; i < treeChildren.size(); i++) { + Edge e = treeChildren.getEdge(i); + if (e.cut !is 0) + growCluster(getTreeTail(e), cluster); + else { + NodeCluster newCluster = new NodeCluster(); + allClusters.add(newCluster); + growCluster(getTreeTail(e), newCluster); + } + } +} + +void map(Node key, Node value) { + map_.put(key, value); +} + +private void moveClusterForward(int i, NodeCluster c) { + if (i is 0) + return; + int swapIndex = i / 2; + Object temp = allClusters.get(swapIndex); + allClusters.set(swapIndex, c); + allClusters.set(i, temp); +} + +void refreshDirtyClusters() { + for (Iterator iter = dirtyClusters.iterator(); iter.hasNext();) + (cast(NodeCluster)iter.next()).refreshValues(); + dirtyClusters.clear(); +} + +void rowSeparation(Edge e) { + Node source = cast(Node)e.source.data; + Node target = cast(Node)e.target.data; + e.delta = source.width + + graph.getPadding(source).right + + graph.getPadding(target).left; +} + +public void visit(DirectedGraph g) { + graph = g; + prime = new DirectedGraph(); + prime.nodes.add(graphLeft = new Node(cast(Object)null)); + prime.nodes.add(graphRight = new Node(cast(Object)null)); + if (g.tensorStrength !is 0) + prime.edges.add(new Edge(graphLeft, graphRight, g.tensorSize, g.tensorStrength)); + buildGPrime(); + (new InitialRankSolver()) + .visit(prime); + (new TightSpanningTreeSolver()) + .visit(prime); + + RankAssignmentSolver solver = new RankAssignmentSolver(); + solver.visit(prime); + graph.size.width = graphRight.rank; + balanceClusters(); + + prime.nodes.adjustRank(-graphLeft.rank); + applyGPrime(); + calculateCellLocations(); +} + +}