package viz;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.File;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.util.Arrays;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import viz.DensiTree.LineColorMode;
import viz.DensiTree.ViewMode;
import viz.graphics.ArcBranchDrawer;
import viz.graphics.BufferedImageBounded;
import viz.graphics.BufferedImageF;
import viz.graphics.SVGTreeDrawer;
import viz.graphics.SteepArcBranchDrawer;
import viz.graphics.TreeDrawer;
/**
* Class for drawing the tree set It uses buffer to do the actual tree
* drawing, then allows inspection of the image through scrolling.
*/
public class TreeSetPanel extends JPanel implements MouseListener, Printable, MouseMotionListener {
private static final long serialVersionUID = 1L;
DensiTree m_dt;
/** number of threads used for drawing **/
int m_nDrawThreads = 2;
/** the set of actual threads **/
Thread[] m_drawThread;
/** image in memory containing tree set drawing **/
private BufferedImageF m_image;
private BufferedImage m_selectedImage;
/** constructor **/
public TreeSetPanel(DensiTree dt) {
m_dt = dt;
addMouseListener(this);
addMouseMotionListener(this);
m_drawThread = new Thread[2];
}
/** stop drawing thread, if any is running */
void stopDrawThreads() {
try {
for (int i = 0; i < m_nDrawThreads; i++) {
if (m_drawThread[i] != null) {
((DrawThread) m_drawThread[i]).m_bStop = true;
}
}
for (int i = 0; i < m_nDrawThreads; i++) {
if (m_drawThread[i] != null) {
m_drawThread[i].join();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} // stopDrawThreads
/** reset image so that it will be redrawn on the next occasion */
public void clearImage() {
m_image = null;
stopDrawThreads();
}
/** return true if any drawing thread is active **/
boolean isDrawing() {
for (int i = 0; i < m_nDrawThreads; i++) {
if (m_drawThread[i] != null) {
return true;
}
}
return false;
}
/** thread for drawing (part of the) tree set **/
class DrawThread extends Thread {
public boolean m_bStop = false;
int m_nFrom = 0;
int m_nTo = 1;
int m_nEvery = 1;
int m_iTreeTopology = -1;
TreeDrawer m_treeDrawer;
public DrawThread(String str, int nFrom, int nTo, int nEvery, int iTreeTopology, TreeDrawer treeDrawer) {
super(str);
m_treeDrawer = treeDrawer;
m_nFrom = nFrom;
m_nTo = nTo;
m_nEvery = nEvery;
m_iTreeTopology = iTreeTopology;
} // c'tor
public DrawThread(String str, int nFrom, int nTo, int nEvery, TreeDrawer treeDrawer) {
super(str);
m_nFrom = nFrom;
m_nTo = nTo;
m_nEvery = nEvery;
m_dt.m_treeDrawer = treeDrawer;
} // c'tor
@Override
public void run() {
if (m_image == null) {
return;
}
Graphics2D g = m_image.createGraphics();
try {
g.setClip(0, 0, m_image.getWidth(), m_image.getHeight());
m_image.scale(g, m_dt.m_fScale, m_dt.m_fScale);
float fScaleX = m_dt.m_fScaleX;
float fScaleY = m_dt.m_fScaleY;
if (m_dt.m_bUseLogScale) {
if (m_treeDrawer.m_bRootAtTop) {
fScaleY *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
} else {
fScaleX *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
}
}
// draw all individual trees if necessary
if (m_dt.m_bViewAllTrees && m_nTo >= m_nEvery) {
int iStart = m_nTo - m_nEvery;
float fAlpha = Math.min(1.0f, 20.0f / iStart * m_dt.m_fTreeIntensity);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fAlpha));
Stroke stroke = new BasicStroke(m_dt.m_nTreeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
m_dt.m_treeDrawer.setJitter(m_dt.m_nJitter);
for (int i = iStart; i >= m_nFrom; i -= m_nEvery) {
if (m_bStop) {
return;
}
if (m_iTreeTopology < 0 || m_iTreeTopology == m_dt.m_nTopologyByPopularity[i]) {
// switch (m_dt.m_nTopologyByPopularity[i]) {
// case 0:
// g.setColor(m_dt.m_color[0]);
// break;
// case 1:
// g.setColor(m_dt.m_color[1]);
// break;
// case 2:
// g.setColor(m_dt.m_color[2]);
// break;
// default:
// g.setColor(m_dt.m_color[3]);
// }
m_dt.m_treeDrawer.draw(i, m_dt.m_fLinesX, m_dt.m_fLinesY, m_dt.m_fLineWidth, m_dt.m_fTopLineWidth, m_dt.m_nLineColor, g, fScaleX,
fScaleY);
if (i % 100 == 0) {
System.err.print('.');
m_dt.m_jStatusBar.setText("Drawing tree " + i);
}
}
}
}
// draw consensus trees if necessary
if (m_dt.m_bViewCTrees) {
m_dt.m_jStatusBar.setText("Drawing consensus trees");
// g.setColor(m_dt.m_color[DensiTree.CONSCOLOR]);
Stroke stroke = new BasicStroke(m_dt.m_nCTreeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
g.setClip(0, 0, getWidth(), getHeight());
m_dt.m_treeDrawer.setJitter(0);
for (int i = m_nFrom; i < m_dt.m_nTopologies; i += m_nEvery) {
if (m_bStop) {
return;
}
// if (m_dt.m_bViewMultiColor) {
// g.setColor(m_dt.m_color[9 + (i % (m_dt.m_color.length - 9))]); }
if (m_iTreeTopology < 0 || m_iTreeTopology == i) {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
Math.min(1.0f, 0.5f * m_dt.m_fCTreeIntensity * m_dt.m_fTreeWeight[i])));
m_dt.m_treeDrawer.draw(i, m_dt.m_fCLinesX, m_dt.m_fCLinesY, m_dt.m_fCLineWidth, m_dt.m_fTopCLineWidth, m_dt.m_nCLineColor, g,
fScaleX, fScaleY);
if (i % 100 == 0) {
System.err.print('x');
m_dt.m_jStatusBar.setText("Drawing consensus tree " + i);
}
}
}
}
if (m_dt.m_viewMode == ViewMode.DRAW) {
m_drawThread[m_nFrom] = null;
if (!isDrawing()) {
if (m_dt.m_bShowRootCanalTopology) {
drawRootCanalTree(g);
}
double fEntropy = calcImageEntropy();
m_dt.m_jStatusBar.setText("Done Drawing trees ");
System.out.println("Entropy(x100): " + fEntropy + " Mean cumulative width: " + m_dt.m_w);
}
repaint();
} else {
DecimalFormat df = new DecimalFormat("##.##");
double fSum = 0;
for (int i = 0; i <= m_dt.m_iAnimateTree; i++) {
fSum += m_dt.m_fTreeWeight[i];
}
m_dt.m_jStatusBar.setText("Consensus tree " + (m_dt.m_iAnimateTree + 1) + " out of " + m_dt.m_nTopologies
+ " covering " + df.format((m_dt.m_fTreeWeight[m_dt.m_iAnimateTree] * 100)) + "% of trees "
+ df.format(fSum * 100) + "% cumultive trees");
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("DRAWING ERROR -- IGNORED");
}
m_drawThread[m_nFrom] = null;
}
} // DrawThread
void drawLabelsSVG(Node node, StringBuffer buf) {
if (node.isLeaf()) {
Color color = null;
if (m_dt.m_bSelection[node.m_iLabel]) {
color = m_dt.m_color[DensiTree.LABELCOLOR];
} else {
color = Color.GRAY;
}
int x = 0;
int y = 0;
if (m_dt.m_treeDrawer.m_bRootAtTop) {
x = (int) (node.m_fPosX * m_dt.m_fScaleX /* m_dt.m_fScale */) + 10;
y = m_dt.getPosY((node.m_fPosY - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale);
} else {
y = (int) (node.m_fPosX * m_dt.m_fScaleY/* m_dt.m_fScale */);
x = m_dt.getPosX((node.m_fPosY - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale);
}
buf.append("<text x='" + x + "' y='" + y + "' " + "font-family='" + m_dt.m_font.getFamily() + "' "
+ "font-size='" + m_dt.m_font.getSize() + "pt' " + "font-style='"
+ (m_dt.m_font.isBold() ? "oblique" : "") + (m_dt.m_font.isItalic() ? "italic" : "") + "' "
+ "stroke='rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ")' "
+ ">" + m_dt.m_sLabels.elementAt(node.m_iLabel) + "</text>\n");
} else {
drawLabelsSVG(node.m_left, buf);
drawLabelsSVG(node.m_right, buf);
}
} // m_dt.drawLabelsSVG
void toSVG(String sFileName) {
try {
if (m_dt.m_font == null) {
m_dt.m_font = new Font("Monospaced", Font.PLAIN, 10);
}
StringBuffer buf = new StringBuffer();
SVGTreeDrawer treeDrawer = new SVGTreeDrawer(buf);
TreeDrawer t = m_dt.m_treeDrawer;
treeDrawer.LINE_WIDTH_SCALE = m_dt.m_treeDrawer.LINE_WIDTH_SCALE;
treeDrawer.m_bRootAtTop = m_dt.m_treeDrawer.m_bRootAtTop;
treeDrawer.m_bViewBlockTree = m_dt.m_treeDrawer.m_bViewBlockTree;
if (m_dt.m_treeDrawer.getBranchDrawer() instanceof SteepArcBranchDrawer) {
treeDrawer.m_bViewBlockTree = false;
JOptionPane.showMessageDialog(this, "Steep arcs not implemented yet for SVG export, using straigh lines instead");
}
if (m_dt.m_treeDrawer.getBranchDrawer() instanceof ArcBranchDrawer) {
treeDrawer.m_branchStyle = 2;
}
DrawThread thread = new DrawThread("draw thread", 0, m_dt.m_trees.length, 1, treeDrawer);
thread.run();
drawLabelsSVG(m_dt.m_trees[0], buf);
m_dt.m_gridDrawer.drawHeightInfoSVG(buf);
PrintStream out = new PrintStream(sFileName);
out.println("<?xml version='1.0'?>\n" + "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n"
+ " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n"
+ "<svg xmlns='http://www.w3.org/2000/svg' version='1.1'\n" + " width='" + getWidth()
+ "' height='" + getHeight() + "' viewBox='0 0 " + getWidth() + " " + getHeight() + "'>\n"
+ "<rect fill='#fff' width='" + getWidth() + "' height='" + getHeight() + "'/>");
out.println(buf.toString());
out.println("</svg>");
// restore original tree drawer
m_dt.m_treeDrawer = t;
} catch (Exception e) {
e.printStackTrace();
}
} // toSVG
double calcImageEntropy() throws Exception {
if (m_image == null) {
return 0;
}
Thread.sleep(100);
int[] nAlpha = new int[256];
try {
for (int i = 0; i < m_image.getWidth() - m_dt.m_nLabelWidth; i++) {
for (int j = 0; j < m_image.getHeight(); j++) {
int x = m_image.getRGB(i, j);
int y = ((x & 0xFF) + ((x & 0xFF00) >> 8) + ((x & 0xFF0000) >> 16)) / 3;
// System.out.print(Integer.toHexString(x)+" " +
// Integer.toHexString(y) + " ");
nAlpha[y]++;
}
// System.out.println();
}
} catch (Exception e) {
return 0.0;
}
double fQ = 0;
for (int i = 1; i < 255; i++) {
fQ -= nAlpha[i] * Math.log(i / 255.0);
}
return 100.0 * fQ / ((m_image.getWidth() - m_dt.m_nLabelWidth) * m_image.getHeight());
} // calcImageEntropy
/**
* Updates the screen contents.
*
* @param g
* the drawing surface.
*/
@Override
public void paintComponent(Graphics g) {
m_dt.a_undo.setEnabled(m_dt.m_doActions.size() > 0 && m_dt.m_iUndo > 1);
m_dt.a_redo.setEnabled(m_dt.m_iUndo < m_dt.m_doActions.size());
g.setFont(m_dt.m_font);
switch (m_dt.m_viewMode) {
case DRAW:
drawTreeSet((Graphics2D) g);
break;
case ANIMATE:
drawFrame(g);
m_dt.m_gridDrawer.paintHeightInfo(g);
try {
Thread.sleep(m_dt.m_nAnimationDelay);
} catch (Exception ex) {
// ignore
}
m_dt.m_iAnimateTree = (m_dt.m_iAnimateTree + 1) % m_dt.m_nTopologies;
repaint();
return;
case BROWSE:
drawFrame(g);
m_dt.m_gridDrawer.paintHeightInfo(g);
m_dt.setDefaultCursor();
//this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
return;
}
if (m_dt.m_sOutputFile != null && !isDrawing()) {
// wait a second for the drawing to be finished
try {
Graphics2D g2 = m_image.createGraphics();
m_dt.drawLabels(m_dt.m_trees[0], g2);
ImageIO.write(m_image.m_localImage, "png", new File(m_dt.m_sOutputFile));
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0) {
viewEditTree(g);
}
if (m_dt.m_bViewClades && m_dt.m_bCladesReady && (m_dt.m_Xmode == 1 || m_dt.m_Xmode == 2)) {
m_dt.m_cladeDrawer.viewClades(g);
}
if (m_dt.m_showLegend &&
(m_dt.m_lineColorMode == LineColorMode.BY_METADATA_PATTERN ||
m_dt.m_lineColorMode == LineColorMode.COLOR_BY_METADATA_TAG)) {
Font font = new Font(g.getFont().getName(), Font.BOLD, 14);
g.setFont(font);
// for (int k = 0; k < m_dt.m_colorMetaDataCategories.size(); k++) {
// g.setColor(m_dt.m_color[9 + k % (m_dt.m_color.length - 9)]);
// g.drawString(m_dt.m_colorMetaDataCategories.get(k), 10, k*15+15);
// }
int k = 0;
for (String s : m_dt.m_colorMetaDataCategories.keySet()) {
g.setColor(m_dt.m_color[9 + m_dt.m_colorMetaDataCategories.get(s) % (m_dt.m_color.length - 9)]);
g.drawString(s, 10, k*15+15);
k++;
}
}
if (m_selectedImage != null) {
int w = m_selectedImage.getWidth();
int h = m_selectedImage.getHeight();
g.drawImage(m_selectedImage, 0, 0, w, h, 0, 0, w, h, null);
}
}
RotationPoint[] m_rotationPoints = null;
/** draw tree that allows editing order **/
void viewEditTree(Graphics g) {
float fScaleX = m_dt.m_fScaleX;
float fScaleY = m_dt.m_fScaleY;
if (m_dt.m_bUseLogScale) {
if (m_dt.m_treeDrawer.m_bRootAtTop) {
fScaleY *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
} else {
fScaleX *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
}
}
int x = 0;
int y = 0;
int x0 = 0;
int y0 = 0;
int x1 = 0;
int y1 = 0;
g.setColor(Color.BLACK);
Stroke stroke = new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
((Graphics2D) g).setStroke(stroke);
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
int h = m_dt.m_rotate.getHeight(null);
int w = m_dt.m_rotate.getWidth(null);
boolean bUpdatePoints = false;
if (m_rotationPoints == null) {
m_rotationPoints = new RotationPoint[m_dt.m_fRLinesX[0].length / 4];
bUpdatePoints = true;
}
for (int i = 1; i < m_dt.m_fRLinesX[0].length - 2; i += 4) {
if (m_dt.m_treeDrawer.m_bRootAtTop) {
x = (int) ((m_dt.m_fRLinesX[0][i] + m_dt.m_fRLinesX[0][i + 1]) * fScaleX / 2.0f);
y = (int) ((m_dt.m_fCLinesY[0][i] + m_dt.m_fRLinesY[0][i + 1]) * fScaleY / 2.0f);
x0 = (int) ((m_dt.m_fRLinesX[0][i - 1]) * fScaleX);
y0 = (int) ((m_dt.m_fRLinesY[0][i - 1]) * fScaleY);
x1 = (int) ((m_dt.m_fRLinesX[0][i + 2]) * fScaleX);
y1 = (int) ((m_dt.m_fRLinesY[0][i + 2]) * fScaleY);
} else {
x = (int) ((m_dt.m_fRLinesY[0][i] + m_dt.m_fRLinesY[0][i + 1]) * fScaleX / 2.0f);
y = (int) ((m_dt.m_fRLinesX[0][i] + m_dt.m_fRLinesX[0][i + 1]) * fScaleY / 2.0f);
x0 = (int) ((m_dt.m_fRLinesY[0][i - 1]) * fScaleX);
y0 = (int) ((m_dt.m_fRLinesX[0][i - 1]) * fScaleY);
x1 = (int) ((m_dt.m_fRLinesY[0][i + 2]) * fScaleX);
y1 = (int) ((m_dt.m_fRLinesX[0][i + 2]) * fScaleY);
}
if (bUpdatePoints) {
m_rotationPoints[i / 4] = new RotationPoint(x, y);
}
g.drawLine(x, y, x0, y0);
g.drawLine(x, y, x1, y1);
g.drawImage(m_dt.m_rotate, x - w / 2, y - h / 2, x + h / 2, y + w / 2, 0, 0, h, w, null);
}
} // viewEditTree
/** draw complete set of trees **/
void drawTreeSet(Graphics2D g) {
Color oldBackground = g.getBackground();
g.setBackground(m_dt.m_color[DensiTree.BGCOLOR]);
Rectangle r = g.getClipBounds();
g.clearRect(r.x, r.y, r.width, r.height);
g.setBackground(oldBackground);
g.setClip(r.x, r.y, r.width, r.height);
if (m_dt.m_trees == null || m_dt.m_fCLinesY == null || m_dt.m_bInitializing) {
// nothing to see
return;
}
synchronized (this) {
m_dt.setWaitCursor();
//this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
if (m_image == null) {
System.err.println("Setting up new image");
if (!m_dt.m_bShowBounds) {
m_image = new BufferedImageF((int) (getWidth() * m_dt.m_fScale), (int) (getHeight() * m_dt.m_fScale));
} else {
m_image = new BufferedImageBounded((int) (getWidth() * m_dt.m_fScale),
(int) (getHeight() * m_dt.m_fScale));
}
m_dt.m_treeDrawer.setImage(m_image);
Graphics2D g2 = m_image.createGraphics();
m_image.init(g2, m_dt.m_color[DensiTree.BGCOLOR], m_dt.m_bgImage, m_dt.m_fBGImageBox, m_dt.m_nLabelWidth, m_dt.m_fMinLong, m_dt.m_fMaxLong,
m_dt.m_fMinLat, m_dt.m_fMaxLat);
//m_dt.drawLabels(m_dt.m_trees[0], g2);
m_dt.m_gridDrawer.paintHeightInfo(g2);
//m_dt.drawLabels(m_dt.m_trees[0], g2);
if (m_image == null) {
return;
} else {
m_image.SyncIntToRGBImage();
}
int nDrawThreads = Math.min(m_nDrawThreads, m_dt.m_trees.length);
for (int i = 0; i < nDrawThreads; i++) {
m_drawThread[i] = new DrawThread("draw thread", i, m_dt.m_trees.length + i, nDrawThreads,
m_dt.m_treeDrawer);
m_drawThread[i].start();
}
if (m_dt.m_bShowRootCanalTopology) {
drawRootCanalTree(g);
}
}
}
;
if (m_image == null) {
return;
}
m_image.drawImage(g, this);
if (m_dt.m_nSelectedRect != null) {
if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0) { // || m_dt.m_bViewClades) {
int h = m_dt.m_rotate.getHeight(null);
int w = m_dt.m_rotate.getWidth(null);
int x = m_dt.m_nSelectedRect.x + (m_dt.m_treeDrawer.m_bRootAtTop ? m_dt.m_nSelectedRect.width : 0);
int y = m_dt.m_nSelectedRect.y + (m_dt.m_treeDrawer.m_bRootAtTop ? 0 : m_dt.m_nSelectedRect.height);
g.drawImage(m_dt.m_rotate, x - w / 2, y - h / 2, x + h / 2, y + w / 2, 0, 0, h, w, null);
} else {
g.drawRect(m_dt.m_nSelectedRect.x + Math.min(m_dt.m_nSelectedRect.width, 0),
m_dt.m_nSelectedRect.y + Math.min(m_dt.m_nSelectedRect.height, 0),
(Math.abs(m_dt.m_nSelectedRect.width)), (Math.abs(m_dt.m_nSelectedRect.height)));
}
}
// need this here so that the screen is updated when selection of
// taxa changes
m_dt.drawLabels(m_dt.m_trees[0], g);
if (m_dt.m_bDrawGeo && m_dt.m_fLatitude.size() > 0) {
g.setColor(m_dt.m_color[DensiTree.GEOCOLOR]);
Stroke stroke = new BasicStroke(m_dt.m_nGeoWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
m_dt.drawGeo(m_dt.m_cTrees[0], g);
}
// ((Graphics2D) g).scale(m_dt.m_fScale, m_dt.m_fScale);
if (isDrawing()) {
try {
Thread.sleep(m_dt.m_nAnimationDelay);
} catch (Exception ex) {
// ignore
}
repaint();
if (m_dt.m_bRecord) {
try {
System.err.println(" writing /tmp/frame" + m_dt.m_nFrameNr + ".jpg " + isDrawing());
ImageIO.write(m_image.m_localImage, "jpg", new File("/tmp/frame" + m_dt.m_nFrameNr + ".jpg"));
m_dt.m_nFrameNr++;
} catch (Exception ex) {
// ignore
}
}
} else {
m_dt.setDefaultCursor();
//this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
if (m_dt.m_bRecord) {
try {
System.err.println(" writing /tmp/frame" + m_dt.m_nFrameNr + ".jpg " + isDrawing());
ImageIO.write(m_image.m_localImage, "jpg", new File("/tmp/frame" + m_dt.m_nFrameNr + ".jpg"));
m_dt.m_nFrameNr++;
} catch (Exception ex) {
// ignore
}
}
m_dt.m_bRecord = false;
}
} // drawTreeSet
void drawRootCanalTree(Graphics2D g) {
float fScaleX = m_dt.m_fScaleX;
float fScaleY = m_dt.m_fScaleY;
if (m_dt.m_bUseLogScale) {
if (m_dt.m_treeDrawer.m_bRootAtTop) {
fScaleY *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
} else {
fScaleX *= m_dt.m_fHeight / (float) Math.log(m_dt.m_fHeight + 1.0);
}
}
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
Stroke stroke = new BasicStroke(m_dt.m_nCTreeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
g.setColor(m_dt.m_color[DensiTree.ROOTCANALCOLOR]);
m_dt.m_treeDrawer.draw(0, m_dt.m_fRLinesX, m_dt.m_fRLinesY, m_dt.m_fRLineWidth,
m_dt.m_fRTopLineWidth, m_dt.m_nRLineColor, g, fScaleX, fScaleY);
}
/** draw new frame in animation or browse action **/
void drawFrame(Graphics g) {
Color oldBackground = ((Graphics2D) g).getBackground();
((Graphics2D) g).setBackground(m_dt.m_color[DensiTree.BGCOLOR]);
Rectangle r = g.getClipBounds();
g.clearRect(r.x, r.y, r.width, r.height);
((Graphics2D) g).setBackground(oldBackground);
g.setClip(r.x, r.y, r.width, r.height);
if (m_dt.m_trees == null || m_dt.m_fCLinesY == null || m_dt.m_bInitializing) {
// nothing to see
return;
}
m_dt.setWaitCursor();
//this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
if (m_image == null || m_dt.m_bAnimateOverwrite || m_dt.m_iAnimateTree == 0) { // ||
// m_viewMode
// ==
// ViewMode.BROWSE)
// {
if (!m_dt.m_bShowBounds) {
m_image = new BufferedImageF((int) (getWidth() * m_dt.m_fScale), (int) (getHeight() * m_dt.m_fScale));
} else {
m_image = new BufferedImageBounded((int) (getWidth() * m_dt.m_fScale), (int) (getHeight() * m_dt.m_fScale));
}
m_dt.m_treeDrawer.setImage(m_image);
Graphics2D g2 = m_image.createGraphics();
// g2.setBackground(m_dt.m_color[DensiTree.BGCOLOR]);
// g2.clearRect(0, 0, m_image.getWidth(), m_image.getHeight());
m_image.init(g2, m_dt.m_color[DensiTree.BGCOLOR], m_dt.m_bgImage, m_dt.m_fBGImageBox, m_dt.m_nLabelWidth, m_dt.m_fMinLong, m_dt.m_fMaxLong,
m_dt.m_fMinLat, m_dt.m_fMaxLat);
// drawBGImage(g2);
// m_image.drawImage(g2 , this);
if (m_dt.m_bDrawGeo && m_dt.m_fLatitude.size() > 0) {
g2.setColor(m_dt.m_color[DensiTree.GEOCOLOR]);
Stroke stroke = new BasicStroke(m_dt.m_nGeoWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g2.setStroke(stroke);
m_dt.drawGeo(m_dt.m_cTrees[0], g2);
}
m_dt.m_gridDrawer.paintHeightInfo(g2);
m_dt.drawLabels(m_dt.m_trees[0], g2);
m_image.SyncIntToRGBImage();
}
for (int i = 0; i < m_nDrawThreads; i++) {
m_drawThread[i] = new DrawThread("draw thread", i, m_dt.m_trees.length + i, m_nDrawThreads, m_dt.m_iAnimateTree,
m_dt.m_treeDrawer);
m_drawThread[i].start();
}
while (isDrawing()) {
try {
Thread.sleep(m_dt.m_nAnimationDelay);
} catch (Exception ex) {
// ignore
}
m_image.drawImage(g, this);
System.err.print("X");
}
m_image.drawImage(g, this);
m_dt.setDefaultCursor();
//this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
} // animate
/**
* implementation of Printable, used for printing
*
* @see Printable
*/
@Override
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return (NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
float fHeight = (float) pageFormat.getImageableHeight();
float fWidth = (float) pageFormat.getImageableWidth();
float fScaleX = m_dt.m_fScaleX;
m_dt.m_fScaleX = fWidth / m_dt.m_sLabels.size();
float fScaleY = m_dt.m_fScaleY;
m_dt.m_fScaleY = (fHeight - 10) / m_dt.m_fHeight;
// Turn off float buffering
paint(g2d);
m_dt.m_fScaleX = fScaleX;
m_dt.m_fScaleY = fScaleY;
// Turn float buffering back on
return (PAGE_EXISTS);
}
} // print
@Override
public void mouseClicked(MouseEvent e) {
// if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0) {
// if (m_rotationPoints != null) {
// if (e.getButton() == MouseEvent.BUTTON1) {
// for (int i = 0; i < m_rotationPoints.length; i++) {
// if (m_rotationPoints[i].intersects(e.getX(), e.getY())) {
// rotateAround(i);
// // repaint();
// return;
// }
// }
// }
// }
// } else {
// Rectangle r = new Rectangle(e.getPoint(), new Dimension(1, 1));
// if (e.getButton() == MouseEvent.BUTTON1) {
// if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
// toggleSelection(r);
// } else if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
// addToSelection(r);
// } else {
// clearSelection();
// addToSelection(r);
// }
// repaint();
// }
// }
}
/** remove all labels from selection **/
void clearSelection() {
for (int i = 0; i < m_dt.m_bSelection.length; i++) {
if (m_dt.m_bSelection[i]) {
m_dt.m_bSelection[i] = false;
m_dt.m_bSelectionChanged = true;
}
}
m_dt.m_cladeSelection.clear();
m_dt.resetCladeSelection();
}
/**
* remove labels overlapping rectangle r currently in selection and
* replace by ones not selected but overlapping with r
**/
void toggleSelection(Rectangle r) {
float f = m_dt.m_fScale;
m_dt.m_fScale = 1;
r.x = (int) (r.x / m_dt.m_fScale);
r.y = (int) (r.y / m_dt.m_fScale);
r.width = 1 + (int) (r.width / m_dt.m_fScale);
r.height = 1 + (int) (r.height / m_dt.m_fScale);
for (int i = 0; i < m_dt.m_bSelection.length; i++) {
if (m_dt.m_bLabelRectangle[i].intersects(r)) {
m_dt.m_bSelection[i] = !m_dt.m_bSelection[i];
m_dt.m_bSelectionChanged = true;
}
}
if (m_rotationPoints != null) {
r.x += 5;
r.y += 5;
Rectangle rotationPoint = new Rectangle(10, 10);
for (int i = 0; i < m_rotationPoints.length; i++) {
rotationPoint.x = m_rotationPoints[i].m_nX;
rotationPoint.y = m_rotationPoints[i].m_nY;
if (r.intersects(rotationPoint)) {
if (m_dt.m_cladeSelection.contains(i)) {
m_dt.m_cladeSelection.remove(i);
} else {
m_dt.m_cladeSelection.add(i);
}
}
}
m_dt.resetCladeSelection();
}
m_dt.m_fScale = f;
}
/** add labels overlapping rectangle r to selection **/
void addToSelection(Rectangle r) {
float f = m_dt.m_fScale;
m_dt.m_fScale = 1;
r.x = (int) (r.x / m_dt.m_fScale);
r.y = (int) (r.y / m_dt.m_fScale);
r.width = 1 + (int) (r.width / m_dt.m_fScale);
r.height = 1 + (int) (r.height / m_dt.m_fScale);
for (int i = 0; i < m_dt.m_bSelection.length; i++) {
if (m_dt.m_bLabelRectangle[i].intersects(r)
|| (m_dt.m_bGeoRectangle[i] != null && m_dt.m_bGeoRectangle[i].intersects(r))) {
if (!m_dt.m_bSelection[i] && m_dt.m_cladeWeight.get(i) >= m_dt.m_smallestCladeSupport) {
m_dt.m_bSelection[i] = true;
m_dt.m_bSelectionChanged = true;
}
}
}
if (m_rotationPoints != null) {
r.x += 5;
r.y += 5;
Rectangle rotationPoint = new Rectangle(10, 10);
for (int i = 0; i < m_rotationPoints.length; i++) {
rotationPoint.x = m_rotationPoints[i].m_nX;
rotationPoint.y = m_rotationPoints[i].m_nY;
if (r.intersects(rotationPoint) && m_dt.m_cladeWeight.get(i) >= m_dt.m_smallestCladeSupport) {
m_dt.m_cladeSelection.add(i);
}
}
System.err.println(Arrays.toString(m_dt.m_cladeSelection.toArray(new Integer[0])));
m_dt.resetCladeSelection();
}
m_dt.m_fScale = f;
System.out.print("selected: ");
for (int i = 0; i < m_dt.m_bSelection.length; i++) {
if (m_dt.m_bSelection[i]) {
System.out.println(m_dt.m_sLabels.get(i) + " ");
}
}
System.out.println();
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
boolean m_bIsMoving = false;
boolean m_bIsDragging = false;
@Override
public void mousePressed(MouseEvent e) {
m_bIsDragging = false;
m_dt.m_nSelectedRect = new Rectangle(e.getPoint(), new Dimension(1, 1));
if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0) { // && e.getButton() != MouseEvent.BUTTON1) {
if (m_rotationPoints != null) {
for (int i = 0; i < m_rotationPoints.length; i++) {
if (m_rotationPoints[i].intersects(e.getPoint().x, e.getPoint().y)) {
m_bIsMoving = true;
m_dt.m_nSelectedRect = new Rectangle(e.getPoint(), new Dimension(1, 1));
return;
}
}
m_dt.m_nSelectedRect = null;
repaint();
}
} else if (m_dt.m_bViewClades && (m_dt.m_Xmode == 1 || m_dt.m_Xmode == 2)) { // && e.getButton() != MouseEvent.BUTTON1) {
// m_dt.m_nSelectedRect = new Rectangle(e.getPoint(), new Dimension(1, 1));
if (m_rotationPoints != null) {
for (int i = 0/* m_dt.m_sLabels.size() */; i < m_rotationPoints.length; i++) {
if (m_rotationPoints[i].intersects(e.getPoint().x, e.getPoint().y) &&
m_dt.m_cladeSelection.contains(i)) {
m_bIsMoving = true;
return;
}
}
//m_dt.m_nSelectedRect = null;
}
repaint();
// } else {
// if (!m_dt.m_bViewEditTree && e.getButton() == MouseEvent.BUTTON1) {
// m_dt.m_nSelectedRect = new Rectangle(e.getPoint(), new Dimension(1, 1));
// }
}
}
/** update selection when mouse is released **/
@Override
public void mouseReleased(MouseEvent e) {
if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0 && e.getButton() == MouseEvent.BUTTON1 && !m_bIsDragging) {
if (m_rotationPoints != null) {
for (int i = 0; i < m_rotationPoints.length; i++) {
if (m_rotationPoints[i].intersects(e.getX(), e.getY())) {
m_dt.rotateAround(i);
m_dt.m_nSelectedRect = null;
// repaint();
return;
}
}
}
} else if (m_dt.m_nSelectedRect != null) {
//if (e.getButton() == MouseEvent.BUTTON1) {
if (!m_bIsMoving) {
// normalize rectangle
if (m_dt.m_nSelectedRect.width < 0) {
m_dt.m_nSelectedRect.x += m_dt.m_nSelectedRect.width;
m_dt.m_nSelectedRect.width = -m_dt.m_nSelectedRect.width;
}
if (m_dt.m_nSelectedRect.height < 0) {
m_dt.m_nSelectedRect.y += m_dt.m_nSelectedRect.height;
m_dt.m_nSelectedRect.height = -m_dt.m_nSelectedRect.height;
}
if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) {
toggleSelection(m_dt.m_nSelectedRect);
} else if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
addToSelection(m_dt.m_nSelectedRect);
} else {
clearSelection();
addToSelection(m_dt.m_nSelectedRect);
}
m_dt.m_nSelectedRect = null;
repaint();
} else { // right click
m_bIsMoving = false;
m_bIsDragging = false;
if (m_rotationPoints != null) {
if (m_dt.m_bViewEditTree && m_dt.m_Xmode == 0) {
for (int i = 0; i < m_rotationPoints.length; i++) {
if (m_rotationPoints[i].intersects(m_dt.m_nSelectedRect.x, m_dt.m_nSelectedRect.y)) {
m_dt.moveRotationPoint(i, m_dt.m_sLabels.size()
* (m_dt.m_treeDrawer.m_bRootAtTop ? (float) m_dt.m_nSelectedRect.width / getWidth()
: (float) m_dt.m_nSelectedRect.height / getHeight()));
m_dt.m_nSelectedRect = null;
repaint();
return;
}
}
} else if (m_dt.m_bViewClades && (m_dt.m_Xmode == 1 || m_dt.m_Xmode == 2)) {
double dF = m_dt.m_sLabels.size() * (m_dt.m_treeDrawer.m_bRootAtTop ? (float) m_dt.m_nSelectedRect.width / getWidth()
: (float) m_dt.m_nSelectedRect.height / getHeight());
//for (int i = 0/* m_dt.m_sLabels.size() */; i < m_rotationPoints.length; i++) {
// if (m_rotationPoints[i].intersects(m_dt.m_nSelectedRect.x, m_dt.m_nSelectedRect.y)) {
// }
for (int i : m_dt.m_cladeSelection) {
m_dt.m_cladePosition[i] += dF;
}
m_dt.calcLines();
m_dt.makeDirty();
m_dt.m_nSelectedRect = null;
repaint();
return;
}
m_dt.m_nSelectedRect = null;
repaint();
}
}
}
}
/** update selection rectangle when mouse is dragged **/
@Override
public void mouseDragged(MouseEvent e) {
if (m_dt.m_nSelectedRect != null) {
m_bIsDragging = true;
m_dt.m_nSelectedRect.width = e.getPoint().x - m_dt.m_nSelectedRect.x;
m_dt.m_nSelectedRect.height = e.getPoint().y - m_dt.m_nSelectedRect.y;
repaint();
return;
}
}
@Override
public void mouseMoved(MouseEvent e) {
if (m_dt.m_bDrawGeo) {
for (int i = 0; i < m_dt.m_bSelection.length; i++) {
if (m_dt.m_bGeoRectangle[i].contains(e.getPoint())) {
m_dt.m_jStatusBar.setText(m_dt.m_sLabels.elementAt(i));
}
}
}
Point p = e.getPoint();
boolean found = false;
if (m_dt.m_bLabelRectangle != null) {
for (int i = 0; i < m_dt.m_bLabelRectangle.length; i++) {
if (m_dt.m_bLabelRectangle[i].contains(p)) {
m_dt.m_jStatusBar.setText(m_dt.m_sLabels.elementAt(i) + " ");
if (m_dt.m_LabelImages != null) {
int w = 0, h = 0;
BufferedImage old = m_selectedImage;
if (m_selectedImage != null) {
w = old.getWidth();
h = old.getHeight();
}
m_selectedImage = m_dt.m_LabelImages[i];
if (m_selectedImage != null) {
w = Math.max(w, m_selectedImage.getWidth());
h = Math.max(h, m_selectedImage.getHeight());
}
if (old != m_selectedImage) {
repaint(0, 0, w, h);
}
} else {
if (m_selectedImage != null) {
int w = m_selectedImage.getWidth();
int h = m_selectedImage.getHeight();
m_selectedImage = null;
repaint(0, 0, w, h);
}
}
found = true;
}
}
if (!found) {
m_dt.m_jStatusBar.setText("");
}
}
String sText = m_dt.m_jStatusBar.getText();
sText = sText.split("\t")[0];
float fHeight = m_dt.screenPosToHeight(e.getX(), e.getY());
if (!Float.isNaN(fHeight)) {
sText += "\theight=" + fHeight;
m_dt.m_jStatusBar.setText(sText);
}
}
} // class TreeSetPanel