/* ********************************************************************** * binarytree: An Asymptote module to draw binary trees * * * * Copyright(C) 2006 * * Tobias Langner tobias[at]langner[dot]nightlabs[dot]de * * * * Modified by John Bowman * * * * Condensed mode: * * Copyright(C) 2012 * * Gerasimos Dimitriadis dimeg [at] intracom [dot] gr * * * ************************************************************************ * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 3 of the License, or(at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, * * Boston, MA 02110-1301 USA * * * * Or get it online: * * http: //www.gnu.org/copyleft/lesser.html * * * ***********************************************************************/ // default values real minDistDefault=0.2cm; real nodeMarginDefault=0.1cm; // structure to represent nodes in a binary tree struct binarytreeNode { int key; binarytreeNode left; binarytreeNode right; binarytreeNode parent; bool spans_calculated=false; int left_span,total_left_span; int right_span,total_right_span; void update_spans(); // Get the horizontal span of the tree consisting of the current // node plus the whole subtree that is rooted at the right child // (condensed mode) int getTotalRightSpan() { if(spans_calculated == false) { update_spans(); } return total_right_span; } // Get the horizontal span of the tree consisting of the current // node plus the whole subtree that is rooted at the left child // (condensed mode) int getTotalLeftSpan() { if(spans_calculated == false) { update_spans(); } return total_left_span; } // Get the horizontal distance between this node and its right child // (condensed mode) int getRightSpan() { if(spans_calculated == false) { update_spans(); } return right_span; } // Get the horizontal distance between this node and its left child // (condensed mode) int getLeftSpan() { if(spans_calculated == false) { update_spans(); } return left_span; } // Update all span figures for this node. // condensed mode) update_spans=new void() { if(spans_calculated == true) return; left_span=0; total_left_span=0; right_span=0; total_right_span=0; if(left != null) { left_span=left.getTotalRightSpan()+1; total_left_span=left_span+left.getTotalLeftSpan(); } if(right != null) { right_span=right.getTotalLeftSpan()+1; total_right_span=right_span+right.getTotalRightSpan(); } spans_calculated=true; }; // set the left child of this node void setLeft(binarytreeNode left) { this.left=left; this.left.parent=this; } // set the right child of this node void setRight(binarytreeNode right) { this.right=right; this.right.parent=this; } // return a boolean indicating whether this node is the root bool isRoot() { return parent == null; } // return the level of the subtree rooted at this node. int getLevel() { if(isRoot()) return 1; else return parent.getLevel()+1; } // set the children of this binarytreeNode void setChildren(binarytreeNode left, binarytreeNode right) { setLeft(left); setRight(right); } // create a new binarytreeNode with key static binarytreeNode binarytreeNode(int key) { binarytreeNode toReturn=new binarytreeNode; toReturn.key=key; return toReturn; } // returns the height of the subtree rooted at this node. int getHeight() { if(left == null && right == null) return 1; if(left == null) return right.getHeight()+1; if(right == null) return left.getHeight()+1; return max(left.getHeight(),right.getHeight())+1; } } binarytreeNode operator init() {return null;} // "constructor" for binarytreeNode binarytreeNode binarytreeNode(int key)=binarytreeNode.binarytreeNode; // draw the tree rooted at the given at the given position , with // =the height of the containing tree, // =the minimal horizontal distance of two nodes at the lowest level, // =the vertical distance between two levels, // =the diameter of one node. object draw(picture pic=currentpicture, binarytreeNode node, pair pos, int height, real minDist, real levelDist, real nodeDiameter, pen p=currentpen, bool condensed=false) { Label label=Label(math((string) node.key),pos); binarytreeNode left=node.left; binarytreeNode right=node.right; // return the distance for two nodes at the given when the // containing tree has height // and the minimal distance between two nodes is . real getDistance(int level, int height, real minDist) { return(nodeDiameter+minDist)*2^(height-level); } // return the horiontal distance between node and its left child // (condensed mode) real getLeftDistance(binarytreeNode n) { return(nodeDiameter+minDist) *(real)n.getLeftSpan() * 0.5; } // return the horiontal distance between node and its right child // (condensed mode) real getRightDistance(binarytreeNode n) { return(nodeDiameter+minDist) *(real)n.getRightSpan() * 0.5; } real dist=getDistance(node.getLevel(),height,minDist)/2; // draw the connection between the two nodes at the given positions // by calculating the connection points and drawing the corresponding // arrow. void deferredDrawNodeConnection(pair parentPos, pair childPos) { pic.add(new void(frame f, transform t) { pair start,end; // calculate connection path transform T=shift(nodeDiameter/2*unit(t*childPos-t*parentPos)); path arr=(T*t*parentPos)--(inverse(T)*t*childPos); draw(f,PenMargin(arr,p).g,p,Arrow(5)); }); pic.addPoint(parentPos); pic.addPoint(childPos); } if(left != null) { pair childPos; if(condensed == false) { childPos=pos-(0,levelDist)-(dist/2,0); } else { childPos=pos-(0,levelDist)-((real)getLeftDistance(node),0); } draw(pic,left,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); deferredDrawNodeConnection(pos,childPos); } if(right != null) { pair childPos; if(condensed == false) { childPos=pos-(0,levelDist)+(dist/2,0); } else { childPos=pos-(0,levelDist)+((real)getRightDistance(node),0); } draw(pic,right,childPos,height,minDist,levelDist,nodeDiameter,p,condensed); deferredDrawNodeConnection(pos,childPos); } picture obj; draw(obj,circle((0,0),nodeDiameter/2),p); label(obj,label,(0,0),p); add(pic,obj,pos); return label; } struct key { int n; bool active; } key key(int n, bool active=true) {key k; k.n=n; k.active=active; return k;} key operator cast(int n) {return key(n);} int operator cast(key k) {return k.n;} int[] operator cast(key[] k) { int[] I; for(int i=0; i < k.length; ++i) I[i]=k[i].n; return I; } key nil=key(0,false); // structure to represent a binary tree. struct binarytree { binarytreeNode root; int[] keys; // add the given to the tree by searching for its place and // inserting it there. void addKey(int key) { binarytreeNode newNode=binarytreeNode(key); if(root == null) { root=newNode; keys.push(key); return; } binarytreeNode n=root; while(n != null) { if(key < n.key) { if(n.left != null) n=n.left; else { n.setLeft(newNode); keys.push(key); return; } } else if(key > n.key) { if(n.right != null) n=n.right; else { n.setRight(newNode); keys.push(key); return; } } } } // return the height of the tree int getHeight() { if(root == null) return 0; else return root.getHeight(); } // add all given keys to the tree sequentially void addSearchKeys(int[] keys) { for(int i=0; i < keys.length; ++i) { int key=keys[i]; // Ignore duplicate keys if(find(this.keys == key) == -1) addKey(key); } } binarytreeNode build(key[] keys, int[] ind) { if(ind[0] >= keys.length) return null; key k=keys[ind[0]]; ++ind[0]; if(!k.active) return null; binarytreeNode bt=binarytreeNode(k); binarytreeNode left=build(keys,ind); binarytreeNode right=build(keys,ind); bt.left=left; bt.right=right; if(left != null) left.parent=bt; if(right != null) right.parent=bt; return bt; } void addKeys(key[] keys) { int[] ind={0}; root=build(keys,ind); this.keys=keys; } // return all key in the tree int[] getKeys() { return keys; } } binarytree searchtree(...int[] keys) { binarytree bt; bt.addSearchKeys(keys); return bt; } binarytree binarytree(...key[] keys) { binarytree bt; bt.addKeys(keys); return bt; } // draw the given binary tree. void draw(picture pic=currentpicture, binarytree tree, real minDist=minDistDefault, real nodeMargin=nodeMarginDefault, pen p=currentpen, bool condensed=false) { int[] keys=tree.getKeys(); // calculate the node diameter so that all keys fit into it frame f; for(int i=0; i < keys.length; ++i) label(f,math(string(keys[i])),p); real nodeDiameter=abs(max(f)-min(f))+2*nodeMargin; real levelDist=nodeDiameter*1.8; draw(pic,tree.root,(0,0),tree.getHeight(),minDist,levelDist,nodeDiameter,p, condensed); }