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;
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);
}
}
}
@@ -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;
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:
*
* <pre>
* Foo
@@ -19,47 +21,111 @@ import java.util.ArrayList;
* |
* +-> Baz
* </pre>
*
* It should be used in tandem with the
* <code>CascadingChainRenderer</code>.
*
* @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;
}
}
@@ -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 <code>Dimension</code> instance representing the size of
* this chain (and all contained subchains). All coordinates are in
* points.
* Returns a <code>Rectangle2D</code> 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();
}
@@ -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 <code>layoutGroup</code> 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;
}
@@ -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);
}
@@ -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);
@@ -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;
@@ -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;
}
@@ -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;
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 <code>ElementLayout</code> 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 <code>setPage</code>.
*/
public int getPage ();
public void setBounds (double x, double y, double width, double height);
}
@@ -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
* <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.
*/
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);
}
@@ -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();
}