1 package JSci.awt; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 import java.awt.geom.Point2D ; 6 import java.text.DecimalFormat ; 7 import java.text.NumberFormat ; 8 import java.text.ParseException ; 9 import java.util.Iterator ; 10 import java.util.List ; 11 import java.util.ArrayList ; 12 import JSci.maths.ExtraMath; 13 14 19 public final class LineTrace extends DoubleBufferedCanvas { 20 23 private final List dataPoints = new ArrayList (); 24 27 private float samplingInterval; 28 31 private boolean numbering=true; 32 private NumberFormat xNumberFormat = new DecimalFormat ("##0.0"); 33 private NumberFormat yNumberFormat = new DecimalFormat ("##0.0"); 34 37 private Point origin=new Point(); 38 41 private float minX,minY,maxX,maxY; 42 45 private float xScale,yScale; 46 private final float xIncPixels = 40.0f; 47 private final float yIncPixels = 40.0f; 48 51 private final int scalePad=5; 52 private final int axisPad=25; 53 private int leftAxisPad; 54 57 public LineTrace(float minx,float maxx,float miny,float maxy) { 58 addMouseMotionListener(new MouseLineAdapter()); 59 setXExtrema(minx,maxx); 60 setYExtrema(miny,maxy); 61 setSamplingInterval(0.2f); 62 } 63 66 public Graph2DModel getModel() { 67 final Point2DListModel model=new Point2DListModel(); 68 model.setData(dataPoints); 69 return model; 70 } 71 74 public final void setNumbering(boolean flag) { 75 numbering=flag; 76 leftAxisPad=axisPad; 77 if(numbering && getFont() != null) { 78 final FontMetrics metrics = getFontMetrics(getFont()); 80 final int maxYNumLen = metrics.stringWidth(yNumberFormat.format(maxY)); 81 final int minYNumLen = metrics.stringWidth(yNumberFormat.format(minY)); 82 int yNumPad = Math.max(minYNumLen, maxYNumLen); 83 if(minX<0.0f) { 84 final int negXLen = (int)((Math.max(getSize().width,getMinimumSize().width)-2*(axisPad+scalePad))*minX/(minX-maxX)); 85 yNumPad = Math.max(yNumPad-negXLen, 0); 86 } 87 leftAxisPad += yNumPad; 88 } 89 } 90 public void addNotify() { 91 super.addNotify(); 92 setNumbering(numbering); 95 } 96 102 public final void setNumberFormat(NumberFormat format) { 103 xNumberFormat = format; 104 yNumberFormat = format; 105 setNumbering(numbering); 106 } 107 110 public final void setXNumberFormat(NumberFormat format) { 111 xNumberFormat = format; 112 setNumbering(numbering); 113 } 114 117 public final void setYNumberFormat(NumberFormat format) { 118 yNumberFormat = format; 119 setNumbering(numbering); 120 } 121 124 public void setXExtrema(float min,float max) { 125 if(max<min) 126 throw new IllegalArgumentException ("Maximum should be greater than minimum; max = "+max+" and min = "+min); 127 minX=min; 128 maxX=max; 129 rescale(); 130 } 131 134 public void setYExtrema(float min,float max) { 135 if(max<min) 136 throw new IllegalArgumentException ("Maximum should be greater than minimum; max = "+max+" and min = "+min); 137 minY=min; 138 maxY=max; 139 rescale(); 140 } 141 145 public void setSamplingInterval(float interval) { 146 samplingInterval = interval; 147 } 148 151 public void clear() { 152 dataPoints.clear(); 153 redraw(); 154 } 155 158 public final void setBounds(int x,int y,int width,int height) { 159 super.setBounds(x,y,width,height); 160 rescale(); 161 } 162 165 private void rescale() { 166 final Dimension s=getMinimumSize(); 167 final int thisWidth=Math.max(getSize().width,s.width); 168 final int thisHeight=Math.max(getSize().height,s.height); 169 xScale = (float) ((double)(thisWidth-(leftAxisPad+axisPad)) / (double)(maxX-minX)); 170 yScale = (float) ((double)(thisHeight-2*axisPad) / (double)(maxY-minY)); 171 origin.x=leftAxisPad-Math.round(minX*xScale); 172 origin.y=thisHeight-axisPad+Math.round(minY*yScale); 173 redraw(); 174 } 175 178 public Dimension getPreferredSize() { 179 return getMinimumSize(); 180 } 181 184 public Dimension getMinimumSize() { 185 return new Dimension(200,200); 186 } 187 190 private Point2D.Float screenToData(Point p) { 191 double x = (double)(p.x-origin.x) / (double)xScale; 192 double y = (double)(origin.y-p.y) / (double)yScale; 193 return new Point2D.Float ((float)x, (float)y); 194 } 195 198 private Point dataToScreen(Point2D.Float p) { 199 return new Point(origin.x+Math.round(xScale*p.x),origin.y-Math.round(yScale*p.y)); 200 } 201 private Point dataToScreen(float x, float y) { 202 return new Point(origin.x+Math.round(xScale*x),origin.y-Math.round(yScale*y)); 203 } 204 207 protected void offscreenPaint(Graphics g) { 208 drawAxes(g); 209 Point p1,p2; 211 Iterator iter = dataPoints.iterator(); 212 if(iter.hasNext()) { 213 p1 = dataToScreen((Point2D.Float ) iter.next()); 214 while(iter.hasNext()) { 215 p2 = dataToScreen((Point2D.Float ) iter.next()); 216 g.drawLine(p1.x, p1.y, p2.x, p2.y); 217 p1 = p2; 218 } 219 } 220 } 221 224 private void drawAxes(Graphics g) { 225 g.setColor(getForeground()); 227 if(minY>0.0f) 228 g.drawLine(leftAxisPad-scalePad,getSize().height-axisPad,getSize().width-(axisPad-scalePad),getSize().height-axisPad); 229 else 230 g.drawLine(leftAxisPad-scalePad,origin.y,getSize().width-(axisPad-scalePad),origin.y); 231 if(minX>0.0f) 232 g.drawLine(leftAxisPad,axisPad-scalePad,leftAxisPad,getSize().height-(axisPad-scalePad)); 233 else 234 g.drawLine(origin.x,axisPad-scalePad,origin.x,getSize().height-(axisPad-scalePad)); 235 if(numbering) { 237 final FontMetrics metrics=g.getFontMetrics(); 238 final int strHeight=metrics.getHeight(); 239 float dx = (float) ExtraMath.round((double)xIncPixels/(double)xScale, 1); 241 if(dx == 0.0f) 242 dx = Float.MIN_VALUE; 243 for(double x=(minX>0.0f)?minX:dx; x<=maxX; x+=dx) { 244 String str = xNumberFormat.format((float)x); 245 int strWidth=metrics.stringWidth('+'+str); 247 Point p=dataToScreen((float)x, (minY>0.0f)?minY:0.0f); 248 g.drawLine(p.x,p.y,p.x,p.y+5); 249 g.drawString(str,p.x-strWidth/2,p.y+5+strHeight); 250 } 251 for(double x=-dx; x>=minX; x-=dx) { 252 String str = xNumberFormat.format((float)x); 253 int strWidth=metrics.stringWidth(str); 254 Point p=dataToScreen((float)x, (minY>0.0f)?minY:0.0f); 255 g.drawLine(p.x,p.y,p.x,p.y+5); 256 g.drawString(str,p.x-strWidth/2,p.y+5+strHeight); 257 } 258 float dy = (float) ExtraMath.round((double)yIncPixels/(double)yScale, 1); 260 if(dy == 0.0f) 261 dy = Float.MIN_VALUE; 262 for(double y=(minY>0.0f)?minY:dy; y<=maxY; y+=dy) { 263 String str = yNumberFormat.format((float)y); 264 int strWidth=metrics.stringWidth(str); 265 Point p=dataToScreen((minX>0.0f)?minX:0.0f, (float)y); 266 g.drawLine(p.x,p.y,p.x-5,p.y); 267 g.drawString(str,p.x-8-strWidth,p.y+strHeight/4); 268 } 269 for(double y=-dy; y>=minY; y-=dy) { 270 String str = yNumberFormat.format((float)y); 271 int strWidth=metrics.stringWidth(str); 272 Point p=dataToScreen((minX>0.0f)?minX:0.0f, (float)y); 273 g.drawLine(p.x,p.y,p.x-5,p.y); 274 g.drawString(str,p.x-8-strWidth,p.y+strHeight/4); 275 } 276 } 277 } 278 class MouseLineAdapter extends MouseMotionAdapter { 279 public void mouseDragged(MouseEvent evt) { 280 Point2D.Float p=screenToData(evt.getPoint()); 281 final int i = dataPoints.size()-1; 282 if(p.x >= i*samplingInterval+minX && p.x <= maxX) { 283 dataPoints.add(p); 284 } 285 redraw(); 286 } 287 } 288 } 289 | Popular Tags |