Switched to rendering via the Java2D graphics API. Finished up first pass.

Everything mostly works and I can visualize my projects and end up with a
useful set of printouts.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@186 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-07-17 01:54:19 +00:00
parent b28665494b
commit 79b7065abf
14 changed files with 416 additions and 320 deletions
@@ -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; package com.samskivert.viztool;
import java.awt.*;
import java.awt.print.*;
import com.samskivert.viztool.enum.*; import com.samskivert.viztool.enum.*;
import com.samskivert.viztool.viz.*; import com.samskivert.viztool.viz.*;
@@ -16,6 +19,11 @@ public class Driver
} }
String pkgroot = args[0]; String pkgroot = args[0];
boolean print = false;
if (args.length > 1) {
print = (args[1].equals("-print"));
}
// run ourselves on the classpath // run ourselves on the classpath
String classpath = System.getProperty("java.class.path"); String classpath = System.getProperty("java.class.path");
ClassEnumerator enum = new ClassEnumerator(classpath); ClassEnumerator enum = new ClassEnumerator(classpath);
@@ -29,6 +37,33 @@ public class Driver
// and finally generate the visualization // and finally generate the visualization
PackageEnumerator penum = new PackageEnumerator(pkgroot, enum, true); PackageEnumerator penum = new PackageEnumerator(pkgroot, enum, true);
HierarchyVisualizer viz = new HierarchyVisualizer(pkgroot, penum); 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);
}
} }
} }
@@ -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);
}
}
@@ -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;
}
@@ -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; 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; import java.util.ArrayList;
/** /**
* The cascading chain layout lays out chains in the standard cascading * The cascading chain visualizer lays out chains in the standard
* format that looks something like this: * cascading format that looks something like this:
* *
* <pre> * <pre>
* Foo * Foo
@@ -19,47 +21,111 @@ import java.util.ArrayList;
* | * |
* +-> Baz * +-> Baz
* </pre> * </pre>
*
* It should be used in tandem with the
* <code>CascadingChainRenderer</code>.
*
* @see CascadingChainRenderer
*/ */
public class CascadingChainLayout public class CascadingChainVisualizer
implements ChainLayout, CascadingConstants implements ChainVisualizer, CascadingConstants
{ {
// docs inherited from interface // 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 // the header will be the name of this chain surrounded by N
// points of space and a box // points of space and a box
int hwid = PostscriptUtil.estimateWidth(chain.getName(), pointSize) + Rectangle2D bounds = getTextBox(gfx, layout);
2*HEADER_BORDER; double maxwid = bounds.getWidth();
int hhei = 2*HEADER_BORDER + pointSize;
int maxwid = hwid;
// the children will be below the name of this chain and inset by // the children will be below the name of this chain and inset by
// four points to make space for the connecty lines // 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(); ArrayList kids = chain.getChildren();
for (int i = 0; i < kids.size(); i++) { for (int i = 0; i < kids.size(); i++) {
Chain kid = (Chain)kids.get(i); Chain kid = (Chain)kids.get(i);
Dimension ksize = kid.getSize(); Rectangle2D kbounds = kid.getBounds();
y += GAP; // add the gap y += GAP; // add the gap
kid.setLocation(x, y); kid.setBounds(x, y, kbounds.getWidth(), kbounds.getHeight());
y += ksize.height; // add the dimensions of the kid y += kbounds.getHeight(); // add the dimensions of the kid
// System.err.println("Locating " + kid.getName() +
// " at +" + x + "+" + y + ".");
// track max width // track max width
if (maxwid < (x + ksize.width)) { if (maxwid < (x + kbounds.getWidth())) {
maxwid = x + ksize.width; maxwid = x + kbounds.getWidth();
} }
} }
// set the dimensions of the main chain // set the dimensions of the main chain
// System.err.println("Sizing " + chain.getName() + Rectangle2D cbounds = chain.getBounds();
// " to " + maxwid + "x" + y + "."); chain.setBounds(cbounds.getX(), cbounds.getY(), maxwid, y);
chain.setSize(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;
} }
} }
@@ -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; package com.samskivert.viztool.viz;
import java.util.ArrayList; import java.util.ArrayList;
import java.awt.Dimension; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.geom.Rectangle2D;
/** /**
* A chain is used by the hierarchy visualizer to represent inheritance * A chain is used by the hierarchy visualizer to represent inheritance
@@ -48,55 +48,22 @@ public class Chain implements Element
} }
/** /**
* Returns a <code>Dimension</code> instance representing the size of * Returns a <code>Rectangle2D</code> instance representing the size
* this chain (and all contained subchains). All coordinates are in * of this chain (and all contained subchains).
* points.
*/ */
public Dimension getSize () public Rectangle2D getBounds ()
{ {
return _size; return _bounds;
} }
/** /**
* Returns the location of this chain relative to its parent chain. * Sets the bounds of this chain.
* All coordinates are in points.
*/
public Point getLocation ()
{
return _location;
}
/**
* Sets the size of this chain. All coordinates are in points.
* *
* @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); _bounds.setRect(x, y, 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;
} }
/** /**
@@ -115,16 +82,16 @@ public class Chain implements Element
* the supplied layout manager arrange those children and compute the * the supplied layout manager arrange those children and compute the
* dimensions of this chain based on all of that information. * 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 // first layout our children
for (int i = 0; i < _children.size(); i++) { for (int i = 0; i < _children.size(); i++) {
Chain child = (Chain)_children.get(i); Chain child = (Chain)_children.get(i);
child.layout(pointSize, clay); child.layout(gfx, cviz);
} }
// now lay ourselves out // 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 Class _root;
protected ArrayList _children = new ArrayList(); protected ArrayList _children = new ArrayList();
protected Dimension _size = new Dimension(0, 0); protected Rectangle2D _bounds = new Rectangle2D.Double();
protected Point _location = new Point(0, 0);
protected int _pageno;
} }
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Dimension; import java.awt.*;
import java.awt.Point; import java.awt.geom.GeneralPath;
import java.io.PrintStream; import java.awt.geom.Rectangle2D;
import java.awt.font.TextLayout;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@@ -32,75 +33,99 @@ public class ChainGroup
/** /**
* Lays out the chains in this group and returns the total size. * 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 // lay out the internal structure of our chains
ChainLayout clay = new CascadingChainLayout(); ChainVisualizer clay = new CascadingChainVisualizer();
for (int i = 0; i < _roots.size(); i++) { for (int i = 0; i < _roots.size(); i++) {
Chain chain = (Chain)_roots.get(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 // arrange them on the page
ElementLayout elay = new PackedColumnElementLayout(); 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 // for now we're punting and assume that no group will exceed a
// single page in size // single page in size
_size = new Dimension(); double width = dims[0].getWidth();
_size.width = dims[0].width + 2*BORDER; double height = dims[0].getHeight() + layout.getAscent();
_size.height = dims[0].height + 2*BORDER;
// 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; 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 <code>layoutGroup</code> has previously * This function requires that <code>layoutGroup</code> has previously
* been called to lay out the group's chains. * been called to lay out the group's chains.
* *
* @return the size of the rectangle occupied by the rendered chain
* group.
*
* @see #layoutGroup * @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 // render our chains
ChainRenderer renderer = new CascadingChainRenderer(); ChainVisualizer renderer = new CascadingChainVisualizer();
for (int i = 0; i < _roots.size(); i++) { for (int i = 0; i < _roots.size(); i++) {
Chain chain = (Chain)_roots.get(i); Chain chain = (Chain)_roots.get(i);
Point loc = chain.getLocation(); Rectangle2D bounds = chain.getBounds();
renderer.renderChain(chain, out, pointSize, // render the chain
x + BORDER, y + BORDER); renderer.renderChain(chain, gfx);
} }
// undo the translation
gfx.translate(-cx, -cy);
// print our title and a box around our border // print our title and a box around our border
out.println("/tname (" + _pkg + ") def"); layout.draw(gfx, (float)(x + BORDER + 2), (float)y);
out.println("/twid tname stringwidth pop def");
int bx = x + BORDER + 2, by = y - pointSize/2; double height = _size.getHeight() - layout.getAscent();
out.println(bx + " " + by + " moveto"); GeneralPath path = new GeneralPath();
out.println("tname abshow"); 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"); public Chain getRoot (int index)
out.println("-" + (BORDER - 2) + " 0 rlineto"); {
out.println("0 " + _size.height + " rlineto"); return (Chain)_roots.get(index);
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");
} }
protected String _pkg; protected String _pkg;
protected ArrayList _roots; protected ArrayList _roots;
protected Dimension _size; protected Rectangle2D _size;
protected static final int PAGE_WIDTH = (int)(72 * 7.5); protected static final double BORDER = 72/8;
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;
} }
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Graphics2D;
/** /**
* The chain layout is used to compute the dimensions of chains and their * The chain visualizer is used to compute the dimensions of chains and
* children in preparation for rendering them in a particular manner. In * their children in preparation for rendering and then to perform said
* general a layout is coupled with one or more renderers which generate * rendering.
* 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
*/ */
public interface ChainLayout public interface ChainVisualizer
{ {
/** /**
* Assigns positions to the children of the supplied chain based on * 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. * (meaning they have dimensions but no position) prior to this call.
* *
* @param chain the chain to be layed out. * @param chain the chain to be layed out.
* @param pointSize the point size of the font to be used in computing * @param gfx the graphics context to use when computing dimensions.
* our dimensions (all coordinates are in points).
*/ */
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);
} }
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Dimension; import java.awt.*;
import java.awt.Point; import java.awt.geom.Rectangle2D;
import java.io.PrintStream; import java.awt.print.*;
import java.util.*; import java.util.*;
import com.samskivert.viztool.Log; 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 * representation so that an entire package can be displayed on a single
* page (or small number of pages). * page (or small number of pages).
*/ */
public class HierarchyVisualizer public class HierarchyVisualizer implements Printable
{ {
/** /**
* Constructs a hierarchy visualizer with the supplied enumerator as * Constructs a hierarchy visualizer with the supplied enumerator as
@@ -62,41 +62,95 @@ public class HierarchyVisualizer
_packages[i], _classes.iterator(), false); _packages[i], _classes.iterator(), false);
_groups[i] = new ChainGroup(pkgroot, _packages[i], penum); _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 * Lays out and renders each of the chain groups that make up this
* package hierarchy visualization. * 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 // adjust the stroke
out.println("0 setlinewidth"); gfx.setStroke(new BasicStroke(0.1f));
for (int i = 0; i < _groups.length; i++) { // and the font
// lay out the group in question Font font = new Font("Courier", Font.PLAIN, 8);
Dimension dims = _groups[i].layout(pointSize); gfx.setFont(font);
// determine if we need to skip to the next page or not // only relay things out if the page format has changed
if ((y > 0) && (y + dims.height > PAGE_HEIGHT)) { if (!pf.equals(_format)) {
// print the postamble for this page and preamble for the // keep this around
// new page _format = pf;
out.println("showpage");
out.println("0 setlinewidth");
y = Y_MARGIN;
}
// render the group at the requested location // and do the layout
_groups[i].render(pointSize, out, x, y); layout(gfx, pf.getImageableX(), pf.getImageableY(),
pf.getImageableWidth(), pf.getImageableHeight());
// increment our y location
y += (dims.height + GAP);
} }
// print the postamble for the final page // render the groups on the requested page
out.println("showpage"); 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; protected String _pkgroot;
@@ -105,6 +159,10 @@ public class HierarchyVisualizer
protected String[] _packages; protected String[] _packages;
protected ChainGroup[] _groups; 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_WIDTH = (int)(72 * 7.5);
protected static final int PAGE_HEIGHT = (int)(72 * 10); protected static final int PAGE_HEIGHT = (int)(72 * 10);
@@ -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);
}
}
}
@@ -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; package com.samskivert.viztool.viz;
@@ -14,10 +14,10 @@ public interface CascadingConstants
/** /**
* The number of points surrounding the name of the chain. * 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. * The number of points of spacing between each child chain.
*/ */
public static final int GAP = 4; public static final double GAP = 4;
} }
@@ -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);
}
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Point; import java.awt.geom.Rectangle2D;
import java.awt.Dimension;
/** /**
* 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 <code>ElementLayout</code> implementations in a * They can be laid out by <code>ElementLayout</code> implementations in a
* general purpose way. * general purpose way.
*/ */
@@ -19,23 +18,12 @@ public interface Element
public String getName (); 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 * Sets the bounds of this element.
* in points.
*/ */
public void setLocation (int x, int y); public void setBounds (double x, double y, double width, double height);
/**
* Sets the page number of this element.
*/
public void setPage (int pageno);
/**
* Returns the page number previously set by <code>setPage</code>.
*/
public int getPage ();
} }
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Dimension; import java.awt.geom.Rectangle2D;
import java.util.List; import java.util.List;
/** /**
* The element layout is used to lay a collection of elements out on a * 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 * page. It computes the desired position of each element and sets it via
* <code>setLocation()</code> with the expectation that the location of the * <code>setBounds()</code> with the expectation that the location of the
* elements will be used later in the rendering process. * elements will be used later in the rendering process.
*/ */
public interface ElementLayout public interface ElementLayout
@@ -18,8 +18,9 @@ public interface ElementLayout
* Lay out the supplied list of elements. Page numbers should be * Lay out the supplied list of elements. Page numbers should be
* assigned to all elements if the layout spans multiple pages. * 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. * 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);
} }
@@ -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; package com.samskivert.viztool.viz;
import java.awt.Dimension; import java.awt.geom.Rectangle2D;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/** /**
* Lays out the elements in columns of balanced height with elements * Lays out the elements in columns of balanced height with elements
@@ -16,7 +13,8 @@ import java.util.List;
public class PackedColumnElementLayout implements ElementLayout public class PackedColumnElementLayout implements ElementLayout
{ {
// docs inherited from interface // 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 // create a new list containing the elements whose order we can
// manipulate willy nilly // manipulate willy nilly
@@ -24,15 +22,22 @@ public class PackedColumnElementLayout implements ElementLayout
elements.toArray(elems); elements.toArray(elems);
Arrays.sort(elems, HEIGHT_COMP); Arrays.sort(elems, HEIGHT_COMP);
System.out.println("Laying out in " +
pageWidth + "x" + pageHeight + ".");
// lay out the elements across the page // lay out the elements across the page
ArrayList pagedims = new ArrayList(); 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++) { 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 // see if we fit into this row or not (but force placement if
// we're currently at the left margin) // 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 // track our maxwidth
if (x > maxwidth) { if (x > maxwidth) {
maxwidth = x; maxwidth = x;
@@ -41,43 +46,46 @@ public class PackedColumnElementLayout implements ElementLayout
x = 0; x = 0;
y += (rowheight + GAP); y += (rowheight + GAP);
// reset our max rowheight // reset our max rowheight
rowheight = size.height; rowheight = bounds.getHeight();
} }
// make sure we fit on this page (but force placement if we're // make sure we fit on this page (but force placement if we're
// currently at the top margin) // 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 // 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 // move to the next page
x = 0; x = 0;
y = 0; y = 0;
rowheight = size.height; rowheight = bounds.getHeight();
maxwidth = 0; maxwidth = 0;
pageno++; pageno++;
} }
// lay this element out at our current coordinates // lay this element out at our current coordinates
elems[i].setLocation(x, y); elems[i].setBounds(x, y, bounds.getWidth(), bounds.getHeight());
elems[i].setPage(pageno); // elems[i].setPage(pageno);
System.out.println("Laying out " + elems[i].getName() +
" at " + elems[i].getBounds() + ".");
// keep track of the maximum row height // keep track of the maximum row height
if (size.height > rowheight) { if (bounds.getHeight() > rowheight) {
rowheight = size.height; rowheight = bounds.getHeight();
} }
// advance in the x direction // advance in the x direction
x += (size.width + GAP); x += (bounds.getWidth() + GAP);
} }
// take a final stab at our maxwidth // take a final stab at our maxwidth
x -= GAP;
if (x > maxwidth) { if (x > maxwidth) {
maxwidth = x; maxwidth = x;
} }
// make a note of how big the final page is // make a note of how big the final page is
pagedims.add(new Dimension(maxwidth, y+rowheight)); pagedims.add(new Rectangle2D.Double(0, 0, maxwidth, y+rowheight));
Dimension[] dims = new Dimension[pagedims.size()]; Rectangle2D[] dims = new Rectangle2D[pagedims.size()];
pagedims.toArray(dims); pagedims.toArray(dims);
return dims; return dims;
} }
@@ -90,7 +98,8 @@ public class PackedColumnElementLayout implements ElementLayout
Element e2 = (Element)o2; Element e2 = (Element)o2;
// tallest element wins // 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 // if they are the same height, sort alphabetically
return (diff != 0) ? diff : e1.getName().compareTo(e2.getName()); 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 // 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(); protected static final Comparator HEIGHT_COMP = new HeightComparator();
} }