/* Copyright (c) 1998-2006 The Regents of the University of California All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package diva.graph.layout; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import diva.graph.GraphModel; import diva.graph.GraphUtilities; import diva.graph.basic.BasicGraphModel; import diva.util.ArrayIterator; /** * A level-based layout algorithm originally implemented * by Ulfar Erlingsson at Cornell/RPI and modified to fit into * this system.

* * The algorithm is structured in the following way: * *

* * TODO: * * * @author Michael Shilman * @version $Id: LevelLayout.java 40814 2006-02-11 17:24:01Z cxh $ * @Pt.AcceptedRating Red */ public class LevelLayout extends AbstractGlobalLayout { /** * Layout the graph in levels from top to bottom. * * @see #setOrientation(int) */ public static final int VERTICAL = 0; /** * Layout the graph in levels from left to right. * * @see #setOrientation(int) */ public static final int HORIZONTAL = 1; /** * _levelData contains the layout information such as the number * of levels in the graph. Users can make queries from this object. */ // private LevelData _levelData = null; /** * Keep track of the orientation; vertical by * default. */ protected int _orientation = VERTICAL; /** * The graph implementation of the graph copy, for adding * dummy nodes/edges to the graph copy. */ private BasicGraphModel _local = null; /** * A flag to determine whether or not we want to randomize the * placement of the nodes. */ private boolean _randomizedPlacement = true; /** * Construct a new levelizing layout with a vertical orientation. */ public LevelLayout(LayoutTarget target) { super(target); _local = new BasicGraphModel(); } /** * Copy the given graph and make the nodes/edges in the copied * graph point to the nodes/edges in the original. */ protected Object copyComposite(Object origComposite) { GraphModel model = getLayoutTarget().getGraphModel(); Object copyComposite = _local.createComposite(null); HashMap map = new HashMap(); for (Iterator i = model.nodes(origComposite); i.hasNext();) { Object origNode = i.next(); if (getLayoutTarget().isNodeVisible(origNode)) { Rectangle2D r = getLayoutTarget().getBounds(origNode); LevelInfo inf = new LevelInfo(); inf.origNode = origNode; inf.x = r.getX(); inf.y = r.getY(); inf.width = r.getWidth(); inf.height = r.getHeight(); Object copyNode = _local.createNode(inf); _local.addNode(this, copyNode, copyComposite); map.put(origNode, copyNode); } } for (Iterator i = model.nodes(origComposite); i.hasNext();) { Object origTail = i.next(); for (Iterator j = model.outEdges(origTail); j.hasNext();) { Object origEdge = j.next(); Object origHead = model.getHead(origEdge); if (origHead != null) { Object copyTail = map.get(origTail); Object copyHead = map.get(origHead); if ((copyHead != null) && (copyTail != null)) { Object copyEdge = _local.createEdge(origEdge); _local.setEdgeTail(this, copyEdge, copyTail); _local.setEdgeHead(this, copyEdge, copyHead); } } } } return copyComposite; } /** * Take the layout generated by the core layout algorithm and copy * it back into the view of the original composite passed in by the * user. */ protected void copyLayout(Object origComposite, Object copyComposite) { //GraphModel model = getLayoutTarget().getGraphModel(); for (Iterator ns = _local.nodes(copyComposite); ns.hasNext();) { Object copyNode = ns.next(); LevelInfo inf = getLevelInfo(copyNode); ASSERT(inf != null, "null inf"); if (inf.origNode != null) { Rectangle2D r = getLayoutTarget().getBounds(inf.origNode); ASSERT(r != null, "null rect"); getLayoutTarget().translate(inf.origNode, inf.x - r.getX(), inf.y - r.getY()); } } LayoutUtilities.routeVisibleEdges(origComposite, getLayoutTarget()); } /** * Return the local graph model. */ public BasicGraphModel getLocalGraphModel() { return _local; } /** * Return the orientation in which the graph is to be laid out, * either VERTICAL or HORIZONTAL. */ public int getOrientation() { return _orientation; } /** * Return whether or not placement will be randomized. */ public boolean getRandomizedPlacement() { return _randomizedPlacement; } /** * Perform the levelizing layout on the given composite in the given * target environment. It operates on a copy of the composite and * then copies the layout results back into the original view (the * given layout target). */ public void layout(Object composite) { LevelData levelData = calculateLayout(composite); if (levelData != null) { applyLayout(levelData, composite); } /* _origComposite = g; _target = t; if (g.getNodeCount() > 0) { _levelData._copyGraph = copyComposite(g, t); // if (isCyclic(_copyGraph)) { // String err = "Unable to perform levelizing layout on cyclic composites"; // throw new IllegalArgumentException(err); // } breakCycles(_levelData._copyGraph); //doLayout(); copyLayout(_levelData._copyGraph, t); cleanupStructures(); } */ } /** * This method performs levelizing layout on the given composite. It * figures out the node levels, but doesn't actually layout the * composite in the target environment yet. The level information can * be accessed through the returned LevelData. This information * can be used to size the viewport. The following are the * operations performed in this method: *