blob: 0e4e4a6a57245672e89228e23beda6d59b6e0dd4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2010 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
*******************************************************************************/
package org.eclipse.draw2d.graph;
/**
* Finds a tight spanning tree from the graphs edges which induce a valid rank
* assignment. This process requires that the nodes be initially given a
* feasible ranking.
*
* @author Randy Hudson
* @since 2.1.2
*/
class TightSpanningTreeSolver extends SpanningTreeVisitor {
protected DirectedGraph graph;
protected CandidateList candidates = new CandidateList();
static final class CandidateList {
private Edge edges[] = new Edge[10];
private int size;
public void add(Edge edge) {
if (size == edges.length - 1) {
Edge newEdges[] = new Edge[edges.length * 2];
System.arraycopy(edges, 0, newEdges, 0, edges.length);
edges = newEdges;
}
edges[size++] = edge;
}
public Edge getEdge(int index) {
return edges[index];
}
public void remove(Edge edge) {
for (int i = 0; i < size; i++) {
if (edges[i] == edge) {
edges[i] = edges[size - 1];
size--;
return;
}
}
throw new RuntimeException("Remove called on invalid Edge"); //$NON-NLS-1$
}
public int size() {
return size;
}
}
protected NodeList members = new NodeList();
public void visit(DirectedGraph graph) {
this.graph = graph;
init();
solve();
}
Node addEdge(Edge edge) {
int delta = edge.getSlack();
edge.tree = true;
Node node;
if (edge.target.flag) {
delta = -delta;
node = edge.source;
setParentEdge(node, edge);
getSpanningTreeChildren(edge.target).add(edge);
} else {
node = edge.target;
setParentEdge(node, edge);
getSpanningTreeChildren(edge.source).add(edge);
}
members.adjustRank(delta);
addNode(node);
return node;
}
private boolean isNodeReachable(Node node) {
return node.flag;
}
private void setNodeReachable(Node node) {
node.flag = true;
}
private boolean isCandidate(Edge e) {
return e.flag;
}
private void setCandidate(Edge e) {
e.flag = true;
}
void addNode(Node node) {
setNodeReachable(node);
EdgeList list = node.incoming;
Edge e;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (!isNodeReachable(e.source)) {
if (!isCandidate(e)) {
setCandidate(e);
candidates.add(e);
}
} else
candidates.remove(e);
}
list = node.outgoing;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (!isNodeReachable(e.target)) {
if (!isCandidate(e)) {
setCandidate(e);
candidates.add(e);
}
} else
candidates.remove(e);
}
members.add(node);
}
void init() {
graph.edges.resetFlags(true);
graph.nodes.resetFlags();
for (int i = 0; i < graph.nodes.size(); i++) {
Node node = (Node) graph.nodes.get(i);
node.workingData[0] = new EdgeList();
}
}
protected void solve() {
Node root = graph.nodes.getNode(0);
setParentEdge(root, null);
addNode(root);
while (members.size() < graph.nodes.size()) {
if (candidates.size() == 0)
throw new RuntimeException("graph is not fully connected");//$NON-NLS-1$
int minSlack = Integer.MAX_VALUE, slack;
Edge minEdge = null, edge;
for (int i = 0; i < candidates.size() && minSlack > 0; i++) {
edge = candidates.getEdge(i);
slack = edge.getSlack();
if (slack < minSlack) {
minSlack = slack;
minEdge = edge;
}
}
addEdge(minEdge);
}
graph.nodes.normalizeRanks();
}
}