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:
@@ -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;
|
||||
}
|
||||
+93
-27
@@ -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);
|
||||
}
|
||||
|
||||
+85
-27
@@ -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);
|
||||
}
|
||||
|
||||
+32
-23
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user