Innovations in Visualization

## Assignment 1

This is code for assignment 1 that you can use as a basis for assignment 2 if your assignment 1 wasn't that fabulous:

import ca.ucalgary.innovis.*;
import java.io.File;

float rootsize;
NAryTree tree;
NAryTreeNode root;
float phyl_angle = 137.5;
int depth;
PFont font;

void setup() {
rootsize = 600;
size((int)rootsize,(int)rootsize);
background(102);
noLoop();

File file = new File(dataPath("product_9_197.tree"));
depth = tree.getDepth(false);

root = (NAryTreeNode)tree.getRoot();
root.setWidth(rootsize);
root.setPosition(rootsize/2.0,rootsize/2.0);

smooth();

}

void draw() {
background(102);

float cx = (float) (rootsize / 2.0);
float cy = (float) (rootsize / 2.0);
fill(138,157,162);
ellipse(cx, cy, rootsize, rootsize);

//draw all the children of this node
drawChildren(root);
}

void drawChildren(NAryTreeNode node){

//first let's find out how many children we need to draw
int childCount = node.getChildCount();

//save the position of the node whose children we want to draw
double xnode = node.getXPosition();
double ynode = node.getYPosition();
double w    = node.getWidth();

//now let's figure out how much space we have
//this is done by inverting the formula r = c * sqrt(n)
//w / 2.0 is our radius available for laying out the children, so...
//this will be our spacing constant for this node's children:
double c = (w / 2.0) / sqrt(childCount);

//the first coding pass. Figuring out the initial position for each node within the parent
//the caveat is that the children can be located on the rim of the parents' circle,
//nevertheless, we will first look at the positions

for(int n = 0; n < childCount; n++){

double radius = c * sqrt(n);
//the angle in polar coordinates
double angleInDegrees  = n * phyl_angle;
//the position in the euclidean coordinate system
double x = radius * cos(angle) + xnode;
double y = radius * sin(angle) + ynode;

//now we get the n'th child and place it
NAryTreeNode child = (NAryTreeNode) node.getChildAt(n);
child.setPosition(x,y);
}

//the second coding pass. Figuring out the initial size of the nodes
//the next question is what size each node should get based on where their siblings are located
//keep in mind that nodes will still sit on the rim of their parent (set the scaleFactor below to 1.0 if you want to know what it looks like
for(int n = 0; n < childCount; n++){

//this is the child we're looking at now
NAryTreeNode child = (NAryTreeNode) node.getChildAt(n);

//get the distance
double xchild = child.getXPosition();
double ychild = child.getYPosition();
//the closest child should be less than the width of the parent away
double closestDistance = w;
String closestN = "";

//this is costly but will give us correct distances
for(int z = 0; z < childCount; z++){

if(z == n) continue; // we don't need to test a distance with ourselves
//get the siblings and their positions
NAryTreeNode sibling = (NAryTreeNode) node.getChildAt(z);

float xsib = (float) sibling.getXPosition();
float ysib = (float) sibling.getYPosition();

//calculate the euclidean distance from our child to this sibling
float distance = dist((float) xchild,(float) ychild, xsib, ysib);
//double distance = Math.sqrt((xchild - xsib)*(xchild - xsib) + (ychild - ysib) * (ychild - ysib));

if(distance < closestDistance){
closestDistance = distance;
closestN = sibling.getLabel();
}
}
//println("Closest neighbor of: " + child.getLabel() + " is: " + closestN + " distance: " + closestDistance);
// if we have only one child then make it a bit smaller than the parent so we know it's there
if(childCount == 1) closestDistance = w / 1.5;

//now we should know the closest distance and can appropriately size the node
//now this only ensures that nodes will never ever overlap
//what we really want is for the nodes to be the biggest size possible
//this is going to get a bit more complicated
child.setWidth(closestDistance);
}

//the third coding pass. Figuring out how much the nodes go over.
//for this we need to shoot a ray through the center of the nodes and intersect twice.
//the second intersection is the one that may lie outside of the parent

double radiusOverParent = w / 2.0;

for(int n = 0; n < childCount; n++){
NAryTreeNode child = (NAryTreeNode) node.getChildAt(n);
double s = child.getWidth();
double xchild = child.getXPosition();
double ychild = child.getYPosition();

//first let's get the distance from the circle center to the parent center
float distance = dist( (float)xchild, (float)ychild, (float)xnode, (float)ynode);
double extend = (distance + s / 2.0);
//if this length is longer than the radius of the parent, then the child sticks out
//here we find the longest amount by which a child sticks out
}

//fourth coding pass

//we now know how much we're over and need to scale.
//the scale factor is the radius of the parent / however much we're over:
double scaleFactor =  (w / 2.0) / radiusOverParent;

//now we need to scale and move each child so it sits inside the parent
for(int n = 0; n < childCount; n++){
NAryTreeNode child = (NAryTreeNode) node.getChildAt(n);
double s = child.getWidth();
double xn = child.getXPosition();
double yn = child.getYPosition();

//first move the coordinates to the center, then scale, then move back (Graphics I rules)
double xtemp = ((xn - xnode) * scaleFactor) + xnode;
double ytemp = ((yn - ynode) * scaleFactor) + ynode;

//set the child to the new position
child.setPosition(xtemp,ytemp);

//now scale the size
double tempSize = s * scaleFactor;
child.setWidth(tempSize);

//now give the node a pretty bording colour
fill((255 / depth) * (depth - child.getLevel()));
//we should finally be done and can draw
ellipse((float)xtemp,(float) ytemp,(float) tempSize,(float) tempSize);

//fill(255,0,0);
//text(child.getLabel(), (float)xtemp, (float) ytemp);

//now that we're in the last coding pass we go recursively and do the same for all the child's children
int children = child.getChildCount();
if(children > 0) drawChildren(child);
}
}

void keyPressed()
{
if (keyCode == LEFT)
{
phyl_angle -= 0.1;
redraw();
}
else if (keyCode == RIGHT)
{
phyl_angle += 0.1;
redraw();
}
}