KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JSci > awt > Graph2D


1 package JSci.awt;
2
3 import java.awt.*;
4 import java.awt.geom.Point2D JavaDoc;
5 import java.awt.geom.Rectangle2D JavaDoc;
6 import java.text.DecimalFormat JavaDoc;
7 import java.text.NumberFormat JavaDoc;
8 import java.text.ParseException JavaDoc;
9 import JSci.maths.ExtraMath;
10
11 /**
12 * The Graph2D superclass provides an abstract encapsulation of 2D graphs.
13 * There is some support for the handling of NaN values.
14 * @version 1.8
15 * @author Mark Hale
16 */

17 public abstract class Graph2D extends DoubleBufferedCanvas implements GraphDataListener {
18         public final static int LINEAR_SCALE = 0;
19         public final static int LOG_SCALE = 1;
20         /**
21         * Data model.
22         */

23         protected Graph2DModel model;
24         /**
25         * Origin.
26         */

27         protected Point origin = new Point();
28         protected Graph2D.DataMarker dataMarker = Graph2D.DataMarker.NONE;
29         /**
30         * Series colors.
31         */

32         protected Color seriesColor[]={Color.black,Color.blue,Color.green,Color.red,Color.yellow,Color.cyan,Color.lightGray,Color.magenta,Color.orange,Color.pink};
33         /**
34         * Axis numbering.
35         */

36         protected boolean xNumbering = true, yNumbering = true;
37         protected NumberFormat JavaDoc xNumberFormat = new DecimalFormat JavaDoc("##0.0");
38         protected NumberFormat JavaDoc yNumberFormat = new DecimalFormat JavaDoc("##0.0");
39     protected boolean xAxisLine = true, yAxisLine = true;
40         protected boolean gridLines = false;
41         private final Color gridLineColor = Color.lightGray;
42         /**
43         * Axis scaling.
44         */

45         private float xScale,yScale;
46         /**
47         * Axis scaling type.
48         */

49         private int xScaleType, yScaleType;
50         /**
51         * Axis extrema.
52         */

53         private float minX, minY, maxX, maxY;
54         private float scaledMinX, scaledMinY, scaledMaxX, scaledMaxY;
55         private boolean autoXExtrema=true, autoYExtrema=true;
56         private float xGrowth, yGrowth;
57         /**
58         * Axis numbering increment.
59         */

60         private final float xIncPixels = 40.0f;
61         private final float yIncPixels = 40.0f;
62         private float xInc,yInc;
63         private boolean autoXInc=true,autoYInc=true;
64         /**
65         * Padding.
66         */

67         protected final int scalePad=5;
68         protected final int axisPad=25;
69         protected int leftAxisPad;
70         /**
71         * Constructs a 2D graph.
72         */

73         public Graph2D(Graph2DModel gm) {
74                 model=gm;
75                 model.addGraphDataListener(this);
76                 dataChanged(new GraphDataEvent(model));
77         }
78         /**
79         * Sets the data plotted by this graph to the specified data.
80         */

81         public final void setModel(Graph2DModel gm) {
82                 model.removeGraphDataListener(this);
83                 model=gm;
84                 model.addGraphDataListener(this);
85                 dataChanged(new GraphDataEvent(model));
86         }
87         /**
88         * Returns the model used by this graph.
89         */

90         public final Graph2DModel getModel() {
91                 return model;
92         }
93         /**
94         * Implementation of GraphDataListener.
95     * Supports {@link JSci.awt.GraphDataEvent#isIncremental() incremental} updates.
96         * Application code will not use this method explicitly, it is used internally.
97         */

98         public void dataChanged(GraphDataEvent e) {
99                 if(e.isIncremental()) {
100                         Graphics g = getOffscreenGraphics();
101                         if(g == null)
102                                 return;
103                         final int series = e.getSeries();
104                         if(series == GraphDataEvent.ALL_SERIES) {
105                                 model.firstSeries();
106                                 int n = 0;
107                                 do {
108                                         final int i = model.seriesLength() - 1;
109                                         incrementalRescale(model.getXCoord(i), model.getYCoord(i));
110                                         g.setColor(seriesColor[n]);
111                                         drawDataPoint(g, i);
112                                         n++;
113                                 } while(model.nextSeries());
114                         } else {
115                                 model.firstSeries();
116                                 int n=0;
117                                 for(; n<series; n++)
118                                         model.nextSeries();
119                                 final int i = model.seriesLength() - 1;
120                                 incrementalRescale(model.getXCoord(i), model.getYCoord(i));
121                                 g.setColor(seriesColor[n]);
122                                 drawDataPoint(g, i);
123                         }
124                         repaint();
125                 } else {
126                         // ensure there are enough colors
127
int n = 1;
128                         model.firstSeries();
129                         while(model.nextSeries())
130                                 n++;
131                         if(n>seriesColor.length) {
132                                 Color tmp[]=seriesColor;
133                                 seriesColor=new Color[n];
134                                 System.arraycopy(tmp,0,seriesColor,0,tmp.length);
135                                 for(int i=tmp.length; i<n; i++)
136                                         seriesColor[i]=seriesColor[i-tmp.length];
137                         }
138                         if(autoXExtrema)
139                                 setXExtrema(0.0f, 0.0f);
140                         if(autoYExtrema)
141                                 setYExtrema(0.0f, 0.0f);
142                         redraw();
143                 }
144         }
145         private void incrementalRescale(final float x, final float y) {
146                 float min, max;
147                 if(x < minX)
148                         min = autoXExtrema ? Math.min(x, minX-xGrowth) : minX-xGrowth;
149                 else
150                         min = minX;
151                 if(x > maxX)
152                         max = autoXExtrema ? Math.max(x, maxX+xGrowth) : maxX+xGrowth;
153                 else
154                         max = maxX;
155                 rescaleX(min, max);
156
157                 if(y < minY)
158                         min = autoYExtrema ? Math.min(y, minY-yGrowth) : minY-yGrowth;
159                 else
160                         min = minY;
161                 if(y > maxY)
162                         max = autoYExtrema ? Math.max(y, maxY+yGrowth) : maxY+yGrowth;
163                 else
164                         max = maxY;
165                 rescaleY(min, max);
166         }
167         /**
168         * Turns axis numbering on/off.
169         * Default is on.
170         */

171         public final void setNumbering(boolean flag) {
172         setNumbering(flag, flag);
173     }
174         public final void setNumbering(boolean xFlag, boolean yFlag) {
175                 xNumbering = xFlag;
176         yNumbering = yFlag;
177                 leftAxisPad = axisPad;
178                 if(yNumbering && getFont() != null) {
179                         // adjust leftAxisPad to accomodate y-axis numbering
180
final FontMetrics metrics = getFontMetrics(getFont());
181                         final int maxYNumLen = metrics.stringWidth(yNumberFormat.format(maxY));
182                         final int minYNumLen = metrics.stringWidth(yNumberFormat.format(minY));
183                         int yNumPad = Math.max(minYNumLen, maxYNumLen);
184                         if(scaledMinX<0.0f) {
185                                 final int negXLen = (int)((Math.max(getSize().width,getMinimumSize().width)-2*(axisPad+scalePad))*scaledMinX/(scaledMinX-scaledMaxX));
186                                 yNumPad = Math.max(yNumPad-negXLen, 0);
187                         }
188                         leftAxisPad += yNumPad;
189                 }
190                 rescale();
191         }
192         public void addNotify() {
193                 super.addNotify();
194                 // getFont() is now not null
195
// recalculate padding
196
setNumbering(xNumbering, yNumbering);
197         }
198         /**
199         * Sets the display format used for axis numbering.
200         * Convenience method.
201         * @see #setXNumberFormat(NumberFormat)
202         * @see #setYNumberFormat(NumberFormat)
203         */

204         public final void setNumberFormat(NumberFormat JavaDoc format) {
205                 xNumberFormat = format;
206                 yNumberFormat = format;
207                 setNumbering(xNumbering, yNumbering);
208         }
209         /**
210         * Sets the display format used for x-axis numbering.
211         */

212         public final void setXNumberFormat(NumberFormat JavaDoc format) {
213                 xNumberFormat = format;
214                 setNumbering(xNumbering, yNumbering);
215         }
216         /**
217         * Sets the display format used for y-axis numbering.
218         */

219         public final void setYNumberFormat(NumberFormat JavaDoc format) {
220                 yNumberFormat = format;
221                 setNumbering(xNumbering, yNumbering);
222         }
223     /**
224     * Turns the axis lines on/off.
225     */

226     public final void setAxisLines(boolean xFlag, boolean yFlag) {
227         xAxisLine = xFlag;
228         yAxisLine = yFlag;
229         redraw();
230     }
231         /**
232         * Turns grid lines on/off.
233         * Default is off.
234         */

235         public final void setGridLines(boolean flag) {
236                 gridLines = flag;
237                 redraw();
238         }
239         /**
240         * Sets the x-axis scale type.
241         * @param t a _SCALE constant.
242         */

243         public final void setXScale(int t) {
244                 if(xScaleType != t) {
245                         xScaleType = t;
246                         dataChanged(new GraphDataEvent(model));
247                 }
248         }
249         /**
250         * Sets the y-axis scale type.
251         * @param t a _SCALE constant.
252         */

253         public final void setYScale(int t) {
254                 if(yScaleType != t) {
255                         yScaleType = t;
256                         dataChanged(new GraphDataEvent(model));
257                 }
258         }
259         /**
260         * Sets the x-axis numbering increment.
261         * @param dx use 0.0f for auto-adjusting (default).
262         */

263         public final void setXIncrement(float dx) {
264                 if(dx < 0.0f) {
265                         throw new IllegalArgumentException JavaDoc("Increment should be positive.");
266                 } else if(dx == 0.0f) {
267                         if(!autoXInc) {
268                                 autoXInc = true;
269                                 rescale();
270                         }
271                 } else {
272                         autoXInc = false;
273                         if(dx != xInc) {
274                                 xInc = dx;
275                                 rescale();
276                         }
277                 }
278         }
279         /**
280         * Returns the x-axis numbering increment.
281         */

282         public final float getXIncrement() {
283                 return xInc;
284         }
285         /**
286         * Sets the y-axis numbering increment.
287         * @param dy use 0.0f for auto-adjusting (default).
288         */

289         public final void setYIncrement(float dy) {
290                 if(dy < 0.0f) {
291                         throw new IllegalArgumentException JavaDoc("Increment should be positive.");
292                 } else if(dy == 0.0f) {
293                         if(!autoYInc) {
294                                 autoYInc = true;
295                                 rescale();
296                         }
297                 } else {
298                         autoYInc = false;
299                         if(dy != yInc) {
300                                 yInc = dy;
301                                 rescale();
302                         }
303                 }
304         }
305         /**
306         * Returns the y-axis numbering increment.
307         */

308         public final float getYIncrement() {
309                 return yInc;
310         }
311         /**
312         * Sets the minimum/maximum values on the x-axis.
313         * Set both min and max to 0.0f for auto-adjusting (default).
314         */

315         public final void setXExtrema(float min, float max) {
316                 if(min==0.0f && max==0.0f) {
317                         autoXExtrema=true;
318                         // determine min and max from model
319
min=Float.POSITIVE_INFINITY;
320                         max=Float.NEGATIVE_INFINITY;
321                         float tmp;
322                         model.firstSeries();
323                         do {
324                                 for(int i=0;i<model.seriesLength();i++) {
325                                         tmp=model.getXCoord(i);
326                                         if(!Float.isNaN(tmp)) {
327                                                 min=Math.min(tmp,min);
328                                                 max=Math.max(tmp,max);
329                                         }
330                                 }
331                         } while(model.nextSeries());
332                         if(min==max) {
333                                 // default values if no variation in data
334
if(yScaleType == LOG_SCALE) {
335                                         min/=10.0f;
336                                         max*=10.0f;
337                                 } else {
338                                         min-=0.5f;
339                                         max+=0.5f;
340                                 }
341                         }
342                         if(min==Float.POSITIVE_INFINITY || max==Float.NEGATIVE_INFINITY) {
343                                 // default values if no data
344
if(xScaleType == LOG_SCALE) {
345                                         min = 1.0f;
346                                         max = 100.0f;
347                                 } else {
348                                         min=-5.0f;
349                                         max=5.0f;
350                                 }
351                         }
352                 } else if(max<=min) {
353                         throw new IllegalArgumentException JavaDoc("Maximum should be greater than minimum; max = "+max+" and min = "+min);
354                 } else {
355                         autoXExtrema=false;
356                 }
357                 rescaleX(min, max);
358         }
359     /**
360      * @param growth amount to extend the x-axis by, when data exceeds the extrema.
361      */

362         public final void setXExtrema(float min, float max, float growth) {
363                 setXExtrema(min, max);
364                 xGrowth = growth;
365         }
366     public final float getXMinimum() {
367         return minX;
368     }
369     public final float getXMaximum() {
370         return maxX;
371     }
372         private void rescaleX(final float min, final float max) {
373                 if(min != minX || max != maxX) {
374                         minX = min;
375                         maxX = max;
376                         if(xScaleType == LOG_SCALE) {
377                                 scaledMinX = (float) Math.log(minX);
378                                 scaledMaxX = (float) Math.log(maxX);
379                         } else {
380                                 scaledMinX = minX;
381                                 scaledMaxX = maxX;
382                         }
383                     setNumbering(xNumbering, yNumbering);
384                 }
385         }
386         /**
387         * Sets the minimum/maximum values on the y-axis.
388         * Set both min and max to 0.0f for auto-adjusting (default).
389         */

390         public final void setYExtrema(float min, float max) {
391                 if(min==0.0f && max==0.0f) {
392                         autoYExtrema=true;
393                         // determine min and max from model
394
min=Float.POSITIVE_INFINITY;
395                         max=Float.NEGATIVE_INFINITY;
396                         float tmp;
397                         model.firstSeries();
398                         do {
399                                 for(int i=0;i<model.seriesLength();i++) {
400                                         tmp=model.getYCoord(i);
401                                         if(!Float.isNaN(tmp)) {
402                                                 min=Math.min(tmp,min);
403                                                 max=Math.max(tmp,max);
404                                         }
405                                 }
406                         } while(model.nextSeries());
407                         if(min==max) {
408                                 // default values if no variation in data
409
if(yScaleType == LOG_SCALE) {
410                                         min/=10.0f;
411                                         max*=10.0f;
412                                 } else {
413                                         min-=0.5f;
414                                         max+=0.5f;
415                                 }
416                         }
417                         if(min==Float.POSITIVE_INFINITY || max==Float.NEGATIVE_INFINITY) {
418                                 // default values if no data
419
if(yScaleType == LOG_SCALE) {
420                                         min = 1.0f;
421                                         max = 100.0f;
422                                 } else {
423                                         min=-5.0f;
424                                         max=5.0f;
425                                 }
426                         }
427                 } else if(max<=min) {
428                         throw new IllegalArgumentException JavaDoc("Maximum should be greater than minimum; max = "+max+" and min = "+min);
429                 } else {
430                         autoYExtrema=false;
431                 }
432                 rescaleY(min, max);
433         }
434     /**
435      * @param growth amount to extend the y-axis by, when data exceeds the extrema.
436      */

437         public final void setYExtrema(float min, float max, float growth) {
438                 setYExtrema(min, max);
439                 yGrowth = growth;
440         }
441     public final float getYMinimum() {
442         return minY;
443     }
444     public final float getYMaximum() {
445         return maxY;
446     }
447         private void rescaleY(final float min, final float max) {
448                 if(min != minY || max != maxY) {
449                         minY = min;
450                         maxY = max;
451                         if(yScaleType == LOG_SCALE) {
452                                 scaledMinY = (float) Math.log(minY);
453                                 scaledMaxY = (float) Math.log(maxY);
454                         } else {
455                                 scaledMinY = minY;
456                                 scaledMaxY = maxY;
457                         }
458                     setNumbering(xNumbering, yNumbering);
459                 }
460         }
461         /**
462         * Returns the bounding box for the axis extrema.
463         */

464         public final Rectangle2D.Float JavaDoc getExtrema() {
465                 return new Rectangle2D.Float JavaDoc(minX, minY, maxX-minX, maxY-minY);
466         }
467         /**
468         * Sets the data marker used to draw data points.
469         */

470         public final void setMarker(Graph2D.DataMarker marker) {
471                 dataMarker = marker;
472                 redraw();
473         }
474         /**
475         * Sets the color of the nth y-series.
476         * @param n the index of the y-series.
477         * @param c the line color.
478         */

479         public final void setColor(int n,Color c) {
480                 seriesColor[n]=c;
481                 redraw();
482         }
483         /**
484         * Gets the color of the nth y-series.
485         * @param n the index of the y-series.
486         */

487         public final Color getColor(int n) {
488                 return seriesColor[n];
489         }
490         /**
491         * Reshapes this graph to the specified bounding box.
492         */

493         public final void setBounds(int x,int y,int width,int height) {
494                 super.setBounds(x,y,width,height);
495                 rescale();
496         }
497         /**
498         * Returns the preferred size of this component.
499         */

500         public Dimension getPreferredSize() {
501                 return getMinimumSize();
502         }
503         /**
504         * Returns the minimum size of this component.
505         */

506         public Dimension getMinimumSize() {
507                 return new Dimension(170, 170);
508         }
509         /**
510         * Rescales this graph.
511         */

512         protected final void rescale() {
513                 final Dimension minSize = getMinimumSize();
514                 final Dimension size = getSize();
515                 final int thisWidth=Math.max(size.width, minSize.width);
516                 final int thisHeight=Math.max(size.height, minSize.height);
517                 xScale = (float) ((double)(thisWidth-(leftAxisPad+axisPad)) / (double)(scaledMaxX-scaledMinX));
518                 yScale = (float) ((double)(thisHeight-2*axisPad) / (double)(scaledMaxY-scaledMinY));
519                 if(autoXInc) {
520                         xInc = (float) ExtraMath.round((double)xIncPixels/(double)xScale, 1);
521                         if(xInc == 0.0f)
522                                 xInc = Float.MIN_VALUE;
523                 }
524                 //assert xInc > 0.0f;
525
if(autoYInc) {
526                         yInc = (float) ExtraMath.round((double)yIncPixels/(double)yScale, 1);
527                         if(yInc == 0.0f)
528                                 yInc = Float.MIN_VALUE;
529                 }
530                 //assert yInc > 0.0f;
531
origin.x=leftAxisPad-Math.round(scaledMinX*xScale);
532                 origin.y=thisHeight-axisPad+Math.round(scaledMinY*yScale);
533                 redraw();
534         }
535         /**
536         * Converts a data point to screen coordinates.
537         */

538         protected final Point dataToScreen(float x,float y) {
539                 if(xScaleType == LOG_SCALE)
540                         x = (float) Math.log(x);
541                 if(yScaleType == LOG_SCALE)
542                         y = (float) Math.log(y);
543                 return scaledDataToScreen(x, y);
544         }
545         /**
546         * Converts a scaled data point to screen coordinates.
547         */

548         protected final Point scaledDataToScreen(float x, float y) {
549                 return new Point(origin.x+Math.round(xScale*x), origin.y-Math.round(yScale*y));
550         }
551         /**
552         * Converts a screen point to data coordinates.
553         */

554         protected final Point2D.Float JavaDoc screenToData(Point p) {
555                 double x = (double)(p.x-origin.x) / (double)xScale;
556                 double y = (double)(origin.y-p.y) / (double)yScale;
557                 if(xScaleType == LOG_SCALE)
558                         x = Math.exp(x);
559                 if(yScaleType == LOG_SCALE)
560                         y = Math.exp(y);
561                 return new Point2D.Float JavaDoc((float)x, (float)y);
562         }
563         /**
564         * Draws the graph axes.
565         */

566         protected final void drawAxes(Graphics g) {
567                 final Dimension size = getSize();
568                 final int width = size.width;
569                 final int height = size.height;
570                 g.setColor(getForeground());
571 // grid lines and numbering
572
if(gridLines || xNumbering) {
573 // x-axis numbering and vertical grid lines
574
float xAxisY;
575                         if(scaledMinY > 0.0f)
576                                 xAxisY = scaledMinY;
577                         else if(scaledMaxY < 0.0f)
578                                 xAxisY = scaledMaxY;
579                         else
580                                 xAxisY = 0.0f;
581                         for(double x=(scaledMinX>0.0f)?scaledMinX:xInc; x<=scaledMaxX; x+=xInc) {
582                                 Point p=scaledDataToScreen((float)x, xAxisY);
583                                 if(gridLines) {
584                                         g.setColor(gridLineColor);
585                                         g.drawLine(p.x, axisPad-scalePad, p.x, height-(axisPad-scalePad));
586                                         g.setColor(getForeground());
587                                 }
588                                 if(xNumbering) {
589                     drawXLabel(g, x, p);
590                                 }
591                         }
592                         for(double x=-xInc; x>=scaledMinX; x-=xInc) {
593                                 Point p=scaledDataToScreen((float)x, xAxisY);
594                                 if(gridLines) {
595                                         g.setColor(gridLineColor);
596                                         g.drawLine(p.x, axisPad-scalePad, p.x, height-(axisPad-scalePad));
597                                         g.setColor(getForeground());
598                                 }
599                                 if(xNumbering) {
600                     drawXLabel(g, x, p);
601                                 }
602                         }
603         }
604         if(gridLines || yNumbering) {
605 // y-axis numbering and horizontal grid lines
606
float yAxisX;
607                         if(scaledMinX > 0.0f)
608                                 yAxisX = scaledMinX;
609                         else if(scaledMaxX < 0.0f)
610                                 yAxisX = scaledMaxX;
611                         else
612                                 yAxisX = 0.0f;
613                         for(double y=(scaledMinY>0.0f)?scaledMinY:yInc; y<=scaledMaxY; y+=yInc) {
614                                 Point p=scaledDataToScreen(yAxisX, (float)y);
615                                 if(gridLines) {
616                                         g.setColor(gridLineColor);
617                                         g.drawLine(leftAxisPad-scalePad, p.y, width-(axisPad-scalePad), p.y);
618                                         g.setColor(getForeground());
619                                 }
620                                 if(yNumbering) {
621                     drawYLabel(g, y, p);
622                                 }
623                         }
624                         for(double y=-yInc; y>=scaledMinY; y-=yInc) {
625                                 Point p=scaledDataToScreen(yAxisX, (float)y);
626                                 if(gridLines) {
627                                         g.setColor(gridLineColor);
628                                         g.drawLine(leftAxisPad-scalePad, p.y, width-(axisPad-scalePad), p.y);
629                                         g.setColor(getForeground());
630                                 }
631                                 if(yNumbering) {
632                     drawYLabel(g, y, p);
633                                 }
634                         }
635                 }
636
637 // axis lines
638
if(xAxisLine) {
639                 // horizontal axis
640
if(scaledMinY > 0.0f) {
641                         // draw at bottom
642
g.drawLine(leftAxisPad-scalePad, height-axisPad, width-(axisPad-scalePad), height-axisPad);
643                 } else if(scaledMaxY < 0.0f) {
644                         // draw at top
645
g.drawLine(leftAxisPad-scalePad, axisPad, width-(axisPad-scalePad), axisPad);
646                 } else {
647                         // draw through y origin
648
g.drawLine(leftAxisPad-scalePad, origin.y, width-(axisPad-scalePad), origin.y);
649                 }
650     }
651     if(yAxisLine) {
652                 // vertical axis
653
if(scaledMinX > 0.0f) {
654                         // draw at left
655
g.drawLine(leftAxisPad, axisPad-scalePad, leftAxisPad, height-(axisPad-scalePad));
656                 } else if(scaledMaxX < 0.0f) {
657                         // draw at right
658
g.drawLine(width-axisPad, axisPad-scalePad, width-axisPad, height-(axisPad-scalePad));
659                 } else {
660                         // draw through x origin
661
g.drawLine(origin.x, axisPad-scalePad, origin.x, height-(axisPad-scalePad));
662                 }
663     }
664         }
665     protected void drawXLabel(Graphics g, double x, Point p) {
666         double scaledX;
667         if(xScaleType == LOG_SCALE)
668             scaledX = Math.exp(x);
669         else
670             scaledX = x;
671         String JavaDoc str = xNumberFormat.format(scaledX);
672         FontMetrics metrics=g.getFontMetrics();
673         int strWidth=metrics.stringWidth(str);
674         int strHeight=metrics.getHeight();
675         g.drawLine(p.x,p.y,p.x,p.y+5);
676         g.drawString(str,p.x-strWidth/2,p.y+5+strHeight);
677     }
678     protected void drawYLabel(Graphics g, double y, Point p) {
679         double scaledY;
680         if(yScaleType == LOG_SCALE)
681             scaledY = Math.exp(y);
682         else
683             scaledY = y;
684         String JavaDoc str = yNumberFormat.format(scaledY);
685         FontMetrics metrics=g.getFontMetrics();
686         int strWidth=metrics.stringWidth(str);
687         int strHeight=metrics.getHeight();
688         g.drawLine(p.x,p.y,p.x-5,p.y);
689         g.drawString(str,p.x-8-strWidth,p.y+strHeight/3);
690     }
691         /**
692         * Draws the graph data.
693         * Override this method to change how the graph data is plotted.
694         */

695         protected void drawData(Graphics g) {
696 // points
697
model.firstSeries();
698                 g.setColor(seriesColor[0]);
699                 int i;
700                 for(i=0; i<model.seriesLength(); i++)
701                         drawDataPoint(g, i);
702                 for(int n=1; model.nextSeries(); n++) {
703                         g.setColor(seriesColor[n]);
704                         for(i=0; i<model.seriesLength(); i++)
705                                 drawDataPoint(g, i);
706                 }
707         }
708         /**
709         * Draws a single data point in the current series.
710         * @param i index of the data point.
711         */

712         private void drawDataPoint(Graphics g, int i) {
713                 Point p = dataToScreen(model.getXCoord(i), model.getYCoord(i));
714                 dataMarker.paint(g, p.x, p.y);
715         }
716         /**
717         * Paints the graph (draws the graph axes and data).
718         */

719         protected void offscreenPaint(Graphics g) {
720                 drawAxes(g);
721                 final Dimension size = getSize();
722                 final int width = size.width;
723                 final int height = size.height;
724                 g.setClip(leftAxisPad-scalePad, axisPad-scalePad, width-(leftAxisPad+axisPad-2*scalePad), height-2*(axisPad-scalePad));
725                 drawData(g);
726         }
727
728         public interface DataMarker {
729                 public void paint(Graphics g, int x, int y);
730
731                 public static final DataMarker NONE = new NoneDataMarker();
732                 public class Circle implements DataMarker {
733                         private final int size;
734                         public Circle(int size) {
735                                 this.size = size;
736                         }
737                         public void paint(Graphics g, int x, int y) {
738                                 g.fillOval(x-size/2, y-size/2, size, size);
739                         }
740                 }
741                 public class Square implements DataMarker {
742                         private final int size;
743                         public Square(int size) {
744                                 this.size = size;
745                         }
746                         public void paint(Graphics g, int x, int y) {
747                                 g.fillRect(x-size/2, y-size/2, size, size);
748                         }
749                 }
750         }
751         private static class NoneDataMarker implements DataMarker {
752                 private NoneDataMarker() {}
753                 public void paint(Graphics g, int x, int y) {}
754         }
755 }
756
757
Popular Tags