package kloss.structures; import java.awt.Point; import java.awt.Image; import java.awt.Event; import java.awt.Canvas; import java.awt.Graphics; import java.awt.Dimension; import java.util.Vector; import kloss.graphics.images.ImageObject; import kloss.graphics.images.ImageFrame; import kloss.graphics.functions.Function; import kloss.structures.nodes.Node; import kloss.structures.nodes.TreeNode; public class BinaryCanvas extends StructureCanvas { //////////////////////////////////////////////////////////// // For DEPTH_MULT and WIDTH_MULT the best results are // achieved when the width multiplier is one fourth the // value of the depth. /** Multiplier for node size to determine how far down a new node * drops froms its parent upon creation (graphics routine). */ final static double DEPTH_MULT = 3.0; /** Multiplier for node size to determine how far left/right a new * node spreads froms its parent upon creation (graphics routine). */ final static double WIDTH_MULT = 0.75; /** Vector which holds 2 member int[] arrays. Each index in the * vector corresponds to a level within the binary tree (zero being * the first level, one being the second, etc.). The int arrays * hold information on the amount a new node drops and spreads from * its parent upon creation (graphics routine). */ Vector levelInfo = new Vector(); int levelDepth = 80; int levelWidth = 20; /** The current number of levels in the tree. Levels begin at zero and * progress from there (i.e. root is at level zero, root's children at * level one, etc.). A value of negative one indicates an empty tree. */ int level = -1; public final static int LEVEL_ONE = 0; public final static int LEVEL_TWO = 1; public final static int LEVEL_THREE = 2; /** The number of nodes in tree (soley). */ int treeNodes = 0; /** The total nodes held in class. This includes all the nodes in the * tree plus the highlight node and delete node if they exist. */ int totalNodes = 0; /** The rank list holds references to all the nodes in the binary * tree sorted by rank. Rank is defined to be *
   *    left child rank  = (2 * parent rank)
   *    right child rank = (2 * parent rank) + 1
   *  
* where the root node has rank 1. This allows all nodes to be * ordered linearly and accessed via a rank index in constant time. */ TreeNode[] rankList; /** The delete node holds a reference to the node which is currently * being deleted. Although the abstract representation of a deleted * node may be removed from the tree immediately, the graphical rep- * resentation often lingers (e.g. due to some animation). Only one * node can be deleted at a time so only a single reference is used. */ TreeNode deleteNode; /** The highlight node holds a reference to the highlight image object. * Only one node can be highlighted at a time so only a single ref- * erence is used. */ ImageObject highLight; /** Reference to a node in the tree which is being dragged (via a mouse * drag). When this reference is not null, drag bounds checking is * skipped and the drag object is moved and repainted immediately, thus * greatly improving performance. */ ImageObject dragObject = null; public BinaryCanvas(ImageFrame frame, int nodeSize) { super(frame, nodeSize); } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } /** Add a new node to the rank list. Note: if the node * has any children, they too are added to the rank list in their cor- * responding rank positions (recursively). * * @param node The node to add to the rank list. */ public void addToRankList(TreeNode node) { int levelCheck; int rank = node.getRank(); TreeNode leftChild = node.getLeftChild(); TreeNode rightChild = node.getRightChild(); //////////////////////////////////////// // Recurse first so that as we filter back // up the tree, the rank list is either // already created or of large enough // size to contain new node. if (leftChild != null) addToRankList(leftChild); if (rightChild != null) addToRankList(rightChild); //////////////////////////////////////// // If rankList not yet created then make // it. if (rankList == null) rankList = new TreeNode[rank]; //////////////////////////////////////// // Else if rankList not large enough for // expanded tree, remake the list. else if (rankList.length < rank) { TreeNode[] tempList = new TreeNode[rank]; System.arraycopy(rankList, 0, tempList, 0, rankList.length); rankList = tempList; } //////////////////////////////////////// // Set rank index to reference the node rankList[rank - 1] = node; //////////////////////////////////////// // Increase the level number of tree if // necessary. if ( (levelCheck = (int) Math.floor((double) Math.log((double) rank) / (double) Math.log((double) 2))) > level) level = levelCheck; } /** Remove a node and all its children from the rank list. * * @param node The node to be removed from the rank list. */ public void removeFromRankList(TreeNode node) { int startCheck = (int) Math.pow(2, level) - 1; int endCheck = (int) Math.pow(2, level + 1) - 1; int size; TreeNode leastChild = node.getLeastChild(); TreeNode greatestChild = node.getGreatestChild(); boolean reduceLevel = true; if ( (size = leastChild.getRank()) < greatestChild.getRank()) size = greatestChild.getRank(); if ( (rankList != null) && (rankList.length >= size)) clearRank(node); //////////////////////////////////////// // If the deleted node was the only node // on its tree level, then decrement the // level instance variable. for (int i = startCheck; (i < rankList.length) && (i < endCheck); i++) if (rankList[i] != null) { reduceLevel = false; break; } if (reduceLevel) { level--; if ( (level > -1) && (levelInfo.size() > level)) levelInfo.removeElementAt(level); } } /** Recursively set the associated indexes in the rank list of the * current node and its children to null. This method is called by * removeFromRankList() above. * * @param node The node to be removed from the rank list. */ protected void clearRank(TreeNode node) { TreeNode leftChild; TreeNode rightChild; if ( (leftChild = node.getLeftChild()) != null) clearRank(leftChild); if ( (rightChild = node.getRightChild()) != null) clearRank(rightChild); rankList[node.getRank() - 1] = null; } /** Retrieve the node with a particular rank. * * @param nodeRank The rank of the requested node. * @return The node with the requested rank or null if * no such node exists. */ public TreeNode getNodeAtRank(int nodeRank) { int rank = nodeRank - 1; if (rank > rankList.length) return null; else return rankList[rank]; } public void setLevelValues(int level, int[] values) { levelInfo.setElementAt(values, level); } public int[] getLevelValues(int level) { int size = levelInfo.size(); if (size == level) { int[] values = new int[3]; //////////////////////////////////////// // Assign the new level with default // values (indicated by LEVEL_ONE ident- // ifier). values[0] = levelWidth; values[1] = levelDepth; values[2] = LEVEL_ONE; levelInfo.addElement(values); } if (size < level) return null; else return (int[]) levelInfo.elementAt(level); } protected synchronized void cloneNodes() { ImageObject[] temp = new ImageObject[totalNodes]; int index = totalNodes - 1; int count = 0; if (highLight != null) temp[count++] = new ImageObject(highLight); if (deleteNode != null) temp[count] = new TreeNode(deleteNode); TreeNode[] tempList = new TreeNode[rankList.length]; int rankLeft; int rankRight; for (int i = 0; i < tempList.length; i++) { if (rankList[i] != null) { tempList[i] = new TreeNode(rankList[i]); rankLeft = ((i + 1) * 2) - 1; rankRight = ((i + 1) * 2); if (rankLeft < tempList.length) tempList[i].setLeftChild(tempList[rankLeft]); if (rankRight < tempList.length) tempList[i].setRightChild(tempList[rankRight]); if (rankList[i].getParent() != null) tempList[i].setParent(tempList[(int) Math .floor((double) (i + 1) / 2) - 1]); temp[index--] = tempList[i]; } } copyVector.addElement(temp); notifyAll(); } public boolean mouseDrag(Event evt, int x, int y) { if (dragObject == null) { for (int i = 0; i < rankList.length; i++) { if (rankList[i] != null) { if (rankList[i].inBounds(x,y)) { dragObject = rankList[i]; break; } } } } if (dragObject != null) { dragObject.move(x, y); repaint(); } return true; } public boolean mouseUp(Event evt, int x, int y) { if (dragObject != null) dragObject = null; return true; } public synchronized void loadBuffer() { while (bufferLoaded || (copyVector.size() == 0)) { try { wait(); } catch (InterruptedException e) { } } Dimension d = size(); if ( (offGraphics == null) || (offDimension.width != d.width) || (offDimension.height != d.height)) { offDimension = d; offImage = createImage(d.width, d.height); offGraphics = offImage.getGraphics(); } offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, d.width, d.height); ImageObject[] objects = (ImageObject[]) copyVector.firstElement(); copyVector.removeElementAt(0); for (int i = 0; i < objects.length; i++) { if (objects[i] != null) objects[i].draw(offGraphics); else System.out.println("objects[" + i + "] is null"); } bufferLoaded = true; notifyAll(); } public synchronized void paintAnimation() { while ( (bufferLoaded == false) || (paintingDone == false)){ try { wait(); } catch (InterruptedException e) { } } paintingDone = false; repaint(); } public void update(Graphics g) { paint(g); } public synchronized void paint(Graphics g) { if (bufferLoaded) { g.drawImage(offImage, 0, 0, this); if (updateVector.size() > 0) { //////////////////////////////////////// // Hack used to update the label element // in the BinaryTree container. if (updateString .equals((String) updateVector. firstElement()) == false) { ((BinaryTree) frame) .updateLabel((String) updateVector.firstElement()); updateString = (String) updateVector.firstElement(); } updateVector.removeElementAt(0); } bufferLoaded = false; paintingDone = true; notifyAll(); } else { Dimension d = size(); if ( (offGraphics == null) || (offDimension.width != d.width) || (offDimension.height != d.height)) { offDimension = d; offImage = createImage(d.width, d.height); offGraphics = offImage.getGraphics(); } offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, d.width, d.height); if (rankList != null) for (int i = (rankList.length - 1); i > -1; i--) { if (rankList[i] != null) rankList[i].draw(offGraphics); } g.drawImage(offImage, 0, 0, this); } } /** Return the root of the tree (if it exists). */ public TreeNode getRootNode() { if ( (rankList == null) || rankList[0] == null) return null; else return rankList[0]; } public void addNode(ImageObject node) { if (node instanceof TreeNode) { addToRankList((TreeNode) node); treeNodes++; totalNodes++; } else { highLight = node; totalNodes++; } } public void removeNode(ImageObject node) { if (node instanceof TreeNode) { removeFromRankList((TreeNode) node); treeNodes--; totalNodes--; } else { highLight = null; totalNodes--; } } /** Sets the delete node reference to node. */ public void setDeleteNode(TreeNode node) { deleteNode = node; totalNodes++; } /** Clears the delete node reference. */ public void clearDeleteNode() { deleteNode = null; totalNodes--; } }