/*
* 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.Random;
import java.util.Set;
import java.awt.Dimension;
import java.awt.geom.Point2D;
import automata.graph.Graph;
import automata.graph.LayoutAlgorithm;
/**
* This algorithm assigns all vertices to random points in the graph, while applying a
* little effort taken to minimize edge intersections.
*
* @see LayoutAlgorithm
* @author Chris Morgan
*/
public class RandomLayoutAlgorithm extends LayoutAlgorithm {
/**
* A list of all movable vertices.
*/
private ArrayList vertices;
/**
* A list of all randomly generated points.
*/
private ArrayList points;
/**
* The VertexChain
used to minimize edge collision.
*/
private VertexChain chain;
/**
* Assigns some default values. To have different values, use the other constructor.
*/
public RandomLayoutAlgorithm() {
super();
}
/**
* Constructor allowing the user to customize certain values. The vertexDim
* is not used in this algorithm, but it is here so the superclass constructor can
* be used and for the LayoutAlgorithmFactory
.
*
* @param pSize
* value for size
.
* @param vDim
* value for vertexDim
.
* @param vBuffer
* value for vertexBuffer
.
*/
public RandomLayoutAlgorithm(Dimension pSize, Dimension vDim, double vBuffer) {
super(pSize, vDim, vBuffer);
}
public void layout(Graph graph, Set notMoving) {
//First, check to see that movable vertices exist
vertices = getMovableVertices(graph, notMoving);
if (graph==null || vertices.size() == 0)
return;
//Then, generate random points and assign the vertices to a
//VertexChain to minimize a few edge collisions
chain = new VertexChain(graph);
assignPointsAndVertices();
//Then minimize vertex overlap.
lessenVertexOverlap();
//Next, find a more optimal point order with which to match the points to the vertices.
findCorrectPointOrder();
//Finally, move all vertices to their corresponding points. Wrap up the algorithm by
//making sure all points are on the screen.
for (int i=0; i=0; j--) {
point = (Point2D) xOrder.get(j);
point.setLocation(point.getX() + xBuffer - xDiff, point.getY());
}
xDiff = ((Point2D)yOrder.get(i)).getX() - ((Point2D)yOrder.get(i+1)).getX();
yDiff = ((Point2D)yOrder.get(i)).getY() - ((Point2D)yOrder.get(i+1)).getY();
if (xDiff < xBuffer && yDiff < yBuffer)
for (int j=i; j>=0; j--) {
point = (Point2D) yOrder.get(j);
point.setLocation(point.getX(), point.getY() + yBuffer - yDiff);
}
}
}
/**
* The method reassigns the randomly generated points into a new order, placing them in an order such
* that the vertices assigned to them will spiral toward the center as one progresses through the chain.
*/
private void findCorrectPointOrder() {
ArrayList notProcessedPoints, newPointOrder;
Point2D current, anchor, minPoint;
double currentTheta, minTheta, anchorTheta;
anchor = new Point2D.Double(0,0);
anchorTheta = 0;
newPointOrder = new ArrayList();
notProcessedPoints = new ArrayList();
notProcessedPoints.addAll(points);
//Find the angle of all points relative to the last point placed and "anchorTheta". Then place
//the point with the minimum angle. "anchorTheta" will slowly rotate around a circle counterclockwise.
while (notProcessedPoints.size() > 0) {
minPoint = (Point2D) notProcessedPoints.get(0);
minTheta = 2*Math.PI + 1;
for (int i=0; i anchor.getX())
currentTheta = Math.PI / 2;
else
currentTheta = Math.PI / -2;
/* atan -> -pi/2...pi/2. Adding 4pi to the currentTheta, subtracting the anchorTheta, and taking
* the remainder when dividing by pi works for all four quadrants the angle could be in.
* The object is to find the smallest absolute polar theta of the current point from the anchor
* which is greater than, or next in a counterclockwise traversal, from the anchorTheta.
*/
currentTheta = (currentTheta + 4*Math.PI - anchorTheta) % (Math.PI);
if (currentTheta < minTheta) {
minTheta = currentTheta;
minPoint = current;
}
}
anchor = minPoint;
anchorTheta = (anchorTheta + minTheta) % (2*Math.PI);
notProcessedPoints.remove(minPoint);
newPointOrder.add(minPoint);
}
points = newPointOrder;
}
}