/* ConcentricCirclesLayout.java COPYRIGHT 2007 KRUPCZAK.ORG, LLC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA For more information, visit: http://www.krupczak.org/ */ package org.krupczak.Cartographer; import java.util.Arrays; import java.util.Comparator; import javax.swing.JScrollPane; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.jgraph.JGraph; import org.jgraph.graph.*; import com.jgraph.layout.JGraphFacade; import com.jgraph.layout.JGraphLayout; /** Layout class that really just does concentric circles of nodes * using the number of edges (in/out) as a basis for choosing which * nodes are closest to the center of the window. Obtain position * and size of vertices to layout, store locally in layout if needed. * Use graph functions to obtain edges and neighbors and then set * locations. Use basic trigonometry to position vertices. * @author Bobby Krupczak, rdk@krupczak.org * @version $Id: ConcentricCirclesLayout.java 42 2008-08-04 02:09:23Z rdk $ **/ public class ConcentricCirclesLayout implements JGraphLayout { /* class variables and methods *********************** */ public static double defaultRadiusFactor = 1.5; public static double defaultRotationFactor = Math.PI/12.0; /* 15-degrees */ /* instance variables ******************************** */ /** radius increment, between concentric circles, is a function of * radius factor and size of node. Incrementing radius factor adds * more spacing between rings. **/ public double radiusFactor; /** rotation factor rotates each ring slightly so that nodes are * not all aligned at the same angle. (0..2*pi) **/ public double rotationFactor; public JScrollPane ourPane; public JGraph theGraph; /* constructors ************************************* */ ConcentricCirclesLayout() { ourPane = null; theGraph = null; radiusFactor = defaultRadiusFactor; rotationFactor = defaultRotationFactor; return; } /* private methods *********************************** */ /* public methods ************************************ */ /** Is there already a node at given location? If so, we do not * want to place a second one there. **/ public boolean nodeAtLocation(JGraphFacade g, Object v, double x, double y) { Object[] vertices; int i; Point2D d; vertices = g.getVertices().toArray(); if ((vertices == null) || (vertices.length == 0)) return false; // System.out.println(" nodeAtLocation looking for "+x+","+y); for (i=0; i Math.PI*2)) rotationFactor = defaultRotationFactor; } public void setScrollPane(JScrollPane thePane) { ourPane = thePane; } public void setGraph(JGraph theGraph) { this.theGraph = theGraph; } public Point2D getNewOrigin(JGraphFacade graph) { Rectangle2D bounds = graph.getCellBounds(); if (bounds == null) return (Point2D) new Point2D.Double(0,0); // bounds of a box that encloses the graph double gh = bounds.getHeight(); double gw = bounds.getWidth(); // bounds of the viewport that displays the graph double vh = ourPane.getViewport().getHeight(); double vw = ourPane.getViewport().getWidth(); // we want to center it in the viewport if possible, otherwise // origin at (0,0) double x = (vw - gw) / 2; if (x < 0) x = 0; double y = (vh - gh) / 2; if (y < 0) y = 0; //System.out.println("ViewPort dimensions: "+vw+","+vh); //System.out.println("Graph Bounds: "+gw+","+gh); //System.out.println("New Origin: "+x+","+y); return (Point2D) new Point2D.Double(x,y); } /** get distance between two vertices using standard geometry * formulas; we do this because even if we move vertices around, * the edges are not necessarily updated until the graph cache * itself is edited dont bother with distance if one of the * vertices is at 0,0 since thats a temporary location used during * the layout algorithm **/ public double getVertexDistance(JGraphFacade g, Object a, Object b) { Point2D pa, pb; Double d; pa = g.getLocation(a); pb = g.getLocation(b); if ((pa.getX() == 0.0) && (pa.getY() == 0.0)) { //System.out.println("getVertexDistance: vertex a is at 0,0"); return 0.0; } if ((pb.getX() == 0.0) && (pb.getY() == 0.0)) { //System.out.println("getVertexDistance: vertex b is at 0,0"); return 0.0; } d = Math.sqrt( Math.pow(pa.getX() - pb.getX(),2) + Math.pow(pa.getY() - pb.getY(),2) ); return d; } /** get total distance from this vertex to its neighbors we use * neighbors because two systems may have multiple edges between * them and we dont want to double count we dont count distance to * neighbors that currently are at 0,0 **/ public double getTotalNeighborDistance(JGraphFacade g, Object v) { Object[] n; int i; Double total; // if vertex is at 0,0, dont bother summing if ((g.getLocation(v).getX() == 0.0) && (g.getLocation(v).getY() == 0.0)) { System.out.println("getTotalDistance: vertex is at 0,0"); return 0.0; } n = g.getNeighbours(v,false).toArray(); //System.out.println("getTotalNeighborDistance: "+ // n.length+" neighbors"); //System.out.println("getTotalNeighborDistance: "+g.getEdges(v).length+ // " edges"); total = 0.0; for (i=0; i= 1, we would like to position // the vertices so that their edges cross the center // the least; we'd like to keep them closest to the // vertices they are connecting to. // even if we set a location of a vertex, the length of edges // does not seem to get updated until the layout cache is // edited with the output of the layout // if we try to calculate the distance between the two // vertices, using graph.getDistance(), // that seems to be incorrect as well if (ringNumber == 0) { graph.setLocation(vertices[i], radius*Math.cos(j*phi+fudge)+xOff, radius*Math.sin(j*phi+fudge)+yOff); //System.out.println("Vertex "+i+" "+c); } else { // ringNumber > 0 ; we use heuristics to place the // node around the ring /* for each node, find place that it has least neighbor distance */ int z=0; int minJ=0; double minDistance = 1e12; double di,newX,newY; for (z=0; z 0 */ j++; i++; } /* while vertices in a ring */ // increment radius for next ring radius += radiusIncrement; ringNumber += 1; } /* while vertices */ } } /* class ConcentricCirclesLayout */