KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > XYDifferenceRenderer


1 /* ======================================
2  * JFreeChart : a free Java chart library
3  * ======================================
4  *
5  * Project Info: http://www.jfree.org/jfreechart/index.html
6  * Project Lead: David Gilbert (david.gilbert@object-refinery.com);
7  *
8  * (C) Copyright 2000-2003, by Object Refinery Limited and Contributors.
9  *
10  * This library is free software; you can redistribute it and/or modify it under the terms
11  * of the GNU Lesser General Public License as published by the Free Software Foundation;
12  * either version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License along with this
19  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * -------------------------
23  * XYDifferenceRenderer.java
24  * -------------------------
25  * (C) Copyright 2003 by Object Refinery Limited.
26  *
27  * Original Author: David Gilbert (for Object Refinery Limited);
28  * Contributor(s): Christian W. Zuckschwerdt;
29  *
30  * $Id: XYDifferenceRenderer.java,v 1.15 2003/11/03 14:21:28 mungady Exp $
31  *
32  * Changes:
33  * --------
34  * 30-Apr-2003 : Version 1 (DG);
35  * 30-Jul-2003 : Modified entity constructor (CZ);
36  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
37  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
38  *
39  */

40
41 package org.jfree.chart.renderer;
42
43 import java.awt.Graphics2D JavaDoc;
44 import java.awt.Paint JavaDoc;
45 import java.awt.Shape JavaDoc;
46 import java.awt.Stroke JavaDoc;
47 import java.awt.geom.GeneralPath JavaDoc;
48 import java.awt.geom.Line2D JavaDoc;
49 import java.awt.geom.Rectangle2D JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.ObjectInputStream JavaDoc;
52 import java.io.ObjectOutputStream JavaDoc;
53 import java.io.Serializable JavaDoc;
54
55 import org.jfree.chart.CrosshairInfo;
56 import org.jfree.chart.axis.ValueAxis;
57 import org.jfree.chart.entity.EntityCollection;
58 import org.jfree.chart.entity.XYItemEntity;
59 import org.jfree.chart.plot.PlotRenderingInfo;
60 import org.jfree.chart.plot.XYPlot;
61 import org.jfree.data.XYDataset;
62 import org.jfree.io.SerialUtilities;
63 import org.jfree.ui.RectangleEdge;
64 import org.jfree.util.PublicCloneable;
65
66 /**
67  * A renderer for an {@link XYPlot} that highlights the differences between two
68  * series. The renderer expects a dataset that:
69  * <ul>
70  * <li>has exactly two series;</li>
71  * <li>each series has the same x-values;</li>
72  * <li>no <code>null</code> values;
73  * </ul>
74  *
75  * @author David Gilbert
76  */

77 public class XYDifferenceRenderer extends AbstractXYItemRenderer implements XYItemRenderer,
78                                                                             Cloneable JavaDoc,
79                                                                             PublicCloneable,
80                                                                             Serializable JavaDoc {
81
82     /** The paint used to highlight positive differences (y(0) > y(1)). */
83     private transient Paint JavaDoc positivePaint;
84
85     /** The paint used to highlight negative differences (y(0) < y(1)). */
86     private transient Paint JavaDoc negativePaint;
87
88     /** Display shapes at each point? */
89     private boolean plotShapes = true;
90
91     /**
92      * Creates a new renderer.
93      *
94      * @param positivePaint the highlight color for positive differences;
95      * @param negativePaint the highlight color for negative differences;
96      * @param shapes draw shapes?
97      */

98     public XYDifferenceRenderer(Paint JavaDoc positivePaint, Paint JavaDoc negativePaint, boolean shapes) {
99         this.positivePaint = positivePaint;
100         this.negativePaint = negativePaint;
101         this.plotShapes = shapes;
102     }
103
104     /**
105      * Initialises the renderer then returns the number of 'passes' through the data that the
106      * renderer will require (usually just one). This method will be called before the first
107      * item is rendered, giving the renderer an opportunity to initialise any
108      * state information it wants to maintain. The renderer can do nothing if it chooses.
109      *
110      * @param g2 the graphics device.
111      * @param dataArea the area inside the axes.
112      * @param plot the plot.
113      * @param data the data.
114      * @param info an optional info collection object to return data back to the caller.
115      *
116      * @return The number of passes the renderer requires.
117      */

118     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
119                                           Rectangle2D JavaDoc dataArea,
120                                           XYPlot plot,
121                                           XYDataset data,
122                                           PlotRenderingInfo info) {
123
124         return super.initialise(g2, dataArea, plot, data, info);
125
126     }
127
128     /**
129      * Returns <code>2</code>, the number of passes required by the renderer. The {@link XYPlot}
130      * will run through the dataset this number of times.
131      *
132      * @return The number of passes required by the renderer.
133      */

134     public int getPassCount() {
135         return 2;
136     }
137     
138     /**
139      * Returns the paint used to highlight positive differences.
140      *
141      * @return The paint.
142      */

143     public Paint JavaDoc getPositivePaint() {
144         return this.positivePaint;
145     }
146
147     /**
148      * Returns the paint used to highlight negative differences.
149      *
150      * @return The paint.
151      */

152     public Paint JavaDoc getNegativePaint() {
153         return this.positivePaint;
154     }
155
156     /**
157      * Draws the visual representation of a single data item.
158      *
159      * @param g2 the graphics device.
160      * @param state the renderer state.
161      * @param dataArea the area within which the data is being drawn.
162      * @param info collects information about the drawing.
163      * @param plot the plot (can be used to obtain standard color information etc).
164      * @param domainAxis the domain (horizontal) axis.
165      * @param rangeAxis the range (vertical) axis.
166      * @param dataset the dataset.
167      * @param series the series index (zero-based).
168      * @param item the item index (zero-based).
169      * @param crosshairInfo information about crosshairs on a plot.
170      * @param pass the pass index.
171      */

172     public void drawItem(Graphics2D JavaDoc g2,
173                          XYItemRendererState state,
174                          Rectangle2D JavaDoc dataArea,
175                          PlotRenderingInfo info,
176                          XYPlot plot,
177                          ValueAxis domainAxis,
178                          ValueAxis rangeAxis,
179                          XYDataset dataset,
180                          int series,
181                          int item,
182                          CrosshairInfo crosshairInfo,
183                          int pass) {
184
185         if (pass == 0) {
186             drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset,
187                           series, item, crosshairInfo);
188         }
189         else if (pass == 1) {
190             drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset,
191                           series, item, crosshairInfo);
192         }
193
194     }
195
196     /**
197      * Draws the visual representation of a single data item, first pass.
198      *
199      * @param g2 the graphics device.
200      * @param dataArea the area within which the data is being drawn.
201      * @param info collects information about the drawing.
202      * @param plot the plot (can be used to obtain standard color information etc).
203      * @param domainAxis the domain (horizontal) axis.
204      * @param rangeAxis the range (vertical) axis.
205      * @param dataset the dataset.
206      * @param series the series index (zero-based).
207      * @param item the item index (zero-based).
208      * @param crosshairInfo information about crosshairs on a plot.
209      */

210     private void drawItemPass0(Graphics2D JavaDoc g2,
211                                Rectangle2D JavaDoc dataArea,
212                                PlotRenderingInfo info,
213                                XYPlot plot,
214                                ValueAxis domainAxis,
215                                ValueAxis rangeAxis,
216                                XYDataset dataset,
217                                int series,
218                                int item,
219                                CrosshairInfo crosshairInfo) {
220
221         if (series == 0) {
222
223             // get the data points...
224
Number JavaDoc y0n = dataset.getYValue(0, item);
225             Number JavaDoc x1n = dataset.getXValue(1, item);
226             Number JavaDoc y1n = dataset.getYValue(1, item);
227
228             RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
229             RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
230             double y0 = y0n.doubleValue();
231             double transY0 = rangeAxis.translateValueToJava2D(y0, dataArea, rangeAxisLocation);
232
233             double x1 = x1n.doubleValue();
234             double y1 = y1n.doubleValue();
235             double transX1 = domainAxis.translateValueToJava2D(x1, dataArea, domainAxisLocation);
236             double transY1 = rangeAxis.translateValueToJava2D(y1, dataArea, rangeAxisLocation);
237
238             if (item > 0) {
239                 // get the previous data points...
240
// get the data points...
241
Number JavaDoc prevx0n = dataset.getXValue(0, item - 1);
242                 Number JavaDoc prevy0n = dataset.getYValue(0, item - 1);
243                 Number JavaDoc prevy1n = dataset.getYValue(1, item - 1);
244
245                 double prevx0 = prevx0n.doubleValue();
246                 double prevy0 = prevy0n.doubleValue();
247                 double prevtransX0 = domainAxis.translateValueToJava2D(prevx0, dataArea,
248                                                                        domainAxisLocation);
249                 double prevtransY0 = rangeAxis.translateValueToJava2D(prevy0, dataArea,
250                                                                       rangeAxisLocation);
251
252                 double prevy1 = prevy1n.doubleValue();
253                 double prevtransY1 = rangeAxis.translateValueToJava2D(prevy1, dataArea,
254                                                                       rangeAxisLocation);
255
256                 Shape JavaDoc positive = getPositiveArea((float) prevtransX0,
257                                                  (float) prevtransY0, (float) prevtransY1,
258                                                  (float) transX1,
259                                                  (float) transY0, (float) transY1);
260                 if (positive != null) {
261                     g2.setPaint(this.positivePaint);
262                     g2.fill(positive);
263                 }
264
265                 Shape JavaDoc negative = getNegativeArea((float) prevtransX0,
266                                                  (float) prevtransY0, (float) prevtransY1,
267                                                  (float) transX1,
268                                                  (float) transY0, (float) transY1);
269
270                 if (negative != null) {
271                     g2.setPaint(this.negativePaint);
272                     g2.fill(negative);
273                 }
274             }
275         }
276
277     }
278
279     /**
280      * Draws the visual representation of a single data item, second pass.
281      *
282      * @param g2 the graphics device.
283      * @param dataArea the area within which the data is being drawn.
284      * @param info collects information about the drawing.
285      * @param plot the plot (can be used to obtain standard color information etc).
286      * @param domainAxis the domain (horizontal) axis.
287      * @param rangeAxis the range (vertical) axis.
288      * @param dataset the dataset.
289      * @param series the series index (zero-based).
290      * @param item the item index (zero-based).
291      * @param crosshairInfo information about crosshairs on a plot.
292      */

293     private void drawItemPass1(Graphics2D JavaDoc g2,
294                                Rectangle2D JavaDoc dataArea,
295                                PlotRenderingInfo info,
296                                XYPlot plot,
297                                ValueAxis domainAxis,
298                                ValueAxis rangeAxis,
299                                XYDataset dataset,
300                                int series,
301                                int item,
302                                CrosshairInfo crosshairInfo) {
303
304         Shape JavaDoc entityArea = null;
305         EntityCollection entities = null;
306         if (info != null) {
307             entities = info.getOwner().getEntityCollection();
308         }
309
310         Paint JavaDoc seriesPaint = getItemPaint(series, item);
311         Stroke JavaDoc seriesStroke = getItemStroke(series, item);
312         g2.setPaint(seriesPaint);
313         g2.setStroke(seriesStroke);
314
315         if (series == 0) {
316
317             // get the data points...
318
Number JavaDoc x0n = dataset.getXValue(0, item);
319             Number JavaDoc y0n = dataset.getYValue(0, item);
320             Number JavaDoc x1n = dataset.getXValue(1, item);
321             Number JavaDoc y1n = dataset.getYValue(1, item);
322
323             RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
324             RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
325
326             double x0 = x0n.doubleValue();
327             double y0 = y0n.doubleValue();
328             double transX0 = domainAxis.translateValueToJava2D(x0, dataArea, domainAxisLocation);
329             double transY0 = rangeAxis.translateValueToJava2D(y0, dataArea, rangeAxisLocation);
330
331             double x1 = x1n.doubleValue();
332             double y1 = y1n.doubleValue();
333             double transX1 = domainAxis.translateValueToJava2D(x1, dataArea, domainAxisLocation);
334             double transY1 = rangeAxis.translateValueToJava2D(y1, dataArea, rangeAxisLocation);
335
336             if (item > 0) {
337                 // get the previous data points...
338
// get the data points...
339
Number JavaDoc prevx0n = dataset.getXValue(0, item - 1);
340                 Number JavaDoc prevy0n = dataset.getYValue(0, item - 1);
341                 Number JavaDoc prevx1n = dataset.getXValue(1, item - 1);
342                 Number JavaDoc prevy1n = dataset.getYValue(1, item - 1);
343
344                 double prevx0 = prevx0n.doubleValue();
345                 double prevy0 = prevy0n.doubleValue();
346                 double prevtransX0 = domainAxis.translateValueToJava2D(prevx0, dataArea,
347                                                                        domainAxisLocation);
348                 double prevtransY0 = rangeAxis.translateValueToJava2D(prevy0, dataArea,
349                                                                       rangeAxisLocation);
350
351                 double prevx1 = prevx1n.doubleValue();
352                 double prevy1 = prevy1n.doubleValue();
353                 double prevtransX1 = domainAxis.translateValueToJava2D(prevx1, dataArea,
354                                                                        domainAxisLocation);
355                 double prevtransY1 = rangeAxis.translateValueToJava2D(prevy1, dataArea,
356                                                                       rangeAxisLocation);
357
358                 Line2D JavaDoc line0 = new Line2D.Double JavaDoc(transX0, transY0, prevtransX0, prevtransY0);
359                 if (line0.intersects(dataArea)) {
360                     g2.setPaint(getItemPaint(series, item));
361                     g2.draw(line0);
362                 }
363                 Line2D JavaDoc line1 = new Line2D.Double JavaDoc(transX1, transY1, prevtransX1, prevtransY1);
364                 if (line1.intersects(dataArea)) {
365                     g2.setPaint(getItemPaint(1, item));
366                     g2.draw(line1);
367                 }
368             }
369
370             if (this.plotShapes) {
371                 Shape JavaDoc shape0 = getItemShape(series, item);
372                 shape0 = createTransformedShape(shape0, transX0, transY0);
373                 if (shape0.intersects(dataArea)) {
374                     g2.setPaint(getItemPaint(series, item));
375                     g2.fill(shape0);
376                 }
377                 entityArea = shape0;
378
379                 // add an entity for the item...
380
if (entities != null) {
381                     if (entityArea == null) {
382                         entityArea = new Rectangle2D.Double JavaDoc(transX0 - 2, transY0 - 2, 4, 4);
383                     }
384                     String JavaDoc tip = null;
385                     if (getToolTipGenerator() != null) {
386                         tip = getToolTipGenerator().generateToolTip(dataset, series, item);
387                     }
388                     String JavaDoc url = null;
389                     if (getURLGenerator() != null) {
390                         url = getURLGenerator().generateURL(dataset, series, item);
391                     }
392                     XYItemEntity entity = new XYItemEntity(entityArea, dataset, series, item,
393                                                            tip, url);
394                     entities.addEntity(entity);
395                 }
396
397                 Shape JavaDoc shape1 = getItemShape(series + 1, item);
398                 shape1 = createTransformedShape(shape1, transX1, transY1);
399                 if (shape1.intersects(dataArea)) {
400                     g2.setPaint(getItemPaint(series + 1, item));
401                     g2.fill(shape1);
402                 }
403                 entityArea = shape1;
404
405                 // add an entity for the item...
406
if (entities != null) {
407                     if (entityArea == null) {
408                         entityArea = new Rectangle2D.Double JavaDoc(transX1 - 2, transY1 - 2, 4, 4);
409                     }
410                     String JavaDoc tip = null;
411                     if (getToolTipGenerator() != null) {
412                         tip = getToolTipGenerator().generateToolTip(dataset, series + 1, item);
413                     }
414                     String JavaDoc url = null;
415                     if (getURLGenerator() != null) {
416                         url = getURLGenerator().generateURL(dataset, series + 1, item);
417                     }
418                     XYItemEntity entity = new XYItemEntity(entityArea, dataset, series + 1, item,
419                                                            tip, url);
420                     entities.addEntity(entity);
421                 }
422             }
423
424         }
425
426     }
427
428     /**
429      * Returns the positive area for a crossover point.
430      *
431      * @param x0 x coordinate.
432      * @param y0A y coordinate A.
433      * @param y0B y coordinate B.
434      * @param x1 x coordinate.
435      * @param y1A y coordinate A.
436      * @param y1B y coordinate B.
437      *
438      * @return The positive area.
439      */

440     private Shape JavaDoc getPositiveArea(float x0, float y0A, float y0B, float x1, float y1A, float y1B) {
441
442         Shape JavaDoc result = null;
443
444         if (y0A >= y0B) { // negative
445
if (y1A >= y1B) {
446                 // all negative - return null
447
}
448             else {
449                 // changed from negative to positive
450
//this.positivePaint = Color.yellow;
451
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
452                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
453                 area.moveTo(x1, y1A);
454                 area.lineTo(p[0], p[1]);
455                 area.lineTo(x1, y1B);
456                 area.closePath();
457                 result = area;
458             }
459         }
460         else {
461             if (y1A >= y1B) {
462                 // changed from positive to negative
463
//this.positivePaint = Color.green;
464
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
465                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
466                 area.moveTo(x0, y0A);
467                 area.lineTo(p[0], p[1]);
468                 area.lineTo(x0, y0B);
469                 area.closePath();
470                 result = area;
471
472             }
473             else {
474                 //this.positivePaint = Color.blue;
475
GeneralPath JavaDoc area = new GeneralPath JavaDoc();
476                 area.moveTo(x0, y0A);
477                 area.lineTo(x1, y1A);
478                 area.lineTo(x1, y1B);
479                 area.lineTo(x0, y0B);
480                 area.closePath();
481                 result = area;
482             }
483
484         }
485
486         return result;
487
488     }
489
490     /**
491      * Returns the negative area for a cross-over section.
492      *
493      * @param x0 x coordinate.
494      * @param y0A y coordinate A.
495      * @param y0B y coordinate B.
496      * @param x1 x coordinate.
497      * @param y1A y coordinate A.
498      * @param y1B y coordinate B.
499      *
500      * @return The negative area.
501      */

502     private Shape JavaDoc getNegativeArea(float x0, float y0A, float y0B, float x1, float y1A, float y1B) {
503
504         Shape JavaDoc result = null;
505
506         if (y0A >= y0B) { // negative
507
if (y1A >= y1B) { // negative
508
//this.negativePaint = Color.red;
509
GeneralPath JavaDoc area = new GeneralPath JavaDoc();
510                 area.moveTo(x0, y0A);
511                 area.lineTo(x1, y1A);
512                 area.lineTo(x1, y1B);
513                 area.lineTo(x0, y0B);
514                 area.closePath();
515                 result = area;
516             }
517             else { // changed from negative to positive
518

519                 //this.negativePaint = Color.pink;
520
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
521                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
522                 area.moveTo(x0, y0A);
523                 area.lineTo(p[0], p[1]);
524                 area.lineTo(x0, y0B);
525                 area.closePath();
526                 result = area;
527             }
528         }
529         else {
530             if (y1A >= y1B) {
531                 // changed from positive to negative
532
//this.negativePaint = Color.gray;
533
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
534                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
535                 area.moveTo(x1, y1A);
536                 area.lineTo(p[0], p[1]);
537                 area.lineTo(x1, y1B);
538                 area.closePath();
539                 result = area;
540             }
541             else {
542                 // all negative - return null
543
}
544
545         }
546
547         return result;
548
549     }
550
551     /**
552      * Returns the intersection point of two lines.
553      *
554      * @param x1 x1
555      * @param y1 y1
556      * @param x2 x2
557      * @param y2 y2
558      * @param x3 x3
559      * @param y3 y3
560      * @param x4 x4
561      * @param y4 y4
562      *
563      * @return The intersection point.
564      */

565     private float[] getIntersection(float x1, float y1, float x2, float y2,
566                                     float x3, float y3, float x4, float y4) {
567
568         float n = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
569         float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
570         float u = n / d;
571
572         float[] result = new float[2];
573         result[0] = x1 + u * (x2 - x1);
574         result[1] = y1 + u * (y2 - y1);
575         return result;
576
577     }
578     
579     /**
580      * Returns a clone of the renderer.
581      *
582      * @return A clone.
583      *
584      * @throws CloneNotSupportedException if the renderer cannot be cloned.
585      */

586     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
587         return super.clone();
588     }
589
590     /**
591      * Provides serialization support.
592      *
593      * @param stream the output stream.
594      *
595      * @throws IOException if there is an I/O error.
596      */

597     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
598         stream.defaultWriteObject();
599         SerialUtilities.writePaint(this.positivePaint, stream);
600         SerialUtilities.writePaint(this.negativePaint, stream);
601     }
602
603     /**
604      * Provides serialization support.
605      *
606      * @param stream the input stream.
607      *
608      * @throws IOException if there is an I/O error.
609      * @throws ClassNotFoundException if there is a classpath problem.
610      */

611     private void readObject(ObjectInputStream JavaDoc stream) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
612         stream.defaultReadObject();
613         this.positivePaint = SerialUtilities.readPaint(stream);
614         this.negativePaint = SerialUtilities.readPaint(stream);
615     }
616
617 }
618
Popular Tags