/* * 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; } }