/*
* TreePlotter.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST 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 2
* of the License, or (at your option) any later version.
*
* BEAST 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 BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.treespace;
import dr.app.treespace.CladeSystem.Clade;
import jebl.evolution.graphs.Node;
import jebl.evolution.trees.RootedTree;
import jebl.math.Random;
import sun.awt.geom.Curve;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.ColorModel;
import java.util.ArrayList;
import java.util.List;
/**
* @author Andrew Rambaut
* @version $Id$
*/
public class TreePlotter extends JComponent {
private final static Color[] COLOUR_SCHEME = {
new Color(0.879F, 0.261F, 0.262F), //Africa,
new Color(0.816F, 0.765F, 0.376F), //USA,
new Color(0.26F, 0.141F, 0.632F), //Taiwan,
new Color(0.242F, 0.445F, 0.718F), //China,
new Color(0.592F, 0.669F, 0.295F), //Russia,
new Color(0.791F, 0.27F, 0.146F), //Oceania,
new Color(0.359F, 0.425F, 0.833F), //Asia,
new Color(0.374F, 0.623F, 0.505F), //Japan,
new Color(0.785F, 0.585F, 0.209F), //Mexico,
new Color(0.917F, 0.58F, 0.322F), //South America,
new Color(0.64F, 0.46F, 0.28F), //Canada,
new Color(0.599F, 0.772F, 0.513F), //Europe,
new Color(0.551F, 0.242F, 0.598F), //Southeast Asia,
new Color(0.43F, 0.674F, 0.744F) //South Korea
};
public TreePlotter() {
setTrees(treeLineages);
}
public TreePlotter(final TreeLineages treeLineages) {
setTrees(treeLineages);
}
public void setTrees(final TreeLineages treeLineages) {
this.treeLineages = treeLineages;
isCalibrated = false;
repaint();
}
public void paint(Graphics g) {
if (treeLineages == null || treeLineages.getRootLineages().size() == 0) {
return;
}
if (!isCalibrated) {
// scaleX = (getBounds().getWidth() - borderWidth - borderWidth) / treeLineages.getMaxWidth();
// scaleY = (getBounds().getHeight() - borderWidth - borderWidth) / (treeLineages.getMaxHeight() - 1.0);
// offsetX = borderWidth;
// offsetY = borderWidth;
scaleX = (getBounds().getWidth() - borderWidth - borderWidth) / treeLineages.getAntigenicBounds().getWidth();
scaleY = (getBounds().getHeight() - topBorder - bottomBorder) / treeLineages.getAntigenicBounds().getHeight();
offsetX = borderWidth - (float)((treeLineages.getAntigenicBounds().getX() * scaleX));
offsetY = bottomBorder - (float)((treeLineages.getAntigenicBounds().getY() * scaleY));
// isCalibrated = true;
}
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
float count = 0.0F;
for (TreeLineages.Lineage root : treeLineages.getRootLineages()) {
float hue = count / treeLineages.getRootLineages().size();
// paintTreeLineages((Graphics2D)g, hue, root);
paintAntigenicLineages((Graphics2D)g, hue, root);
count += 1.0F;
}
}
private void paintTreeLineages(Graphics2D g2, float hue, TreeLineages.Lineage lineage) {
double originX = treeLineages.getTreeBounds().getWidth() - lineage.dx;
double y1 = lineage.dy;
paintLineage(g2, hue, lineage.child1, originX, 0.0, y1);
paintLineage(g2, hue, lineage.child2, originX, 0.0, y1);
}
private void paintLineage(Graphics2D g2, float hue, TreeLineages.Lineage lineage, double originX, double x, double y) {
double x1 = lineage.dx;
double y1 = lineage.dy;
if (lineage.child1 != null) {
paintLineage(g2, hue, lineage.child1, originX, x1, y1);
paintLineage(g2, hue, lineage.child2, originX, x1, y1);
} else {
paintTip(g2, lineage, originX, x1, y1);
}
// g2.setColor(Color.getHSBColor(hue, 1.0F, 1.0F));
// g2.setColor(new Color(0.0F, 0.0F, 0.0F, 0.1F));
// g2.setColor(COLOUR_SCHEME[lineage.state]);
g2.setColor(new Color(COLOUR_SCHEME[lineage.state].getRed(), COLOUR_SCHEME[lineage.state].getGreen(), COLOUR_SCHEME[lineage.state].getBlue(), 32));
float thickness = 0.25F;
thickness = 2.5F * (Math.min((float)lineage.tipCount, 10.0F) / 10.0F);
g2.setStroke(new BasicStroke(thickness));
// Shape curve = new Line2D.Double(transformX(x), transformY(y), transformX(x1), transformY(y1));
double xp1 = transformX(originX + x);
double yp1 = transformY(y);
double xp2 = transformX(originX + x1);
double yp2 = transformY(y1);
double xc = (xp1 + xp2) / 2.0;
Shape curve = new CubicCurve2D.Double(xp1, yp1, xc, yp1, xc, yp2, xp2, yp2);
g2.draw(curve);
}
private void paintAntigenicLineages(Graphics2D g2, float hue, TreeLineages.Lineage lineage) {
double originX = treeLineages.getTreeBounds().getWidth() - lineage.dx;
double x1 = lineage.ax;
double y1 = lineage.ay;
paintAntigenicLineage(g2, hue, lineage.child1, originX, x1, y1);
paintAntigenicLineage(g2, hue, lineage.child2, originX, x1, y1);
}
private void paintAntigenicLineage(Graphics2D g2, float hue, TreeLineages.Lineage lineage, double originX, double x, double y) {
double x1 = lineage.ax;
double y1 = lineage.ay;
if (lineage.child1 != null) {
paintAntigenicLineage(g2, hue, lineage.child1, originX, x1, y1);
paintAntigenicLineage(g2, hue, lineage.child2, originX, x1, y1);
} else {
paintTip(g2, lineage, originX, x1, y1);
}
// g2.setColor(Color.getHSBColor(hue, 1.0F, 1.0F));
// g2.setColor(new Color(0.0F, 0.0F, 0.0F, 0.1F));
// g2.setColor(COLOUR_SCHEME[lineage.state]);
g2.setColor(new Color(COLOUR_SCHEME[lineage.state].getRed(), COLOUR_SCHEME[lineage.state].getGreen(), COLOUR_SCHEME[lineage.state].getBlue(), 32));
float thickness = 0.25F;
// thickness = 2.5F * (Math.min((float)lineage.tipCount, 10.0F) / 10.0F);
Shape curve = new Line2D.Double(transformX(originX + x), transformY(y), transformX(originX + x1), transformY(y1));
g2.draw(curve);
}
private void paintTip(Graphics2D g2, TreeLineages.Lineage lineage, double originX, double x, double y) {
Shape node = new Ellipse2D.Float((float)transformX(originX + x) - nodeWidth * 0.5F, (float)transformY(y) - nodeWidth * 0.5F, nodeWidth, nodeWidth);
// g2.setColor(Color.getHSBColor(((float)lineage.tipNumber) / (float)treeLineages.getMaxHeight(), 0.5F, 1.0F));
// g2.setColor(COLOUR_SCHEME[lineage.state]);
g2.setColor(new Color(COLOUR_SCHEME[lineage.state].getRed(), COLOUR_SCHEME[lineage.state].getGreen(), COLOUR_SCHEME[lineage.state].getBlue(), 32));
g2.fill(node);
// g2.setColor(Color.black);
// g2.draw(node);
}
private double transformX(double x) {
return (x * scaleX) + offsetX;
}
private double transformY(double y) {
return (y * scaleY) + offsetY;
}
private TreeLineages treeLineages;
private float xLabelOffset = 5.0F;
private float yLabelOffset = 5.0F;
private boolean isCalibrated = false;
private double scaleX = 1.0;
private double scaleY = 1.0;
private float nodeWidth = 2.0F;
private float topBorder = -450.0F;
private float bottomBorder = -350.0F;
private float borderWidth = 10.0F;
private float offsetX = 5.0F;
private float offsetY = 5.0F;
}