package statalign.postprocess.gui.treeviews; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.ImageIcon; import javax.swing.JToggleButton; import statalign.postprocess.plugins.TreeNode; /** * The graphical interface for showing the current tree. * @author Jonsson based on previous work by miklos, novak */ public class HorizontalCladogramTreeView extends TreeView { private static final long serialVersionUID = 1L; // Variables private int noOfSamples; // Functions @Override public JToggleButton getToolBarButton() { JToggleButton button = new JToggleButton( new ImageIcon(ClassLoader.getSystemResource("icons/align.png"))); button.setToolTipText("Tree visualized without branch lengths"); return button; } @Override public void newSample(TreeNode root) { super.newSample(root); noOfSamples++; } @Override public void beforeFirstSample(int noOfTaxa) { super.beforeFirstSample(noOfTaxa); root = null; this.repaint(); noOfSamples = 1; } /** It repaints the graphics */ @Override public void paintComponent(Graphics gr) { if (root == null) { return; } //System.out.println("Updating tree"); //String newick = owner.mcmc.tree.printedTree(); Graphics2D g = (Graphics2D) gr.create(); g.setBackground(Color.WHITE); g.setFont(new Font("MONOSPACED", Font.PLAIN, 12)); //g.setClip(0,0, panel.getWidth(), panel.getHeight()); g.clearRect(0, 0, getWidth(), getHeight()); g.setColor(Color.BLACK); //g.drawString(newick,100,100); //CTreeNode root = owner.output.getRoot(); if (root == null) { g.drawString("Waiting for 2 sample trees...", 30, 30); return; } root.countLeaves(); int maxSteps = root.maxSteps(); double stepWidth = (getWidth() * 0.8) / maxSteps; int bottom = (int) (getWidth() * 0.05) + (int) (stepWidth * maxSteps); drawHorzTree(g, root, bottom, stepWidth, (int) (0 + getHeight() * 0.05), (int) (getHeight() * 0.95), (int) (getWidth() * 0.05)); } private void drawHorzTree(Graphics g, TreeNode v, int bottom, double stepWidth, int y1, int y2, int x) { if (v.isLeaf()) { String s = ((v.name.trim()).length() > 20 ? v.name.substring(0, 15) + "..." : v.name); g.drawString(s, bottom + 5, (y1 + y2) / 2); g.drawLine(x - (int) stepWidth, (y1 + y2) / 2, bottom, (y1 + y2) / 2); } else { int nrChildren = v.children.size(); //divide the available vertical space between each child based on leafs to account for int[] ySplits = new int[nrChildren]; ySplits[nrChildren - 1] = y2; //use y2 as the bottom split for (int i = 0; i < nrChildren - 1; i++) { if (i == 0) { ySplits[i] = y1 + (y2 - y1) * v.children.get(i).leafCount / (v.leafCount); } else { ySplits[i] = ySplits[i - 1] + (y2 - y1) * v.children.get(i).leafCount / (v.leafCount); } } if (v.edgeLength > 0.0d) { g.drawLine(x, ySplits[0], x - (int) stepWidth, ySplits[0]); if (v.hasProperty("noOfOccurrences")) { Integer noOfOccurrences = (Integer) v.getProperty("noOfOccurrences"); String s = String.format("%.0f", noOfOccurrences * 100 / (double) noOfSamples); g.drawString(s, x + 5, ySplits[0] + 4); } } //place branch positions for the edge leading to each child int[] yBranches = new int[nrChildren]; for (int i = 0; i < nrChildren; i++) { if (i == 0) { yBranches[i] = (!v.children.get(i).isLeaf()) ? y1 + (ySplits[i] - y1) * v.children.get(i).children.get(0).leafCount / (v.children.get(i).leafCount) : y1 + (ySplits[i] - y1) / 2; } else { yBranches[i] = (!v.children.get(i).isLeaf()) ? ySplits[i - 1] + (ySplits[i] - ySplits[i - 1]) * v.children.get(i).children.get(0).leafCount / (v.children.get(i).leafCount) : ySplits[i - 1] + (ySplits[i] - ySplits[i - 1]) / 2; } } g.drawLine(x, yBranches[0], x, yBranches[nrChildren - 1]); for (int i = 0; i < nrChildren; i++) { if (i == 0) { drawHorzTree(g, v.children.get(i), bottom, stepWidth, y1, ySplits[i], x + (int) stepWidth); } else { drawHorzTree(g, v.children.get(i), bottom, stepWidth, ySplits[i - 1], ySplits[i], x + (int) stepWidth); } } } } /** Gives the minimum size of the component */ @Override public Dimension getMinimumSize() { return getPreferredSize(); } /** It gives the preferred size of the component */ @Override public Dimension getPreferredSize() { return new Dimension(0, noOfTaxa * 30); } }