DrawingObj
interface
* and hence can be freely added to the drawing panel using the
* addDrawingObj
method. e.g.
* TreeNode node = new TreeNode();
* drawingPanel.addDrawingObj(node);
*
* Any added drawing object can be remove from the panel by using the
* removeObj
method. e.g.
* drawingPanel.removeObj(node);
*
* @see DrawingPanel#addDrawingObj
* @see DrawingPanel#removeObj
*/
public class TreeNode extends Drawable {
protected int weight;
protected TreeNode left, right;
protected int depth = -1;
private boolean red; // Used by red-black trees
public static final Color DEF_TREENODE_COL = Color.red;
private Dimension node_size = new Dimension( 20, 30 );
/**
* Attribute to indicate if the left branch is to be highlighted.
*/
protected boolean highlightLeft = false;
/**
* Attribute to indicate if the right branch is to be highlighted.
*/
protected boolean highlightRight = false;
private static final boolean DEBUG = false;
/**
* Create a node with the left node as set and weight of the current node
* set to 0.
* @param node The left node of the newly created node.
*/
public TreeNode( TreeNode node ) {
left = node;
right = null;
weight = 0;
colour = DEF_TREENODE_COL;
}
/**
* Create a new node with weight 0.
*/
public TreeNode() {
this( 0 );
}
/**
* Create a new leaf node with label and weight as specified in the
* parameters.
* @param label The label of the new node.
* @param weight The weight of the new node.
*/
public TreeNode(String label, int weight) {
this( weight );
this.label = label;
} // Constructor 2
/**
* Create a new leaf node with the specified weight.
* @param weight The weight of the new node.
*/
public TreeNode( int weight ) {
label = null;
this.weight = weight;
left = right = null;
colour = DEF_TREENODE_COL;
} // Constructor 3
/**
* Create a new leaf node with 0 weight and label as specified.
* @param label Label of the new node.
*/
public TreeNode( String label ) {
this( 0 );
this.label = new String(label);
} // Constructor 4
/**
* Set the weight of this node.
* @param weight The weight to be assigned to this node.
*/
public void setWeight(int weight) {
this.weight = weight;
}
/**
* Set the label of this node.
* @param label The label to be assigned to this node.
*/
public void setLabel(String label) {
this.label = new String(label);
}
/**
* Get the weight of this node.
* @return Weight of this node.
*/
public int getWeight() {
return weight;
}
/**
* Get the label of this node.
* @return Label of this node.
*/
public String getLabel() {
return new String(label);
}
/**
* Link the left branch of this node to the node passed in as the parameter.
* @param node The new left child of this node.
*/
public void setLeft(TreeNode node) { left = node; }
public void setLeftTreeNode(TreeNode node) { left = node; }
/**
* Link the right branch of this node to
* the node passed in as the parameter.
* @param node The new right child of this node.
*
*/
public void setRight( TreeNode node ) { right = node; }
public void setRightTreeNode(TreeNode node) { right = node; }
/**
* Get the left child of this node.
* @return the left child this this node.
*/
public TreeNode getLeft() { return left; }
public TreeNode getLeftTreeNode() { return left; }
/**
* Get the right child of this node.
* @return the right child this this node.
*
*/
public TreeNode getRight() { return right; }
public TreeNode getRightTreeNode() { return right; }
/**
* Check if this node is a leaf node. A leaf node has both left and right
* nodes null.
* @return true if the node is a leaf node; false otherwise.
*/
public boolean isLeaf() {
return ((right==null) && (left==null));
}
public int getHeight() {
int lh = 0, rh = 0;
if ( left != null ) lh = left.getHeight();
if ( right != null ) rh = right.getHeight();
return (lh>rh)?lh:rh + 1;
}
/**
* Get the depth of his node.
* @return The depth of this node in a tree.
*/
public int getDepth() { return depth; }
// Somewhat of a hack to allow the position of a new child to be
// computed
public static int last_dy, last_dx;
/** Start at a node and set the positions for the sub-tree elements **/
public void setPosition( int x, int y, int dx, int dy ) {
this.x = x; this.y = y;
last_dx = dx; last_dy = dy;
int dx2 = dx/2;
if( left != null ) {
left.setPosition( x-dx2, y+dy, dx2, dy );
}
if( right != null ) {
right.setPosition( x+dx2, y+dy, dx2, dy );
}
}
/**
* Sets the depth of this node corresponding to the root node of the tree.
* @param depth Depth of the node.
*/
public void setDepth(int depth) {
this.depth = depth;
}
/**
* Move the node and all its branches based on the parameters.
* @param x The horizontal destination position of this node.
* @param y The vertical destination position of this node.
*/
public void move(int x, int y) {
int dx = x - this.x;
int dy = y - this.y;
moveTreeNode(dx, dy);
}
/**
* Move the tree starting with node dx pixels to the right and dy
* pixels down.
* @param node The root node of the tree to be moved.
* @param dx The change in x direction.
* @param dy The change in y direction.
*/
public void moveTreeNode(int dx, int dy) {
x += dx;
y += dy;
if (!isLeaf()) {
if (getLeftTreeNode() != null)
getLeftTreeNode().moveTreeNode(dx, dy);
if (getRightTreeNode() != null)
getRightTreeNode().moveTreeNode(dx, dy);
}
}
static Font weight_font = new Font("Courier", Font.PLAIN, 12);
static Font label_font = new Font("Courier", Font.BOLD, 12 );
/**
* Assign some font instances to reduce initialization over during
* redraw.
*/
public void initFonts(Font hugeFont, Font bigFont) {
weight_font = hugeFont;
label_font = bigFont;
}
protected Color labelColor = Color.yellow;
/**
* Set the color of the node.
* @param nodeColor new color of the node.
*/
public void initColors(Color nodeColor) {
setColour( nodeColor );
}
public void setLabelColour( Color x ) {
labelColor = x;
}
/** Adjust the node size by a factor
* @param factor - expansion factor: factor > 1.0 => expansion,
factor < 1.0 => shrinkage
**/
public void expandSize( float factor ) {
float x = (float)(node_size.width) * factor;
node_size.width = Math.round( x );
x = (float)(node_size.height) * factor;
node_size.height = Math.round( x );
}
/** Return the current node size - used by other routines, eg, TreeEdge,
* to control their drawing
**/
public Dimension getNodeSize() { return node_size; }
/**
* This method draws the node on the corresponding graphical context
* normally passed in from the drawing panel.
*/
public void draw( Graphics g ) {
/* String representing weight */
String ws = "" + getWeight();
Graphics2D g2d = (Graphics2D)g;
if( DEBUG ) System.out.println("TreeNode:draw [" + ws + "]["
+ ((label==null)?"~":label) + "]" );
/* Establish the size for the label */
FontRenderContext frc = g2d.getFontRenderContext();
TextLayout layout = new TextLayout( ws, weight_font, frc );
Rectangle2D bounds = layout.getBounds();
double text_height = 1.5*bounds.getHeight();
double text_width = bounds.getWidth()+4;
double bly = y + (bounds.getY());
int text_left = (int)(x - text_width / 2);
// bounds.setRect(bounds.getX()+x,bounds.getY()+y,
// bounds.getWidth(),bounds.getHeight());
System.out.print("TreeNode:draw - bounds (" + bounds.getX() + "," + bly + ") [" +
text_height + "," + text_width + "]");
System.out.println(" x,y (" + x + "," + y + ")" );
g.setColor( getCurrentColour() );
g.fillRect( text_left, y, (int)text_width, (int)text_height );
g.setColor(Color.black);
g.drawRect( text_left, y, (int)text_width, (int)text_height );
if ( label != null ) {
if (getLabel().length() > 0) {
g.setColor( labelColor );
g.setFont( label_font );
g.drawString(getLabel(), x + (int)text_width, y + (int)text_height);
}
}
g.setColor( Color.white );
// g.setFont( weight_font );
// g.drawString("" + getWeight(), x + 2, y+27);
layout.draw(g2d, (float)(text_left+2), (float)(y+text_height) );
if( DEBUG ) System.out.println("TreeNode:draw [" + ws + "] exit" );
}
} // class TreeNode