KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JSci > awt > LineTrace


1 package JSci.awt;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import java.awt.geom.Point2D JavaDoc;
6 import java.text.DecimalFormat JavaDoc;
7 import java.text.NumberFormat JavaDoc;
8 import java.text.ParseException JavaDoc;
9 import java.util.Iterator JavaDoc;
10 import java.util.List JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import JSci.maths.ExtraMath;
13
14 /**
15 * A line trace AWT component.
16 * @version 1.4
17 * @author Mark Hale
18 */

19 public final class LineTrace extends DoubleBufferedCanvas {
20         /**
21         * Data points.
22         */

23         private final List JavaDoc dataPoints = new ArrayList JavaDoc();
24         /**
25         * Sampling interval.
26         */

27         private float samplingInterval;
28         /**
29         * Axis numbering.
30         */

31         private boolean numbering=true;
32         private NumberFormat JavaDoc xNumberFormat = new DecimalFormat JavaDoc("##0.0");
33         private NumberFormat JavaDoc yNumberFormat = new DecimalFormat JavaDoc("##0.0");
34         /**
35         * Origin.
36         */

37         private Point origin=new Point();
38         /**
39         * Min and max data points.
40         */

41         private float minX,minY,maxX,maxY;
42         /**
43         * Axis scaling.
44         */

45         private float xScale,yScale;
46         private final float xIncPixels = 40.0f;
47         private final float yIncPixels = 40.0f;
48         /**
49         * Padding.
50         */

51         private final int scalePad=5;
52         private final int axisPad=25;
53         private int leftAxisPad;
54         /**
55         * Constructs a line trace.
56         */

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         /**
64         * Gets the data sampled by this line trace.
65         */

66         public Graph2DModel getModel() {
67                 final Point2DListModel model=new Point2DListModel();
68                 model.setData(dataPoints);
69                 return model;
70         }
71         /**
72         * Turns axis numbering on/off.
73         */

74         public final void setNumbering(boolean flag) {
75                 numbering=flag;
76                 leftAxisPad=axisPad;
77                 if(numbering && getFont() != null) {
78                         // adjust leftAxisPad to accomodate y-axis numbering
79
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                 // getFont() is now not null
93
// recalculate padding
94
setNumbering(numbering);
95         }
96         /**
97         * Sets the display format used for axis numbering.
98         * Convenience method.
99         * @see #setXNumberFormat(NumberFormat)
100         * @see #setYNumberFormat(NumberFormat)
101         */

102         public final void setNumberFormat(NumberFormat JavaDoc format) {
103                 xNumberFormat = format;
104                 yNumberFormat = format;
105                 setNumbering(numbering);
106         }
107         /**
108         * Sets the display format used for x-axis numbering.
109         */

110         public final void setXNumberFormat(NumberFormat JavaDoc format) {
111                 xNumberFormat = format;
112                 setNumbering(numbering);
113         }
114         /**
115         * Sets the display format used for y-axis numbering.
116         */

117         public final void setYNumberFormat(NumberFormat JavaDoc format) {
118                 yNumberFormat = format;
119                 setNumbering(numbering);
120         }
121         /**
122         * Sets the minimum/maximum values on the x-axis.
123         */

124         public void setXExtrema(float min,float max) {
125                 if(max<min)
126                         throw new IllegalArgumentException JavaDoc("Maximum should be greater than minimum; max = "+max+" and min = "+min);
127                 minX=min;
128                 maxX=max;
129                 rescale();
130         }
131         /**
132         * Sets the minimum/maximum values on the y-axis.
133         */

134         public void setYExtrema(float min,float max) {
135                 if(max<min)
136                         throw new IllegalArgumentException JavaDoc("Maximum should be greater than minimum; max = "+max+" and min = "+min);
137                 minY=min;
138                 maxY=max;
139                 rescale();
140         }
141         /**
142         * Sets the sampling interval.
143         * Smaller values give a more accurate trace, but more susceptible to mouse noise.
144         */

145         public void setSamplingInterval(float interval) {
146                 samplingInterval = interval;
147         }
148         /**
149         * Clears the current trace.
150         */

151         public void clear() {
152                 dataPoints.clear();
153                 redraw();
154         }
155         /**
156         * Reshapes the line trace to the specified bounding box.
157         */

158         public final void setBounds(int x,int y,int width,int height) {
159                 super.setBounds(x,y,width,height);
160                 rescale();
161         }
162         /**
163         * Rescales the line trace.
164         */

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         /**
176         * Returns the preferred size of this component.
177         */

178         public Dimension getPreferredSize() {
179                 return getMinimumSize();
180         }
181         /**
182         * Returns the minimum size of this component.
183         */

184         public Dimension getMinimumSize() {
185                 return new Dimension(200,200);
186         }
187         /**
188         * Converts a screen point to data coordinates.
189         */

190         private Point2D.Float JavaDoc 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 JavaDoc((float)x, (float)y);
194         }
195         /**
196         * Converts a data point to screen coordinates.
197         */

198         private Point dataToScreen(Point2D.Float JavaDoc 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         /**
205         * Paint the trace.
206         */

207         protected void offscreenPaint(Graphics g) {
208                 drawAxes(g);
209 // lines
210
Point p1,p2;
211                 Iterator JavaDoc iter = dataPoints.iterator();
212                 if(iter.hasNext()) {
213                         p1 = dataToScreen((Point2D.Float JavaDoc) iter.next());
214                         while(iter.hasNext()) {
215                                 p2 = dataToScreen((Point2D.Float JavaDoc) iter.next());
216                                 g.drawLine(p1.x, p1.y, p2.x, p2.y);
217                                 p1 = p2;
218                         }
219                 }
220         }
221         /**
222         * Draws the graph axes.
223         */

224         private void drawAxes(Graphics g) {
225 // axis
226
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 // numbering
236
if(numbering) {
237                         final FontMetrics metrics=g.getFontMetrics();
238                         final int strHeight=metrics.getHeight();
239 // x-axis numbering
240
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 JavaDoc str = xNumberFormat.format((float)x);
245 // add a + prefix to compensate for - prefix in negative number strings when calculating length
246
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 JavaDoc 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 // y-axis numbering
259
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 JavaDoc 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 JavaDoc 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 JavaDoc 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