/* Tree.java
Binary tree class
- Trees consist of any number of objects of class Tree
- Each object points to its parent (closer to root)
- Each object may point to a left- and right-tree child
- object is a continuation of a staight segment if
ltree point to an object and rtree is null
- object is a bifurcation point if both ltree and
rtree point to tree objects.
BPG 4-3-03 time parameters changed to doubles
BPG 7-1-03 compiler errors fixed
BPG 6-6-00 intermediate path length calculation fixed
BPG 26-11-99 updated to use Graphics2D
BPG 28-9-99
*/
import java.awt.*;
import java.lang.Math;
import java.lang.Thread;
import java.io.*;
// Public generic binary tree class
public class Tree {
// Public class variables
public static int brkey; // identifying branch key
// normally 1 + no.of branches
// Public instance variables
// Basic
public String name;
public Tree parent;
public Tree ltree;
public Tree rtree;
public int key; // identifying key
public int order; // centrifugal order
public float length; // length
public float diam; // diameter
public float pathlength; // path length to segment
// Graphics
public int locx; // x-coord for screen drawing
public int locy; // y-coord for screen drawing
// File IO
public FileOutputStream fout;
public PrintWriter pout;
// Class variables for housekeeping and graphics
public static int currx; // current x position
public static int curry; // current y position
public static float scalex; // x-scaling (for drawing)
public static int incx; // increment in x per node (for printing)
public static int incy; // increment in y per node (for both)
public static float bangle; // branch angle (for real drawing)
public static float badel; // scale factor on branch angle
public static long drawdel; // delay between draw updates
public static float maxLength; // max possible path length
public static float scsoma; // scale factor for soma
public static float scdend; // scale factor for dendrite
// File IO
public static String tname; // tree class name
public static int fstore=0; // flag to indicate "store data"
// Basic tree constructors
// More complex constructors are contained in subclasses
// Constructor without parent node (root tree)
public Tree() {
key = 0;
order = 0;
parent = null;
ltree = null;
rtree = null;
}
// Constructor with parent node (subtree)
public Tree(Tree parent) {
this(); // basic constructor
this.parent = parent;
}
// Constructor with parent node and identifying key and order
public Tree(Tree parent, int key, int order) {
this(parent);
this.key = key;
this.order = order;
}
// Constructor with parent node, key, order, length and diameter
public Tree(Tree parent, int key, int order, float length, float diam) {
this(parent, key, order);
this.length = length;
this.diam = diam;
}
// Tree housekeeping methods
// Count segments in tree
public int countSegments() {
if (ltree == null && rtree == null)
return 1;
else if (rtree == null)
return ltree.countSegments();
else
return 1 + ltree.countSegments() + rtree.countSegments();
}
// Count terminal nodes in tree
public int countTerminals() {
if (ltree == null && rtree == null)
return 1;
else if (rtree == null)
return ltree.countTerminals();
else
return ltree.countTerminals() + rtree.countTerminals();
}
// Store terminal values (indexed by key)
public void termValues(float[] termV, String pname) {
if (parent == null) // soma
termV[key] = getValue(pname);
if (ltree == null && rtree == null) {
if (termV.length > key)
termV[key] = getValue(pname); // store terminal value
};
if (ltree != null) ltree.termValues(termV, pname);
if (rtree != null) rtree.termValues(termV, pname);
}
// Calculate total path length in tree
public float totPathLength() {
if (parent == null) // soma not included
return ltree.totPathLength();
else if (ltree == null && rtree == null)
return length;
else if (rtree == null)
return ltree.totPathLength() + length;
else
return length + ltree.totPathLength() + rtree.totPathLength();
}
// Find maximum path length in tree
public float maxPathLength() {
float lpath, rpath;
if (parent == null) // soma not included
pathlength = 0;
// pathlength = length;
else
pathlength = parent.pathlength + length;
if (ltree == null && rtree == null) {
return(pathlength);
}
else if (rtree == null)
return ltree.maxPathLength();
else {
lpath = ltree.maxPathLength();
rpath = rtree.maxPathLength();
};
if (lpath > rpath) return lpath;
else return rpath;
}
// Store terminal path lengths
public int termLengths(float[] termL, int i) {
if (ltree == null && rtree == null) {
termL[i] = length; // store terminal length
return i+1;
}
else if (rtree == null)
return ltree.termLengths(termL, i);
else
return rtree.termLengths(termL, ltree.termLengths(termL, i));
}
// Store intermediate path lengths (NOT QUITE RIGHT)
public int intLengths(float[] intL, int i) {
if (ltree == null && rtree == null)
return i;
else if (ltree != null && rtree != null) {
intL[i] = length; // store intermediate length
return rtree.intLengths(intL, ltree.intLengths(intL, i+1));
}
else if (ltree == null && rtree != null)
return rtree.intLengths(intL, i);
else
return ltree.intLengths(intL, i);
}
// Store all path lengths
public int pathLengths(float[] pathL, int i) {
if (ltree == null && rtree == null) {
pathL[i] = pathlength; // store path length
return i+1;
}
else if (ltree == null)
return rtree.pathLengths(pathL, i);
else if (rtree == null)
return ltree.pathLengths(pathL, i);
else
return rtree.pathLengths(pathL, ltree.pathLengths(pathL, i));
}
// Store centrifugal orders
public int centOrders(int[] centO, int i) {
if (ltree == null && rtree == null) {
centO[i] = order; // store centrifugal order
return i+1;
}
else if (rtree == null)
return ltree.centOrders(centO, i);
else
return rtree.centOrders(centO, ltree.centOrders(centO, i));
}
// Find maximum centrifugal order in tree
public int maxOrder() {
if (ltree == null && rtree == null) {
return order; // terminal order
}
else if (rtree == null)
return ltree.maxOrder();
else return Math.max(ltree.maxOrder(), rtree.maxOrder());
}
// Calculate asymmetry partition of tree
public float asymPart() {
int nTerms, lnTerms, rnTerms;
float currPart;
lnTerms = 0;
rnTerms = 0;
nTerms = countTerminals();
if (nTerms == 1) {
return 0;
}
else if (rtree == null) // continuation of branch
return(ltree.asymPart());
else {
lnTerms = ltree.countTerminals();
rnTerms = rtree.countTerminals();
};
if (lnTerms == 1 && rnTerms == 1)
return 0;
currPart = (float)Math.abs(lnTerms-rnTerms) / (float)(lnTerms+rnTerms-2);
return(currPart + ltree.asymPart() + rtree.asymPart());
}
// Calculate asymmetry of tree
public float asymIndex() {
int nTerms;
nTerms = countTerminals();
if (nTerms == 1) {
return 0;
}
else return(asymPart() / (float)(nTerms-1));
}
// Dummy routines for growing trees
// - must be defined by subclasses
// Branch all terminal nodes
public void branchTree(double t) {
}
// Grow terminal nodes
public void elongateTree(double t) {
}
// Set segment diameters
public void diamTree(double t) {
}
// Update previous values
public void updateTree(double t) {
}
// Get variable value
public float getValue(String vname) {
return 0f;
}
// Get terminal parameter value
public float termValue() {
return 0f;
}
// Methods for displaying trees
// Display colour tree with active parameter value
public void displayTree(Graphics g, int x0, int y0, int dw, int dh, String pname, float pmaxval) {
// (x0, y0) is the top left-hand corner of display area
// dh, dw are the height and width of the display area
// Display tree
Tree.currx = x0;
Tree.curry = y0 + (dh / 2); // middle height
Tree.scalex = (float)dw / maxLength;
drawRealTree(g, bangle, pname, pmaxval);
try {Thread.sleep(drawdel);} catch(InterruptedException eIE){};
}
// Draw tree as realistic branching structure in colour
public void drawRealTree(Graphics g1, float bang, String pname, float pmval) {
float dl, dh, dx, dy;
float pval;
Graphics2D g = (Graphics2D) g1;
Stroke dendStroke;
locx = currx;
locy = curry;
// soma
if (parent == null) {
dl = scsoma * scalex * length;
dh = scsoma * scalex * diam;
// Get parameter value and maximum value
pval = getValue(pname);
g.setColor(ColScale.ColVal(pval, pmval));
g.fillOval(locx, locy-(int)(dh/2), (int)dl, (int)dh);
// g.fillRect(locx, locy-(int)(dh/2), (int)dl, (int)dh);
// g.drawLine(locx, locy, locx + (int)dl, locy);
locx = currx + (int)dl;
// set fixed line thickness for dendrites
// dendStroke = new BasicStroke(scdend);
// g.setStroke(dendStroke);
};
// draw continuing left tree
if (ltree != null && rtree == null) {
// dl = scalex * ltree.length;
// dx = dl * (float)Math.cos((double)bangle*Math.PI/180);
// dy = dl * (float)Math.sin((double)bangle*Math.PI/180);
if (parent == null) {
dx = scalex * ltree.length;
dy = 0;
} else {
dx = (locx - parent.locx) * (ltree.length / length);
dy = (locy - parent.locy) * (ltree.length / length);
};
currx = locx+(int)dx;
curry = locy+(int)dy;
// Get parameter value
pval = ltree.getValue(pname);
dendStroke = new BasicStroke(ltree.diam*scdend);
g.setStroke(dendStroke);
g.setColor(ColScale.ColVal(pval, pmval));
g.drawLine(locx, locy, currx, curry);
ltree.drawRealTree(g, bang, pname, pmval);
}
else {
// draw left tree
if (ltree != null) {
dl = scalex * ltree.length;
dx = dl * (float)Math.cos((double)bang*Math.PI/180);
dy = dl * (float)Math.sin((double)bang*Math.PI/180);
currx = locx+(int)Math.ceil((double)dx);
curry = locy-(int)Math.ceil((double)dy);
// Get parameter value
pval = ltree.getValue(pname);
dendStroke = new BasicStroke(ltree.diam*scdend);
g.setStroke(dendStroke);
g.setColor(ColScale.ColVal(pval, pmval));
g.drawLine(locx, locy, currx, curry);
ltree.drawRealTree(g, bang+(badel*bang), pname, pmval);
};
// draw right tree
if (rtree != null) {
dl = scalex * rtree.length;
dx = dl * (float)Math.cos((double)bang*Math.PI/180);
dy = dl * (float)Math.sin((double)bang*Math.PI/180);
currx = locx+(int)Math.ceil((double)dx);
curry = locy+(int)Math.ceil((double)dy);
// Get parameter value
pval = rtree.getValue(pname);
dendStroke = new BasicStroke(rtree.diam*scdend);
g.setStroke(dendStroke);
g.setColor(ColScale.ColVal(pval, pmval));
g.drawLine(locx, locy, currx, curry);
rtree.drawRealTree(g, bang+(badel*bang), pname, pmval);
};
};
}
// Print tree to system output
public void printTree() {
System.out.println("Key: "+key+" Order: "+order);
if (ltree != null) ltree.printTree();
if (rtree != null) rtree.printTree();
}
// Setup File IO for saving real-time segment data
public void setupFileIO() throws IOException {
fout = new FileOutputStream(name+".pbr");
pout = new PrintWriter(fout);
}
// Close output files
public void closeFileIO() throws IOException {
pout.close();
if (ltree != null) ltree.closeFileIO();
if (rtree != null) rtree.closeFileIO();
}
}