blob: dbde2c58b2096def7963f16327161ec38279cc18 [file] [log] [blame]
/*
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package com.sun.hotspot.igv.hierarchicallayout;
import com.sun.hotspot.igv.layout.LayoutGraph;
import com.sun.hotspot.igv.layout.LayoutManager;
import com.sun.hotspot.igv.layout.Link;
import com.sun.hotspot.igv.layout.Vertex;
import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeSet;
/**
*
* @author Thomas Wuerthinger
*/
public class HierarchicalLayoutManager implements LayoutManager {
public static final boolean TRACE = false;
public static final boolean CHECK = false;
public static final int SWEEP_ITERATIONS = 1;
public static final int CROSSING_ITERATIONS = 2;
public static final int DUMMY_HEIGHT = 1;
public static final int DUMMY_WIDTH = 1;
public static final int X_OFFSET = 9;
public static final int LAYER_OFFSET = 30;
public static final int MAX_LAYER_LENGTH = -1;
public static final int MIN_LAYER_DIFFERENCE = 1;
public enum Combine {
NONE,
SAME_INPUTS,
SAME_OUTPUTS
}
// Options
private Combine combine;
private int dummyWidth;
private int dummyHeight;
private int xOffset;
private int layerOffset;
private int maxLayerLength;
private int minLayerDifference;
// Algorithm global datastructures
private Set<Link> reversedLinks;
private List<LayoutNode> nodes;
private HashMap<Vertex, LayoutNode> vertexToLayoutNode;
private HashMap<Link, List<Point>> reversedLinkStartPoints;
private HashMap<Link, List<Point>> reversedLinkEndPoints;
private HashMap<LayoutEdge, LayoutEdge> bottomEdgeHash;
private HashMap<Link, List<Point>> splitStartPoints;
private HashMap<Link, List<Point>> splitEndPoints;
private LayoutGraph graph;
private List<LayoutNode>[] layers;
private int layerCount;
private Set<? extends Vertex> firstLayerHint;
private Set<? extends Vertex> lastLayerHint;
private Set<? extends Link> importantLinks;
private Set<Link> linksToFollow;
private class LayoutNode {
public int x;
public int y;
public int width;
public int height;
public int layer = -1;
public int xOffset;
public int yOffset;
public int bottomYOffset;
public Vertex vertex; // Only used for non-dummy nodes, otherwise null
public List<LayoutEdge> preds = new ArrayList<LayoutEdge>();
public List<LayoutEdge> succs = new ArrayList<LayoutEdge>();
public HashMap<Integer, Integer> outOffsets = new HashMap<Integer, Integer>();
public HashMap<Integer, Integer> inOffsets = new HashMap<Integer, Integer>();
public int pos = -1; // Position within layer
public int crossingNumber;
@Override
public String toString() {
return "Node " + vertex;
}
}
private class LayoutEdge {
public LayoutNode from;
public LayoutNode to;
public int relativeFrom;
public int relativeTo;
public Link link;
}
private abstract class AlgorithmPart {
public void start() {
if (CHECK) {
preCheck();
}
long start = 0;
if (TRACE) {
System.out.println("##################################################");
System.out.println("Starting part " + this.getClass().getName());
start = System.currentTimeMillis();
}
run();
if (TRACE) {
System.out.println("Timing for " + this.getClass().getName() + " is " + (System.currentTimeMillis() - start));
printStatistics();
}
if (CHECK) {
postCheck();
}
}
protected abstract void run();
protected void printStatistics() {
}
protected void postCheck() {
}
protected void preCheck() {
}
}
public HierarchicalLayoutManager() {
this(Combine.NONE);
}
public HierarchicalLayoutManager(Combine b) {
this.combine = b;
this.dummyWidth = DUMMY_WIDTH;
this.dummyHeight = DUMMY_HEIGHT;
this.xOffset = X_OFFSET;
this.layerOffset = LAYER_OFFSET;
this.maxLayerLength = MAX_LAYER_LENGTH;
this.minLayerDifference = MIN_LAYER_DIFFERENCE;
this.linksToFollow = new HashSet<Link>();
}
public int getMaxLayerLength() {
return maxLayerLength;
}
public void setMaxLayerLength(int v) {
maxLayerLength = v;
}
public void setMinLayerDifference(int v) {
minLayerDifference = v;
}
public void doLayout(LayoutGraph graph) {
doLayout(graph, new HashSet<Vertex>(), new HashSet<Vertex>(), new HashSet<Link>());
}
public void doLayout(LayoutGraph graph, Set<? extends Vertex> firstLayerHint, Set<? extends Vertex> lastLayerHint, Set<? extends Link> importantLinks) {
this.importantLinks = importantLinks;
this.graph = graph;
this.firstLayerHint = firstLayerHint;
this.lastLayerHint = lastLayerHint;
vertexToLayoutNode = new HashMap<Vertex, LayoutNode>();
reversedLinks = new HashSet<Link>();
reversedLinkStartPoints = new HashMap<Link, List<Point>>();
reversedLinkEndPoints = new HashMap<Link, List<Point>>();
bottomEdgeHash = new HashMap<LayoutEdge, LayoutEdge>();
nodes = new ArrayList<LayoutNode>();
splitStartPoints = new HashMap<Link, List<Point>>();
splitEndPoints = new HashMap<Link, List<Point>>();
// #############################################################
// Step 1: Build up data structure
new BuildDatastructure().start();
// #############################################################
// STEP 2: Reverse edges, handle backedges
new ReverseEdges().start();
for (LayoutNode n : nodes) {
ArrayList<LayoutEdge> tmpArr = new ArrayList<LayoutEdge>();
for (LayoutEdge e : n.succs) {
if (importantLinks.contains(e.link)) {
tmpArr.add(e);
}
}
for (LayoutEdge e : tmpArr) {
//System.out.println("Removed " + e);
e.from.succs.remove(e);
e.to.preds.remove(e);
}
}
// #############################################################
// STEP 3: Assign layers
new AssignLayers().start();
// #############################################################
// STEP 4: Create dummy nodes
new CreateDummyNodes().start();
// #############################################################
// STEP 5: Crossing Reduction
new CrossingReduction().start();
// #############################################################
// STEP 7: Assign X coordinates
//new AssignXCoordinates().start();
new AssignXCoordinates2().start();
// #############################################################
// STEP 6: Assign Y coordinates
new AssignYCoordinates().start();
// #############################################################
// STEP 8: Write back to interface
new WriteResult().start();
}
private class WriteResult extends AlgorithmPart {
private int pointCount;
protected void run() {
HashMap<Vertex, Point> vertexPositions = new HashMap<Vertex, Point>();
HashMap<Link, List<Point>> linkPositions = new HashMap<Link, List<Point>>();
for (Vertex v : graph.getVertices()) {
LayoutNode n = vertexToLayoutNode.get(v);
assert !vertexPositions.containsKey(v);
vertexPositions.put(v, new Point(n.x + n.xOffset, n.y + n.yOffset));
}
for (LayoutNode n : nodes) {
for (LayoutEdge e : n.preds) {
if (e.link != null) {
ArrayList<Point> points = new ArrayList<Point>();
Point p = new Point(e.to.x + e.relativeTo, e.to.y + e.to.yOffset);
points.add(p);
if (e.to.inOffsets.containsKey(e.relativeTo)) {
points.add(new Point(p.x, p.y + e.to.inOffsets.get(e.relativeTo)));
}
LayoutNode cur = e.from;
LayoutNode other = e.to;
LayoutEdge curEdge = e;
while (cur.vertex == null && cur.preds.size() != 0) {
if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
points.remove(points.size() - 1);
}
points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height));
if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
points.remove(points.size() - 1);
}
points.add(new Point(cur.x + cur.width / 2, cur.y));
assert cur.preds.size() == 1;
curEdge = cur.preds.get(0);
cur = curEdge.from;
}
p = new Point(cur.x + curEdge.relativeFrom, cur.y + cur.height - cur.bottomYOffset);
if (curEdge.from.outOffsets.containsKey(curEdge.relativeFrom)) {
points.add(new Point(p.x, p.y + curEdge.from.outOffsets.get(curEdge.relativeFrom)));
}
points.add(p);
Collections.reverse(points);
if (cur.vertex == null && cur.preds.size() == 0) {
if (reversedLinkEndPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkEndPoints.get(e.link)) {
points.add(new Point(p1.x + e.to.x, p1.y + e.to.y));
}
}
if (splitStartPoints.containsKey(e.link)) {
points.add(0, null);
points.addAll(0, splitStartPoints.get(e.link));
//checkPoints(points);
if (reversedLinks.contains(e.link)) {
Collections.reverse(points);
}
assert !linkPositions.containsKey(e.link);
linkPositions.put(e.link, points);
} else {
splitEndPoints.put(e.link, points);
}
} else {
if (reversedLinks.contains(e.link)) {
Collections.reverse(points);
}
if (reversedLinkStartPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkStartPoints.get(e.link)) {
points.add(new Point(p1.x + cur.x, p1.y + cur.y));
}
}
if (reversedLinkEndPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkEndPoints.get(e.link)) {
points.add(0, new Point(p1.x + other.x, p1.y + other.y));
}
}
assert !linkPositions.containsKey(e.link);
linkPositions.put(e.link, points);
}
pointCount += points.size();
// No longer needed!
e.link = null;
}
}
for (LayoutEdge e : n.succs) {
if (e.link != null) {
ArrayList<Point> points = new ArrayList<Point>();
Point p = new Point(e.from.x + e.relativeFrom, e.from.y + e.from.height - e.from.bottomYOffset);
points.add(p);
if (e.from.outOffsets.containsKey(e.relativeFrom)) {
points.add(new Point(p.x, p.y + e.from.outOffsets.get(e.relativeFrom)));
}
LayoutNode cur = e.to;
LayoutNode other = e.from;
LayoutEdge curEdge = e;
while (cur.vertex == null && cur.succs.size() != 0) {
if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
points.remove(points.size() - 1);
}
points.add(new Point(cur.x + cur.width / 2, cur.y));
if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
points.remove(points.size() - 1);
}
points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height));
if (cur.succs.size() == 0) {
break;
}
assert cur.succs.size() == 1;
curEdge = cur.succs.get(0);
cur = curEdge.to;
}
p = new Point(cur.x + curEdge.relativeTo, cur.y + cur.yOffset);
points.add(p);
if (curEdge.to.inOffsets.containsKey(curEdge.relativeTo)) {
points.add(new Point(p.x, p.y + curEdge.to.inOffsets.get(curEdge.relativeTo)));
}
if (cur.succs.size() == 0 && cur.vertex == null) {
if (reversedLinkStartPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkStartPoints.get(e.link)) {
points.add(0, new Point(p1.x + other.x, p1.y + other.y));
}
}
if (splitEndPoints.containsKey(e.link)) {
points.add(null);
points.addAll(splitEndPoints.get(e.link));
//checkPoints(points);
if (reversedLinks.contains(e.link)) {
Collections.reverse(points);
}
assert !linkPositions.containsKey(e.link);
linkPositions.put(e.link, points);
} else {
splitStartPoints.put(e.link, points);
}
} else {
if (reversedLinkStartPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkStartPoints.get(e.link)) {
points.add(0, new Point(p1.x + other.x, p1.y + other.y));
}
}
if (reversedLinkEndPoints.containsKey(e.link)) {
for (Point p1 : reversedLinkEndPoints.get(e.link)) {
points.add(new Point(p1.x + cur.x, p1.y + cur.y));
}
}
if (reversedLinks.contains(e.link)) {
Collections.reverse(points);
}
//checkPoints(points);
assert !linkPositions.containsKey(e.link);
linkPositions.put(e.link, points);
}
pointCount += points.size();
e.link = null;
}
}
}
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
for (Vertex v : vertexPositions.keySet()) {
Point p = vertexPositions.get(v);
minX = Math.min(minX, p.x);
minY = Math.min(minY, p.y);
}
for (Link l : linkPositions.keySet()) {
List<Point> points = linkPositions.get(l);
for (Point p : points) {
if (p != null) {
minX = Math.min(minX, p.x);
minY = Math.min(minY, p.y);
}
}
}
for (Vertex v : vertexPositions.keySet()) {
Point p = vertexPositions.get(v);
p.x -= minX;
p.y -= minY;
v.setPosition(p);
}
for (Link l : linkPositions.keySet()) {
List<Point> points = linkPositions.get(l);
for (Point p : points) {
if (p != null) {
p.x -= minX;
p.y -= minY;
}
}
l.setControlPoints(points);
}
}
@Override
protected void printStatistics() {
System.out.println("Number of nodes: " + nodes.size());
int edgeCount = 0;
for (LayoutNode n : nodes) {
edgeCount += n.succs.size();
}
System.out.println("Number of edges: " + edgeCount);
System.out.println("Number of points: " + pointCount);
}
}
private static class Segment {
public float d;
public int orderNumber = -1;
public ArrayList<LayoutNode> nodes = new ArrayList<LayoutNode>();
public HashSet<Segment> succs = new HashSet<Segment>();
public HashSet<Segment> preds = new HashSet<Segment>();
public Region region;
}
private static final Comparator<Segment> segmentComparator = new Comparator<Segment>() {
public int compare(Segment s1, Segment s2) {
return s1.orderNumber - s2.orderNumber;
}
};
private static class Region {
public float d;
public int minOrderNumber;
public SortedSet<Segment> segments = new TreeSet<Segment>(segmentComparator);
public HashSet<Region> succs = new HashSet<Region>(4);
public HashSet<Region> preds = new HashSet<Region>(4);
}
private static final Comparator<Region> regionComparator = new Comparator<Region>() {
public int compare(Region r1, Region r2) {
return r1.minOrderNumber - r2.minOrderNumber;
}
};
private static final Comparator<LayoutNode> nodePositionComparator = new Comparator<LayoutNode>() {
public int compare(LayoutNode n1, LayoutNode n2) {
return n1.pos - n2.pos;
}
};
private static final Comparator<LayoutNode> nodeProcessingDownComparator = new Comparator<LayoutNode>() {
public int compare(LayoutNode n1, LayoutNode n2) {
if (n1.vertex == null) {
return -1;
}
if (n2.vertex == null) {
return 1;
}
return n1.preds.size() - n2.preds.size();
}
};
private static final Comparator<LayoutNode> nodeProcessingUpComparator = new Comparator<LayoutNode>() {
public int compare(LayoutNode n1, LayoutNode n2) {
if (n1.vertex == null) {
return -1;
}
if (n2.vertex == null) {
return 1;
}
return n1.succs.size() - n2.succs.size();
}
};
private class AssignXCoordinates2 extends AlgorithmPart {
private ArrayList<Integer>[] space;
private ArrayList<LayoutNode>[] downProcessingOrder;
private ArrayList<LayoutNode>[] upProcessingOrder;
private void initialPositions() {
for (LayoutNode n : nodes) {
n.x = space[n.layer].get(n.pos);
}
}
protected void run() {
space = new ArrayList[layers.length];
downProcessingOrder = new ArrayList[layers.length];
upProcessingOrder = new ArrayList[layers.length];
for (int i = 0; i < layers.length; i++) {
space[i] = new ArrayList<Integer>();
downProcessingOrder[i] = new ArrayList<LayoutNode>();
upProcessingOrder[i] = new ArrayList<LayoutNode>();
int curX = 0;
for (LayoutNode n : layers[i]) {
space[i].add(curX);
curX += n.width + xOffset;
downProcessingOrder[i].add(n);
upProcessingOrder[i].add(n);
}
Collections.sort(downProcessingOrder[i], nodeProcessingDownComparator);
Collections.sort(upProcessingOrder[i], nodeProcessingUpComparator);
}
initialPositions();
for (int i = 0; i < SWEEP_ITERATIONS; i++) {
sweepDown();
sweepUp();
}
for (int i = 0; i < SWEEP_ITERATIONS; i++) {
doubleSweep();
}
}
private int calculateOptimalDown(LayoutNode n) {
List<Integer> values = new ArrayList<Integer>();
if (n.preds.size() == 0) {
return n.x;
}
for (LayoutEdge e : n.preds) {
int cur = e.from.x + e.relativeFrom - e.relativeTo;
values.add(cur);
}
return median(values);
}
private int calculateOptimalBoth(LayoutNode n) {
List<Integer> values = new ArrayList<Integer>();
if (n.preds.size() == 0 + n.succs.size()) {
return n.x;
}
for (LayoutEdge e : n.preds) {
int cur = e.from.x + e.relativeFrom - e.relativeTo;
values.add(cur);
}
for (LayoutEdge e : n.succs) {
int cur = e.to.x + e.relativeTo - e.relativeFrom;
values.add(cur);
}
return median(values);
}
private int calculateOptimalUp(LayoutNode n) {
//List<Integer> values = new ArrayList<Integer>();
int size = n.succs.size();
if (size == 0) {
return n.x;
} else {
int result = 0;
for (LayoutEdge e : n.succs) {
int cur = e.to.x + e.relativeTo - e.relativeFrom;
result += cur;
}
return result / size; //median(values);
}
}
private int median(List<Integer> values) {
Collections.sort(values);
if (values.size() % 2 == 0) {
return (values.get(values.size() / 2 - 1) + values.get(values.size() / 2)) / 2;
} else {
return values.get(values.size() / 2);
}
}
private void sweepUp() {
for (int i = layers.length - 1; i >= 0; i--) {
NodeRow r = new NodeRow(space[i]);
for (LayoutNode n : upProcessingOrder[i]) {
int optimal = calculateOptimalUp(n);
r.insert(n, optimal);
}
}
/*
for(int i=0; i<layers.length; i++) {
NodeRow r = new NodeRow(space[i]);
for(LayoutNode n : upProcessingOrder[i]) {
int optimal = calculateOptimalUp(n);
r.insert(n, optimal);
}
}*/
}
private void doubleSweep() {
for (int i = layers.length - 2; i >= 0; i--) {
NodeRow r = new NodeRow(space[i]);
for (LayoutNode n : upProcessingOrder[i]) {
int optimal = calculateOptimalBoth(n);
r.insert(n, optimal);
}
}
}
private void sweepDown() {
for (int i = 1; i < layers.length; i++) {
NodeRow r = new NodeRow(space[i]);
for (LayoutNode n : downProcessingOrder[i]) {
int optimal = calculateOptimalDown(n);
r.insert(n, optimal);
}
}
}
}
private static class NodeRow {
private TreeSet<LayoutNode> treeSet;
private ArrayList<Integer> space;
public NodeRow(ArrayList<Integer> space) {
treeSet = new TreeSet<LayoutNode>(nodePositionComparator);
this.space = space;
}
public int offset(LayoutNode n1, LayoutNode n2) {
int v1 = space.get(n1.pos) + n1.width;
int v2 = space.get(n2.pos);
return v2 - v1;
}
public void insert(LayoutNode n, int pos) {
SortedSet<LayoutNode> headSet = treeSet.headSet(n);
LayoutNode leftNeighbor = null;
int minX = Integer.MIN_VALUE;
if (!headSet.isEmpty()) {
leftNeighbor = headSet.last();
minX = leftNeighbor.x + leftNeighbor.width + offset(leftNeighbor, n);
}
if (pos < minX) {
n.x = minX;
} else {
LayoutNode rightNeighbor = null;
SortedSet<LayoutNode> tailSet = treeSet.tailSet(n);
int maxX = Integer.MAX_VALUE;
if (!tailSet.isEmpty()) {
rightNeighbor = tailSet.first();
maxX = rightNeighbor.x - offset(n, rightNeighbor) - n.width;
}
if (pos > maxX) {
n.x = maxX;
} else {
n.x = pos;
}
assert minX <= maxX;
}
treeSet.add(n);
}
}
private class AssignXCoordinates extends AlgorithmPart {
HashMap<LayoutNode, Segment> hashMap = new HashMap<LayoutNode, Segment>();
ArrayList<Segment> segments = new ArrayList<Segment>();
private void generateSegments() {
for (int i = 0; i < layerCount; i++) {
for (LayoutNode n : layers[i]) {
if (!hashMap.containsKey(n)) {
Segment s = new Segment();
segments.add(s);
LayoutNode curNode = n;
int maxLength = 0;
while (curNode.succs.size() == 1 && curNode.preds.size() == 1) {
s.nodes.add(curNode);
assert !hashMap.containsKey(curNode);
hashMap.put(curNode, s);
curNode = curNode.succs.get(0).to;
maxLength++;
//if(maxLength > 10) break;
}
if (s.nodes.size() > 0 && curNode.preds.size() == 1 && curNode.vertex == null) {
s.nodes.add(curNode);
assert !hashMap.containsKey(curNode);
hashMap.put(curNode, s);
}
if (s.nodes.size() == 0) {
// Simple segment with a single node
s.nodes.add(n);
hashMap.put(n, s);
}
}
}
}
}
private void addEdges() {
for (int i = 0; i < layerCount; i++) {
LayoutNode prev = null;
for (LayoutNode n : layers[i]) {
if (prev != null) {
Segment s1 = hashMap.get(prev);
Segment s2 = hashMap.get(n);
if (s1 != s2) {
s1.succs.add(s2);
s2.preds.add(s1);
}
}
prev = n;
}
}
}
private void topologicalSorting() {
Queue<Segment> queue = new LinkedList<Segment>();
int index = 0;
ArrayList<Segment> newList = new ArrayList<Segment>();
for (Segment s : segments) {
if (s.preds.size() == 0) {
s.orderNumber = index;
newList.add(s);
index++;
queue.add(s);
}
}
while (!queue.isEmpty()) {
Segment s = queue.remove();
for (Segment succ : s.succs) {
succ.preds.remove(s);
if (succ.preds.size() == 0) {
queue.add(succ);
succ.orderNumber = index;
newList.add(succ);
index++;
}
}
}
segments = newList;
}
private void initialPositions() {
int[] minPos = new int[layers.length];
for (Segment s : segments) {
int max = 0;
for (LayoutNode n : s.nodes) {
int x = minPos[n.layer];
if (x > max) {
max = x;
}
}
for (LayoutNode n : s.nodes) {
minPos[n.layer] = max + n.width + xOffset;
n.x = max;
}
}
}
private int predSum(LayoutNode n) {
int sum = 0;
for (LayoutEdge e : n.preds) {
assert e.to == n;
//sum += (e.from.x + e.relativeFrom + (int)hashMap.get(e.from).d) - (e.to.x + e.relativeTo + (int)hashMap.get(e.to).d);
sum += (e.from.x + e.relativeFrom) - (e.to.x + e.relativeTo);
}
return sum;
}
private int succSum(LayoutNode n) {
int sum = 0;
for (LayoutEdge e : n.succs) {
assert e.from == n;
sum += (e.to.x + e.relativeTo) - (e.from.x + e.relativeFrom);
//sum += (e.to.x + e.relativeTo + (int)hashMap.get(e.to).d) - (e.from.x + e.relativeFrom + (int)hashMap.get(e.from).d);
}
return sum;
}
private void downValues() {
for (Segment s : segments) {
downValues(s);
}
}
private void downValues(Segment s) {
LayoutNode n = s.nodes.get(0); // Only first node needed, all other have same coordinate
if (n.preds.size() == 0) {
// Value is 0.0;
if (n.succs.size() == 0) {
s.d = 0.0f;
} else {
s.d = (((float) succSum(n) / (float) n.succs.size())) / 2;
}
} else {
s.d = (float) predSum(n) / (float) n.preds.size();
}
}
private void upValues() {
for (Segment s : segments) {
upValues(s);
}
}
private void upValues(Segment s) {
LayoutNode n = s.nodes.get(0); // Only first node needed, all other have same coordinate
if (n.succs.size() == 0) {
// Value is 0.0;
if (n.preds.size() == 0) {
s.d = 0.0f;
} else {
s.d = (float) predSum(n) / (float) n.preds.size();
}
} else {
s.d = ((float) succSum(n) / (float) n.succs.size()) / 2;
}
}
private void sweep(boolean down) {
if (down) {
downValues();
} else {
upValues();
}
SortedSet<Region> regions = new TreeSet<Region>(regionComparator);
for (Segment s : segments) {
s.region = new Region();
s.region.minOrderNumber = s.orderNumber;
s.region.segments.add(s);
s.region.d = s.d;
regions.add(s.region);
}
for (Segment s : segments) {
for (LayoutNode n : s.nodes) {
if (n.pos != 0) {
LayoutNode prevNode = layers[n.layer].get(n.pos - 1);
if (prevNode.x + prevNode.width + xOffset == n.x) {
Segment other = hashMap.get(prevNode);
Region r1 = s.region;
Region r2 = other.region;
// They are close together
if (r1 != r2 && r2.d >= r1.d) {
if (r2.segments.size() < r1.segments.size()) {
r1.d = (r1.d * r1.segments.size() + r2.d * r2.segments.size()) / (r1.segments.size() + r2.segments.size());
for (Segment tempS : r2.segments) {
r1.segments.add(tempS);
tempS.region = r1;
r1.minOrderNumber = Math.min(r1.minOrderNumber, tempS.orderNumber);
}
regions.remove(r2);
} else {
r2.d = (r1.d * r1.segments.size() + r2.d * r2.segments.size()) / (r1.segments.size() + r2.segments.size());
for (Segment tempS : r1.segments) {
r2.segments.add(tempS);
tempS.region = r2;
r2.minOrderNumber = Math.min(r2.minOrderNumber, tempS.orderNumber);
}
regions.remove(r1);
}
}
}
}
}
}
ArrayList<Region> reversedRegions = new ArrayList<Region>();
for (Region r : regions) {
if (r.d < 0) {
processRegion(r, down);
} else {
reversedRegions.add(0, r);
}
}
for (Region r : reversedRegions) {
processRegion(r, down);
}
}
private void processRegion(Region r, boolean down) {
// Do not move
if ((int) r.d == 0) {
return;
}
ArrayList<Segment> arr = new ArrayList<Segment>();
for (Segment s : r.segments) {
arr.add(s);
}
if (r.d > 0) {
Collections.reverse(arr);
}
for (Segment s : arr) {
int min = (int) r.d;
if (min < 0) {
min = -min;
}
for (LayoutNode n : s.nodes) {
int layer = n.layer;
int pos = n.pos;
if (r.d > 0) {
if (pos != layers[layer].size() - 1) {
int off = layers[layer].get(pos + 1).x - n.x - xOffset - n.width;
assert off >= 0;
if (off < min) {
min = off;
}
}
} else {
if (pos != 0) {
int off = n.x - xOffset - layers[layer].get(pos - 1).x - layers[layer].get(pos - 1).width;
assert off >= 0;
if (off < min) {
min = off;
}
}
}
}
assert min >= 0;
if (min != 0) {
for (LayoutNode n : s.nodes) {
if (r.d > 0) {
n.x += min;
} else {
n.x -= min;
}
}
}
}
}
protected void run() {
generateSegments();
addEdges();
topologicalSorting();
initialPositions();
for (int i = 0; i < SWEEP_ITERATIONS; i++) {
sweep(true);
sweep(true);
sweep(false);
sweep(false);
}
sweep(true);
sweep(true);
}
}
private static Comparator<LayoutNode> crossingNodeComparator = new Comparator<LayoutNode>() {
public int compare(LayoutNode n1, LayoutNode n2) {
return n1.crossingNumber - n2.crossingNumber;
}
};
private class CrossingReduction extends AlgorithmPart {
@Override
public void preCheck() {
for (LayoutNode n : nodes) {
assert n.layer < layerCount;
}
}
protected void run() {
layers = new List[layerCount];
for (int i = 0; i < layerCount; i++) {
layers[i] = new ArrayList<LayoutNode>();
}
// Generate initial ordering
HashSet<LayoutNode> visited = new HashSet<LayoutNode>();
for (LayoutNode n : nodes) {
if (n.layer == 0) {
layers[0].add(n);
visited.add(n);
} else if (n.preds.size() == 0) {
layers[n.layer].add(n);
visited.add(n);
}
}
for (int i = 0; i < layers.length - 1; i++) {
for (LayoutNode n : layers[i]) {
for (LayoutEdge e : n.succs) {
if (!visited.contains(e.to)) {
visited.add(e.to);
layers[i + 1].add(e.to);
}
}
}
}
updatePositions();
initX();
// Optimize
for (int i = 0; i < CROSSING_ITERATIONS; i++) {
downSweep();
upSweep();
}
/*for(int i=0; i<CROSSING_ITERATIONS; i++) {
doubleSweep();
}*/
}
private void initX() {
for (int i = 0; i < layers.length; i++) {
updateXOfLayer(i);
}
}
private void updateXOfLayer(int index) {
int x = 0;
for (LayoutNode n : layers[index]) {
n.x = x;
x += n.width + X_OFFSET;
}
}
private void updatePositions() {
for (int i = 0; i < layers.length; i++) {
int z = 0;
for (LayoutNode n : layers[i]) {
n.pos = z;
z++;
}
}
}
private void downSweep() {
// Downsweep
for (int i = 1; i < layerCount; i++) {
for (LayoutNode n : layers[i]) {
n.crossingNumber = 0;
}
for (LayoutNode n : layers[i]) {
int sum = 0;
for (LayoutEdge e : n.preds) {
int cur = e.from.x + e.relativeFrom;
/*pos;
if(e.from.width != 0 && e.relativeFrom != 0) {
cur += (float)e.relativeFrom / (float)(e.from.width);
}*/
sum += cur;
}
if (n.preds.size() > 0) {
sum /= n.preds.size();
n.crossingNumber = sum;
//if(n.vertex == null) n.crossingNumber += layers[i].size();
}
}
updateCrossingNumbers(i, true);
Collections.sort(layers[i], crossingNodeComparator);
updateXOfLayer(i);
int z = 0;
for (LayoutNode n : layers[i]) {
n.pos = z;
z++;
}
}
}
private void updateCrossingNumbers(int index, boolean down) {
for (int i = 0; i < layers[index].size(); i++) {
LayoutNode n = layers[index].get(i);
LayoutNode prev = null;
if (i > 0) {
prev = layers[index].get(i - 1);
}
LayoutNode next = null;
if (i < layers[index].size() - 1) {
next = layers[index].get(i + 1);
}
boolean cond = (n.succs.size() == 0);
if (down) {
cond = (n.preds.size() == 0);
}
if (cond) {
if (prev != null && next != null) {
n.crossingNumber = (prev.crossingNumber + next.crossingNumber) / 2;
} else if (prev != null) {
n.crossingNumber = prev.crossingNumber;
} else if (next != null) {
n.crossingNumber = next.crossingNumber;
}
}
}
}
/*
private void doubleSweep() {
// Downsweep
for(int i=0; i<layerCount*2; i++) {
int index = i;
if(index >= layerCount) {
index = 2*layerCount - i - 1;
}
for(LayoutNode n : layers[index]) {
float sum = 0.0f;
for(LayoutEdge e : n.preds) {
float cur = e.from.pos;
if(e.from.width != 0 && e.relativeFrom != 0) {
cur += (float)e.relativeFrom / (float)(e.from.width);
}
sum += cur;
}
for(LayoutEdge e : n.succs) {
float cur = e.to.pos;
if(e.to.width != 0 && e.relativeTo != 0) {
cur += (float)e.relativeTo / (float)(e.to.width);
}
sum += cur;
}
if(n.preds.size() + n.succs.size() > 0) {
sum /= n.preds.size() + n.succs.size();
n.crossingNumber = sum;
}
}
Collections.sort(layers[index], crossingNodeComparator);
updateXOfLayer(index);
int z = 0;
for(LayoutNode n : layers[index]) {
n.pos = z;
z++;
}
}
}*/
private void upSweep() {
// Upsweep
for (int i = layerCount - 2; i >= 0; i--) {
for (LayoutNode n : layers[i]) {
n.crossingNumber = 0;
}
for (LayoutNode n : layers[i]) {
int sum = 0;
for (LayoutEdge e : n.succs) {
int cur = e.to.x + e.relativeTo;//pos;
/*
if(e.to.width != 0 && e.relativeTo != 0) {
cur += (float)e.relativeTo / (float)(e.to.width);
}*/
sum += cur;
}
if (n.succs.size() > 0) {
sum /= n.succs.size();
n.crossingNumber = sum;
//if(n.vertex == null) n.crossingNumber += layers[i].size();
}
}
updateCrossingNumbers(i, false);
Collections.sort(layers[i], crossingNodeComparator);
updateXOfLayer(i);
int z = 0;
for (LayoutNode n : layers[i]) {
n.pos = z;
z++;
}
}
}
private int evaluate() {
// TODO: Implement efficient evaluate / crossing min
return 0;
}
@Override
public void postCheck() {
HashSet<LayoutNode> visited = new HashSet<LayoutNode>();
for (int i = 0; i < layers.length; i++) {
for (LayoutNode n : layers[i]) {
assert !visited.contains(n);
assert n.layer == i;
visited.add(n);
}
}
}
}
private class AssignYCoordinates extends AlgorithmPart {
protected void run() {
int curY = 0;
//maxLayerHeight = new int[layers.length];
for (int i = 0; i < layers.length; i++) {
int maxHeight = 0;
int baseLine = 0;
int bottomBaseLine = 0;
for (LayoutNode n : layers[i]) {
maxHeight = Math.max(maxHeight, n.height - n.yOffset - n.bottomYOffset);
baseLine = Math.max(baseLine, n.yOffset);
bottomBaseLine = Math.max(bottomBaseLine, n.bottomYOffset);
}
int maxXOffset = 0;
for (LayoutNode n : layers[i]) {
if (n.vertex == null) {
// Dummy node
n.y = curY;
n.height = maxHeight + baseLine + bottomBaseLine;
} else {
n.y = curY + baseLine + (maxHeight - (n.height - n.yOffset - n.bottomYOffset)) / 2 - n.yOffset;
}
for (LayoutEdge e : n.succs) {
int curXOffset = Math.abs(n.x - e.to.x);
maxXOffset = Math.max(curXOffset, maxXOffset);
}
}
//maxLayerHeight[i] = maxHeight + baseLine + bottomBaseLine;
curY += maxHeight + baseLine + bottomBaseLine;
curY += layerOffset + (int) Math.sqrt(maxXOffset);
}
}
}
private class CreateDummyNodes extends AlgorithmPart {
private int oldNodeCount;
@Override
protected void preCheck() {
for (LayoutNode n : nodes) {
for (LayoutEdge e : n.succs) {
assert e.from != null;
assert e.from == n;
assert e.from.layer < e.to.layer;
}
for (LayoutEdge e : n.preds) {
assert e.to != null;
assert e.to == n;
}
}
}
protected void run() {
oldNodeCount = nodes.size();
if (combine == Combine.SAME_OUTPUTS) {
Comparator<LayoutEdge> comparator = new Comparator<LayoutEdge>() {
public int compare(LayoutEdge e1, LayoutEdge e2) {
return e1.to.layer - e2.to.layer;
}
};
HashMap<Integer, List<LayoutEdge>> portHash = new HashMap<Integer, List<LayoutEdge>>();
ArrayList<LayoutNode> currentNodes = new ArrayList<LayoutNode>(nodes);
for (LayoutNode n : currentNodes) {
portHash.clear();
ArrayList<LayoutEdge> succs = new ArrayList<LayoutEdge>(n.succs);
HashMap<Integer, LayoutNode> topNodeHash = new HashMap<Integer, LayoutNode>();
HashMap<Integer, HashMap<Integer, LayoutNode>> bottomNodeHash = new HashMap<Integer, HashMap<Integer, LayoutNode>>();
for (LayoutEdge e : succs) {
assert e.from.layer < e.to.layer;
if (e.from.layer != e.to.layer - 1) {
if (maxLayerLength != -1 && e.to.layer - e.from.layer > maxLayerLength/* && e.to.preds.size() > 1 && e.from.succs.size() > 1*/) {
assert maxLayerLength > 2;
e.to.preds.remove(e);
e.from.succs.remove(e);
LayoutEdge topEdge = null;
if (combine == Combine.SAME_OUTPUTS && topNodeHash.containsKey(e.relativeFrom)) {
LayoutNode topNode = topNodeHash.get(e.relativeFrom);
topEdge = new LayoutEdge();
topEdge.relativeFrom = e.relativeFrom;
topEdge.from = e.from;
topEdge.relativeTo = topNode.width / 2;
topEdge.to = topNode;
topEdge.link = e.link;
e.from.succs.add(topEdge);
topNode.preds.add(topEdge);
} else {
LayoutNode topNode = new LayoutNode();
topNode.layer = e.from.layer + 1;
topNode.width = DUMMY_WIDTH;
topNode.height = DUMMY_HEIGHT;
nodes.add(topNode);
topEdge = new LayoutEdge();
topEdge.relativeFrom = e.relativeFrom;
topEdge.from = e.from;
topEdge.relativeTo = topNode.width / 2;
topEdge.to = topNode;
topEdge.link = e.link;
e.from.succs.add(topEdge);
topNode.preds.add(topEdge);
topNodeHash.put(e.relativeFrom, topNode);
bottomNodeHash.put(e.relativeFrom, new HashMap<Integer, LayoutNode>());
}
HashMap<Integer, LayoutNode> hash = bottomNodeHash.get(e.relativeFrom);
LayoutNode bottomNode = null;
if (hash.containsKey(e.to.layer)) {
bottomNode = hash.get(e.to.layer);
} else {
bottomNode = new LayoutNode();
bottomNode.layer = e.to.layer - 1;
bottomNode.width = DUMMY_WIDTH;
bottomNode.height = DUMMY_HEIGHT;
nodes.add(bottomNode);
hash.put(e.to.layer, bottomNode);
}
LayoutEdge bottomEdge = new LayoutEdge();
bottomEdge.relativeTo = e.relativeTo;
bottomEdge.to = e.to;
bottomEdge.relativeFrom = bottomNode.width / 2;
bottomEdge.from = bottomNode;
bottomEdge.link = e.link;
e.to.preds.add(bottomEdge);
bottomEdgeHash.put(topEdge, bottomEdge);
bottomNode.succs.add(bottomEdge);
} else {
Integer i = e.relativeFrom;
if (!portHash.containsKey(i)) {
portHash.put(i, new ArrayList<LayoutEdge>());
}
if (n.vertex.toString().equals("1012 CastPP")) {
int x = 0;
}
portHash.get(i).add(e);
}
}
}
succs = new ArrayList<LayoutEdge>(n.succs);
for (LayoutEdge e : succs) {
Integer i = e.relativeFrom;
if (portHash.containsKey(i)) {
List<LayoutEdge> list = portHash.get(i);
Collections.sort(list, comparator);
if (list.size() == 1) {
processSingleEdge(list.get(0));
} else {
int maxLayer = list.get(0).to.layer;
for (LayoutEdge curEdge : list) {
maxLayer = Math.max(maxLayer, curEdge.to.layer);
}
int cnt = maxLayer - n.layer - 1;
LayoutEdge[] edges = new LayoutEdge[cnt];
LayoutNode[] nodes = new LayoutNode[cnt];
edges[0] = new LayoutEdge();
edges[0].from = n;
edges[0].relativeFrom = i;
n.succs.add(edges[0]);
nodes[0] = new LayoutNode();
nodes[0].width = dummyWidth;
nodes[0].height = dummyHeight;
nodes[0].layer = n.layer + 1;
nodes[0].preds.add(edges[0]);
edges[0].to = nodes[0];
edges[0].relativeTo = nodes[0].width / 2;
for (int j = 1; j < cnt; j++) {
edges[j] = new LayoutEdge();
edges[j].from = nodes[j - 1];
edges[j].relativeFrom = nodes[j - 1].width / 2;
nodes[j - 1].succs.add(edges[j]);
nodes[j] = new LayoutNode();
nodes[j].width = dummyWidth;
nodes[j].height = dummyHeight;
nodes[j].layer = n.layer + j + 1;
nodes[j].preds.add(edges[j]);
edges[j].to = nodes[j];
edges[j].relativeTo = nodes[j].width / 2;
}
for (LayoutEdge curEdge : list) {
assert curEdge.to.layer - n.layer - 2 >= 0;
assert curEdge.to.layer - n.layer - 2 < cnt;
LayoutNode anchor = nodes[curEdge.to.layer - n.layer - 2];
anchor.succs.add(curEdge);
curEdge.from = anchor;
curEdge.relativeFrom = anchor.width / 2;
n.succs.remove(curEdge);
}
}
portHash.remove(i);
}
}
}
} else if (combine == Combine.SAME_INPUTS) {
throw new UnsupportedOperationException("Currently not supported");
} else {
ArrayList<LayoutNode> currentNodes = new ArrayList<LayoutNode>(nodes);
for (LayoutNode n : currentNodes) {
for (LayoutEdge e : n.succs) {
processSingleEdge(e);
}
}
}
}
private void processSingleEdge(LayoutEdge e) {
LayoutNode n = e.from;
if (e.to.layer > n.layer + 1) {
LayoutEdge last = e;
for (int i = n.layer + 1; i < last.to.layer; i++) {
last = addBetween(last, i);
}
}
}
private LayoutEdge addBetween(LayoutEdge e, int layer) {
LayoutNode n = new LayoutNode();
n.width = dummyWidth;
n.height = dummyHeight;
n.layer = layer;
n.preds.add(e);
nodes.add(n);
LayoutEdge result = new LayoutEdge();
n.succs.add(result);
result.from = n;
result.relativeFrom = n.width / 2;
result.to = e.to;
result.relativeTo = e.relativeTo;
e.relativeTo = n.width / 2;
e.to.preds.remove(e);
e.to.preds.add(result);
e.to = n;
return result;
}
@Override
public void printStatistics() {
System.out.println("Dummy nodes created: " + (nodes.size() - oldNodeCount));
}
@Override
public void postCheck() {
ArrayList<LayoutNode> currentNodes = new ArrayList<LayoutNode>(nodes);
for (LayoutNode n : currentNodes) {
for (LayoutEdge e : n.succs) {
assert e.from.layer == e.to.layer - 1;
}
}
for (int i = 0; i < layers.length; i++) {
assert layers[i].size() > 0;
for (LayoutNode n : layers[i]) {
assert n.layer == i;
}
}
}
}
private class AssignLayers extends AlgorithmPart {
@Override
public void preCheck() {
for (LayoutNode n : nodes) {
assert n.layer == -1;
}
}
protected void run() {
HashSet<LayoutNode> set = new HashSet<LayoutNode>();
for (LayoutNode n : nodes) {
if (n.preds.size() == 0) {
set.add(n);
n.layer = 0;
}
}
int z = minLayerDifference;
HashSet<LayoutNode> newSet = new HashSet<LayoutNode>();
HashSet<LayoutNode> failed = new HashSet<LayoutNode>();
while (!set.isEmpty()) {
newSet.clear();
failed.clear();
for (LayoutNode n : set) {
for (LayoutEdge se : n.succs) {
LayoutNode s = se.to;
if (!newSet.contains(s) && !failed.contains(s)) {
boolean ok = true;
for (LayoutEdge pe : s.preds) {
LayoutNode p = pe.from;
if (p.layer == -1) {
ok = false;
break;
}
}
if (ok) {
newSet.add(s);
} else {
failed.add(s);
}
}
}
}
for (LayoutNode n : newSet) {
n.layer = z;
}
// Swap sets
HashSet<LayoutNode> tmp = set;
set = newSet;
newSet = tmp;
z += minLayerDifference;
}
optimize(set);
layerCount = z - minLayerDifference;
for (Vertex v : lastLayerHint) {
LayoutNode n = vertexToLayoutNode.get(v);
assert n.succs.size() == 0;
n.layer = layerCount - 1;
}
for (Vertex v : firstLayerHint) {
LayoutNode n = vertexToLayoutNode.get(v);
assert n.preds.size() == 0;
assert n.layer == 0;
}
}
public void optimize(HashSet<LayoutNode> set) {
for (LayoutNode n : set) {
if (n.preds.size() == 0 && n.succs.size() > 0) {
int minLayer = n.succs.get(0).to.layer;
for (LayoutEdge e : n.succs) {
minLayer = Math.min(minLayer, e.to.layer);
}
n.layer = minLayer - 1;
}
}
}
@Override
public void postCheck() {
for (LayoutNode n : nodes) {
assert n.layer >= 0;
assert n.layer < layerCount;
for (LayoutEdge e : n.succs) {
assert e.from.layer < e.to.layer;
}
}
}
}
private class ReverseEdges extends AlgorithmPart {
private HashSet<LayoutNode> visited;
private HashSet<LayoutNode> active;
protected void run() {
// Remove self-edges, TODO: Special treatment
for (LayoutNode node : nodes) {
ArrayList<LayoutEdge> succs = new ArrayList<LayoutEdge>(node.succs);
for (LayoutEdge e : succs) {
assert e.from == node;
if (e.to == node) {
node.succs.remove(e);
node.preds.remove(e);
}
}
}
// Reverse inputs of roots
for (LayoutNode node : nodes) {
if (node.vertex.isRoot()) {
boolean ok = true;
for (LayoutEdge e : node.preds) {
if (e.from.vertex.isRoot()) {
ok = false;
break;
}
}
if (ok) {
reverseAllInputs(node);
}
}
}
// Start DFS and reverse back edges
visited = new HashSet<LayoutNode>();
active = new HashSet<LayoutNode>();
for (LayoutNode node : nodes) {
DFS(node);
}
for (LayoutNode node : nodes) {
SortedSet<Integer> reversedDown = new TreeSet<Integer>();
for (LayoutEdge e : node.succs) {
if (reversedLinks.contains(e.link)) {
reversedDown.add(e.relativeFrom);
}
}
SortedSet<Integer> reversedUp = null;
if (reversedDown.size() == 0) {
reversedUp = new TreeSet<Integer>(Collections.reverseOrder());
} else {
reversedUp = new TreeSet<Integer>();
}
for (LayoutEdge e : node.preds) {
if (reversedLinks.contains(e.link)) {
reversedUp.add(e.relativeTo);
}
}
final int offset = X_OFFSET + DUMMY_WIDTH;
int curX = 0;
int curWidth = node.width + reversedDown.size() * offset;
for (int pos : reversedDown) {
ArrayList<LayoutEdge> reversedSuccs = new ArrayList<LayoutEdge>();
for (LayoutEdge e : node.succs) {
if (e.relativeFrom == pos && reversedLinks.contains(e.link)) {
reversedSuccs.add(e);
e.relativeFrom = curWidth;
}
}
ArrayList<Point> startPoints = new ArrayList<Point>();
startPoints.add(new Point(curWidth, curX));
startPoints.add(new Point(pos, curX));
startPoints.add(new Point(pos, reversedDown.size() * offset));
for (LayoutEdge e : reversedSuccs) {
reversedLinkStartPoints.put(e.link, startPoints);
}
node.inOffsets.put(pos, -curX);
curX += offset;
node.height += offset;
node.yOffset += offset;
curWidth -= offset;
}
node.width += reversedDown.size() * offset;
if (reversedDown.size() == 0) {
curX = offset;
} else {
curX = -offset;
}
curX = 0;
int minX = 0;
if (reversedDown.size() != 0) {
minX = -offset * reversedUp.size();
}
int oldNodeHeight = node.height;
for (int pos : reversedUp) {
ArrayList<LayoutEdge> reversedPreds = new ArrayList<LayoutEdge>();
for (LayoutEdge e : node.preds) {
if (e.relativeTo == pos && reversedLinks.contains(e.link)) {
if (reversedDown.size() == 0) {
e.relativeTo = node.width + offset;
} else {
e.relativeTo = curX - offset;
}
reversedPreds.add(e);
}
}
node.height += offset;
ArrayList<Point> endPoints = new ArrayList<Point>();
if (reversedDown.size() == 0) {
curX += offset;
node.width += offset;
endPoints.add(new Point(node.width, node.height));
} else {
curX -= offset;
node.width += offset;
endPoints.add(new Point(curX, node.height));
}
node.outOffsets.put(pos - minX, curX);
curX += offset;
node.bottomYOffset += offset;
endPoints.add(new Point(pos, node.height));
endPoints.add(new Point(pos, oldNodeHeight));
for (LayoutEdge e : reversedPreds) {
reversedLinkEndPoints.put(e.link, endPoints);
}
}
if (minX < 0) {
for (LayoutEdge e : node.preds) {
e.relativeTo -= minX;
}
for (LayoutEdge e : node.succs) {
e.relativeFrom -= minX;
}
node.xOffset = -minX;
node.width += -minX;
}
}
}
private void DFS(LayoutNode startNode) {
if (visited.contains(startNode)) {
return;
}
Stack<LayoutNode> stack = new Stack<LayoutNode>();
stack.push(startNode);
while (!stack.empty()) {
LayoutNode node = stack.pop();
if (visited.contains(node)) {
// Node no longer active
active.remove(node);
continue;
}
// Repush immediately to know when no longer active
stack.push(node);
visited.add(node);
active.add(node);
ArrayList<LayoutEdge> succs = new ArrayList<LayoutEdge>(node.succs);
for (LayoutEdge e : succs) {
if (active.contains(e.to)) {
assert visited.contains(e.to);
// Encountered back edge
reverseEdge(e);
} else if (!visited.contains(e.to) && (linksToFollow.size() == 0 || linksToFollow.contains(e.link))) {
stack.push(e.to);
}
}
}
}
private void reverseAllInputs(LayoutNode node) {
for (LayoutEdge e : node.preds) {
assert !reversedLinks.contains(e.link);
reversedLinks.add(e.link);
node.succs.add(e);
e.from.preds.add(e);
e.from.succs.remove(e);
int oldRelativeFrom = e.relativeFrom;
int oldRelativeTo = e.relativeTo;
e.to = e.from;
e.from = node;
e.relativeFrom = oldRelativeTo;
e.relativeTo = oldRelativeFrom;
}
node.preds.clear();
}
private void reverseEdge(LayoutEdge e) {
assert !reversedLinks.contains(e.link);
reversedLinks.add(e.link);
LayoutNode oldFrom = e.from;
LayoutNode oldTo = e.to;
int oldRelativeFrom = e.relativeFrom;
int oldRelativeTo = e.relativeTo;
e.from = oldTo;
e.to = oldFrom;
e.relativeFrom = oldRelativeTo;
e.relativeTo = oldRelativeFrom;
oldFrom.succs.remove(e);
oldFrom.preds.add(e);
oldTo.preds.remove(e);
oldTo.succs.add(e);
}
@Override
public void postCheck() {
for (LayoutNode n : nodes) {
HashSet<LayoutNode> curVisited = new HashSet<LayoutNode>();
Queue<LayoutNode> queue = new LinkedList<LayoutNode>();
for (LayoutEdge e : n.succs) {
LayoutNode s = e.to;
queue.add(s);
curVisited.add(s);
}
while (!queue.isEmpty()) {
LayoutNode curNode = queue.remove();
for (LayoutEdge e : curNode.succs) {
assert e.to != n;
if (!curVisited.contains(e.to)) {
queue.add(e.to);
curVisited.add(e.to);
}
}
}
}
}
}
private Comparator<Link> linkComparator = new Comparator<Link>() {
public int compare(Link l1, Link l2) {
int result = l1.getFrom().getVertex().compareTo(l2.getFrom().getVertex());
if (result != 0) {
return result;
}
result = l1.getTo().getVertex().compareTo(l2.getTo().getVertex());
if (result != 0) {
return result;
}
result = l1.getFrom().getRelativePosition().x - l2.getFrom().getRelativePosition().x;
if (result != 0) {
return result;
}
result = l1.getTo().getRelativePosition().x - l2.getTo().getRelativePosition().x;
return result;
}
};
private class BuildDatastructure extends AlgorithmPart {
protected void run() {
// Set up nodes
List<Vertex> vertices = new ArrayList<Vertex>(graph.getVertices());
Collections.sort(vertices);
for (Vertex v : vertices) {
LayoutNode node = new LayoutNode();
Dimension size = v.getSize();
node.width = (int) size.getWidth();
node.height = (int) size.getHeight();
node.vertex = v;
nodes.add(node);
vertexToLayoutNode.put(v, node);
}
// Set up edges
List<Link> links = new ArrayList<Link>(graph.getLinks());
Collections.sort(links, linkComparator);
for (Link l : links) {
LayoutEdge edge = new LayoutEdge();
assert vertexToLayoutNode.containsKey(l.getFrom().getVertex());
assert vertexToLayoutNode.containsKey(l.getTo().getVertex());
edge.from = vertexToLayoutNode.get(l.getFrom().getVertex());
edge.to = vertexToLayoutNode.get(l.getTo().getVertex());
edge.relativeFrom = l.getFrom().getRelativePosition().x;
edge.relativeTo = l.getTo().getRelativePosition().x;
edge.link = l;
edge.from.succs.add(edge);
edge.to.preds.add(edge);
//assert edge.from != edge.to; // No self-loops allowed
}
for (Link l : importantLinks) {
if (!vertexToLayoutNode.containsKey(l.getFrom().getVertex()) ||
vertexToLayoutNode.containsKey(l.getTo().getVertex())) {
continue;
}
LayoutNode from = vertexToLayoutNode.get(l.getFrom().getVertex());
LayoutNode to = vertexToLayoutNode.get(l.getTo().getVertex());
for (LayoutEdge e : from.succs) {
if (e.to == to) {
linksToFollow.add(e.link);
}
}
}
}
@Override
public void postCheck() {
assert vertexToLayoutNode.keySet().size() == nodes.size();
assert nodes.size() == graph.getVertices().size();
for (Vertex v : graph.getVertices()) {
LayoutNode node = vertexToLayoutNode.get(v);
assert node != null;
for (LayoutEdge e : node.succs) {
assert e.from == node;
}
for (LayoutEdge e : node.preds) {
assert e.to == node;
}
}
}
}
public void doRouting(LayoutGraph graph) {
// Do nothing for now
}
}