Mercurial > projects > dwt-addons
diff dwtx/draw2d/graph/BreakCycles.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/BreakCycles.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2003, 2005 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.BreakCycles; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; +import dwtx.draw2d.graph.GraphVisitor; +import dwtx.draw2d.graph.NodeList; +import dwtx.draw2d.graph.DirectedGraph; +import dwtx.draw2d.graph.Node; +import dwtx.draw2d.graph.Edge; + +/** + * This visitor eliminates cycles in the graph using a "greedy" heuristic. Nodes which + * are sources and sinks are marked and placed in a source and sink list, leaving only + * nodes involved in cycles. A remaining node with the highest (outgoing-incoming) edges + * score is then chosen greedily as if it were a source. The process is repeated until all + * nodes have been marked and placed in a list. The lists are then concatenated, and any + * edges which go backwards in this list will be inverted during the layout procedure. + * + * @author Daniel Lee + * @since 2.1.2 + */ +class BreakCycles : GraphVisitor { + +// Used in identifying cycles and in cycle removal. +// Flag field indicates "presence". If true, the node has been removed from the list. +NodeList graphNodes; + +public this(){ + graphNodes = new NodeList(); +} + +private bool allNodesFlagged() { + for (int i = 0; i < graphNodes.size(); i++) { + if (graphNodes.getNode(i).flag is false) + return false; + } + return true; +} + +private void breakCycles(DirectedGraph g) { + initializeDegrees(g); + greedyCycleRemove(g); + invertEdges(g); +} + +/* + * Returns true if g contains cycles, false otherwise + */ +private bool containsCycles(DirectedGraph g) { + List noLefts = new ArrayList(); + //Identify all initial nodes for removal + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getIncomingCount(node) is 0) + sortedInsert(noLefts, node); + } + + while (noLefts.size() > 0) { + Node node = cast(Node)noLefts.remove(noLefts.size() - 1); + node.flag = true; + for (int i = 0; i < node.outgoing.size(); i++) { + Node right = node.outgoing.getEdge(i).target; + setIncomingCount(right, getIncomingCount(right) - 1); + if (getIncomingCount(right) is 0) + sortedInsert(noLefts, right); + } + } + + if (allNodesFlagged()) + return false; + return true; +} + +/* + * Returns the node in graphNodes with the largest + * (outgoing edge count - incoming edge count) value + */ +private Node findNodeWithMaxDegree() { + int max = Integer.MIN_VALUE; + Node maxNode = null; + + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getDegree(node) >= max && node.flag is false) { + max = getDegree(node); + maxNode = node; + } + } + return maxNode; +} + +private int getDegree(Node n) { + return n.workingInts[3]; +} + +private int getIncomingCount(Node n) { + return n.workingInts[0]; +} + +private int getInDegree(Node n) { + return n.workingInts[1]; +} + +private int getOrderIndex(Node n) { + return n.workingInts[0]; +} + +private int getOutDegree(Node n) { + return n.workingInts[2]; +} + +private void greedyCycleRemove(DirectedGraph g) { + NodeList sL = new NodeList(); + NodeList sR = new NodeList(); + + do { + // Add all sinks and isolated nodes to sR + bool hasSink; + do { + hasSink = false; + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getOutDegree(node) is 0 && node.flag is false) { + hasSink = true; + node.flag = true; + updateIncoming(node); + sR.add(node); + break; + } + } + } while (hasSink); + + // Add all sources to sL + bool hasSource; + do { + hasSource = false; + for (int i = 0; i < graphNodes.size(); i++) { + Node node = graphNodes.getNode(i); + if (getInDegree(node) is 0 && node.flag is false) { + hasSource = true; + node.flag = true; + updateOutgoing(node); + sL.add(node); + break; + } + } + } while (hasSource); + + // When all sinks and sources are removed, choose a node with the + // maximum degree (outDegree - inDegree) and add it to sL + Node max = findNodeWithMaxDegree(); + if (max !is null) { + sL.add(max); + max.flag = true; + updateIncoming(max); + updateOutgoing(max); + } + } while (!allNodesFlagged()); + + // Assign order indexes + int orderIndex = 0; + for (int i = 0; i < sL.size(); i++) { + setOrderIndex(sL.getNode(i), orderIndex++); + } + for (int i = sR.size() - 1; i >= 0; i--) { + setOrderIndex(sR.getNode(i), orderIndex++); + } +} + +private void initializeDegrees(DirectedGraph g) { + graphNodes.resetFlags(); + for (int i = 0; i < g.nodes.size(); i++) { + Node n = graphNodes.getNode(i); + setInDegree(n, n.incoming.size()); + setOutDegree(n, n.outgoing.size()); + setDegree(n, n.outgoing.size() - n.incoming.size()); + } +} + +private void invertEdges(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (getOrderIndex(e.source) > getOrderIndex(e.target)) { + e.invert(); + e.isFeedback_ = true; + } + } +} + +private void setDegree(Node n, int deg) { + n.workingInts[3] = deg; +} + +private void setIncomingCount(Node n, int count) { + n.workingInts[0] = count; +} + +private void setInDegree(Node n, int deg) { + n.workingInts[1] = deg; +} + +private void setOutDegree(Node n, int deg) { + n.workingInts[2] = deg; +} + +private void setOrderIndex(Node n, int index) { + n.workingInts[0] = index; +} + +private void sortedInsert(List list, Node node) { + int insert = 0; + while (insert < list.size() + && (cast(Node)list.get(insert)).sortValue > node.sortValue) + insert++; + list.add(insert, node); +} + +/* + * Called after removal of n. Updates the degree values of n's incoming nodes. + */ +private void updateIncoming(Node n) { + for (int i = 0; i < n.incoming.size(); i++) { + Node in_ = n.incoming.getEdge(i).source; + if (in_.flag is false) { + setOutDegree(in_, getOutDegree(in_) - 1); + setDegree(in_, getOutDegree(in_) - getInDegree(in_)); + } + } +} + +/* + * Called after removal of n. Updates the degree values of n's outgoing nodes. + */ +private void updateOutgoing(Node n) { + for (int i = 0; i < n.outgoing.size(); i++) { + Node out_ = n.outgoing.getEdge(i).target; + if (out_.flag is false) { + setInDegree(out_, getInDegree(out_) - 1); + setDegree(out_, getOutDegree(out_) - getInDegree(out_)); + } + } +} + +public void revisit(DirectedGraph g) { + for (int i = 0; i < g.edges.size(); i++) { + Edge e = g.edges.getEdge(i); + if (e.isFeedback()) + e.invert(); + } +} + +/** + * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph) + */ +public void visit(DirectedGraph g) { + // put all nodes in list, initialize index + graphNodes.resetFlags(); + for (int i = 0; i < g.nodes.size(); i++) { + Node n = g.nodes.getNode(i); + setIncomingCount(n, n.incoming.size()); + graphNodes.add(n); + } + if (containsCycles(g)) { + breakCycles(g); + } +} + +}