package org.phylowidget.render;
import java.awt.geom.Rectangle2D;
import org.phylowidget.PWContext;
import org.phylowidget.PWPlatform;
import org.phylowidget.PhyloWidget;
import org.phylowidget.tree.PhyloNode;
import org.phylowidget.tree.RootedTree;
import org.phylowidget.ui.PhyloConfig;
import processing.core.PApplet;
import processing.core.PGraphics;
public abstract class LayoutBase
{
protected RootedTree tree;
protected PhyloNode[] leaves;
protected PhyloNode[] nodes;
protected PWContext context = PWPlatform.getInstance().getThisAppContext();
protected double loX;
protected double loY;
protected double hiX;
protected double hiY;
public static final float TWOPI = (float) (Math.PI*2);
public static final float PI = (float) Math.PI;
protected int angleHandling;
public static final int ANGLE_NONE = 0;
public static final int ANGLE_QUANTIZE = 1;
public static final int ANGLE_LEVEL = 2;
float scaleX;
float scaleY;
float dX;
float dY;
float drawScaleX;
float drawScaleY;
public void layout(RootedTree tree, PhyloNode[] leaves, PhyloNode[] nodes)
{
this.tree = tree;
this.leaves = leaves;
this.nodes = nodes;
loX = loY = Double.MAX_VALUE;
hiX = hiY = Double.MIN_VALUE;
PhyloConfig cfg = PWPlatform.getInstance().getThisAppContext().config();
if (cfg.angleHandling.toLowerCase().equals("quantize"))
angleHandling = ANGLE_QUANTIZE;
else if (cfg.angleHandling.toLowerCase().equals("level"))
angleHandling = ANGLE_LEVEL;
else
angleHandling = ANGLE_NONE;
layoutImpl();
// Now, take the arbitrarily-scaled layout and transform it to fit into the unit square (0,1).
rect.setFrame(loX, loY, hiX - loX, hiY - loY);
dX = -rect.x;
dY = -rect.y;
scaleX = 1f / rect.width;
scaleY = 1f / rect.height;
float scale = PApplet.min(scaleX,scaleY);
if (Float.isInfinite(scale))
scaleX = scaleY = scale = 0;
else
scaleX = scaleY = scale;
// Center the layout properly if it's not square.
float offsetX = 0;
float offsetY = 0;
if (scale * rect.width < 1 && scale != 0)
{
offsetX = (1 - scale * rect.width) / 2;
}
if (scale * rect.height < 0.9 && scale != 0)
{
offsetY = (1 - scale * rect.height) / 2;
}
for (PhyloNode n : nodes)
{
n.setPosition((n.getLayoutX() + dX) * scaleX + offsetX, (n.getLayoutY() + dY) * scaleY + offsetY);
}
// Keep the node centered and on the left if it's the only one.
if (leaves.length < 10 && (this instanceof LayoutCladogram || this instanceof LayoutDiagonal))
{
float offsetAmount = (10f-leaves.length) / 10f;
float maxOffset = -1f;
float dX = offsetAmount * maxOffset;
// System.out.println(dX);
for (PhyloNode n : nodes)
{
n.setPosition(n.getLayoutX()+dX,n.getLayoutY());
}
}
}
protected void setPosition(PhyloNode n, float newX, float newY)
{
n.setPosition(newX, newY);
if (newX < loX) // If this node extends the bounds of the rectangle, update them.
loX = newX;
if (newX > hiX)
hiX = newX;
if (newY < loY)
loY = newY;
if (newY > hiY)
hiY = newY;
}
public abstract void drawLine(PGraphics canvas, PhyloNode p, PhyloNode c);
protected abstract void layoutImpl();
private Rectangle2D.Float rect = new Rectangle2D.Float();
static final boolean between(float a, float lo, float hi)
{
if (a <= hi && a >= lo)
return true;
else
return false;
}
protected final void setAngle(PhyloNode n, float theta)
{
theta += (float)TWOPI;
theta %= (float)TWOPI;
n.setAngle(theta);
String s = n.getAnnotation("collapse");
if (s != null && PhyloNode.parseTruth(s))
{
return;
}
switch (angleHandling)
{
case (ANGLE_NONE):
noneAngles(n);
break;
case (ANGLE_QUANTIZE):
quantizeAngles(n);
break;
case (ANGLE_LEVEL):
levelAngles(n);
break;
}
}
protected float getLayoutMult(PhyloNode n)
{
float lm = 1;
String layoutSize = n.getAnnotation("layout_size");
if (layoutSize != null)
{
lm = Float.parseFloat(layoutSize);
}
return lm;
}
private static final void noneAngles(PhyloNode n)
{
float theta = n.getAngle();
float degrees = theta / (float) (Math.PI * 2) * 360f;
boolean alignRight = false;
if (between(degrees,0,90) || between(degrees,270,360))
{
;
} else
{
alignRight = true;
degrees += 180;
}
setDegreesAndAlignment(n, degrees, alignRight);
}
private static final void setDegreesAndAlignment(PhyloNode n, float degrees, boolean alignRight)
{
if (alignRight)
n.setTextAlign(PhyloNode.ALIGN_RIGHT);
else
n.setTextAlign(PhyloNode.ALIGN_LEFT);
n.setAngle(degrees / 360f * 2 * (float) Math.PI);
}
private static final void levelAngles(PhyloNode n)
{
float theta = n.getAngle();
float degrees = theta / (float) (Math.PI * 2) * 360f;
degrees = degrees % 360;
boolean alignRight = false;
if (between(degrees, 0, 90) || between(degrees, 270, 360))
{
degrees = 0;
alignRight = false;
} else
{
degrees = 0;
alignRight = true;
}
setDegreesAndAlignment(n, degrees, alignRight);
}
private static final void quantizeAngles(PhyloNode n)
{
float theta = n.getAngle();
float degrees = theta / (float) (Math.PI * 2) * 360f;
degrees += 720;
degrees %= 360;
float oldDegrees = degrees;
boolean alignRight = false;
if (between(degrees, 0, 45))
{
degrees = 0;
alignRight = false;
} else if (between(degrees, 45, 90))
{
degrees = 45;
alignRight = false;
} else if (between(degrees, 90, 135))
{
degrees = -45;
alignRight = true;
} else if (between(degrees, 135, 225))
{
degrees = 0;
alignRight = true;
} else if (between(degrees, 225, 270))
{
degrees = 45;
alignRight = true;
} else if (between(degrees, 270, 315))
{
degrees = -45;
alignRight = false;
} else if (between(degrees, 315, 360))
{
degrees = 0;
alignRight = false;
}
setDegreesAndAlignment(n, degrees, alignRight);
}
}