de.jfschaefer.layeredgraphlayout.gengraph.GenGraph Maven / Gradle / Ivy
package de.jfschaefer.layeredgraphlayout.gengraph;
import de.jfschaefer.layeredgraphlayout.Edge;
import de.jfschaefer.layeredgraphlayout.Node;
import de.jfschaefer.layeredgraphlayout.pgraph.PGraph;
import java.util.*;
/**
* Created by user on 7/30/15.
*/
public class GenGraph {
protected Map> nodeMap;
protected Map> edgeMap;
protected boolean locked = false;
protected boolean cyclesRemoved = false;
public GenGraph() {
nodeMap = new HashMap>();
edgeMap = new HashMap>();
}
public void addNode(V node, double width, double height) {
assert !locked;
nodeMap.put(node, new Node(node, width, height));
}
public void addEdge(E edge, V from, V to) {
assert !locked;
Node nfrom = nodeMap.get(from);
Node nto = nodeMap.get(to);
edgeMap.put(edge, new Edge(edge, nfrom, nto));
}
public final Collection> getNodes() {
return nodeMap.values();
}
public final Collection> getEdges() {
return edgeMap.values();
}
public final Set getOriginalNodes() {
return nodeMap.keySet();
}
public final Set getOriginalEdges() {
return edgeMap.keySet();
}
public void removeCyclesGreedy() {
if (cyclesRemoved) return;
assert !locked;
Set> remainingNodes = new HashSet>();
for (Node n : getNodes()) {
remainingNodes.add(n);
}
while (!remainingNodes.isEmpty()) {
//remove remaining sources and sinks
int size;
do {
size = remainingNodes.size();
Set> done = new HashSet>();
for (Node node : remainingNodes) {
if (node.isSinkIn(remainingNodes) || node.isSourceIn(remainingNodes)) {
done.add(node);
}
}
remainingNodes.removeAll(done);
} while (size != remainingNodes.size());
//reverse a few edges to make one more node a source/sink.
if (remainingNodes.isEmpty()) break;
double bestRatio = 1d; // worse than worst case
Node chosenNode = null;
boolean indegreeGToutdegree = false; // in degree greater than out degree
for (Node node : remainingNodes) {
int indegree = node.inDegreeIn(remainingNodes);
int outdegree = node.outDegreeIn(remainingNodes);
assert indegree != 0; //otherwise it would have been a source in the previous step
assert outdegree != 0; //otherwise it would have been a sink in the previous step
double thisRatio = ((double) Math.min(indegree, outdegree)) / (indegree + outdegree);
if (thisRatio < bestRatio) {
bestRatio = thisRatio;
chosenNode = node;
indegreeGToutdegree = (indegree > outdegree);
}
}
assert chosenNode != null;
Set> edgesToBeFlipped = new HashSet>();
if (indegreeGToutdegree) { //flip outgoing edges
for (Edge edge : chosenNode.getOutgoingEdges()) {
if (remainingNodes.contains(edge.getTo())) {
edgesToBeFlipped.add(edge);
}
}
} else { //flip ingoing edges
for (Edge edge : chosenNode.getIngoingEdges()) {
if (remainingNodes.contains(edge.getFrom())) {
edgesToBeFlipped.add(edge);
}
}
}
for (Edge e : edgesToBeFlipped) {
e.flip();
}
}
locked = true;
cyclesRemoved = true;
}
public PGraph generatePGraph() {
removeCyclesGreedy();
PGraph result = new PGraph();
for (Node node : getNodes()) {
result.addNode(node);
}
for (Edge edge : getEdges()) {
result.addEdge(edge);
}
return result;
}
}