package viz; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.text.DecimalFormat; public class GridDrawer { public Font m_gridfont = Font.getFont(Font.MONOSPACED); public enum GridMode {NONE, SHORT, FULL}; public GridMode m_nGridMode = GridMode.NONE; public float m_fGridOrigin = 0; public boolean m_bReverseGrid = false; public boolean m_bAutoGrid = true; public float m_fGridTicks = 100; public float m_fGridOffset = 0; public int m_nGridDigits = 2; DensiTree m_dt; public GridDrawer(DensiTree dt) { m_dt = dt; } void drawHeightInfoSVG(StringBuffer buf) { if (m_nGridMode != GridMode.NONE && m_dt.m_fHeight > 0) { DecimalFormat formatter = new DecimalFormat("##.##"); float fTreeHeight = m_dt.m_fHeight * m_dt.m_fUserScale; //float fUserScale = Math.abs(m_dt.m_fUserScale); float fUserSign = Math.signum(m_dt.m_fUserScale); if (m_bReverseGrid) { fUserSign = - fUserSign; } //boolean bReverseGrid = false; if (m_dt.m_treeDrawer.m_bRootAtTop) { int nW = m_dt.getWidth(); if (m_nGridMode == GridMode.SHORT) { nW = 10; } buf.append("<path " + "fill='none' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + "stroke-width='" + 1 + "' " + " d='"); if (m_bAutoGrid) { float fHeight = (float) adjust(fTreeHeight); for (int i = 0; i <= m_nTicks; i++) { int y = m_dt.getPosY((m_dt.m_fHeight - fHeight/m_dt.m_fUserScale * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("M" + 0 + " " + y + "L" + nW + " " + y); } buf.append("'/>\n"); for (int i = 0; i <= m_nTicks; i++) { int y = m_dt.getPosY((m_dt.m_fHeight - fHeight/m_dt.m_fUserScale * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fHeight - fHeight * (i) / m_nTicks) *fUserSign) : formatter.format(m_fGridOrigin + (fHeight * (i) / m_nTicks) *fUserSign) //) ; buf.append("<text x='" + m_gridfont.getSize() + "' y='" + y + "' font-family='" + m_gridfont.getFamily() + "' " + "font-size='" + m_gridfont.getSize() + "pt' " + "font-style='" + (m_gridfont.isBold() ? "oblique" : "") + (m_gridfont.isItalic() ? "italic" : "") + "' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + ">" + sStr + "</text>\n"); } } else { float fHeight = m_fGridOffset; while (fHeight < fTreeHeight) { int y = m_dt.getPosY((m_dt.m_fHeight - fHeight/m_dt.m_fUserScale - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("M" + 0 + " " + y + "L" + nW + " " + y); fHeight += Math.abs(m_fGridTicks); } buf.append("'/>\n"); fHeight = m_fGridOffset; while (fHeight < fTreeHeight) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fTreeHeight - fHeight) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight) * fUserSign) //) ; int y = m_dt.getPosY((m_dt.m_fHeight - fHeight/m_dt.m_fUserScale - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("<text x='" + m_gridfont.getSize() + "' y='" + y + "' font-family='" + m_gridfont.getFamily() + "' " + "font-size='" + m_gridfont.getSize() + "pt' " + "font-style='" + (m_gridfont.isBold() ? "oblique" : "") + (m_gridfont.isItalic() ? "italic" : "") + "' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + ">" + sStr + "</text>\n"); fHeight += Math.abs(m_fGridTicks); } } } else { int nH = m_dt.getHeight(); if (m_nGridMode == GridMode.SHORT) { nH = 10; } buf.append("<path " + "fill='none' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + "stroke-width='" + 1 + "' " + " d='"); if (m_bAutoGrid) { float fHeight = (float) adjust(m_dt.m_fHeight); for (int i = 0; i <= m_nTicks; i++) { int x = m_dt.getPosX((m_dt.m_fHeight - fHeight * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("M" + x + " " + 0 + "L" + x + " " + nH); } buf.append("'/>\n"); for (int i = 0; i <= m_nTicks; i++) { int x = m_dt.getPosX((m_dt.m_fHeight - fHeight * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fHeight - fHeight * (i) / m_nTicks) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight * (i) / m_nTicks) * fUserSign) //) ; buf.append("<text x='" + x + "' y='" + m_gridfont.getSize() + "' font-family='" + m_gridfont.getFamily() + "' " + "font-size='" + m_gridfont.getSize() + "pt' " + "font-style='" + (m_gridfont.isBold() ? "oblique" : "") + (m_gridfont.isItalic() ? "italic" : "") + "' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + ">" + sStr + "</text>\n"); } } else { float fHeight = m_fGridOffset; while (fHeight < m_dt.m_fHeight) { int x = m_dt.getPosX((m_dt.m_fHeight - fHeight - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("M" + x + " " + 0 + "L" + x + " " + nH); fHeight += Math.abs(m_fGridTicks); } buf.append("'/>\n"); fHeight = m_fGridOffset; while (fHeight < m_dt.m_fHeight) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (m_dt.m_fHeight - fHeight) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight) * fUserSign) //) ; int x = m_dt.getPosX((m_dt.m_fHeight - fHeight - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); buf.append("<text x='" + x + "' y='" + m_gridfont.getSize() + "' font-family='" + m_gridfont.getFamily() + "' " + "font-size='" + m_gridfont.getSize() + "pt' " + "font-style='" + (m_gridfont.isBold() ? "oblique" : "") + (m_gridfont.isItalic() ? "italic" : "") + "' " + "stroke='rgb(" + m_dt.m_color[DensiTree.HEIGHTCOLOR].getRed() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getGreen() + "," + m_dt.m_color[DensiTree.HEIGHTCOLOR].getBlue() + ")' " + ">" + sStr + "</text>\n"); fHeight += Math.abs(m_fGridTicks); } } } } } // drawHeightInfoSVG /** draw height bar and/or height grid if desired **/ void paintHeightInfo(Graphics g) { if (m_nGridMode != GridMode.NONE && m_dt.m_fHeight > 0) { String format = "##."; for (int i = 0; i < m_nGridDigits; i++) { format += "#"; } DecimalFormat formatter = new DecimalFormat(format); ((Graphics2D) g).setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); ((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); if (m_gridfont == null) { m_gridfont = new Font(Font.SANS_SERIF, Font.PLAIN, 10); } g.setFont(m_gridfont); float fUserScale = Math.abs(m_dt.m_fUserScale); float fUserSign = Math.signum(m_dt.m_fUserScale); if (m_bReverseGrid) { fUserSign = - fUserSign; } //boolean bReverseGrid = false; if (m_dt.m_treeDrawer.m_bRootAtTop) { int nW = (int)(m_dt.getWidth() * m_dt.m_fScale); if (m_nGridMode == GridMode.SHORT) { nW = 10; } g.setColor(m_dt.m_color[DensiTree.HEIGHTCOLOR]); float fTreeHeight = m_dt.m_fHeight * fUserScale; if (m_bAutoGrid) { float fHeight = (float) adjust(fTreeHeight); for (int i = 0; i <= m_nTicks; i++) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fHeight - fHeight * i / m_nTicks) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight * i / m_nTicks) * fUserSign) //) ; int y = m_dt.getPosY((m_dt.m_fHeight - fHeight / fUserScale * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); g.drawString(sStr, 0, y - 2); g.drawLine(0, y, nW, y); } } else { float fHeight = m_fGridOffset; while (fHeight < m_dt.m_fHeight * fUserScale) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fTreeHeight - fHeight) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight) * fUserSign) //) ; int y = m_dt.getPosY((m_dt.m_fHeight - fHeight / fUserScale- m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); g.drawString(sStr, 0, y - 2); g.drawLine(0, y, nW, y); fHeight += Math.abs(m_fGridTicks); } } } else { int nH = (int) (m_dt.getHeight() * m_dt.m_fScale); if (m_nGridMode == GridMode.SHORT) { nH = 10; } g.setColor(m_dt.m_color[DensiTree.HEIGHTCOLOR]); float fTreeHeight = m_dt.m_fHeight * fUserScale; if (m_bAutoGrid) { float fHeight = (float) adjust(fTreeHeight); for (int i = 0; i <= m_nTicks; i++) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fHeight - fHeight * i / m_nTicks) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight * i / m_nTicks) * fUserSign) //) ; int x = m_dt.getPosX((m_dt.m_fHeight - fHeight / fUserScale * i / m_nTicks - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); g.drawString(sStr, x+2, m_gridfont.getSize()); g.drawLine(x, 0, x, nH); } } else { float fHeight = m_fGridOffset; while (fHeight < fTreeHeight) { String sStr = //(bReverseGrid ? //formatter.format(m_fGridOffset + (fTreeHeight - fHeight) * fUserSign) : formatter.format(m_fGridOrigin + (fHeight) * fUserSign) //) ; int x = m_dt.getPosX((m_dt.m_fHeight - fHeight / fUserScale - m_dt.m_fTreeOffset) * m_dt.m_fTreeScale); g.drawString(sStr, x+2, m_gridfont.getSize()); g.drawLine(x, 0, x, nH); fHeight += Math.abs(m_fGridTicks); } } } } } // paintHeightInfo /** maps most significant digit to nr of ticks on graph **/ final int [] NR_OF_TICKS = new int [] {5,10,8,6,8,10,6,7,8,9, 10}; int m_nTicks = 10; private double adjust(double fYMax) { // adjust fYMax so that the ticks come out right int k = 0; double fY = fYMax; while (fY > 10) { fY /= 10; k++; } while (fY < 1 && fY > 0) { fY *= 10; k--; } fY = Math.ceil(fY); m_nTicks = NR_OF_TICKS[(int) fY]; m_nTicks *= (int) m_dt.m_fTreeScale; for (int i = 0; i < k; i++) { fY *= 10; } for (int i = k; i < 0; i++) { fY /= 10; } return fY; } }