/* * DiversityContinuousColourDecorator.java * * Copyright (C) 2006-2014 Andrew Rambaut * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package figtree.treeviewer.decorators; import jebl.evolution.graphs.Node; import jebl.evolution.trees.RootedTree; import jebl.evolution.trees.Tree; import jebl.util.Attributable; import java.awt.*; /** * This draws a continuous colour spectrum to show diversity across the tree. * * @author Andrew Rambaut * @version $Id$ * * $HeadURL$ * * $LastChangedBy$ * $LastChangedDate$ * $LastChangedRevision$ */ public class DiversityContinuousColourDecorator extends ColourDecorator { public DiversityContinuousColourDecorator() throws NumberFormatException { super(null); } public DiversityContinuousColourDecorator(String attribute, String settings) { super(attribute); setup(settings); } public void setup(String settings) { if (!settings.startsWith("{") || !settings.endsWith("}")) { throw new IllegalArgumentException("DiversityContinuousColourDecorator settings string not in correct format"); } String[] parts1 = settings.substring(1, settings.length() - 1).split("}[, ]+"); if (parts1.length != 2) { throw new IllegalArgumentException("DiversityContinuousColourDecorator settings string not in correct format"); } String[] parts2 = parts1[1].split("[, ]+"); if (parts2.length != 7) { throw new IllegalArgumentException("DiversityContinuousColourDecorator settings string not in correct format"); } try { hueLower = Float.parseFloat(parts2[0]); hueUpper = Float.parseFloat(parts2[1]); saturationLower = Float.parseFloat(parts2[2]); saturationUpper = Float.parseFloat(parts2[3]); brightnessLower = Float.parseFloat(parts2[4]); brightnessUpper = Float.parseFloat(parts2[5]); reverseHue = Boolean.parseBoolean(parts2[6]); } catch (NumberFormatException nfe) { throw new IllegalArgumentException("HSBContinuousColourDecorator settings string not in correct format"); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException("HSBContinuousColourDecorator settings string not in correct format"); } } public void setup(float hueUpper, float hueLower, float saturationUpper, float saturationLower, float brightnessUpper, float brightnessLower, boolean reverseHue) { this.hueUpper = hueUpper; this.hueLower = hueLower; this.saturationUpper = saturationUpper; this.saturationLower = saturationLower; this.brightnessUpper = brightnessUpper; this.brightnessLower = brightnessLower; this.reverseHue = reverseHue; } public void setTree(RootedTree tree) { this.tree = tree; double[] maxValue = { 0 }; traverseTree((RootedTree)tree, ((RootedTree) tree).getRootNode(), 0.0, 1.0, maxValue); for (Node node : tree.getNodes()) { double value = (Double)node.getAttribute("@hue"); node.setAttribute("@hue", value / maxValue[0]); } } private double traverseTree(RootedTree tree, Node node, double lower, double upper, double[] maxValue) { double value; if (tree.isExternal(node)) { value = (upper + lower) / 2; } else { value = 0.0; double range = upper - lower; int n = tree.getChildren(node).size(); double[] counts = new double[n]; int i = 0; double count = 0.0; for (Node child : tree.getChildren(node)) { double c = countTips(tree, child); count += c; counts[i] = c; i++; } double l = lower; i = 0; for (Node child : tree.getChildren(node)) { double prop = range * (counts[i] / count); double u = l + prop; value += traverseTree(tree, child, l, u, maxValue); l = u; i++; } value /= n; } // value *= tree.getLength(node); node.setAttribute("@hue", value); if (value > maxValue[0]) { maxValue[0] = value; } return value; } private double countTips(RootedTree tree, Node node) { double count; if (tree.isExternal(node)) { count = 1; } else { count = 0; for (Node child : tree.getChildren(node)) { count += countTips(tree, child); } } return count; } @Override public void setItem(Object item) { if (item instanceof Attributable) { hue = (Double)((Attributable) item).getAttribute("@hue"); paint = getColourForValue(item); } } @Override protected Color getColourForValue(Object value) { return Color.getHSBColor(getHue((float)hue), getSaturation((float)0.5), getBrightness((float)0.5)); } private float getHue(float value) { if (reverseHue) { return hueUpper - ((hueUpper - hueLower) * value); } return ((hueUpper - hueLower) * value) + hueLower; } private float getSaturation(float value) { return ((saturationUpper - saturationLower) * value) + saturationLower; } private float getBrightness(float value) { return ((brightnessUpper - brightnessLower) * value) + brightnessLower; } public float getHueUpper() { return hueUpper; } public void setHueUpper(float hueUpper) { this.hueUpper = hueUpper; } public float getHueLower() { return hueLower; } public void setHueLower(float hueLower) { this.hueLower = hueLower; } public float getSaturationUpper() { return saturationUpper; } public void setSaturationUpper(float saturationUpper) { this.saturationUpper = saturationUpper; } public float getSaturationLower() { return saturationLower; } public void setSaturationLower(float saturationLower) { this.saturationLower = saturationLower; } public float getBrightnessUpper() { return brightnessUpper; } public void setBrightnessUpper(float brightnessUpper) { this.brightnessUpper = brightnessUpper; } public float getBrightnessLower() { return brightnessLower; } public void setBrightnessLower(float brightnessLower) { this.brightnessLower = brightnessLower; } public boolean isReverseHue() { return reverseHue; } public void setReverseHue(boolean reverseHue) { this.reverseHue = reverseHue; } /** * Create a string representation suitable for writing to a text file * @return the string */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append(hueLower); sb.append(","); sb.append(hueUpper); sb.append(","); sb.append(saturationLower); sb.append(","); sb.append(saturationUpper); sb.append(","); sb.append(brightnessLower); sb.append(","); sb.append(brightnessUpper); sb.append(","); sb.append(reverseHue); sb.append("}"); return sb.toString(); } private RootedTree tree; private double hue; private float hueUpper = 1.0F; private float hueLower = 0.0F; private float saturationUpper = 0.6F; private float saturationLower = 0.6F; private float brightnessUpper = 0.8F; private float brightnessLower = 0.4F; private boolean reverseHue = false; }