diff --git a/projects/viztool/src/java/com/samskivert/viztool/Driver.java b/projects/viztool/src/java/com/samskivert/viztool/Driver.java index 0b1e3d0d..c2f886f8 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/Driver.java +++ b/projects/viztool/src/java/com/samskivert/viztool/Driver.java @@ -1,8 +1,11 @@ // -// $Id: Driver.java,v 1.1 2001/07/14 00:55:21 mdb Exp $ +// $Id: Driver.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool; +import java.awt.*; +import java.awt.print.*; + import com.samskivert.viztool.enum.*; import com.samskivert.viztool.viz.*; @@ -16,6 +19,11 @@ public class Driver } String pkgroot = args[0]; + boolean print = false; + if (args.length > 1) { + print = (args[1].equals("-print")); + } + // run ourselves on the classpath String classpath = System.getProperty("java.class.path"); ClassEnumerator enum = new ClassEnumerator(classpath); @@ -29,6 +37,33 @@ public class Driver // and finally generate the visualization PackageEnumerator penum = new PackageEnumerator(pkgroot, enum, true); HierarchyVisualizer viz = new HierarchyVisualizer(pkgroot, penum); - viz.render(10, System.out); + + if (print) { + // we use the print system to render things + PrinterJob job = PrinterJob.getPrinterJob(); + // pop up a dialog to format our pages + // PageFormat format = job.pageDialog(job.defaultPage()); + PageFormat format = job.defaultPage(); + job.setPrintable(viz); + // pop up a dialog to control printing + job.printDialog(); + + try { + job.print(); + } catch (PrinterException pe) { + pe.printStackTrace(System.err); + } + System.exit(0); + } else { + TestFrame frame = new TestFrame(viz); + + // center the frame in the screen and show it + Toolkit tk = frame.getToolkit(); + Dimension ss = tk.getScreenSize(); + int width = 640, height = 480; + frame.setBounds((ss.width-width)/2, (ss.height-height)/2, + width, height); + frame.setVisible(true); + } } } diff --git a/projects/viztool/src/java/com/samskivert/viztool/VizFrame.java b/projects/viztool/src/java/com/samskivert/viztool/VizFrame.java new file mode 100644 index 00000000..264996d2 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/VizFrame.java @@ -0,0 +1,23 @@ +// +// $Id: VizFrame.java,v 1.1 2001/07/17 01:54:19 mdb Exp $ + +package com.samskivert.viztool; + +import java.awt.BorderLayout; +import javax.swing.*; + +import com.samskivert.viztool.viz.HierarchyVisualizer; + +public class TestFrame extends JFrame +{ + public TestFrame (HierarchyVisualizer viz) + { + super("Test Frame"); + + // quit if we're closed + setDefaultCloseOperation(EXIT_ON_CLOSE); + + TestPanel panel = new TestPanel(viz); + getContentPane().add(panel, BorderLayout.CENTER); + } +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/VizPanel.java b/projects/viztool/src/java/com/samskivert/viztool/VizPanel.java new file mode 100644 index 00000000..1f5f4ffc --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/VizPanel.java @@ -0,0 +1,32 @@ +// +// $Id: VizPanel.java,v 1.1 2001/07/17 01:54:19 mdb Exp $ + +package com.samskivert.viztool; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import javax.swing.*; + +import com.samskivert.viztool.viz.HierarchyVisualizer; + +public class TestPanel extends JPanel +{ + public TestPanel (HierarchyVisualizer viz) + { + _viz = viz; + + // set the font + Font font = new Font("Courier", Font.PLAIN, 10); + setFont(font); + } + + public void paintComponent (Graphics g) + { + Graphics2D gfx = (Graphics2D)g; + Rectangle2D bounds = getBounds(); + _viz.layout(gfx, 0, 0, bounds.getWidth(), bounds.getHeight()); + _viz.paint(gfx, 0); + } + + protected HierarchyVisualizer _viz; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java index 6caf8ce4..6aab73ce 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java @@ -1,14 +1,16 @@ // -// $Id: CascadingChainVisualizer.java,v 1.2 2001/07/14 00:55:21 mdb Exp $ +// $Id: CascadingChainVisualizer.java,v 1.3 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.*; +import java.awt.font.TextLayout; import java.util.ArrayList; /** - * The cascading chain layout lays out chains in the standard cascading - * format that looks something like this: + * The cascading chain visualizer lays out chains in the standard + * cascading format that looks something like this: * *
  * Foo
@@ -19,47 +21,111 @@ import java.util.ArrayList;
  * |
  * +-> Baz
  * 
- * - * It should be used in tandem with the - * CascadingChainRenderer. - * - * @see CascadingChainRenderer */ -public class CascadingChainLayout - implements ChainLayout, CascadingConstants +public class CascadingChainVisualizer + implements ChainVisualizer, CascadingConstants { // docs inherited from interface - public void layoutChain (Chain chain, int pointSize) + public void layoutChain (Chain chain, Graphics2D gfx) { + // create a text layout based on the current rendering conditions + TextLayout layout = new TextLayout(chain.getName(), gfx.getFont(), + gfx.getFontRenderContext()); + // the header will be the name of this chain surrounded by N // points of space and a box - int hwid = PostscriptUtil.estimateWidth(chain.getName(), pointSize) + - 2*HEADER_BORDER; - int hhei = 2*HEADER_BORDER + pointSize; - int maxwid = hwid; + Rectangle2D bounds = getTextBox(gfx, layout); + double maxwid = bounds.getWidth(); // the children will be below the name of this chain and inset by // four points to make space for the connecty lines - int x = 2*GAP, y = hhei; + double x = 2*GAP, y = bounds.getHeight(); ArrayList kids = chain.getChildren(); for (int i = 0; i < kids.size(); i++) { Chain kid = (Chain)kids.get(i); - Dimension ksize = kid.getSize(); + Rectangle2D kbounds = kid.getBounds(); y += GAP; // add the gap - kid.setLocation(x, y); - y += ksize.height; // add the dimensions of the kid -// System.err.println("Locating " + kid.getName() + -// " at +" + x + "+" + y + "."); + kid.setBounds(x, y, kbounds.getWidth(), kbounds.getHeight()); + y += kbounds.getHeight(); // add the dimensions of the kid // track max width - if (maxwid < (x + ksize.width)) { - maxwid = x + ksize.width; + if (maxwid < (x + kbounds.getWidth())) { + maxwid = x + kbounds.getWidth(); } } // set the dimensions of the main chain -// System.err.println("Sizing " + chain.getName() + -// " to " + maxwid + "x" + y + "."); - chain.setSize(maxwid, y); + Rectangle2D cbounds = chain.getBounds(); + chain.setBounds(cbounds.getX(), cbounds.getY(), maxwid, y); + } + + // docs inherited from interface + public void renderChain (Chain chain, Graphics2D gfx) + { + // figure out where we'll be rendering + Rectangle2D bounds = chain.getBounds(); + double x = bounds.getX(); + double y = bounds.getY(); + +// System.err.println("Rendering " + chain.getName() + +// " at +" + x + "+" + y + "."); + + // create a text layout based on the current rendering conditions + TextLayout layout = new TextLayout(chain.getName(), gfx.getFont(), + gfx.getFontRenderContext()); + + // stroke a box that will contain the name + Rectangle2D tbounds = getTextBox(gfx, layout); + double tx = -bounds.getX(), ty = -bounds.getY(); + tbounds.setRect(x, y, tbounds.getWidth(), tbounds.getHeight()); + gfx.draw(tbounds); + + // now draw the name + layout.draw(gfx, (float)(x + HEADER_BORDER), + (float)(y + layout.getAscent() + HEADER_BORDER)); + + // render our connecty lines + ArrayList kids = chain.getChildren(); + if (kids.size() > 0) { + GeneralPath path = new GeneralPath(); + Rectangle2D kbounds = ((Chain)kids.get(0)).getBounds(); + double half = kbounds.getX()/2; + path.moveTo((float)(x + half), (float)(y + tbounds.getHeight())); + + for (int i = 0; i < kids.size(); i++) { + Chain kid = (Chain)kids.get(i); + kbounds = kid.getBounds(); + double ly = y + kbounds.getY() + layout.getAscent() + + HEADER_BORDER; + path.lineTo((float)(x + half), (float)ly); + path.lineTo((float)(x + kbounds.getX()), (float)ly); + path.moveTo((float)(x + half), (float)ly); + } + + gfx.draw(path); + } + + // translate the gfx so that 0,0 is at our origin + gfx.translate(x, y); + + // now render the kids + for (int i = 0; i < kids.size(); i++) { + Chain kid = (Chain)kids.get(i); + renderChain(kid, gfx); + } + + // undo our prior translation + gfx.translate(-x, -y); + } + + protected static Rectangle2D getTextBox (Graphics2D gfx, + TextLayout layout) + { + Rectangle2D bounds = layout.getBounds(); + // incorporate room for the border in the bounds + bounds.setRect(bounds.getX(), bounds.getY(), + bounds.getWidth() + 2*HEADER_BORDER, + bounds.getHeight() + 2*HEADER_BORDER); + return bounds; } } diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java index e3d2a404..981abe00 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java @@ -1,11 +1,11 @@ // -// $Id: Chain.java,v 1.3 2001/07/14 00:55:21 mdb Exp $ +// $Id: Chain.java,v 1.4 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; import java.util.ArrayList; -import java.awt.Dimension; -import java.awt.Point; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; /** * A chain is used by the hierarchy visualizer to represent inheritance @@ -48,55 +48,22 @@ public class Chain implements Element } /** - * Returns a Dimension instance representing the size of - * this chain (and all contained subchains). All coordinates are in - * points. + * Returns a Rectangle2D instance representing the size + * of this chain (and all contained subchains). */ - public Dimension getSize () + public Rectangle2D getBounds () { - return _size; + return _bounds; } /** - * Returns the location of this chain relative to its parent chain. - * All coordinates are in points. - */ - public Point getLocation () - { - return _location; - } - - /** - * Sets the size of this chain. All coordinates are in points. + * Sets the bounds of this chain. * - * @see #getSize + * @see #getBounds */ - public void setSize (int width, int height) + public void setBounds (double x, double y, double width, double height) { - _size = new Dimension(width, height); - } - - /** - * Sets the location of this chain relative to its parent. All - * coordinates are in points. - * - * @see #getLocation - */ - public void setLocation (int x, int y) - { - _location = new Point(x, y); - } - - // inherited from interface - public void setPage (int pageno) - { - _pageno = pageno; - } - - // inherited from interface - public int getPage () - { - return _pageno; + _bounds.setRect(x, y, width, height); } /** @@ -115,16 +82,16 @@ public class Chain implements Element * the supplied layout manager arrange those children and compute the * dimensions of this chain based on all of that information. */ - public void layout (int pointSize, ChainLayout clay) + public void layout (Graphics2D gfx, ChainVisualizer cviz) { // first layout our children for (int i = 0; i < _children.size(); i++) { Chain child = (Chain)_children.get(i); - child.layout(pointSize, clay); + child.layout(gfx, cviz); } // now lay ourselves out - clay.layoutChain(this, pointSize); + cviz.layoutChain(this, gfx); } /** @@ -192,7 +159,5 @@ public class Chain implements Element protected Class _root; protected ArrayList _children = new ArrayList(); - protected Dimension _size = new Dimension(0, 0); - protected Point _location = new Point(0, 0); - protected int _pageno; + protected Rectangle2D _bounds = new Rectangle2D.Double(); } diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java index 1134c362..0f4c0a2c 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java @@ -1,11 +1,12 @@ // -// $Id: ChainGroup.java,v 1.1 2001/07/14 00:55:21 mdb Exp $ +// $Id: ChainGroup.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Dimension; -import java.awt.Point; -import java.io.PrintStream; +import java.awt.*; +import java.awt.geom.GeneralPath; +import java.awt.geom.Rectangle2D; +import java.awt.font.TextLayout; import java.util.ArrayList; import java.util.Iterator; @@ -32,75 +33,99 @@ public class ChainGroup /** * Lays out the chains in this group and returns the total size. */ - public Dimension layout (int pointSize) + public Rectangle2D layout (Graphics2D gfx, double pageWidth, + double pageHeight) { // lay out the internal structure of our chains - ChainLayout clay = new CascadingChainLayout(); + ChainVisualizer clay = new CascadingChainVisualizer(); for (int i = 0; i < _roots.size(); i++) { Chain chain = (Chain)_roots.get(i); - chain.layout(pointSize, clay); + chain.layout(gfx, clay); } + // we'll need room to incorporate our title + TextLayout layout = new TextLayout(_pkg, gfx.getFont(), + gfx.getFontRenderContext()); + + // keep room for our border + pageWidth -= 2*BORDER; + pageHeight -= (2*BORDER + layout.getAscent()); + // arrange them on the page ElementLayout elay = new PackedColumnElementLayout(); - Dimension[] dims = elay.layout(_roots, MAX_WIDTH, MAX_HEIGHT); + Rectangle2D[] dims = elay.layout(_roots, pageWidth, pageHeight); // for now we're punting and assume that no group will exceed a // single page in size - _size = new Dimension(); - _size.width = dims[0].width + 2*BORDER; - _size.height = dims[0].height + 2*BORDER; + double width = dims[0].getWidth(); + double height = dims[0].getHeight() + layout.getAscent(); + // make sure we're wide enough for our title + width = Math.max(width, layout.getAdvance() + 4); + + _size = new Rectangle2D.Double(); + _size.setRect(0, 0, width + 2*BORDER, height + 2*BORDER); + + System.out.println("L(" + _pkg + ") " + _size.getWidth() + "x" + + _size.getHeight() + "+" + _size.getX() + "+" + _size.getY() + "."); return _size; } /** - * Renders the chains in this group to the supplied output stream. + * Renders the chains in this group to the supplied graphics object. * This function requires that layoutGroup has previously * been called to lay out the group's chains. * - * @return the size of the rectangle occupied by the rendered chain - * group. - * * @see #layoutGroup */ - public void render (int pointSize, PrintStream out, int x, int y) + public void render (Graphics2D gfx, double x, double y) { + TextLayout layout = new TextLayout(_pkg, gfx.getFont(), + gfx.getFontRenderContext()); + + // shift everything down to the ascent of the title + y += layout.getAscent(); + + // translate to our rendering area + double cx = x + BORDER; + double cy = y + BORDER; + gfx.translate(cx, cy); + // render our chains - ChainRenderer renderer = new CascadingChainRenderer(); + ChainVisualizer renderer = new CascadingChainVisualizer(); for (int i = 0; i < _roots.size(); i++) { Chain chain = (Chain)_roots.get(i); - Point loc = chain.getLocation(); - renderer.renderChain(chain, out, pointSize, - x + BORDER, y + BORDER); + Rectangle2D bounds = chain.getBounds(); + // render the chain + renderer.renderChain(chain, gfx); } + // undo the translation + gfx.translate(-cx, -cy); + // print our title and a box around our border - out.println("/tname (" + _pkg + ") def"); - out.println("/twid tname stringwidth pop def"); + layout.draw(gfx, (float)(x + BORDER + 2), (float)y); - int bx = x + BORDER + 2, by = y - pointSize/2; - out.println(bx + " " + by + " moveto"); - out.println("tname abshow"); + double height = _size.getHeight() - layout.getAscent(); + GeneralPath path = new GeneralPath(); + path.moveTo((float)(x + BORDER), (float)y); + path.lineTo((float)x, (float)y); + path.lineTo((float)x, (float)(y + height)); + path.lineTo((float)(x + _size.getWidth()), + (float)(y + height)); + path.lineTo((float)(x + _size.getWidth()), (float)y); + path.lineTo((float)(x + BORDER + layout.getAdvance() + 4), (float)y); + gfx.draw(path); + } - out.println((x + BORDER - 2) + " " + y + " moveto"); - out.println("-" + (BORDER - 2) + " 0 rlineto"); - out.println("0 " + _size.height + " rlineto"); - out.println(_size.width + " 0 rlineto"); - out.println("0 -" + _size.height + " rlineto"); - out.println((_size.width - BORDER - 4) + " twid sub neg 0 rlineto"); - out.println("stroke"); + public Chain getRoot (int index) + { + return (Chain)_roots.get(index); } protected String _pkg; protected ArrayList _roots; - protected Dimension _size; + protected Rectangle2D _size; - protected static final int PAGE_WIDTH = (int)(72 * 7.5); - protected static final int PAGE_HEIGHT = (int)(72 * 10); - - protected static final int BORDER = 72/8; - - protected static final int MAX_WIDTH = PAGE_WIDTH - 2*BORDER; - protected static final int MAX_HEIGHT = PAGE_HEIGHT - 2*BORDER; + protected static final double BORDER = 72/8; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java index b20bc3e9..12d8317a 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java @@ -1,20 +1,16 @@ // -// $Id: ChainVisualizer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: ChainVisualizer.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; +import java.awt.Graphics2D; + /** - * The chain layout is used to compute the dimensions of chains and their - * children in preparation for rendering them in a particular manner. In - * general a layout is coupled with one or more renderers which generate - * the proper rendering instructions based on the layout information - * computed by this layout manager. The reason rendering is decoupled from - * layout is to facilitate easier support for rendering the same layout - * via different formatting languages (postscript and vml for example). - * - * @see ChainRenderer + * The chain visualizer is used to compute the dimensions of chains and + * their children in preparation for rendering and then to perform said + * rendering. */ -public interface ChainLayout +public interface ChainVisualizer { /** * Assigns positions to the children of the supplied chain based on @@ -25,8 +21,16 @@ public interface ChainLayout * (meaning they have dimensions but no position) prior to this call. * * @param chain the chain to be layed out. - * @param pointSize the point size of the font to be used in computing - * our dimensions (all coordinates are in points). + * @param gfx the graphics context to use when computing dimensions. */ - public void layoutChain (Chain chain, int pointSize); + public void layoutChain (Chain chain, Graphics2D gfx); + + /** + * Renders the specified chain (and its subchains) based on the layout + * information (dimensions) already computed for this chain. + * + * @param chain the chain to be rendered. + * @param gfx the graphics context in which to render the chain. + */ + public void renderChain (Chain chain, Graphics2D gfx); } diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java index e376a58e..d791a104 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java @@ -1,11 +1,11 @@ // -// $Id: HierarchyVisualizer.java,v 1.3 2001/07/14 00:55:21 mdb Exp $ +// $Id: HierarchyVisualizer.java,v 1.4 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Dimension; -import java.awt.Point; -import java.io.PrintStream; +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.print.*; import java.util.*; import com.samskivert.viztool.Log; @@ -17,7 +17,7 @@ import com.samskivert.util.Comparators; * representation so that an entire package can be displayed on a single * page (or small number of pages). */ -public class HierarchyVisualizer +public class HierarchyVisualizer implements Printable { /** * Constructs a hierarchy visualizer with the supplied enumerator as @@ -62,41 +62,95 @@ public class HierarchyVisualizer _packages[i], _classes.iterator(), false); _groups[i] = new ChainGroup(pkgroot, _packages[i], penum); } + + // we'll need these for later + _bounds = new Rectangle2D.Double[_packages.length]; + _pagenos = new int[_packages.length]; } /** * Lays out and renders each of the chain groups that make up this * package hierarchy visualization. */ - public void render (int pointSize, PrintStream out) + public int print (Graphics g, PageFormat pf, int pageIndex) + throws PrinterException { - int x = X_MARGIN, y = Y_MARGIN; + Graphics2D gfx = (Graphics2D)g; - // print the preamble for the first page - out.println("0 setlinewidth"); + // adjust the stroke + gfx.setStroke(new BasicStroke(0.1f)); - for (int i = 0; i < _groups.length; i++) { - // lay out the group in question - Dimension dims = _groups[i].layout(pointSize); + // and the font + Font font = new Font("Courier", Font.PLAIN, 8); + gfx.setFont(font); - // determine if we need to skip to the next page or not - if ((y > 0) && (y + dims.height > PAGE_HEIGHT)) { - // print the postamble for this page and preamble for the - // new page - out.println("showpage"); - out.println("0 setlinewidth"); - y = Y_MARGIN; - } + // only relay things out if the page format has changed + if (!pf.equals(_format)) { + // keep this around + _format = pf; - // render the group at the requested location - _groups[i].render(pointSize, out, x, y); - - // increment our y location - y += (dims.height + GAP); + // and do the layout + layout(gfx, pf.getImageableX(), pf.getImageableY(), + pf.getImageableWidth(), pf.getImageableHeight()); } - // print the postamble for the final page - out.println("showpage"); + // render the groups on the requested page + int rendered = 0; + for (int i = 0; i < _groups.length; i++) { + // skip groups not on this page + if (_pagenos[i] != pageIndex) { + continue; + } + _groups[i].render(gfx, _bounds[i].getX(), _bounds[i].getY()); + rendered++; + } + + return (rendered > 0) ? PAGE_EXISTS : NO_SUCH_PAGE; + } + + public void layout (Graphics2D gfx, double x, double y, + double width, double height) + { + double starty = x; + int pageno = 0; + + System.out.println("Laying out " + width + "x" + height + + "+" + x + "+" + y + "."); + + // lay out our groups + for (int i = 0; i < _groups.length; i++) { + // lay out the group in question + Rectangle2D bounds = _groups[i].layout(gfx, width, height); + + // determine if we need to skip to the next page or not + if ((y > 0) && (y + bounds.getHeight() > height)) { + y = starty; + pageno++; + } + + // assign x and y coordinates to this group + bounds.setRect(x, y, bounds.getWidth(), bounds.getHeight()); + // and store it + _bounds[i] = bounds; + // also make a note of our page index + _pagenos[i] = pageno; + + // increment our y location + y += (bounds.getHeight() + GAP); + } + } + + public void paint (Graphics2D gfx, int pageIndex) + { + // render the groups on the requested page + for (int i = 0; i < _groups.length; i++) { + // skip groups not on this page + if (_pagenos[i] != pageIndex) { + continue; + } + _groups[i].render((Graphics2D)gfx, + _bounds[i].getX(), _bounds[i].getY()); + } } protected String _pkgroot; @@ -105,6 +159,10 @@ public class HierarchyVisualizer protected String[] _packages; protected ChainGroup[] _groups; + protected PageFormat _format; + protected Rectangle2D[] _bounds; + protected int[] _pagenos; + protected static final int PAGE_WIDTH = (int)(72 * 7.5); protected static final int PAGE_HEIGHT = (int)(72 * 10); diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java deleted file mode 100644 index 1e3321df..00000000 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java +++ /dev/null @@ -1,73 +0,0 @@ -// -// $Id: CascadingChainRenderer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ - -package com.samskivert.viztool.viz; - -import java.awt.Dimension; -import java.awt.Point; -import java.io.PrintStream; -import java.util.ArrayList; - -/** - * Renders a chain that has been layed out by the cascading chain layout - * manager. - * - * @see CascadingChainLayout - */ -public class CascadingChainRenderer - implements ChainRenderer, CascadingConstants -{ - // docs inherited from interface - public void renderChain (Chain chain, PrintStream out, - int pointSize, int x, int y) - { - Point loc = chain.getLocation(); - - // figure out where we'll be located - x += loc.x; - y += loc.y; - -// System.err.println("Rendering " + chain.getName() + -// " at +" + x + "+" + y + "."); - - // work out some useful stuff - out.println("gsave"); - out.println("/size " + pointSize + " def"); - out.println(x + " " + y + " translate"); - out.println("/cname (" + chain.getName() + ") def"); - out.println("/nwid cname stringwidth pop def"); - out.println("/nhei size def"); - out.println("/border " + HEADER_BORDER + " def"); - out.println("/dborder border 2 mul def"); - - // stroke a box that will contain the name - out.println("0 0 nwid dborder add nhei dborder add rectstroke"); -// out.println("border border nwid nhei rectstroke"); - out.println("border border moveto cname abshow"); - - // render our connecty lines - ArrayList kids = chain.getChildren(); - if (kids.size() > 0) { - Point kloc = ((Chain)kids.get(0)).getLocation(); - int half = kloc.x/2; - out.println(half + " nhei dborder add moveto"); - - for (int i = 0; i < kids.size(); i++) { - Chain kid = (Chain)kids.get(i); - kloc = kid.getLocation(); - out.println(half + " " + - (kloc.y + pointSize/2 + HEADER_BORDER) + " lineto"); - out.println(half + " 0 rlineto"); - out.println(half + " neg 0 rmoveto"); - } - out.println("stroke"); - } - out.println("grestore"); - - // now render the kids - for (int i = 0; i < kids.size(); i++) { - Chain kid = (Chain)kids.get(i); - renderChain(kid, out, pointSize, x, y); - } - } -} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java index c1acf0c2..878d7298 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java @@ -1,5 +1,5 @@ // -// $Id: CascadingConstants.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: CascadingConstants.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; @@ -14,10 +14,10 @@ public interface CascadingConstants /** * The number of points surrounding the name of the chain. */ - public static final int HEADER_BORDER = 3; + public static final double HEADER_BORDER = 3; /** * The number of points of spacing between each child chain. */ - public static final int GAP = 4; + public static final double GAP = 4; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java b/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java deleted file mode 100644 index bd17c9cd..00000000 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java +++ /dev/null @@ -1,37 +0,0 @@ -// -// $Id: ChainRenderer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ - -package com.samskivert.viztool.viz; - -import java.io.PrintStream; - -/** - * The chain renderer is used to generate rendering output based on a - * previously layed-out chain. This generally means that renderers are - * tightly coupled to layout managers. This is intentional and the - * decoupling that exists only exists to facilitate multiple renderer - * implementations for multiple output formats (postscript and vml, for - * example). - * - * @see ChainLayout - */ -public interface ChainRenderer -{ - /** - * Generates rendering instructions for the specified chain (and its - * subchains) based on the layout information (dimensions) already - * computed for this chain. - * - * @param chain the chain to be rendered. - * @param out the output stream on which to write the rendering - * instructions. - * @param pointSize the point size of the font to be used when - * rendering this chain. - * @param x the x coordinate at which to render the chain (any - * relative coordinates maintained by the chain should be used as - * offsets to this absolute coordinate). - * @param y the y coordinate at which to render the chain. - */ - public void renderChain (Chain chain, PrintStream out, - int pointSize, int x, int y); -} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java b/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java index 06924f8c..498ebf31 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java @@ -1,13 +1,12 @@ // -// $Id: Element.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: Element.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Point; -import java.awt.Dimension; +import java.awt.geom.Rectangle2D; /** - * A page is composed of elements which have some rectangular dimension. + * A page is composed of elements which have some rectangular bounds. * They can be laid out by ElementLayout implementations in a * general purpose way. */ @@ -19,23 +18,12 @@ public interface Element public String getName (); /** - * Returns the size of this element. All coordinates are in points. + * Returns the bounds of this element. */ - public Dimension getSize (); + public Rectangle2D getBounds (); /** - * Sets the upper left position of this element. All coordinates are - * in points. + * Sets the bounds of this element. */ - public void setLocation (int x, int y); - - /** - * Sets the page number of this element. - */ - public void setPage (int pageno); - - /** - * Returns the page number previously set by setPage. - */ - public int getPage (); + public void setBounds (double x, double y, double width, double height); } diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java b/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java index c96adfc0..8e9865e8 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java @@ -1,15 +1,15 @@ // -// $Id: ElementLayout.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: ElementLayout.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Dimension; +import java.awt.geom.Rectangle2D; import java.util.List; /** * The element layout is used to lay a collection of elements out on a * page. It computes the desired position of each element and sets it via - * setLocation() with the expectation that the location of the + * setBounds() with the expectation that the location of the * elements will be used later in the rendering process. */ public interface ElementLayout @@ -18,8 +18,9 @@ public interface ElementLayout * Lay out the supplied list of elements. Page numbers should be * assigned to all elements if the layout spans multiple pages. * - * @return an array of dimension objects representing the width and + * @return an array of rectangle objects representing the width and * height of each page that was laid out. */ - public Dimension[] layout (List elements, int pageWidth, int pageHeight); + public Rectangle2D[] layout (List elements, + double pageWidth, double pageHeight); } diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java index 2cdfec99..3369f150 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java @@ -1,13 +1,10 @@ // -// $Id: PackedColumnElementLayout.java,v 1.2 2001/07/14 00:55:21 mdb Exp $ +// $Id: PackedColumnElementLayout.java,v 1.3 2001/07/17 01:54:19 mdb Exp $ package com.samskivert.viztool.viz; -import java.awt.Dimension; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.awt.geom.Rectangle2D; +import java.util.*; /** * Lays out the elements in columns of balanced height with elements @@ -16,7 +13,8 @@ import java.util.List; public class PackedColumnElementLayout implements ElementLayout { // docs inherited from interface - public Dimension[] layout (List elements, int pageWidth, int pageHeight) + public Rectangle2D[] layout (List elements, + double pageWidth, double pageHeight) { // create a new list containing the elements whose order we can // manipulate willy nilly @@ -24,15 +22,22 @@ public class PackedColumnElementLayout implements ElementLayout elements.toArray(elems); Arrays.sort(elems, HEIGHT_COMP); + System.out.println("Laying out in " + + pageWidth + "x" + pageHeight + "."); + // lay out the elements across the page ArrayList pagedims = new ArrayList(); - int x = 0, y = 0, rowheight = 0, maxwidth = 0, pageno = 0; + double x = 0, y = 0, rowheight = 0, maxwidth = 0; + int pageno = 0; + for (int i = 0; i < elems.length; i++) { - Dimension size = elems[i].getSize(); + Rectangle2D bounds = elems[i].getBounds(); // see if we fit into this row or not (but force placement if // we're currently at the left margin) - if ((x > 0) && ((x + size.width) > pageWidth)) { + if ((x > 0) && ((x + bounds.getWidth()) > pageWidth)) { + // strip off the trailing GAP + x -= GAP; // track our maxwidth if (x > maxwidth) { maxwidth = x; @@ -41,43 +46,46 @@ public class PackedColumnElementLayout implements ElementLayout x = 0; y += (rowheight + GAP); // reset our max rowheight - rowheight = size.height; + rowheight = bounds.getHeight(); } // make sure we fit on this page (but force placement if we're // currently at the top margin) - if ((y > 0) && ((y + size.height) > pageHeight)) { + if ((y > 0) && ((y + bounds.getHeight()) > pageHeight)) { // make a note of how big the current page is - pagedims.add(new Dimension(maxwidth, y)); + pagedims.add(new Rectangle2D.Double(0, 0, maxwidth, y)); // move to the next page x = 0; y = 0; - rowheight = size.height; + rowheight = bounds.getHeight(); maxwidth = 0; pageno++; } // lay this element out at our current coordinates - elems[i].setLocation(x, y); - elems[i].setPage(pageno); + elems[i].setBounds(x, y, bounds.getWidth(), bounds.getHeight()); +// elems[i].setPage(pageno); + System.out.println("Laying out " + elems[i].getName() + + " at " + elems[i].getBounds() + "."); // keep track of the maximum row height - if (size.height > rowheight) { - rowheight = size.height; + if (bounds.getHeight() > rowheight) { + rowheight = bounds.getHeight(); } // advance in the x direction - x += (size.width + GAP); + x += (bounds.getWidth() + GAP); } // take a final stab at our maxwidth + x -= GAP; if (x > maxwidth) { maxwidth = x; } // make a note of how big the final page is - pagedims.add(new Dimension(maxwidth, y+rowheight)); - Dimension[] dims = new Dimension[pagedims.size()]; + pagedims.add(new Rectangle2D.Double(0, 0, maxwidth, y+rowheight)); + Rectangle2D[] dims = new Rectangle2D[pagedims.size()]; pagedims.toArray(dims); return dims; } @@ -90,7 +98,8 @@ public class PackedColumnElementLayout implements ElementLayout Element e2 = (Element)o2; // tallest element wins - int diff = e2.getSize().height - e1.getSize().height; + int diff = (int)(e2.getBounds().getHeight() - + e1.getBounds().getHeight()); // if they are the same height, sort alphabetically return (diff != 0) ? diff : e1.getName().compareTo(e2.getName()); @@ -103,7 +112,7 @@ public class PackedColumnElementLayout implements ElementLayout } // hard coded for now, half inch margins - protected static final int GAP = 72/4; + protected static final double GAP = 72/4; protected static final Comparator HEIGHT_COMP = new HeightComparator(); }