/*
* JFLAP - Formal Languages and Automata Package
*
*
* Susan H. Rodger
* Computer Science Department
* Duke University
* August 27, 2009
* Copyright (c) 2002-2009
* All rights reserved.
* JFLAP is open source software. Please see the LICENSE for terms.
*
*/
package automata.graph.layout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.awt.Dimension;
import java.awt.geom.Point2D;
import automata.graph.Graph;
import automata.graph.AutomatonDirectedGraph;
import automata.graph.LayoutAlgorithm;
/**
* This algorithm will lay out the graph in a tree.
*
* @author Chris Morgan
*/
public class TreeLayoutAlgorithm extends LayoutAlgorithm {
/**
* The graph used for this LayoutAlgorithm
*/
protected Graph graph;
/**
* If true, this is a hierarchical tree. If false, a degree tree.
*/
protected boolean hierarchical;
/**
* Assigns some default values, although one must specify whether the tree is vertical
* or horizontal. To have different values, use the other constructor.
*
* @param hier
* if true
, a hierarchy tree layout. If false, a degree one.
*/
public TreeLayoutAlgorithm(boolean hier)
{
super();
hierarchical = hier;
}
/**
* Constructor allowing the user to customize certain values.
*
* @param pSize
* value for size
.
* @param vDim
* value for vertexDim
.
* @param vBuffer
* value for vertexBuffer
.
* @param hier
* if true
, a hierarchy tree layout. If false, a degree one.
*/
public TreeLayoutAlgorithm(Dimension pSize, Dimension vDim, double vBuffer, boolean hier)
{
super(pSize, vDim, vBuffer);
hierarchical = hier;
}
public void layout(Graph g, Set notMoving) {
graph = g;
ArrayList vertices = getMovableVertices(graph, notMoving);
if (graph==null || vertices.size() == 0)
return;
/* After checking to see if the graph has movable vertices, sort the vertices
* degree in the graph, and then insert them in a VertexChain to minimize
* edge intersections. If hierarchical, then those vertices with the smallest
* number of edges in them are moved to the top. If otherwise, then those edges
* with the highest degree are moved to the top.
*/
if (hierarchical) {
//It is up to the programmer to make sure the right kind of graph is present for
//hierarchical graphs. If not, the algorithm will return with nothing happening.
if (!(graph instanceof AutomatonDirectedGraph))
return;
final AutomatonDirectedGraph adg = (AutomatonDirectedGraph) graph;
Collections.sort(vertices, new Comparator() {
public int compare(Object o1, Object o2) {
if (adg.toDegree(o1, true) == adg.toDegree(o2, true))
return 0;
else if (adg.toDegree(o1, true) > adg.toDegree(o2, true))
return 1;
else
return -1;
}});
}
else
Collections.sort(vertices, new Comparator() {
public int compare(Object o1, Object o2) {
if (graph.degree(o1) == graph.degree(o2))
return 0;
else if (graph.degree(o1) > graph.degree(o2))
return -1;
else
return 1;
}});
// Finally, add the vertices to levels and adjust them so all vertices are on the screen
ArrayList notPlaced = new ArrayList();
notPlaced.addAll(vertices);
Level firstLevel, counter;
firstLevel = new Level();
while (notPlaced.size() > 0) {
firstLevel.vertices.add(notPlaced.get(0));
notPlaced.remove(notPlaced.get(0));
counter = firstLevel;
while (counter!=null && notPlaced.size() > 0) {
counter.processChildren(notPlaced);
counter = counter.nextLevel;
}
}
firstLevel.layout(0);
shiftOntoScreen(graph, size, vertexDim, true);
}
/**
* This class represents a level in the tree. It points to the next level in the hierarchy, so thus
* the levels are implemented as a linked list.
*
* @author Chris Morgan
*/
private class Level {
/**
* The list of vertices in this level.
*/
public ArrayList vertices;
/**
* The next level in the hierarchy.
*/
public Level nextLevel;
/**
* The constructor.
*/
public Level() {
vertices = new ArrayList();
nextLevel = null;
}
/**
* This method checks the list of vertices that haven't been placed in a level to determine if any
* vertices in this level have any non-placed vertices as children. All children found are placed in
* the next level down the hierarchy.
*
* @param notPlaced
*/
public void processChildren(ArrayList notPlaced) {
VertexChain chain, lastChain;
lastChain = null;
for (int i=0; i=0; j--) {
if (graph.hasEdge(vertices.get(i), notPlaced.get(j)) &&
!vertices.get(i).equals(notPlaced.get(j))) {
chain.addVertex(notPlaced.get(j));
notPlaced.remove(j);
}
}
/* Then, align this VertexChain to the last chain generated in the level to minimize overlaps.
* If there are vertices in the last chain, add the vertices in the last chain generated to the next,
* level, since the last chain is done with alignment. Define the next level if necessary. After
* this, set the current chain to be the last chain.
*/
if (lastChain!=null) {
VertexChain.alignTwoChains(lastChain, chain, graph);
if (lastChain.size() > 0) {
if (nextLevel == null)
nextLevel = new Level();
nextLevel.vertices.addAll(lastChain.getVertices());
}
}
lastChain = chain;
}
//Finally, add the last chain generated to the graph.
if (lastChain != null && lastChain.size() > 0) {
if (nextLevel == null)
nextLevel = new Level();
nextLevel.vertices.addAll(lastChain.getVertices());
}
}
/**
* Lay out all vertices in the current row, centering the vertex row along the central
* vertical axis of the tree for symmetry. This method also calls the layout method of the
* next level, so the tree will be laid out throughout the linked list through recursion.
*
* @param height the height of the current row.
*/
public void layout(double height) {
double currentX = -1.0 * vertices.size() * (vertexDim.getWidth() + vertexBuffer) / 2;
for (int i=0; i