package prefuse.action.layout; import java.awt.Insets; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import prefuse.Display; import prefuse.action.GroupAction; import prefuse.util.PrefuseLib; import prefuse.visual.VisualItem; /** * Abstract base class providing convenience methods for layout algorithms. * * @author <a href="http://jheer.org">jeffrey heer</a> */ public abstract class Layout extends GroupAction { /** The explicitly set layout bounds. May be null. */ protected Rectangle2D m_bounds = null; /** The explicitly set anchor point at which the layout can * be centered or rooted. May be null. */ protected Point2D m_anchor = null; protected boolean m_margin = false; protected Insets m_insets = new Insets(0,0,0,0); protected double[] m_bpts = new double[4]; protected Rectangle2D m_tmpb = new Rectangle2D.Double(); protected Point2D m_tmpa = new Point2D.Double(); // ------------------------------------------------------------------------ /** * Create a new Layout. */ public Layout() { super(); } /** * Create a new Layout. * @param group the data group to layout. */ public Layout(String group) { super(group); } public Layout(String group, long duration) { super(group, duration); } // ------------------------------------------------------------------------ /** * Set the margins the layout should observe within its layout bounds. * @param top the top margin, in pixels * @param left the left margin, in pixels * @param bottom the bottom margin, in pixels * @param right the right margin, in pixels */ public void setMargin(int top, int left, int bottom, int right) { m_insets.top = top; m_insets.left = left; m_insets.bottom = bottom; m_insets.right = right; m_margin = true; } /** * Returns the bounds in which the layout should be computed. If the * bounds have been explicitly set, that value is used. Otherwise, * an attempt is made to compute the bounds based upon the display * region of the first display found in this action's associated * Visualization. * @return the layout bounds within which to constain the layout. */ public Rectangle2D getLayoutBounds() { if ( m_bounds != null ) return m_bounds; if ( m_vis != null && m_vis.getDisplayCount() > 0 ) { Display d = m_vis.getDisplay(0); Insets i = m_margin ? m_insets : d.getInsets(m_insets); m_bpts[0] = i.left; m_bpts[1] = i.top; m_bpts[2] = d.getWidth()-i.right; m_bpts[3] = d.getHeight()-i.bottom; d.getInverseTransform().transform(m_bpts,0,m_bpts,0,2); m_tmpb.setRect(m_bpts[0],m_bpts[1], m_bpts[2]-m_bpts[0], m_bpts[3]-m_bpts[1]); return m_tmpb; } else { return null; } } /** * Explicitly set the layout bounds. A reference to the input rectangle * instance is maintained, not a copy, and so any subsequent changes to * the rectangle object will also change the layout bounds. * @param b a rectangle specifying the layout bounds. A reference to this * same instance is kept. */ public void setLayoutBounds(Rectangle2D b) { m_bounds = b; } /** * Return the layout anchor at which to center or root the layout. How this * point is used (if it is used at all) is dependent on the particular * Layout implementation. If no anchor point has been explicitly set, the * center coordinate for the first display found in this action's * associated Visualization is used, if available. * @return the layout anchor point. */ public Point2D getLayoutAnchor() { if ( m_anchor != null ) return m_anchor; m_tmpa.setLocation(0,0); if ( m_vis != null ) { Display d = m_vis.getDisplay(0); m_tmpa.setLocation(d.getWidth()/2.0,d.getHeight()/2.0); d.getInverseTransform().transform(m_tmpa, m_tmpa); } return m_tmpa; } /** * Explicitly set the layout anchor point. The provided object will be * used directly (rather than copying its values), so subsequent * changes to that point object will change the layout anchor. * @param a the layout anchor point to use */ public void setLayoutAnchor(Point2D a) { m_anchor = a; } /** * Convenience method for setting an x-coordinate. The start value of the * x-coordinate will be set to the current value, and the current and end * values will be set to the provided x-coordinate. If the current value * is not a number (NaN), the x-coordinate of the provided referrer * item (if non null) will be used to set the start coordinate. * @param item the item to set * @param referrer the referrer item to use for the start location if * the current valu eis not a number (NaN) * @param x the x-coordinate value to set. This will be set for both * the current and end values. * @see prefuse.util.PrefuseLib#setX(VisualItem, VisualItem, double) */ public void setX(VisualItem item, VisualItem referrer, double x) { PrefuseLib.setX(item, referrer, x); } /** * Convenience method for setting an y-coordinate. The start value of the * y-coordinate will be set to the current value, and the current and end * values will be set to the provided y-coordinate. If the current value * is not a number (NaN), the y-coordinate of the provided referrer * item (if non null) will be used to set the start coordinate. * @param item the item to set * @param referrer the referrer item to use for the start location if * the current valu eis not a number (NaN) * @param y the y-coordinate value to set. This will be set for both * the current and end values. * @see prefuse.util.PrefuseLib#setY(VisualItem, VisualItem, double) */ public void setY(VisualItem item, VisualItem referrer, double y) { PrefuseLib.setY(item, referrer, y); } } // end of abstract class Layout