import jdsl.core.algo.traversals.*;
import jdsl.core.api.*;
import java.awt.*;
import java.awt.geom.*;
/**
* The first step in drawing the tree. The tree is drawn so:
* - Edges are straight lines
* - nodes are centered above the drawings of their children
* - bounding boxes of the drawings of subtrees rooted at siblings are drawn
* adjacent.
* - The width of a bounding box is the maximum of the label width and the
* sum of the bounding boxes of the children.
*
* @author Lucy Perry (lep)
* @version JDSL 2
*/
public class BoundingBoxCalculator extends EulerTour {
/* ************************************ */
/* The members described in the lesson. */
/* ************************************ */
//b6.4
protected int offset = 50; // size of the grid squares
protected int width = 0; // a running total width of the explored tree drawing
protected int depth = 0; // a running total depth of the explored tree drawing
protected Graphics g; // the graphics object where the tree will be drawn
protected FontMetrics fm; // the fontMetrics object for g
//e6.4
/**
* When a node is visited first in the Euler Tour we know the upper-left hand
* corner of its bounding box, which we store as decorations.
*/
//b6.5
protected void visitFirstTime(Position pos){
pos.set("y", new Integer(depth));
pos.set("x", new Integer(width));
depth += offset;
}
//e6.5
/**
* When a node is visited last in the Euler Tour we know the width
* of its bounding box, which we store as a decoration.
*/
//b6.6
protected void visitLastTime(Position pos){
int textWidth = textWidth(pos);
int shift = 0;
int x = ((Integer)pos.get("x")).intValue();
int boxWidth = width-x;
if (textWidth>boxWidth) {
int delta = textWidth-boxWidth;
boxWidth = textWidth;
width += delta;
shift = delta/2;
}
pos.set("width", new Integer(boxWidth));
// The distance that the children's bounding boxes need to
// be shifted in the drawing.
pos.set("shift", new Integer(shift));
depth -= offset;
}
//e6.6
/*
* Sets all attributes for an external node
*/
//b6.7
protected void visitExternal( Position pos ) {
pos.set("y", new Integer(depth));
pos.set("x", new Integer(width));
int textWidth = textWidth(pos);
width+=textWidth;
pos.set("width", new Integer(textWidth));
pos.set("shift", new Integer(0));
}
//e6.7
/* ************************************ */
/* Members not described in the lesson. */
/* ************************************ */
protected int pad=5; // the distance to separate bounding boxes;
public BoundingBoxCalculator(Graphics gg) {
g=gg;
fm = g.getFontMetrics();
}
/**
* Calculates the width of the drawing of the node label. Stores
* the attributes needed to calculate the exact position to draw the
* label.
*/
protected int textWidth(Position pos) {
String str=pos.element().toString();
Rectangle2D bounds = fm.getStringBounds(str,g);
pos.set("bounds", bounds);
pos.set("ascent", new Integer(fm.getMaxAscent()));
pos.set("descent", new Integer(fm.getMaxDescent()));
return (int)bounds.getWidth()+pad;
}
}