package org.phylowidget.render;
import java.util.List;
import org.andrewberman.ui.UIUtils;
import org.phylowidget.PWPlatform;
import org.phylowidget.PhyloTree;
import org.phylowidget.PhyloWidget;
import org.phylowidget.tree.PhyloNode;
import processing.core.PConstants;
import processing.core.PGraphics;
public class LayoutCladogram extends LayoutBase
{
int numLeaves;
float depthLeafRatio;
float totalLayoutHeight;
public synchronized void layoutImpl()
{
numLeaves = leaves.length;
float maxDepth = tree.getMaxDepthToLeaf(tree.getRoot());
depthLeafRatio = maxDepth / numLeaves;
depthLeafRatio *= context.config().branchScaling;
totalLayoutHeight = 0;
for (PhyloNode leaf : leaves)
{
totalLayoutHeight += getLayoutMult(leaf);
}
double index = 0;
PhyloNode previousLeaf = null;
for (PhyloNode leaf : leaves)
{
index+= getLayoutMult(leaf)/2;
leaf.setTextAlign(PhyloNode.ALIGN_LEFT);
leafPosition(leaf, previousLeaf, index);
index+= getLayoutMult(leaf)/2;
previousLeaf = leaf;
}
branchPosition((PhyloNode) tree.getRoot());
}
public void drawSquareLine(PGraphics canvas, PhyloNode p, PhyloNode c)
{
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
canvas.strokeJoin(canvas.ROUND);
}
canvas.noFill();
canvas.beginShape();
canvas.vertex(p.getX(), p.getY());
canvas.vertex(p.getX(), c.getY());
canvas.vertex(c.getX(), c.getY());
canvas.endShape();
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
}
}
public void drawBezierLine(PGraphics canvas, PhyloNode p, PhyloNode c)
{
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
canvas.strokeJoin(canvas.ROUND);
}
canvas.noFill();
PhyloNode nearestChild = c;
PhyloTree t = p.getTree();
List<PhyloNode> children = t.getChildrenOf(p);
for (PhyloNode child : children)
{
if (child.getX() < nearestChild.getX())
{
nearestChild = child;
}
}
float w = (nearestChild.getX() - p.getX());
float curveFraction = 1f;
float wouldBeDx = w * curveFraction;
float roundOffX = 0;
roundOffX = wouldBeDx;
canvas.noFill();
canvas.beginShape();
canvas.vertex(p.getX(), p.getY()); // Parent point.
canvas.bezierVertex(p.getX() + roundOffX, p.getY(), p.getX(), c.getY(), p.getX() + roundOffX, c.getY());
canvas.vertex(c.getX(), c.getY());
canvas.endShape();
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
}
}
public void drawLine(PGraphics canvas, PhyloNode p, PhyloNode c)
{
char ch = PWPlatform.getInstance().getThisAppContext().config().lineStyle.charAt(0);
switch (ch)
{
case ('r'): // R is for round.
drawCurvedLine(canvas, p, c);
break;
case ('b'): // B is for bezier.
drawBezierLine(canvas, p, c);
break;
case ('s'): // S is for square.
default:
drawSquareLine(canvas, p, c);
break;
}
}
public void drawCurvedLine(PGraphics canvas, PhyloNode p, PhyloNode c)
{
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
canvas.strokeJoin(canvas.ROUND);
}
int oldMode = canvas.ellipseMode;
// Find the left-most child.
PhyloNode nearestChild = c;
boolean findNearestChild = true;
if (findNearestChild)
{
PhyloTree t = p.getTree();
List<PhyloNode> children = t.getChildrenOf(p);
for (PhyloNode child : children)
{
if (child.getX() < nearestChild.getX())
{
nearestChild = child;
}
}
}
// Find the "ideal" curve amount for each direction.
float w = (nearestChild.getX() - p.getX());
float h = (c.getY() - p.getY());
float absH = Math.abs(h);
float curveFraction = 0.8f;
float wouldBeDx = w * curveFraction;
float wouldBeDy = absH * (curveFraction);
// We want the curve to be "square", so use the minimum curve as the true value.
float roundOffX = 0;
float roundOffY = 0;
// float max_dist = 10;
// if (wouldBeDy > max_dist) wouldBeDy = max_dist;
// if (wouldBeDx > max_dist) wouldBeDx = max_dist;
boolean keepSquare = true;
if (keepSquare)
{
if (wouldBeDx > wouldBeDy)
wouldBeDx = wouldBeDy;
if (wouldBeDy > wouldBeDx)
wouldBeDy = wouldBeDx;
}
roundOffX = wouldBeDx;
roundOffY = wouldBeDy;
// reverse Y direction if < 0.
roundOffY = (h > 0 ? roundOffY : -roundOffY);
canvas.noFill();
canvas.beginShape();
canvas.vertex(p.getX(), p.getY()); // Parent point.
canvas.vertex(p.getX(), p.getY() + (h - roundOffY)); // Partly down the vertical line.
canvas.endShape();
canvas.ellipseMode(PConstants.CENTER);
float pi = PConstants.PI;
if (h > 0)
{
canvas.arc(p.getX() + roundOffX, p.getY() + (h - roundOffY), roundOffX * 2, (roundOffY) * 2, pi / 2, pi);
} else
{
canvas.arc(p.getX() + roundOffX, p.getY() + (h - roundOffY), roundOffX * 2, Math.abs(roundOffY) * 2, pi,
3 * pi / 2);
}
canvas.beginShape();
canvas.vertex(p.getX() + roundOffX, c.getY()); // Mid-way on horizontal line.
canvas.vertex(c.getX(), c.getY());
canvas.endShape();
canvas.ellipseMode(oldMode);
if (UIUtils.isJava2D(canvas))
{
canvas.strokeCap(canvas.ROUND);
}
}
private float branchPosition(PhyloNode n)
{
setAngle(n, 0);
if (tree.isLeaf(n))
{
// If N is a leaf, then it's already been laid out.
return n.getTargetY();
} else
{
// If not:
// Y coordinate should be the average of its children's heights
List children = tree.getChildrenOf(n);
float sum = 0;
float count = 0;
for (int i = 0; i < children.size(); i++)
{
PhyloNode child = (PhyloNode) children.get(i);
sum += branchPosition(child);
count++;
}
float yPos = (float) sum / (float) count;
float xPos = 1;
xPos = calcXPosition(n);
setPosition(n, xPos, yPos);
return yPos;
}
}
private void leafPosition(PhyloNode n, PhyloNode previousLeaf, double index)
{
/**
* Set the leaf position.
*/
float yPos = ((float) (index + .5f) / (float) (numLeaves));
// float yPos = 0;
// float addedPos = 0;
// if (previousLeaf != null)
// {
// yPos = previousLeaf.getLayoutY();
// addedPos = getLayoutMult(previousLeaf)/2;
// }
// addedPos += getLayoutMult(n)/2;
float xPos = calcXPosition(n);
setPosition(n,xPos,yPos);
// setPosition(n, xPos, yPos+addedPos/totalLayoutHeight);
}
private float calcXPosition(PhyloNode n)
{
if (context.config().useBranchLengths)
{
if (tree.isRoot(n))
return 0;
float asdf = 0;
if (tree.getMaxHeightToLeaf(tree.getRoot()) == 0)
{
System.out.println("Tree height is zero!");
}
asdf = (float) tree.getHeightToRoot(n) / (float) tree.getMaxHeightToLeaf(tree.getRoot());
return asdf * depthLeafRatio;
} else
{
if (tree.isRoot(n))
return 0;
float md = 1f - (float) tree.getMaxDepthToLeaf(n) / (float) tree.getMaxDepthToLeaf(tree.getRoot());
return md * depthLeafRatio;
}
}
}