KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > xy > StandardXYItemRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ---------------------------
28  * StandardXYItemRenderer.java
29  * ---------------------------
30  * (C) Copyright 2001-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): Mark Watson (www.markwatson.com);
34  * Jonathan Nash;
35  * Andreas Schneider;
36  * Norbert Kiesel (for TBD Networks);
37  * Christian W. Zuckschwerdt;
38  * Bill Kelemen;
39  * Nicolas Brodu (for Astrium and EADS Corporate Research
40  * Center);
41  *
42  * $Id: StandardXYItemRenderer.java,v 1.18.2.6 2006/07/06 09:31:46 mungady Exp $
43  *
44  * Changes:
45  * --------
46  * 19-Oct-2001 : Version 1, based on code by Mark Watson (DG);
47  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
48  * 21-Dec-2001 : Added working line instance to improve performance (DG);
49  * 22-Jan-2002 : Added code to lock crosshairs to data points. Based on code
50  * by Jonathan Nash (DG);
51  * 23-Jan-2002 : Added DrawInfo parameter to drawItem() method (DG);
52  * 28-Mar-2002 : Added a property change listener mechanism so that the
53  * renderer no longer needs to be immutable (DG);
54  * 02-Apr-2002 : Modified to handle null values (DG);
55  * 09-Apr-2002 : Modified draw method to return void. Removed the translated
56  * zero from the drawItem method. Override the initialise()
57  * method to calculate it (DG);
58  * 13-May-2002 : Added code from Andreas Schneider to allow changing
59  * shapes/colors per item (DG);
60  * 24-May-2002 : Incorporated tooltips into chart entities (DG);
61  * 25-Jun-2002 : Removed redundant code (DG);
62  * 05-Aug-2002 : Incorporated URLs for HTML image maps into chart entities (RA);
63  * 08-Aug-2002 : Added discontinuous lines option contributed by
64  * Norbert Kiesel (DG);
65  * 20-Aug-2002 : Added user definable default values to be returned by
66  * protected methods unless overridden by a subclass (DG);
67  * 23-Sep-2002 : Updated for changes in the XYItemRenderer interface (DG);
68  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
69  * 25-Mar-2003 : Implemented Serializable (DG);
70  * 01-May-2003 : Modified drawItem() method signature (DG);
71  * 15-May-2003 : Modified to take into account the plot orientation (DG);
72  * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
73  * 30-Jul-2003 : Modified entity constructor (CZ);
74  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
75  * 24-Aug-2003 : Added null/NaN checks in drawItem (BK);
76  * 08-Sep-2003 : Fixed serialization (NB);
77  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
78  * 21-Jan-2004 : Override for getLegendItem() method (DG);
79  * 27-Jan-2004 : Moved working line into state object (DG);
80  * 10-Feb-2004 : Changed drawItem() method to make cut-and-paste overriding
81  * easier (DG);
82  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed
83  * XYToolTipGenerator --> XYItemLabelGenerator (DG);
84  * 08-Jun-2004 : Modified to use getX() and getY() methods (DG);
85  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
86  * getYValue() (DG);
87  * 25-Aug-2004 : Created addEntity() method in superclass (DG);
88  * 08-Oct-2004 : Added 'gapThresholdType' as suggested by Mike Watts (DG);
89  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
90  * 23-Feb-2005 : Fixed getLegendItem() method to show lines. Fixed bug
91  * 1077108 (shape not visible for first item in series) (DG);
92  * 10-Apr-2005 : Fixed item label positioning with horizontal orientation (DG);
93  * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
94  * 27-Apr-2005 : Use generator for series label in legend (DG);
95  * 15-Jun-2006 : Fixed bug (1380480) for rendering series as path (DG);
96  *
97  */

98
99 package org.jfree.chart.renderer.xy;
100
101 import java.awt.Graphics2D JavaDoc;
102 import java.awt.Image JavaDoc;
103 import java.awt.Paint JavaDoc;
104 import java.awt.Point JavaDoc;
105 import java.awt.Shape JavaDoc;
106 import java.awt.Stroke JavaDoc;
107 import java.awt.geom.GeneralPath JavaDoc;
108 import java.awt.geom.Line2D JavaDoc;
109 import java.awt.geom.Rectangle2D JavaDoc;
110 import java.io.IOException JavaDoc;
111 import java.io.ObjectInputStream JavaDoc;
112 import java.io.ObjectOutputStream JavaDoc;
113 import java.io.Serializable JavaDoc;
114
115 import org.jfree.chart.LegendItem;
116 import org.jfree.chart.axis.ValueAxis;
117 import org.jfree.chart.entity.EntityCollection;
118 import org.jfree.chart.event.RendererChangeEvent;
119 import org.jfree.chart.labels.XYToolTipGenerator;
120 import org.jfree.chart.plot.CrosshairState;
121 import org.jfree.chart.plot.Plot;
122 import org.jfree.chart.plot.PlotOrientation;
123 import org.jfree.chart.plot.PlotRenderingInfo;
124 import org.jfree.chart.plot.XYPlot;
125 import org.jfree.chart.urls.XYURLGenerator;
126 import org.jfree.data.xy.XYDataset;
127 import org.jfree.io.SerialUtilities;
128 import org.jfree.ui.RectangleEdge;
129 import org.jfree.util.BooleanList;
130 import org.jfree.util.BooleanUtilities;
131 import org.jfree.util.ObjectUtilities;
132 import org.jfree.util.PublicCloneable;
133 import org.jfree.util.ShapeUtilities;
134 import org.jfree.util.UnitType;
135
136 /**
137  * Standard item renderer for an {@link XYPlot}. This class can draw (a)
138  * shapes at each point, or (b) lines between points, or (c) both shapes and
139  * lines.
140  */

141 public class StandardXYItemRenderer extends AbstractXYItemRenderer
142                                     implements XYItemRenderer,
143                                                Cloneable JavaDoc,
144                                                PublicCloneable,
145                                                Serializable JavaDoc {
146
147     /** For serialization. */
148     private static final long serialVersionUID = -3271351259436865995L;
149     
150     /** Constant for the type of rendering (shapes only). */
151     public static final int SHAPES = 1;
152
153     /** Constant for the type of rendering (lines only). */
154     public static final int LINES = 2;
155
156     /** Constant for the type of rendering (shapes and lines). */
157     public static final int SHAPES_AND_LINES = SHAPES | LINES;
158
159     /** Constant for the type of rendering (images only). */
160     public static final int IMAGES = 4;
161
162     /** Constant for the type of rendering (discontinuous lines). */
163     public static final int DISCONTINUOUS = 8;
164
165     /** Constant for the type of rendering (discontinuous lines). */
166     public static final int DISCONTINUOUS_LINES = LINES | DISCONTINUOUS;
167
168     /** A flag indicating whether or not shapes are drawn at each XY point. */
169     private boolean baseShapesVisible;
170
171     /** A flag indicating whether or not lines are drawn between XY points. */
172     private boolean plotLines;
173
174     /** A flag indicating whether or not images are drawn between XY points. */
175     private boolean plotImages;
176
177     /** A flag controlling whether or not discontinuous lines are used. */
178     private boolean plotDiscontinuous;
179
180     /** Specifies how the gap threshold value is interpreted. */
181     private UnitType gapThresholdType = UnitType.RELATIVE;
182     
183     /** Threshold for deciding when to discontinue a line. */
184     private double gapThreshold = 1.0;
185
186     /** A flag that controls whether or not shapes are filled for ALL series. */
187     private Boolean JavaDoc shapesFilled;
188
189     /**
190      * A table of flags that control (per series) whether or not shapes are
191      * filled.
192      */

193     private BooleanList seriesShapesFilled;
194
195     /** The default value returned by the getShapeFilled() method. */
196     private boolean baseShapesFilled;
197
198     /**
199      * A flag that controls whether or not each series is drawn as a single
200      * path.
201      */

202     private boolean drawSeriesLineAsPath;
203
204     /**
205      * The shape that is used to represent a line in the legend.
206      * This should never be set to <code>null</code>.
207      */

208     private transient Shape JavaDoc legendLine;
209     
210     /**
211      * Constructs a new renderer.
212      */

213     public StandardXYItemRenderer() {
214         this(LINES, null);
215     }
216
217     /**
218      * Constructs a new renderer.
219      * <p>
220      * To specify the type of renderer, use one of the constants: SHAPES, LINES
221      * or SHAPES_AND_LINES.
222      *
223      * @param type the type.
224      */

225     public StandardXYItemRenderer(int type) {
226         this(type, null);
227     }
228
229     /**
230      * Constructs a new renderer.
231      * <p>
232      * To specify the type of renderer, use one of the constants: SHAPES, LINES
233      * or SHAPES_AND_LINES.
234      *
235      * @param type the type of renderer.
236      * @param toolTipGenerator the item label generator (<code>null</code>
237      * permitted).
238      */

239     public StandardXYItemRenderer(int type,
240                                   XYToolTipGenerator toolTipGenerator) {
241         this(type, toolTipGenerator, null);
242     }
243
244     /**
245      * Constructs a new renderer.
246      * <p>
247      * To specify the type of renderer, use one of the constants: SHAPES, LINES
248      * or SHAPES_AND_LINES.
249      *
250      * @param type the type of renderer.
251      * @param toolTipGenerator the item label generator (<code>null</code>
252      * permitted).
253      * @param urlGenerator the URL generator.
254      */

255     public StandardXYItemRenderer(int type,
256                                   XYToolTipGenerator toolTipGenerator,
257                                   XYURLGenerator urlGenerator) {
258
259         super();
260         setToolTipGenerator(toolTipGenerator);
261         setURLGenerator(urlGenerator);
262         if ((type & SHAPES) != 0) {
263             this.baseShapesVisible = true;
264         }
265         if ((type & LINES) != 0) {
266             this.plotLines = true;
267         }
268         if ((type & IMAGES) != 0) {
269             this.plotImages = true;
270         }
271         if ((type & DISCONTINUOUS) != 0) {
272             this.plotDiscontinuous = true;
273         }
274
275         this.shapesFilled = null;
276         this.seriesShapesFilled = new BooleanList();
277         this.baseShapesFilled = true;
278         this.legendLine = new Line2D.Double JavaDoc(-7.0, 0.0, 7.0, 0.0);
279         this.drawSeriesLineAsPath = false;
280     }
281
282     /**
283      * Returns true if shapes are being plotted by the renderer.
284      *
285      * @return <code>true</code> if shapes are being plotted by the renderer.
286      */

287     public boolean getBaseShapesVisible() {
288         return this.baseShapesVisible;
289     }
290
291     /**
292      * Sets the flag that controls whether or not a shape is plotted at each
293      * data point.
294      *
295      * @param flag the flag.
296      */

297     public void setBaseShapesVisible(boolean flag) {
298         if (this.baseShapesVisible != flag) {
299             this.baseShapesVisible = flag;
300             notifyListeners(new RendererChangeEvent(this));
301         }
302     }
303
304     // SHAPES FILLED
305

306     /**
307      * Returns the flag used to control whether or not the shape for an item is
308      * filled.
309      * <p>
310      * The default implementation passes control to the
311      * <code>getSeriesShapesFilled</code> method. You can override this method
312      * if you require different behaviour.
313      *
314      * @param series the series index (zero-based).
315      * @param item the item index (zero-based).
316      *
317      * @return A boolean.
318      */

319     public boolean getItemShapeFilled(int series, int item) {
320         return getSeriesShapesFilled(series);
321     }
322
323     /**
324      * Returns the flag used to control whether or not the shapes for a series
325      * are filled.
326      *
327      * @param series the series index (zero-based).
328      *
329      * @return A boolean.
330      */

331     public boolean getSeriesShapesFilled(int series) {
332
333         // return the overall setting, if there is one...
334
if (this.shapesFilled != null) {
335             return this.shapesFilled.booleanValue();
336         }
337
338         // otherwise look up the paint table
339
Boolean JavaDoc flag = this.seriesShapesFilled.getBoolean(series);
340         if (flag != null) {
341             return flag.booleanValue();
342         }
343         else {
344             return this.baseShapesFilled;
345         }
346
347     }
348
349     /**
350      * Sets the 'shapes filled' for ALL series.
351      *
352      * @param filled the flag.
353      */

354     public void setShapesFilled(boolean filled) {
355         // here we use BooleanUtilities to remain compatible with JDKs < 1.4
356
setShapesFilled(BooleanUtilities.valueOf(filled));
357     }
358
359     /**
360      * Sets the 'shapes filled' for ALL series.
361      *
362      * @param filled the flag (<code>null</code> permitted).
363      */

364     public void setShapesFilled(Boolean JavaDoc filled) {
365         this.shapesFilled = filled;
366     }
367
368     /**
369      * Sets the 'shapes filled' flag for a series.
370      *
371      * @param series the series index (zero-based).
372      * @param flag the flag.
373      */

374     public void setSeriesShapesFilled(int series, Boolean JavaDoc flag) {
375         this.seriesShapesFilled.setBoolean(series, flag);
376     }
377
378     /**
379      * Returns the base 'shape filled' attribute.
380      *
381      * @return The base flag.
382      */

383     public boolean getBaseShapesFilled() {
384         return this.baseShapesFilled;
385     }
386
387     /**
388      * Sets the base 'shapes filled' flag.
389      *
390      * @param flag the flag.
391      */

392     public void setBaseShapesFilled(boolean flag) {
393         this.baseShapesFilled = flag;
394     }
395
396     /**
397      * Returns true if lines are being plotted by the renderer.
398      *
399      * @return <code>true</code> if lines are being plotted by the renderer.
400      */

401     public boolean getPlotLines() {
402         return this.plotLines;
403     }
404
405     /**
406      * Sets the flag that controls whether or not a line is plotted between
407      * each data point.
408      *
409      * @param flag the flag.
410      */

411     public void setPlotLines(boolean flag) {
412         if (this.plotLines != flag) {
413             this.plotLines = flag;
414             notifyListeners(new RendererChangeEvent(this));
415         }
416     }
417
418     /**
419      * Returns the gap threshold type (relative or absolute).
420      *
421      * @return The type.
422      */

423     public UnitType getGapThresholdType() {
424         return this.gapThresholdType;
425     }
426     
427     /**
428      * Sets the gap threshold type.
429      *
430      * @param thresholdType the type (<code>null</code> not permitted).
431      */

432     public void setGapThresholdType(UnitType thresholdType) {
433         if (thresholdType == null) {
434             throw new IllegalArgumentException JavaDoc(
435                     "Null 'thresholdType' argument.");
436         }
437         this.gapThresholdType = thresholdType;
438         notifyListeners(new RendererChangeEvent(this));
439     }
440     
441     /**
442      * Returns the gap threshold for discontinuous lines.
443      *
444      * @return The gap threshold.
445      */

446     public double getGapThreshold() {
447         return this.gapThreshold;
448     }
449
450     /**
451      * Sets the gap threshold for discontinuous lines.
452      *
453      * @param t the threshold.
454      */

455     public void setGapThreshold(double t) {
456         this.gapThreshold = t;
457         notifyListeners(new RendererChangeEvent(this));
458     }
459
460     /**
461      * Returns true if images are being plotted by the renderer.
462      *
463      * @return <code>true</code> if images are being plotted by the renderer.
464      */

465     public boolean getPlotImages() {
466         return this.plotImages;
467     }
468
469     /**
470      * Sets the flag that controls whether or not an image is drawn at each
471      * data point.
472      *
473      * @param flag the flag.
474      */

475     public void setPlotImages(boolean flag) {
476         if (this.plotImages != flag) {
477             this.plotImages = flag;
478             notifyListeners(new RendererChangeEvent(this));
479         }
480     }
481
482     /**
483      * Returns true if lines should be discontinuous.
484      *
485      * @return <code>true</code> if lines should be discontinuous.
486      */

487     public boolean getPlotDiscontinuous() {
488         return this.plotDiscontinuous;
489     }
490
491     /**
492      * Returns a flag that controls whether or not each series is drawn as a
493      * single path.
494      *
495      * @return A boolean.
496      */

497     public boolean getDrawSeriesLineAsPath() {
498         return this.drawSeriesLineAsPath;
499     }
500     
501     /**
502      * Sets the flag that controls whether or not each series is drawn as a
503      * single path.
504      *
505      * @param flag the flag.
506      */

507     public void setDrawSeriesLineAsPath(boolean flag) {
508         this.drawSeriesLineAsPath = flag;
509     }
510     
511     /**
512      * Returns the shape used to represent a line in the legend.
513      *
514      * @return The legend line (never <code>null</code>).
515      */

516     public Shape JavaDoc getLegendLine() {
517         return this.legendLine;
518     }
519     
520     /**
521      * Sets the shape used as a line in each legend item and sends a
522      * {@link RendererChangeEvent} to all registered listeners.
523      *
524      * @param line the line (<code>null</code> not permitted).
525      */

526     public void setLegendLine(Shape JavaDoc line) {
527         if (line == null) {
528             throw new IllegalArgumentException JavaDoc("Null 'line' argument.");
529         }
530         this.legendLine = line;
531         notifyListeners(new RendererChangeEvent(this));
532     }
533
534     /**
535      * Returns a legend item for a series.
536      *
537      * @param datasetIndex the dataset index (zero-based).
538      * @param series the series index (zero-based).
539      *
540      * @return A legend item for the series.
541      */

542     public LegendItem getLegendItem(int datasetIndex, int series) {
543         XYPlot plot = getPlot();
544         if (plot == null) {
545             return null;
546         }
547         LegendItem result = null;
548         XYDataset dataset = plot.getDataset(datasetIndex);
549         if (dataset != null) {
550             if (getItemVisible(series, 0)) {
551                 String JavaDoc label = getLegendItemLabelGenerator().generateLabel(
552                         dataset, series);
553                 String JavaDoc description = label;
554                 String JavaDoc toolTipText = null;
555                 if (getLegendItemToolTipGenerator() != null) {
556                     toolTipText = getLegendItemToolTipGenerator().generateLabel(
557                             dataset, series);
558                 }
559                 String JavaDoc urlText = null;
560                 if (getLegendItemURLGenerator() != null) {
561                     urlText = getLegendItemURLGenerator().generateLabel(
562                             dataset, series);
563                 }
564                 Shape JavaDoc shape = getSeriesShape(series);
565                 boolean shapeFilled = getSeriesShapesFilled(series);
566                 Paint JavaDoc paint = getSeriesPaint(series);
567                 Paint JavaDoc linePaint = paint;
568                 Stroke JavaDoc lineStroke = getSeriesStroke(series);
569                 result = new LegendItem(label, description, toolTipText,
570                         urlText, this.baseShapesVisible, shape, shapeFilled,
571                         paint, !shapeFilled, paint, lineStroke,
572                         this.plotLines, this.legendLine, lineStroke, linePaint);
573             }
574         }
575         return result;
576     }
577
578     /**
579      * Records the state for the renderer. This is used to preserve state
580      * information between calls to the drawItem() method for a single chart
581      * drawing.
582      */

583     public static class State extends XYItemRendererState {
584         
585         /** The path for the current series. */
586         public GeneralPath JavaDoc seriesPath;
587         
588         /** The series index. */
589         private int seriesIndex;
590         
591         /**
592          * A flag that indicates if the last (x, y) point was 'good'
593          * (non-null).
594          */

595         private boolean lastPointGood;
596         
597         /**
598          * Creates a new state instance.
599          *
600          * @param info the plot rendering info.
601          */

602         public State(PlotRenderingInfo info) {
603             super(info);
604         }
605         
606         /**
607          * Returns a flag that indicates if the last point drawn (in the
608          * current series) was 'good' (non-null).
609          *
610          * @return A boolean.
611          */

612         public boolean isLastPointGood() {
613             return this.lastPointGood;
614         }
615         
616         /**
617          * Sets a flag that indicates if the last point drawn (in the current
618          * series) was 'good' (non-null).
619          *
620          * @param good the flag.
621          */

622         public void setLastPointGood(boolean good) {
623             this.lastPointGood = good;
624         }
625         
626         /**
627          * Returns the series index for the current path.
628          *
629          * @return The series index for the current path.
630          */

631         public int getSeriesIndex() {
632             return seriesIndex;
633         }
634         
635         /**
636          * Sets the series index for the current path.
637          *
638          * @param index the index.
639          */

640         public void setSeriesIndex(int index) {
641             this.seriesIndex = index;
642         }
643     }
644     
645     /**
646      * Initialises the renderer.
647      * <P>
648      * This method will be called before the first item is rendered, giving the
649      * renderer an opportunity to initialise any state information it wants to
650      * maintain. The renderer can do nothing if it chooses.
651      *
652      * @param g2 the graphics device.
653      * @param dataArea the area inside the axes.
654      * @param plot the plot.
655      * @param data the data.
656      * @param info an optional info collection object to return data back to
657      * the caller.
658      *
659      * @return The renderer state.
660      */

661     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
662                                           Rectangle2D JavaDoc dataArea,
663                                           XYPlot plot,
664                                           XYDataset data,
665                                           PlotRenderingInfo info) {
666
667         State state = new State(info);
668         state.seriesPath = new GeneralPath JavaDoc();
669         state.seriesIndex = -1;
670         return state;
671
672     }
673     
674     /**
675      * Draws the visual representation of a single data item.
676      *
677      * @param g2 the graphics device.
678      * @param state the renderer state.
679      * @param dataArea the area within which the data is being drawn.
680      * @param info collects information about the drawing.
681      * @param plot the plot (can be used to obtain standard color information
682      * etc).
683      * @param domainAxis the domain axis.
684      * @param rangeAxis the range axis.
685      * @param dataset the dataset.
686      * @param series the series index (zero-based).
687      * @param item the item index (zero-based).
688      * @param crosshairState crosshair information for the plot
689      * (<code>null</code> permitted).
690      * @param pass the pass index.
691      */

692     public void drawItem(Graphics2D JavaDoc g2,
693                          XYItemRendererState state,
694                          Rectangle2D JavaDoc dataArea,
695                          PlotRenderingInfo info,
696                          XYPlot plot,
697                          ValueAxis domainAxis,
698                          ValueAxis rangeAxis,
699                          XYDataset dataset,
700                          int series,
701                          int item,
702                          CrosshairState crosshairState,
703                          int pass) {
704
705         boolean itemVisible = getItemVisible(series, item);
706         
707         // setup for collecting optional entity info...
708
Shape JavaDoc entityArea = null;
709         EntityCollection entities = null;
710         if (info != null) {
711             entities = info.getOwner().getEntityCollection();
712         }
713
714         PlotOrientation orientation = plot.getOrientation();
715         Paint JavaDoc paint = getItemPaint(series, item);
716         Stroke JavaDoc seriesStroke = getItemStroke(series, item);
717         g2.setPaint(paint);
718         g2.setStroke(seriesStroke);
719
720         // get the data point...
721
double x1 = dataset.getXValue(series, item);
722         double y1 = dataset.getYValue(series, item);
723         if (Double.isNaN(x1) || Double.isNaN(y1)) {
724             itemVisible = false;
725         }
726
727         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
728         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
729         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
730         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
731
732         if (getPlotLines()) {
733             if (this.drawSeriesLineAsPath) {
734                 State s = (State) state;
735                 if (s.getSeriesIndex() != series) {
736                     // we are starting a new series path
737
s.seriesPath.reset();
738                     s.lastPointGood = false;
739                     s.setSeriesIndex(series);
740                 }
741                 
742                 // update path to reflect latest point
743
if (itemVisible && !Double.isNaN(transX1)
744                         && !Double.isNaN(transY1)) {
745                     float x = (float) transX1;
746                     float y = (float) transY1;
747                     if (orientation == PlotOrientation.HORIZONTAL) {
748                         x = (float) transY1;
749                         y = (float) transX1;
750                     }
751                     if (s.isLastPointGood()) {
752                         // TODO: check threshold
753
s.seriesPath.lineTo(x, y);
754                     }
755                     else {
756                         s.seriesPath.moveTo(x, y);
757                     }
758                     s.setLastPointGood(true);
759                 }
760                 else {
761                     s.setLastPointGood(false);
762                 }
763                 if (item == dataset.getItemCount(series) - 1) {
764                     if (s.seriesIndex == series) {
765                         // draw path
766
g2.setStroke(getSeriesStroke(series));
767                         g2.setPaint(getSeriesPaint(series));
768                         g2.draw(s.seriesPath);
769                     }
770                 }
771             }
772
773             else if (item != 0 && itemVisible) {
774                 // get the previous data point...
775
double x0 = dataset.getXValue(series, item - 1);
776                 double y0 = dataset.getYValue(series, item - 1);
777                 if (!Double.isNaN(x0) && !Double.isNaN(y0)) {
778                     boolean drawLine = true;
779                     if (getPlotDiscontinuous()) {
780                         // only draw a line if the gap between the current and
781
// previous data point is within the threshold
782
int numX = dataset.getItemCount(series);
783                         double minX = dataset.getXValue(series, 0);
784                         double maxX = dataset.getXValue(series, numX - 1);
785                         if (this.gapThresholdType == UnitType.ABSOLUTE) {
786                             drawLine = Math.abs(x1 - x0) <= this.gapThreshold;
787                         }
788                         else {
789                             drawLine = Math.abs(x1 - x0) <= ((maxX - minX)
790                                 / numX * getGapThreshold());
791                         }
792                     }
793                     if (drawLine) {
794                         double transX0 = domainAxis.valueToJava2D(x0, dataArea,
795                                 xAxisLocation);
796                         double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
797                                 yAxisLocation);
798
799                         // only draw if we have good values
800
if (Double.isNaN(transX0) || Double.isNaN(transY0)
801                             || Double.isNaN(transX1) || Double.isNaN(transY1)) {
802                             return;
803                         }
804
805                         if (orientation == PlotOrientation.HORIZONTAL) {
806                             state.workingLine.setLine(transY0, transX0,
807                                     transY1, transX1);
808                         }
809                         else if (orientation == PlotOrientation.VERTICAL) {
810                             state.workingLine.setLine(transX0, transY0,
811                                     transX1, transY1);
812                         }
813
814                         if (state.workingLine.intersects(dataArea)) {
815                             g2.draw(state.workingLine);
816                         }
817                     }
818                 }
819             }
820         }
821         
822         // we needed to get this far even for invisible items, to ensure that
823
// seriesPath updates happened, but now there is nothing more we need
824
// to do for non-visible items...
825
if (!itemVisible) {
826             return;
827         }
828
829         if (getBaseShapesVisible()) {
830
831             Shape JavaDoc shape = getItemShape(series, item);
832             if (orientation == PlotOrientation.HORIZONTAL) {
833                 shape = ShapeUtilities.createTranslatedShape(shape, transY1,
834                         transX1);
835             }
836             else if (orientation == PlotOrientation.VERTICAL) {
837                 shape = ShapeUtilities.createTranslatedShape(shape, transX1,
838                         transY1);
839             }
840             if (shape.intersects(dataArea)) {
841                 if (getItemShapeFilled(series, item)) {
842                     g2.fill(shape);
843                 }
844                 else {
845                     g2.draw(shape);
846                 }
847             }
848             entityArea = shape;
849
850         }
851
852         if (getPlotImages()) {
853             Image JavaDoc image = getImage(plot, series, item, transX1, transY1);
854             if (image != null) {
855                 Point JavaDoc hotspot = getImageHotspot(plot, series, item, transX1,
856                         transY1, image);
857                 g2.drawImage(image, (int) (transX1 - hotspot.getX()),
858                         (int) (transY1 - hotspot.getY()), null);
859                 entityArea = new Rectangle2D.Double JavaDoc(transX1 - hotspot.getX(),
860                         transY1 - hotspot.getY(), image.getWidth(null),
861                         image.getHeight(null));
862             }
863
864         }
865
866         // draw the item label if there is one...
867
if (isItemLabelVisible(series, item)) {
868             double xx = transX1;
869             double yy = transY1;
870             if (orientation == PlotOrientation.HORIZONTAL) {
871                 xx = transY1;
872                 yy = transX1;
873             }
874             drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
875                     (y1 < 0.0));
876         }
877
878         updateCrosshairValues(crosshairState, x1, y1, transX1, transY1,
879                 orientation);
880
881         // add an entity for the item...
882
if (entities != null) {
883             addEntity(entities, entityArea, dataset, series, item,
884                     transX1, transY1);
885         }
886
887     }
888
889     /**
890      * Tests this renderer for equality with another object.
891      *
892      * @param obj the object (<code>null</code> permitted).
893      *
894      * @return A boolean.
895      */

896     public boolean equals(Object JavaDoc obj) {
897
898         if (obj == this) {
899             return true;
900         }
901         if (!(obj instanceof StandardXYItemRenderer)) {
902             return false;
903         }
904         if (!super.equals(obj)) {
905             return false;
906         }
907         StandardXYItemRenderer that = (StandardXYItemRenderer) obj;
908         if (this.baseShapesVisible != that.baseShapesVisible) {
909             return false;
910         }
911         if (this.plotLines != that.plotLines) {
912             return false;
913         }
914         if (this.plotImages != that.plotImages) {
915             return false;
916         }
917         if (this.plotDiscontinuous != that.plotDiscontinuous) {
918             return false;
919         }
920         if (this.gapThresholdType != that.gapThresholdType) {
921             return false;
922         }
923         if (this.gapThreshold != that.gapThreshold) {
924             return false;
925         }
926         if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
927             return false;
928         }
929         if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
930             return false;
931         }
932         return true;
933
934     }
935
936     /**
937      * Returns a clone of the renderer.
938      *
939      * @return A clone.
940      *
941      * @throws CloneNotSupportedException if the renderer cannot be cloned.
942      */

943     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
944         return super.clone();
945     }
946
947     ////////////////////////////////////////////////////////////////////////////
948
// PROTECTED METHODS
949
// These provide the opportunity to subclass the standard renderer and
950
// create custom effects.
951
////////////////////////////////////////////////////////////////////////////
952

953     /**
954      * Returns the image used to draw a single data item.
955      *
956      * @param plot the plot (can be used to obtain standard color information
957      * etc).
958      * @param series the series index.
959      * @param item the item index.
960      * @param x the x value of the item.
961      * @param y the y value of the item.
962      *
963      * @return The image.
964      */

965     protected Image JavaDoc getImage(Plot plot, int series, int item,
966                              double x, double y) {
967         // should this be added to the plot as well ?
968
// return plot.getShape(series, item, x, y, scale);
969
// or should this be left to the user - like this:
970
return null;
971     }
972
973     /**
974      * Returns the hotspot of the image used to draw a single data item.
975      * The hotspot is the point relative to the top left of the image
976      * that should indicate the data item. The default is the center of the
977      * image.
978      *
979      * @param plot the plot (can be used to obtain standard color information
980      * etc).
981      * @param image the image (can be used to get size information about the
982      * image)
983      * @param series the series index
984      * @param item the item index
985      * @param x the x value of the item
986      * @param y the y value of the item
987      *
988      * @return The hotspot used to draw the data item.
989      */

990     protected Point JavaDoc getImageHotspot(Plot plot, int series, int item,
991                                     double x, double y, Image JavaDoc image) {
992
993         int height = image.getHeight(null);
994         int width = image.getWidth(null);
995         return new Point JavaDoc(width / 2, height / 2);
996
997     }
998     
999     /**
1000     * Provides serialization support.
1001     *
1002     * @param stream the input stream.
1003     *
1004     * @throws IOException if there is an I/O error.
1005     * @throws ClassNotFoundException if there is a classpath problem.
1006     */

1007    private void readObject(ObjectInputStream JavaDoc stream)
1008            throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1009        stream.defaultReadObject();
1010        this.legendLine = SerialUtilities.readShape(stream);
1011    }
1012    
1013    /**
1014     * Provides serialization support.
1015     *
1016     * @param stream the output stream.
1017     *
1018     * @throws IOException if there is an I/O error.
1019     */

1020    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
1021        stream.defaultWriteObject();
1022        SerialUtilities.writeShape(this.legendLine, stream);
1023    }
1024}
1025
Popular Tags