/*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* TreeDrawer.java
* Copyright Remco Bouckaert remco@cs.auckland.ac.nz (C) 2011
*/
package viz.graphics;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.Random;
/** this class takes care of drawing a single tree out of a tree set on
* a BufferedImageF. It uses a BranchDrawer to draw the actual branches
* on the image to allow for different branch types (e.g. simple lines,
* trapeziums, arcs, etc.)s
*/
public class TreeDrawer {
BranchDrawer m_branchDrawer = new BranchDrawer();
public void setBranchDrawer(BranchDrawer bd) {m_branchDrawer = bd;}
public BranchDrawer getBranchDrawer() {return m_branchDrawer;}
/** show trees as block tree, otherwise as triangular tree **/
public boolean m_bViewBlockTree = false;
/** show tree with root at the top, instead of on the left **/
public boolean m_bRootAtTop = false;
/** jitter of x-positions for x-coordinate **/
int m_nJitter;
public void setJitter(int nJitter) {m_nJitter= nJitter;}
/** random number generator used for jitter **/
Random m_random = new Random();
/** image in memory containing tree set drawing **/
BufferedImageF m_image;
public void setImage(BufferedImageF image) {m_image = image;}
/** width of lines used for drawing trees, etc. **/
int m_nTreeWidth = 1;
/** scale factor of width for meta data **/
public float LINE_WIDTH_SCALE = 20;
/**
* draw block tree using array representation of a tree. Adds jitter if
* required
**/
void drawBlockTree(float[] nX, float[] nY, int [] color, Graphics2D g, float fScaleX, float fScaleY) {
if (nX == null || nY == null) {
return;
}
if (m_nJitter <= 0) {
for (int i = 0; i < nX.length - 4; i+=4) {
if (m_bRootAtTop) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i] * fScaleX), (int) (nY[i+1] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i+1] * fScaleY), (int) (nX[i + 3] * fScaleX), (int) (nY[i + 1] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i+3] * fScaleX), (int) (nY[i+2] * fScaleY), (int) (nX[i + 3] * fScaleX), (int) (nY[i + 3] * fScaleY), 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nY[i] * fScaleX), (int) (nX[i] * fScaleY), (int) (nY[i + 1] * fScaleX), (int) (nX[i] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nY[i+1] * fScaleX), (int) (nX[i] * fScaleY), (int) (nY[i + 1] * fScaleX), (int) (nX[i + 3] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nY[i+2] * fScaleX), (int) (nX[i+3] * fScaleY), (int) (nY[i + 3] * fScaleX), (int) (nX[i + 3] * fScaleY), 0, 0);
}
// if (i % 4 != 3) {
// m_branchDrawer.draw(m_image,g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY), 0, 0);
// }
}
} else {
int[] nXJ = new int[nX.length];
for (int i = 0; i < nX.length; i++) {
nXJ[i] = (int) (nX[i] * fScaleX) + m_random.nextInt(m_nJitter);
}
for (int i = 0; i < nX.length - 4; i += 4) {
if (m_bRootAtTop) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i] * fScaleX), (int) (nY[i+1] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i+1] * fScaleY), (int) (nX[i + 3] * fScaleX), (int) (nY[i + 1] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i+3] * fScaleX), (int) (nY[i+2] * fScaleY), (int) (nX[i + 3] * fScaleX), (int) (nY[i + 3] * fScaleY), 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i + 1] * fScaleX), (int) (nY[i] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i+1] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 3] * fScaleY), 0, 0);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i+2] * fScaleX), (int) (nY[i+3] * fScaleY), (int) (nX[i + 3] * fScaleX), (int) (nY[i + 3] * fScaleY), 0, 0);
}
}
}
}
/** draw block tree with variable line widths, where line width represents some information in the metadata **/
void drawBlockTree(float[] nX, float[] nY, float[]fLineWidth, float [] fTopLineWidth, int [] color, Graphics2D g, float fScaleX, float fScaleY) {
if (nX == null || nY == null) {
return;
}
if (m_nJitter <= 0) {
for (int i = 0; i < nX.length - 2; i++) {
if (i % 4 != 3) {
if (i % 4 == 0 || i % 4 == 2) {
float fWidth = fLineWidth[i] * LINE_WIDTH_SCALE;
float fTopWidth = fTopLineWidth[i] * LINE_WIDTH_SCALE;
Stroke stroke = new BasicStroke(fWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
if (m_bViewBlockTree) {
if (i % 4 == 0) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fWidth/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fTopWidth/2.0f), fTopWidth, fWidth);
} else {
// i % 4 == 2
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fTopWidth/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fWidth/2.0f), fWidth, fTopWidth);
}
} else {
if (i % 4 == 0) {
float fTopWidth2 = fTopLineWidth[i+2] * LINE_WIDTH_SCALE;
if (nY[i+1] <= nY[i+2]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fWidth/2.0f), (int) (nX[i + 1] * fScaleX)-1, (int) (((nY[i+1] + nY[i+2])/2.0) * fScaleY - (fTopWidth+fTopWidth2)/2.0f), fTopWidth, fWidth);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fWidth/2.0f), (int) (nX[i + 1] * fScaleX)-1, (int) (((nY[i+1] + nY[i+2])/2.0) * fScaleY - (fTopWidth-fTopWidth2)/2.0f), fTopWidth, fWidth);
}
} else {
// i % 4 == 2
float fTopWidth2 = fTopLineWidth[i-2] * LINE_WIDTH_SCALE;
if (nY[i-1] <= nY[i]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX)-1, (int) (((nY[i] +nY[i-1])/2.0)* fScaleY - (fTopWidth-fTopWidth2)/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fWidth/2.0f), fWidth, fTopWidth);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX)-1, (int) (((nY[i] +nY[i-1])/2.0)* fScaleY - (fTopWidth+fTopWidth2)/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fWidth/2.0f), fWidth, fTopWidth);
}
}
}
} else if (i % 4 == 1 && m_bViewBlockTree) {
Stroke stroke = new BasicStroke(m_nTreeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY), 0, 0);
}
}
}
} else {
int[] nXJ = new int[nX.length];
for (int i = 0; i < nX.length; i++) {
nXJ[i] = (int) (nX[i] * fScaleX) + m_random.nextInt(m_nJitter);
}
for (int i = 0; i < nX.length - 1; i++) {
if (i % 4 != 3) {
m_branchDrawer.draw(m_image, color[i], g, nXJ[i], (int) (nY[i] * fScaleY), nXJ[i + 1], (int) (nY[i + 1] * fScaleY), 0, 0);
}
if (i % 4 == 0 || i % 4 == 2) {
float fWidth = fLineWidth[i] * LINE_WIDTH_SCALE;
Stroke stroke = new BasicStroke(fWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
m_branchDrawer.draw(m_image, color[i], g, nXJ[i], (int) (nY[i] * fScaleY - fWidth/2.0f), nXJ[i + 1], (int) (nY[i + 1] * fScaleY - fWidth/2.0f), fWidth, fTopLineWidth[i] * LINE_WIDTH_SCALE);
} else if (i % 4 == 1) {
Stroke stroke = new BasicStroke(m_nTreeWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
m_branchDrawer.draw(m_image, color[i], g, nXJ[i], (int) (nY[i] * fScaleY), nXJ[i + 1], (int) (nY[i + 1] * fScaleY), 0, 0);
}
}
}
}
/**
* draw triangle tree using array representation of a tree. Adds jitter
* if required
**/
void drawTriangleTree(float[] nX, float[] nY, int [] color, Graphics2D g, float fScaleX, float fScaleY) {
if (nX == null || nY == null) {
return;
}
// ignore jitter for triangle trees
if (m_bRootAtTop) {
for (int i = 0; i < nX.length - 4; i++) {
float fWidth = 0;
float fTopWidth = 0;
float fTopWidth2 = 0;
if (i % 4 != 3) {
if (i % 4 == 0 || i % 4 == 2) {
if (i % 4 == 0) {
if (nX[i+1] < nX[i+2]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX - fWidth/2.0f), (int) (nY[i] * fScaleY), (int) (((nX[i+1] + nX[i+2])/2.0) * fScaleX - (fTopWidth+fTopWidth2)/2.0f), (int) (nY[i + 1] * fScaleY)-1, 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX - fWidth/2.0f), (int) (nY[i] * fScaleY), (int) (((nX[i+1] + nX[i+2])/2.0) * fScaleX - (fTopWidth-fTopWidth2)/2.0f), (int) (nY[i + 1] * fScaleY)-1, 0, 0);
}
} else {
// i % 4 == 2
if (nX[i-1] < nX[i]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (((nX[i] +nX[i-1])/2.0)* fScaleX - (fTopWidth-fTopWidth2)/2.0f), (int) (nY[i] * fScaleY)-1, (int) (nX[i + 1] * fScaleX - fWidth/2.0f), (int) (nY[i + 1] * fScaleY)-1, 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (((nX[i] +nX[i-1])/2.0)* fScaleX - (fTopWidth+fTopWidth2)/2.0f), (int) (nY[i] * fScaleY)-1, (int) (nX[i + 1] * fScaleX - fWidth/2.0f), (int) (nY[i + 1] * fScaleY)-1, 0, 0);
}
}
}
}
}
} else {
for (int i = 0; i < nX.length - 4; i++) {
float fWidth = 0;
float fTopWidth = 0;
float fTopWidth2 = 0;
if (i % 4 != 3) {
if (i % 4 == 0 || i % 4 == 2) {
if (i % 4 == 0) {
if (nY[i+1] < nY[i+2]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fWidth/2.0f), (int) (nX[i + 1] * fScaleX)-1, (int) (((nY[i+1] + nY[i+2])/2.0) * fScaleY - (fTopWidth+fTopWidth2)/2.0f), 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX), (int) (nY[i] * fScaleY - fWidth/2.0f), (int) (nX[i + 1] * fScaleX)-1, (int) (((nY[i+1] + nY[i+2])/2.0) * fScaleY - (fTopWidth-fTopWidth2)/2.0f), 0, 0);
}
} else {
// i % 4 == 2
if (nY[i-1] < nY[i]) {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX)-1, (int) (((nY[i] +nY[i-1])/2.0)* fScaleY - (fTopWidth-fTopWidth2)/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fWidth/2.0f), 0, 0);
} else {
m_branchDrawer.draw(m_image, color[i], g, (int) (nX[i] * fScaleX)-1, (int) (((nY[i] +nY[i-1])/2.0)* fScaleY - (fTopWidth+fTopWidth2)/2.0f), (int) (nX[i + 1] * fScaleX), (int) (nY[i + 1] * fScaleY - fWidth/2.0f), 0, 0);
}
}
}
}
}
}
}
public void draw(int i, float[][] fLinesX, float[][] fLinesY, float [][] fLineWidth, float [][] fTopLineWidth, int [][] nLineColor, Graphics2D g, float fScaleX, float fScaleY) {
if (m_bViewBlockTree) {
if (fLineWidth == null) {
// if (m_bRootAtTop) {
drawBlockTree(fLinesX[i], fLinesY[i], nLineColor[i], g, fScaleX, fScaleY);
// } else {
// drawBlockTree(fLinesY[i], fLinesX[i], g, fScaleX, fScaleY);
// }
} else {
if (m_bRootAtTop) {
drawBlockTree(fLinesX[i], fLinesY[i], fLineWidth[i], fTopLineWidth[i], nLineColor[i], g, fScaleX, fScaleY);
} else {
drawBlockTree(fLinesY[i], fLinesX[i], fLineWidth[i], fTopLineWidth[i], nLineColor[i], g, fScaleX, fScaleY);
}
}
} else {
if (fLineWidth == null) {
if (m_bRootAtTop) {
drawTriangleTree(fLinesX[i], fLinesY[i], nLineColor[i], g, fScaleX, fScaleY);
} else {
drawTriangleTree(fLinesY[i], fLinesX[i], nLineColor[i], g, fScaleX, fScaleY);
}
} else {
if (m_bRootAtTop) {
drawBlockTree(fLinesX[i], fLinesY[i], fLineWidth[i], fTopLineWidth[i], nLineColor[i], g, fScaleX, fScaleY);
} else {
drawBlockTree(fLinesY[i], fLinesX[i], fLineWidth[i], fTopLineWidth[i], nLineColor[i], g, fScaleX, fScaleY);
}
}
}
} // draw
// public void draw(Image rotate, float[][] fLinesX, float[][] fLinesY, float [][] fLineWidth, float [][] fTopLineWidth, Graphics2D g, float fScaleX, float fScaleY) {
// if (m_bRootAtTop) {
// drawTriangleTree(fLinesX[0], fLinesY[0], g, fScaleX, fScaleY);
// } else {
// drawTriangleTree(fLinesY[0], fLinesX[0], g, fScaleX, fScaleY);
// }
// for (int i = 2; i < fLinesX[0].length - 2; i += 4) {
// int x = (int) (fLinesX[0][i] * fScaleX);
// int y = (int) (fLinesY[0][i] * fScaleY);
// g.drawImage(rotate, x, y, x + rotate.getWidth(null), y + rotate.getHeight(null),
// 0, 0, rotate.getWidth(null), rotate.getHeight(null), null);
// }
// }
} // class BranchDrawer