KickJava   Java API By Example, From Geeks To Geeks.

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


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  * CyclicXYItemRenderer.java
29  * ---------------------------
30  * (C) Copyright 2003-2006, by Nicolas Brodu and Contributors.
31  *
32  * Original Author: Nicolas Brodu;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: CyclicXYItemRenderer.java,v 1.4.2.2 2006/07/06 10:44:54 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 19-Nov-2003 : Initial import to JFreeChart from the JSynoptic project (NB);
40  * 23-Dec-2003 : Added missing Javadocs (DG);
41  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
42  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
43  * getYValue() (DG);
44  * ------------- JFREECHART 1.0.0 ---------------------------------------------
45  * 06-Jul-2006 : Modified to call only dataset methods that return double
46  * primitives (DG);
47  *
48  */

49
50 package org.jfree.chart.renderer.xy;
51
52 import java.awt.Graphics2D JavaDoc;
53 import java.awt.geom.Rectangle2D JavaDoc;
54 import java.io.Serializable JavaDoc;
55
56 import org.jfree.chart.axis.CyclicNumberAxis;
57 import org.jfree.chart.axis.ValueAxis;
58 import org.jfree.chart.labels.XYToolTipGenerator;
59 import org.jfree.chart.plot.CrosshairState;
60 import org.jfree.chart.plot.PlotRenderingInfo;
61 import org.jfree.chart.plot.XYPlot;
62 import org.jfree.chart.urls.XYURLGenerator;
63 import org.jfree.data.DomainOrder;
64 import org.jfree.data.general.DatasetChangeListener;
65 import org.jfree.data.general.DatasetGroup;
66 import org.jfree.data.xy.XYDataset;
67
68 /**
69  * The Cyclic XY item renderer is specially designed to handle cyclic axis.
70  * While the standard renderer would draw a line across the plot when a cycling
71  * occurs, the cyclic renderer splits the line at each cycle end instead. This
72  * is done by interpolating new points at cycle boundary. Thus, correct
73  * appearance is restored.
74  *
75  * The Cyclic XY item renderer works exactly like a standard XY item renderer
76  * with non-cyclic axis.
77  */

78 public class CyclicXYItemRenderer extends StandardXYItemRenderer
79                                   implements Serializable JavaDoc {
80
81     /** For serialization. */
82     private static final long serialVersionUID = 4035912243303764892L;
83     
84     /**
85      * Default constructor.
86      */

87     public CyclicXYItemRenderer() {
88         super();
89     }
90
91     /**
92      * Creates a new renderer.
93      *
94      * @param type the renderer type.
95      */

96     public CyclicXYItemRenderer(int type) {
97         super(type);
98     }
99
100     /**
101      * Creates a new renderer.
102      *
103      * @param type the renderer type.
104      * @param labelGenerator the tooltip generator.
105      */

106     public CyclicXYItemRenderer(int type, XYToolTipGenerator labelGenerator) {
107         super(type, labelGenerator);
108     }
109
110     /**
111      * Creates a new renderer.
112      *
113      * @param type the renderer type.
114      * @param labelGenerator the tooltip generator.
115      * @param urlGenerator the url generator.
116      */

117     public CyclicXYItemRenderer(int type,
118                                 XYToolTipGenerator labelGenerator,
119                                 XYURLGenerator urlGenerator) {
120         super(type, labelGenerator, urlGenerator);
121     }
122
123     
124     /**
125      * Draws the visual representation of a single data item.
126      * When using cyclic axis, do not draw a line from right to left when
127      * cycling as would a standard XY item renderer, but instead draw a line
128      * from the previous point to the cycle bound in the last cycle, and a line
129      * from the cycle bound to current point in the current cycle.
130      *
131      * @param g2 the graphics device.
132      * @param state the renderer state.
133      * @param dataArea the data area.
134      * @param info the plot rendering info.
135      * @param plot the plot.
136      * @param domainAxis the domain axis.
137      * @param rangeAxis the range axis.
138      * @param dataset the dataset.
139      * @param series the series index.
140      * @param item the item index.
141      * @param crosshairState crosshair information for the plot
142      * (<code>null</code> permitted).
143      * @param pass the current pass index.
144      */

145     public void drawItem(Graphics2D JavaDoc g2,
146                          XYItemRendererState state,
147                          Rectangle2D JavaDoc dataArea,
148                          PlotRenderingInfo info,
149                          XYPlot plot,
150                          ValueAxis domainAxis,
151                          ValueAxis rangeAxis,
152                          XYDataset dataset,
153                          int series,
154                          int item,
155                          CrosshairState crosshairState,
156                          int pass) {
157
158         if ((!getPlotLines()) || ((!(domainAxis instanceof CyclicNumberAxis))
159                 && (!(rangeAxis instanceof CyclicNumberAxis))) || (item <= 0)) {
160             super.drawItem(g2, state, dataArea, info, plot, domainAxis,
161                     rangeAxis, dataset, series, item, crosshairState, pass);
162             return;
163         }
164
165         // get the previous data point...
166
double xn = dataset.getXValue(series, item - 1);
167         double yn = dataset.getYValue(series, item - 1);
168         // If null, don't draw line => then delegate to parent
169
if (Double.isNaN(yn)) {
170             super.drawItem(g2, state, dataArea, info, plot, domainAxis,
171                     rangeAxis, dataset, series, item, crosshairState, pass);
172             return;
173         }
174         double[] x = new double[2];
175         double[] y = new double[2];
176         x[0] = xn;
177         y[0] = yn;
178         
179         // get the data point...
180
xn = dataset.getXValue(series, item);
181         yn = dataset.getYValue(series, item);
182         // If null, don't draw line at all
183
if (Double.isNaN(yn)) {
184             return;
185         }
186         x[1] = xn;
187         y[1] = yn;
188
189         // Now split the segment as needed
190
double xcycleBound = Double.NaN;
191         double ycycleBound = Double.NaN;
192         boolean xBoundMapping = false, yBoundMapping = false;
193         CyclicNumberAxis cnax = null, cnay = null;
194
195         if (domainAxis instanceof CyclicNumberAxis) {
196             cnax = (CyclicNumberAxis) domainAxis;
197             xcycleBound = cnax.getCycleBound();
198             xBoundMapping = cnax.isBoundMappedToLastCycle();
199             // If the segment must be splitted, insert a new point
200
// Strict test forces to have real segments (not 2 equal points)
201
// and avoids division by 0
202
if ((x[0] != x[1])
203                     && ((xcycleBound >= x[0])
204                     && (xcycleBound <= x[1])
205                     || (xcycleBound >= x[1])
206                     && (xcycleBound <= x[0]))) {
207                 double[] nx = new double[3];
208                 double[] ny = new double[3];
209                 nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1];
210                 nx[1] = xcycleBound;
211                 ny[1] = (y[1] - y[0]) * (xcycleBound - x[0])
212                         / (x[1] - x[0]) + y[0];
213                 x = nx; y = ny;
214             }
215         }
216
217         if (rangeAxis instanceof CyclicNumberAxis) {
218             cnay = (CyclicNumberAxis) rangeAxis;
219             ycycleBound = cnay.getCycleBound();
220             yBoundMapping = cnay.isBoundMappedToLastCycle();
221             // The split may occur in either x splitted segments, if any, but
222
// not in both
223
if ((y[0] != y[1]) && ((ycycleBound >= y[0])
224                     && (ycycleBound <= y[1])
225                     || (ycycleBound >= y[1]) && (ycycleBound <= y[0]))) {
226                 double[] nx = new double[x.length + 1];
227                 double[] ny = new double[y.length + 1];
228                 nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1];
229                 ny[1] = ycycleBound;
230                 nx[1] = (x[1] - x[0]) * (ycycleBound - y[0])
231                         / (y[1] - y[0]) + x[0];
232                 if (x.length == 3) {
233                     nx[3] = x[2]; ny[3] = y[2];
234                 }
235                 x = nx; y = ny;
236             }
237             else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1])
238                     && (ycycleBound <= y[2])
239                     || (ycycleBound >= y[2]) && (ycycleBound <= y[1]))) {
240                 double[] nx = new double[4];
241                 double[] ny = new double[4];
242                 nx[0] = x[0]; nx[1] = x[1]; nx[3] = x[2];
243                 ny[0] = y[0]; ny[1] = y[1]; ny[3] = y[2];
244                 ny[2] = ycycleBound;
245                 nx[2] = (x[2] - x[1]) * (ycycleBound - y[1])
246                         / (y[2] - y[1]) + x[1];
247                 x = nx; y = ny;
248             }
249         }
250         
251         // If the line is not wrapping, then parent is OK
252
if (x.length == 2) {
253             super.drawItem(g2, state, dataArea, info, plot, domainAxis,
254                     rangeAxis, dataset, series, item, crosshairState, pass);
255             return;
256         }
257
258         OverwriteDataSet newset = new OverwriteDataSet(x, y, dataset);
259
260         if (cnax != null) {
261             if (xcycleBound == x[0]) {
262                 cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound);
263             }
264             if (xcycleBound == x[1]) {
265                 cnax.setBoundMappedToLastCycle(x[0] <= xcycleBound);
266             }
267         }
268         if (cnay != null) {
269             if (ycycleBound == y[0]) {
270                 cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound);
271             }
272             if (ycycleBound == y[1]) {
273                 cnay.setBoundMappedToLastCycle(y[0] <= ycycleBound);
274             }
275         }
276         super.drawItem(
277             g2, state, dataArea, info, plot, domainAxis, rangeAxis,
278             newset, series, 1, crosshairState, pass
279         );
280
281         if (cnax != null) {
282             if (xcycleBound == x[1]) {
283                 cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound);
284             }
285             if (xcycleBound == x[2]) {
286                 cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound);
287             }
288         }
289         if (cnay != null) {
290             if (ycycleBound == y[1]) {
291                 cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound);
292             }
293             if (ycycleBound == y[2]) {
294                 cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound);
295             }
296         }
297         super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis,
298                 newset, series, 2, crosshairState, pass);
299
300         if (x.length == 4) {
301             if (cnax != null) {
302                 if (xcycleBound == x[2]) {
303                     cnax.setBoundMappedToLastCycle(x[3] <= xcycleBound);
304                 }
305                 if (xcycleBound == x[3]) {
306                     cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound);
307                 }
308             }
309             if (cnay != null) {
310                 if (ycycleBound == y[2]) {
311                     cnay.setBoundMappedToLastCycle(y[3] <= ycycleBound);
312                 }
313                 if (ycycleBound == y[3]) {
314                     cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound);
315                 }
316             }
317             super.drawItem(g2, state, dataArea, info, plot, domainAxis,
318                     rangeAxis, newset, series, 3, crosshairState, pass);
319         }
320         
321         if (cnax != null) {
322             cnax.setBoundMappedToLastCycle(xBoundMapping);
323         }
324         if (cnay != null) {
325             cnay.setBoundMappedToLastCycle(yBoundMapping);
326         }
327     }
328
329     /**
330      * A dataset to hold the interpolated points when drawing new lines.
331      */

332     protected static class OverwriteDataSet implements XYDataset {
333         
334         /** The delegate dataset. */
335         protected XYDataset delegateSet;
336         
337         /** Storage for the x and y values. */
338         Double JavaDoc[] x, y;
339         
340         /**
341          * Creates a new dataset.
342          *
343          * @param x the x values.
344          * @param y the y values.
345          * @param delegateSet the dataset.
346          */

347         public OverwriteDataSet(double [] x, double[] y,
348                                 XYDataset delegateSet) {
349             this.delegateSet = delegateSet;
350             this.x = new Double JavaDoc[x.length]; this.y = new Double JavaDoc[y.length];
351             for (int i = 0; i < x.length; ++i) {
352                 this.x[i] = new Double JavaDoc(x[i]);
353                 this.y[i] = new Double JavaDoc(y[i]);
354             }
355         }
356
357         /**
358          * Returns the order of the domain (X) values.
359          *
360          * @return The domain order.
361          */

362         public DomainOrder getDomainOrder() {
363             return DomainOrder.NONE;
364         }
365         
366         /**
367          * Returns the number of items for the given series.
368          *
369          * @param series the series index (zero-based).
370          *
371          * @return The item count.
372          */

373         public int getItemCount(int series) {
374             return this.x.length;
375         }
376
377         /**
378          * Returns the x-value.
379          *
380          * @param series the series index (zero-based).
381          * @param item the item index (zero-based).
382          *
383          * @return The x-value.
384          */

385         public Number JavaDoc getX(int series, int item) {
386             return this.x[item];
387         }
388
389         /**
390          * Returns the x-value (as a double primitive) for an item within a
391          * series.
392          *
393          * @param series the series (zero-based index).
394          * @param item the item (zero-based index).
395          *
396          * @return The x-value.
397          */

398         public double getXValue(int series, int item) {
399             double result = Double.NaN;
400             Number JavaDoc x = getX(series, item);
401             if (x != null) {
402                 result = x.doubleValue();
403             }
404             return result;
405         }
406
407         /**
408          * Returns the y-value.
409          *
410          * @param series the series index (zero-based).
411          * @param item the item index (zero-based).
412          *
413          * @return The y-value.
414          */

415         public Number JavaDoc getY(int series, int item) {
416             return this.y[item];
417         }
418
419         /**
420          * Returns the y-value (as a double primitive) for an item within a
421          * series.
422          *
423          * @param series the series (zero-based index).
424          * @param item the item (zero-based index).
425          *
426          * @return The y-value.
427          */

428         public double getYValue(int series, int item) {
429             double result = Double.NaN;
430             Number JavaDoc y = getY(series, item);
431             if (y != null) {
432                 result = y.doubleValue();
433             }
434             return result;
435         }
436
437         /**
438          * Returns the number of series in the dataset.
439          *
440          * @return The series count.
441          */

442         public int getSeriesCount() {
443             return this.delegateSet.getSeriesCount();
444         }
445
446         /**
447          * Returns the name of the given series.
448          *
449          * @param series the series index (zero-based).
450          *
451          * @return The series name.
452          */

453         public Comparable JavaDoc getSeriesKey(int series) {
454             return this.delegateSet.getSeriesKey(series);
455         }
456
457         /**
458          * Returns the index of the named series, or -1.
459          *
460          * @param seriesName the series name.
461          *
462          * @return The index.
463          */

464         public int indexOf(Comparable JavaDoc seriesName) {
465             return this.delegateSet.indexOf(seriesName);
466         }
467
468         /**
469          * Does nothing.
470          *
471          * @param listener ignored.
472          */

473         public void addChangeListener(DatasetChangeListener listener) {
474             // unused in parent
475
}
476
477         /**
478          * Does nothing.
479          *
480          * @param listener ignored.
481          */

482         public void removeChangeListener(DatasetChangeListener listener) {
483             // unused in parent
484
}
485
486         /**
487          * Returns the dataset group.
488          *
489          * @return The dataset group.
490          */

491         public DatasetGroup getGroup() {
492             // unused but must return something, so while we are at it...
493
return this.delegateSet.getGroup();
494         }
495
496         /**
497          * Does nothing.
498          *
499          * @param group ignored.
500          */

501         public void setGroup(DatasetGroup group) {
502             // unused in parent
503
}
504         
505     }
506     
507 }
508
509
510
Popular Tags