/* * Copyright (c) 2003, the JUNG Project and the Regents of the University of * California All rights reserved. * * This software is open-source under the BSD license; see either "license.txt" * or http://jung.sourceforge.net/license.txt for a description. * * * */ package edu.uci.ics.jung.algorithms.layout; import java.awt.Dimension; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections15.Transformer; import edu.uci.ics.jung.algorithms.util.IterativeContext; import edu.uci.ics.jung.graph.Graph; /** * A {@code Layout} implementation that combines * multiple other layouts so that they may be manipulated * as one layout. The relaxer thread will step each layout * in sequence. * * @author Tom Nelson - tomnelson@dev.java.net * * @param the vertex type * @param the edge type */ public class AggregateLayout implements Layout, IterativeContext { protected Layout delegate; protected Map,Point2D> layouts = new HashMap,Point2D>(); /** * Creates an instance backed by the specified {@code delegate}. * @param delegate */ public AggregateLayout(Layout delegate) { this.delegate = delegate; } /** * @return the delegate */ public Layout getDelegate() { return delegate; } /** * @param delegate the delegate to set */ public void setDelegate(Layout delegate) { this.delegate = delegate; } /** * adds the passed layout as a sublayout, also specifying * the center of where this sublayout should appear * @param layout * @param center */ public void put(Layout layout, Point2D center) { layouts.put(layout,center); } /** * Returns the center of the passed layout. * @param layout * @return the center of the passed layout */ public Point2D get(Layout layout) { return layouts.get(layout); } /** * Removes {@code layout} from this instance. */ public void remove(Layout layout) { layouts.remove(layout); } /** * Removes all layouts from this instance. */ public void removeAll() { layouts.clear(); } /** * Returns the graph for which this layout is defined. * @return the graph for which this layout is defined * @see edu.uci.ics.jung.algorithms.layout.Layout#getGraph() */ public Graph getGraph() { return delegate.getGraph(); } /** * Returns the size of the underlying layout. * @return the size of the underlying layout * @see edu.uci.ics.jung.algorithms.layout.Layout#getSize() */ public Dimension getSize() { return delegate.getSize(); } /** * * @see edu.uci.ics.jung.algorithms.layout.Layout#initialize() */ public void initialize() { delegate.initialize(); for(Layout layout : layouts.keySet()) { layout.initialize(); } } /** * Override to test if the passed vertex is locked in * any of the layouts. * @param v * @return true if v is locked in any of the layouts, and false otherwise * @see edu.uci.ics.jung.algorithms.layout.Layout#isLocked(java.lang.Object) */ public boolean isLocked(V v) { boolean locked = false; for(Layout layout : layouts.keySet()) { locked |= layout.isLocked(v); } locked |= delegate.isLocked(v); return locked; } /** * override to lock or unlock this vertex in any layout with * a subgraph containing it * @param v * @param state * @see edu.uci.ics.jung.algorithms.layout.Layout#lock(java.lang.Object, boolean) */ public void lock(V v, boolean state) { for(Layout layout : layouts.keySet()) { if(layout.getGraph().getVertices().contains(v)) { layout.lock(v, state); } } delegate.lock(v, state); } /** * * @see edu.uci.ics.jung.algorithms.layout.Layout#reset() */ public void reset() { for(Layout layout : layouts.keySet()) { layout.reset(); } delegate.reset(); } /** * @param graph * @see edu.uci.ics.jung.algorithms.layout.Layout#setGraph(edu.uci.ics.jung.graph.Graph) */ public void setGraph(Graph graph) { delegate.setGraph(graph); } /** * @param initializer * @see edu.uci.ics.jung.algorithms.layout.Layout#setInitializer(org.apache.commons.collections15.Transformer) */ public void setInitializer(Transformer initializer) { delegate.setInitializer(initializer); } /** * @param v * @param location * @see edu.uci.ics.jung.algorithms.layout.Layout#setLocation(java.lang.Object, java.awt.geom.Point2D) */ public void setLocation(V v, Point2D location) { boolean wasInSublayout = false; for(Layout layout : layouts.keySet()) { if(layout.getGraph().getVertices().contains(v)) { Point2D center = layouts.get(layout); // transform by the layout itself, but offset to the // center of the sublayout Dimension d = layout.getSize(); AffineTransform at = AffineTransform.getTranslateInstance(-center.getX()+d.width/2,-center.getY()+d.height/2); Point2D localLocation = at.transform(location, null); layout.setLocation(v, localLocation); wasInSublayout = true; } } if(wasInSublayout == false && getGraph().getVertices().contains(v)) { delegate.setLocation(v, location); } } /** * @param d * @see edu.uci.ics.jung.algorithms.layout.Layout#setSize(java.awt.Dimension) */ public void setSize(Dimension d) { delegate.setSize(d); } /** * Returns a map from each {@code Layout} instance to its center point. */ public Map,Point2D> getLayouts() { return layouts; } /** * Returns the location of the vertex. The location is specified first * by the sublayouts, and then by the base layout if no sublayouts operate * on this vertex. * @return the location of the vertex * @see org.apache.commons.collections15.Transformer#transform(java.lang.Object) */ public Point2D transform(V v) { boolean wasInSublayout = false; for(Layout layout : layouts.keySet()) { if(layout.getGraph().getVertices().contains(v)) { wasInSublayout = true; Point2D center = layouts.get(layout); // transform by the layout itself, but offset to the // center of the sublayout Dimension d = layout.getSize(); AffineTransform at = AffineTransform.getTranslateInstance(center.getX()-d.width/2, center.getY()-d.height/2); return at.transform(layout.transform(v),null); } } if(wasInSublayout == false) { return delegate.transform(v); } return null; } /** * Check all sublayouts.keySet() and the delegate layout, returning * done == true iff all are done. */ public boolean done() { boolean done = true; for(Layout layout : layouts.keySet()) { if(layout instanceof IterativeContext) { done &= ((IterativeContext)layout).done(); } } if(delegate instanceof IterativeContext) { done &= ((IterativeContext)delegate).done(); } return done; } /** * call step on any sublayout that is also an IterativeContext * and is not done */ public void step() { for(Layout layout : layouts.keySet()) { if(layout instanceof IterativeContext) { IterativeContext context = (IterativeContext)layout; if(context.done() == false) { context.step(); } } } if(delegate instanceof IterativeContext) { IterativeContext context = (IterativeContext)delegate; if(context.done() == false) { context.step(); } } } }