Modified code to deal better with splitting chain groups across pages. It

still chokes if a single chain won't fit on a page, but what to do there
is complicated and non-obvious.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@205 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-07-24 18:07:35 +00:00
parent 8e80f60a40
commit e78b957839
5 changed files with 138 additions and 71 deletions
@@ -1,5 +1,5 @@
// //
// $Id: Driver.java,v 1.5 2001/07/17 07:18:09 mdb Exp $ // $Id: Driver.java,v 1.6 2001/07/24 18:07:35 mdb Exp $
package com.samskivert.viztool; package com.samskivert.viztool;
@@ -31,6 +31,7 @@ public class Driver
// run ourselves on the classpath // run ourselves on the classpath
String classpath = System.getProperty("java.class.path"); String classpath = System.getProperty("java.class.path");
// System.err.println("Scanning " + classpath + ".");
ClassEnumerator enum = new ClassEnumerator(classpath); ClassEnumerator enum = new ClassEnumerator(classpath);
// print out the warnings // print out the warnings
@@ -1,5 +1,5 @@
// //
// $Id: ChainGroup.java,v 1.6 2001/07/17 06:01:08 mdb Exp $ // $Id: ChainGroup.java,v 1.7 2001/07/24 18:07:35 mdb Exp $
package com.samskivert.viztool.viz; package com.samskivert.viztool.viz;
@@ -28,13 +28,60 @@ public class ChainGroup
// process the classes provided by our enumerator // process the classes provided by our enumerator
_roots = ChainUtil.buildChains(pkgroot, iter); _roots = ChainUtil.buildChains(pkgroot, iter);
// System.err.println(_roots.size() + " chains for " + pkg + ".");
}
protected ChainGroup (String pkg, ArrayList roots)
{
_pkg = pkg;
_roots = roots;
} }
/** /**
* Lays out the chains in this group and returns the total size. * Returns the dimensions of this chain group. This value is only
* valid after <code>layout</code> has been called.
*/ */
public Rectangle2D layout (Graphics2D gfx, double pageWidth, public Rectangle2D getBounds ()
double pageHeight) {
return _size;
}
/**
* Sets the upper left coordinate of this group. The group itself
* never looks at this information, but it will be made available as
* the x and y coordinates of the rectangle returned by
* <code>getBounds</code>.
*/
public void setPosition (double x, double y)
{
_size.setRect(x, y, _size.getWidth(), _size.getHeight());
}
/**
* Returns the page on which this group should be rendered.
*/
public int getPage ()
{
return _page;
}
/**
* Sets the page on which this group should be rendered.
*/
public void setPage (int page)
{
_page = page;
}
/**
* Lays out the chains in this group and returns the total size. If
* the layout process requires that this chain group be split across
* multiple pages, a new chain group containing the overflow chains
* will be returned. If the group fits in the allotted space, null
* will be returned.
*/
public ChainGroup layout (
Graphics2D gfx, double pageWidth, double pageHeight)
{ {
// lay out the internal structure of our chains // lay out the internal structure of our chains
ChainVisualizer clay = new CascadingChainVisualizer(); ChainVisualizer clay = new CascadingChainVisualizer();
@@ -58,19 +105,28 @@ public class ChainGroup
// arrange them on the page // arrange them on the page
ElementLayout elay = new PackedColumnElementLayout(); ElementLayout elay = new PackedColumnElementLayout();
Rectangle2D[] dims = elay.layout(_roots, pageWidth, pageHeight); ArrayList overflow = new ArrayList();
_size = elay.layout(_roots, pageWidth, pageHeight, overflow);
// for now we're punting and assume that no group will exceed a
// single page in size
double width = dims[0].getWidth();
double height = dims[0].getHeight() + titleAscent;
// make sure we're wide enough for our title // make sure we're wide enough for our title
width = Math.max(width, layout.getAdvance() + 4); double width = Math.max(_size.getWidth(), layout.getAdvance() + 4);
_size = new Rectangle2D.Double(); // adjust for our border and title
double height = _size.getHeight() + titleAscent;
_size.setRect(0, 0, width + 2*BORDER, height + 2*BORDER); _size.setRect(0, 0, width + 2*BORDER, height + 2*BORDER);
return _size;
// if we have overflow elements, create a new chain group with
// these elements, remove them from our roots list and be on our
// way
if (overflow.size() > 0) {
// remove the overflow roots from our list
for (int i = 0; i < overflow.size(); i++) {
_roots.remove(overflow.get(i));
}
return new ChainGroup(_pkg, overflow);
}
return null;
} }
/** /**
@@ -132,9 +188,17 @@ public class ChainGroup
return (Chain)_roots.get(index); return (Chain)_roots.get(index);
} }
public String toString ()
{
return "[pkg=" + _pkg + ", roots=" + _roots.size() +
", size=" + _size + ", page=" + _page + "]";
}
protected String _pkg; protected String _pkg;
protected ArrayList _roots; protected ArrayList _roots;
protected Rectangle2D _size; protected Rectangle2D _size;
protected int _page;
protected static final double BORDER = 72/8; protected static final double BORDER = 72/8;
} }
@@ -1,5 +1,5 @@
// //
// $Id: HierarchyVisualizer.java,v 1.7 2001/07/17 07:18:09 mdb Exp $ // $Id: HierarchyVisualizer.java,v 1.8 2001/07/24 18:07:35 mdb Exp $
package com.samskivert.viztool.viz; package com.samskivert.viztool.viz;
@@ -45,6 +45,7 @@ public class HierarchyVisualizer implements Printable
} }
_classes.add(name); _classes.add(name);
} }
// System.err.println("Scanned " + _classes.size() + " classes.");
// compile a list of all packages in our collection // compile a list of all packages in our collection
HashSet pkgset = new HashSet(); HashSet pkgset = new HashSet();
@@ -60,18 +61,15 @@ public class HierarchyVisualizer implements Printable
_packages[i] = (String)iter.next(); _packages[i] = (String)iter.next();
} }
Arrays.sort(_packages, Comparators.STRING); Arrays.sort(_packages, Comparators.STRING);
// System.err.println("Scanned " + _packages.length + " packages.");
// now create chain groups for each package // now create chain groups for each package
_groups = new ChainGroup[_packages.length]; _groups = new ArrayList();
for (int i = 0; i < _groups.length; i++) { for (int i = 0; i < _packages.length; i++) {
PackageEnumerator penum = new PackageEnumerator( PackageEnumerator penum = new PackageEnumerator(
_packages[i], _classes.iterator(), false); _packages[i], _classes.iterator(), false);
_groups[i] = new ChainGroup(pkgroot, _packages[i], penum); _groups.add(new ChainGroup(pkgroot, _packages[i], penum));
} }
// we'll need these for later
_bounds = new Rectangle2D.Double[_packages.length];
_pagenos = new int[_packages.length];
} }
/** /**
@@ -98,12 +96,16 @@ public class HierarchyVisualizer implements Printable
// render the groups on the requested page // render the groups on the requested page
int rendered = 0; int rendered = 0;
for (int i = 0; i < _groups.length; i++) { for (int i = 0; i < _groups.size(); i++) {
ChainGroup group = (ChainGroup)_groups.get(i);
// skip groups not on this page // skip groups not on this page
if (_pagenos[i] != pageIndex) { if (group.getPage() != pageIndex) {
continue; continue;
} }
_groups[i].render(gfx, _bounds[i].getX(), _bounds[i].getY());
Rectangle2D bounds = group.getBounds();
group.render(gfx, bounds.getX(), bounds.getY());
rendered++; rendered++;
} }
@@ -113,26 +115,32 @@ public class HierarchyVisualizer implements Printable
public void layout (Graphics2D gfx, double x, double y, public void layout (Graphics2D gfx, double x, double y,
double width, double height) double width, double height)
{ {
double starty = x; double starty = y;
int pageno = 0; int pageno = 0;
// lay out our groups // lay out our groups
for (int i = 0; i < _groups.length; i++) { for (int i = 0; i < _groups.size(); i++) {
ChainGroup group = (ChainGroup)_groups.get(i);
// lay out the group in question // lay out the group in question
Rectangle2D bounds = _groups[i].layout(gfx, width, height); ChainGroup ngrp = group.layout(gfx, width, height);
// if the process of laying this group out caused it to become
// split across pages, insert this new group into the list
if (ngrp != null) {
_groups.add(i+1, ngrp);
}
// determine if we need to skip to the next page or not // determine if we need to skip to the next page or not
if ((y > 0) && (y + bounds.getHeight() > height)) { Rectangle2D bounds = group.getBounds();
if ((y > starty) && (y + bounds.getHeight() > height + starty)) {
y = starty; y = starty;
pageno++; pageno++;
} }
// assign x and y coordinates to this group // assign x and y coordinates to this group
bounds.setRect(x, y, bounds.getWidth(), bounds.getHeight()); group.setPosition(x, y);
// and store it // make a note of our page index
_bounds[i] = bounds; group.setPage(pageno);
// also make a note of our page index
_pagenos[i] = pageno;
// increment our y location // increment our y location
y += (bounds.getHeight() + GAP); y += (bounds.getHeight() + GAP);
@@ -142,13 +150,16 @@ public class HierarchyVisualizer implements Printable
public void paint (Graphics2D gfx, int pageIndex) public void paint (Graphics2D gfx, int pageIndex)
{ {
// render the groups on the requested page // render the groups on the requested page
for (int i = 0; i < _groups.length; i++) { for (int i = 0; i < _groups.size(); i++) {
ChainGroup group = (ChainGroup)_groups.get(i);
// skip groups not on this page // skip groups not on this page
if (_pagenos[i] != pageIndex) { if (group.getPage() != pageIndex) {
continue; continue;
} }
_groups[i].render((Graphics2D)gfx,
_bounds[i].getX(), _bounds[i].getY()); Rectangle2D bounds = group.getBounds();
group.render((Graphics2D)gfx, bounds.getX(), bounds.getY());
} }
} }
@@ -156,11 +167,9 @@ public class HierarchyVisualizer implements Printable
protected ArrayList _classes = new ArrayList(); protected ArrayList _classes = new ArrayList();
protected String[] _packages; protected String[] _packages;
protected ChainGroup[] _groups; protected ArrayList _groups;
protected PageFormat _format; 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,5 +1,5 @@
// //
// $Id: ElementLayout.java,v 1.2 2001/07/17 01:54:19 mdb Exp $ // $Id: ElementLayout.java,v 1.3 2001/07/24 18:07:35 mdb Exp $
package com.samskivert.viztool.viz; package com.samskivert.viztool.viz;
@@ -15,12 +15,13 @@ import java.util.List;
public interface ElementLayout public interface ElementLayout
{ {
/** /**
* Lay out the supplied list of elements. Page numbers should be * Lay out the supplied list of elements. Any elements that do not fit
* assigned to all elements if the layout spans multiple pages. * into the allotted space should be added to the overflow list. The
* supplied elements list should not be modified.
* *
* @return an array of rectangle objects representing the width and * @return the bounding dimensions of the collection of elements that
* height of each page that was laid out. * were laid out.
*/ */
public Rectangle2D[] layout (List elements, public Rectangle2D layout (List elements, double pageWidth,
double pageWidth, double pageHeight); double pageHeight, List overflow);
} }
@@ -1,5 +1,5 @@
// //
// $Id: PackedColumnElementLayout.java,v 1.4 2001/07/17 05:16:16 mdb Exp $ // $Id: PackedColumnElementLayout.java,v 1.5 2001/07/24 18:07:35 mdb Exp $
package com.samskivert.viztool.viz; package com.samskivert.viztool.viz;
@@ -13,19 +13,16 @@ import java.util.*;
public class PackedColumnElementLayout implements ElementLayout public class PackedColumnElementLayout implements ElementLayout
{ {
// docs inherited from interface // docs inherited from interface
public Rectangle2D[] layout (List elements, public Rectangle2D layout (List elements, double pageWidth,
double pageWidth, double pageHeight) double pageHeight, List overflow)
{ {
// create a new list containing the elements whose order we can // sort the elements first by height then alphabetically
// manipulate willy nilly
Element[] elems = new Element[elements.size()]; Element[] elems = new Element[elements.size()];
elements.toArray(elems); elements.toArray(elems);
Arrays.sort(elems, HEIGHT_COMP); Arrays.sort(elems, HEIGHT_COMP);
// lay out the elements across the page // lay out the elements across the page
ArrayList pagedims = new ArrayList();
double x = 0, y = 0, rowheight = 0, maxwidth = 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++) {
Rectangle2D bounds = elems[i].getBounds(); Rectangle2D bounds = elems[i].getBounds();
@@ -42,26 +39,25 @@ public class PackedColumnElementLayout implements ElementLayout
// move down to the next row // move down to the next row
x = 0; x = 0;
y += (rowheight + GAP); y += (rowheight + GAP);
// reset our max rowheight // reset our max rowheight (we set it to -GAP because we
rowheight = bounds.getHeight(); // will add rowheight to our last y position to compute
// the total height and if no more elements are laid out,
// we'll want to remove that trailing gap)
rowheight = -GAP;
} }
// 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 + bounds.getHeight()) > pageHeight)) { if ((y > 0) && ((y + bounds.getHeight()) > pageHeight)) {
// make a note of how big the current page is // if we didn't fit, we go onto the overflow list
pagedims.add(new Rectangle2D.Double(0, 0, maxwidth, y)); overflow.add(elems[i]);
// move to the next page // and continue on because maybe some shorter elements
x = 0; // further down the list will fit
y = 0; continue;
rowheight = bounds.getHeight();
maxwidth = 0;
pageno++;
} }
// lay this element out at our current coordinates // lay this element out at our current coordinates
elems[i].setBounds(x, y, bounds.getWidth(), bounds.getHeight()); elems[i].setBounds(x, y, bounds.getWidth(), bounds.getHeight());
// elems[i].setPage(pageno);
// keep track of the maximum row height // keep track of the maximum row height
if (bounds.getHeight() > rowheight) { if (bounds.getHeight() > rowheight) {
@@ -78,11 +74,7 @@ public class PackedColumnElementLayout implements ElementLayout
maxwidth = x; maxwidth = x;
} }
// make a note of how big the final page is return new Rectangle2D.Double(0, 0, maxwidth, y+rowheight);
pagedims.add(new Rectangle2D.Double(0, 0, maxwidth, y+rowheight));
Rectangle2D[] dims = new Rectangle2D[pagedims.size()];
pagedims.toArray(dims);
return dims;
} }
protected static class HeightComparator implements Comparator protected static class HeightComparator implements Comparator