/* * 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; }