import java.awt.*; import java.awt.image.*; import java.text.NumberFormat; import java.text.DecimalFormat; import java.util.Locale; import java.text.ParseException; public class DrawableAxis extends Component { static final boolean VERTICAL = true; static final boolean HORIZONTAL = false; static final int pMajorTickLength = 3; static final int pMinorTickLength = 1; int iLength; int maxWidth; Font f; FontMetrics fm; int fontAscent, fontHeight; int xnow, ynow; boolean bOrientation; double axisStart, axisFinish; double increment; DecimalFormat decform; Font numberFont; boolean bHaveLabel; String sLabel; Image iLabel; Font labelFont; // Should just be a length and horizontal or vertical. public DrawableAxis(int iLength, boolean bVertical, double start, double finish, Font f) { this.iLength = iLength; this.bOrientation = bVertical; axisStart = start; axisFinish = finish; NumberFormat numform = NumberFormat.getInstance(Locale.US); if (numform instanceof DecimalFormat) { decform = (DecimalFormat) numform; decform.applyPattern("0.###"); } else decform = null; numberFont = f; increment = chooseIncrement(axisFinish-axisStart,iLength,bVertical); bHaveLabel = false; } public void setLabel(String s1, Font lf ) { sLabel = s1; labelFont = lf; bHaveLabel = true; } public void setLabel(Image i1) { iLabel = i1; bHaveLabel = true; } public void draw(Graphics g, int x, int y) { xnow = x; ynow = y; maxWidth = 0; if (bOrientation==VERTICAL) { g.drawLine(xnow,ynow,xnow,ynow-iLength); } else { g.drawLine(xnow,ynow,xnow+iLength,ynow); } fm = Toolkit.getDefaultToolkit().getFontMetrics(numberFont); fontAscent = fm.getAscent(); fontHeight = fm.getHeight(); g.setFont(numberFont); makeTicks(g,increment,pMajorTickLength,true); makeTicks(g,increment/5,pMinorTickLength,false); if (bHaveLabel) drawLabel(g); } private void drawLabel(Graphics g) { int width; if (bOrientation==HORIZONTAL) { FontMetrics lfm = Toolkit.getDefaultToolkit().getFontMetrics(labelFont); width = lfm.stringWidth(sLabel); // fontHeight is the height of the tick font. g.setFont(labelFont); g.drawString(sLabel,xnow+(iLength-width)/2,ynow+pMajorTickLength+fontHeight+ lfm.getHeight()); } else { width = iLabel.getWidth(null); int height = iLabel.getHeight(null); //System.out.println("Label width "+width+" height "+height); g.drawImage(iLabel,xnow-pMajorTickLength-maxWidth-width-3, ynow-(iLength+height)/2,null); } } private void makeTicks(Graphics g,double spacing, int ticklength, boolean numbered) { int iMark; double axisMark = axisStart; while (axisMark < axisFinish) { iMark = (int)(iLength*(axisMark-axisStart)/(axisFinish-axisStart)); String s1; if (decform == null) s1 = Double.toString(axisMark); else s1 = decform.format(axisMark); if (bOrientation==VERTICAL) { g.drawLine(xnow,ynow-iMark,xnow-ticklength,ynow-iMark); if (numbered) textToLeft(s1,g,xnow-ticklength,ynow-iMark); } else { g.drawLine(xnow+iMark,ynow,xnow+iMark,ynow+ticklength); if (numbered) textBelow(s1,g,xnow+iMark,ynow+ticklength); } axisMark += spacing; } } protected void textBelow(String s1, Graphics g, int x, int y) { int width = fm.stringWidth(s1); g.drawString(s1,x-width/2,y+fontHeight); } protected void textToLeft(String s1, Graphics g, int x, int y) { int width = fm.stringWidth(s1); if (width>maxWidth) maxWidth = width; g.drawString(s1,x-width-1,y-fontHeight/2+fontAscent); } // A method to help axes choose their increments. // Use increments of 10, 20, 25 or 50 in whatever power of ten is appropriate. private double chooseIncrement(double domain, int pixels, boolean bOrientation) { double bases[] = {1, 2, 2.5, 5}; if (domain<0) return(0.0); FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(numberFont); int height = fm.getHeight(); // estimate the increment to allow 30 pixels per label. double estimatedIncrement = domain*30/pixels; // Use that estimate to get an order of magnitude to look for the real label. int order = (int) Math.floor(Math.log(estimatedIncrement)/Math.log(10.0)); int sizeNeeded; double numLabels = 5, lastLabel = 1.0; // Loop through powers of ten. while (order<5) { // Loop through acceptable increments. for (int i = 0; i<4; i++) { double testIncrement = Math.pow(10,order)*bases[i]; // how many pixels per label this increment leaves. int widthAvailable = (int) (pixels*testIncrement/domain); if (bOrientation==DrawableAxis.HORIZONTAL) { // how many labels we need to draw. numLabels = Math.floor(domain/testIncrement); // find the last label b/c it is likely the largest. lastLabel = numLabels*testIncrement; // Then see how wide it is. sizeNeeded= fm.stringWidth(Double.toString(lastLabel)); } else sizeNeeded = height; // If the label width (or height) is larger than a percentage of the // available width per label, than keep looking. If the label width // is small enough, then return. if (sizeNeeded*1.4< widthAvailable) return testIncrement; } order += 1; } return(20.0); } }