view 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 source

/*******************************************************************************
 * 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);
    }
}

}