KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, 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  * XYDifferenceRenderer.java
29  * -------------------------
30  * (C) Copyright 2003-2005, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): Christian W. Zuckschwerdt;
34  *
35  * $Id: XYDifferenceRenderer.java,v 1.12.2.6 2005/12/10 21:51:02 mungady Exp $
36  *
37  * Changes:
38  * --------
39  * 30-Apr-2003 : Version 1 (DG);
40  * 30-Jul-2003 : Modified entity constructor (CZ);
41  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
42  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
43  * 09-Feb-2004 : Updated to support horizontal plot orientation (DG);
44  * 10-Feb-2004 : Added default constructor, setter methods and updated
45  * Javadocs (DG);
46  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
47  * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG);
48  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
49  * getYValue() (DG);
50  * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG);
51  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
52  * 19-Jan-2005 : Now accesses only primitive values from dataset (DG);
53  * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG);
54  * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG);
55  * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
56  * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() -->
57  * get/setShapesVisible (DG);
58  * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG);
59  * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG);
60  *
61  */

62
63 package org.jfree.chart.renderer.xy;
64
65 import java.awt.Color JavaDoc;
66 import java.awt.Graphics2D JavaDoc;
67 import java.awt.Paint JavaDoc;
68 import java.awt.Shape JavaDoc;
69 import java.awt.Stroke JavaDoc;
70 import java.awt.geom.GeneralPath JavaDoc;
71 import java.awt.geom.Line2D JavaDoc;
72 import java.awt.geom.Rectangle2D JavaDoc;
73 import java.io.IOException JavaDoc;
74 import java.io.ObjectInputStream JavaDoc;
75 import java.io.ObjectOutputStream JavaDoc;
76 import java.io.Serializable JavaDoc;
77
78 import org.jfree.chart.LegendItem;
79 import org.jfree.chart.axis.ValueAxis;
80 import org.jfree.chart.entity.EntityCollection;
81 import org.jfree.chart.entity.XYItemEntity;
82 import org.jfree.chart.event.RendererChangeEvent;
83 import org.jfree.chart.labels.XYToolTipGenerator;
84 import org.jfree.chart.plot.CrosshairState;
85 import org.jfree.chart.plot.PlotOrientation;
86 import org.jfree.chart.plot.PlotRenderingInfo;
87 import org.jfree.chart.plot.XYPlot;
88 import org.jfree.data.xy.XYDataset;
89 import org.jfree.io.SerialUtilities;
90 import org.jfree.ui.RectangleEdge;
91 import org.jfree.util.PaintUtilities;
92 import org.jfree.util.PublicCloneable;
93 import org.jfree.util.ShapeUtilities;
94
95 /**
96  * A renderer for an {@link XYPlot} that highlights the differences between two
97  * series. The renderer expects a dataset that:
98  * <ul>
99  * <li>has exactly two series;</li>
100  * <li>each series has the same x-values;</li>
101  * <li>no <code>null</code> values;
102  * </ul>
103  */

104 public class XYDifferenceRenderer extends AbstractXYItemRenderer
105                                   implements XYItemRenderer,
106                                              Cloneable JavaDoc,
107                                              PublicCloneable,
108                                              Serializable JavaDoc {
109
110     /** For serialization. */
111     private static final long serialVersionUID = -8447915602375584857L;
112     
113     /** The paint used to highlight positive differences (y(0) > y(1)). */
114     private transient Paint JavaDoc positivePaint;
115
116     /** The paint used to highlight negative differences (y(0) < y(1)). */
117     private transient Paint JavaDoc negativePaint;
118
119     /** Display shapes at each point? */
120     private boolean shapesVisible;
121     
122     /** The shape to display in the legend item. */
123     private transient Shape JavaDoc legendLine;
124
125     /**
126      * Creates a new renderer with default attributes.
127      */

128     public XYDifferenceRenderer() {
129         this(Color.green, Color.red, false);
130     }
131     
132     /**
133      * Creates a new renderer.
134      *
135      * @param positivePaint the highlight color for positive differences
136      * (<code>null</code> not permitted).
137      * @param negativePaint the highlight color for negative differences
138      * (<code>null</code> not permitted).
139      * @param shapes draw shapes?
140      */

141     public XYDifferenceRenderer(Paint JavaDoc positivePaint, Paint JavaDoc negativePaint,
142                                 boolean shapes) {
143         if (positivePaint == null) {
144             throw new IllegalArgumentException JavaDoc(
145                     "Null 'positivePaint' argument.");
146         }
147         if (negativePaint == null) {
148             throw new IllegalArgumentException JavaDoc(
149                     "Null 'negativePaint' argument.");
150         }
151         this.positivePaint = positivePaint;
152         this.negativePaint = negativePaint;
153         this.shapesVisible = shapes;
154         this.legendLine = new Line2D.Double JavaDoc(-7.0, 0.0, 7.0, 0.0);
155     }
156
157     /**
158      * Returns the paint used to highlight positive differences.
159      *
160      * @return The paint (never <code>null</code>).
161      */

162     public Paint JavaDoc getPositivePaint() {
163         return this.positivePaint;
164     }
165
166     /**
167      * Sets the paint used to highlight positive differences.
168      *
169      * @param paint the paint (<code>null</code> not permitted).
170      */

171     public void setPositivePaint(Paint JavaDoc paint) {
172         if (paint == null) {
173             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
174         }
175         this.positivePaint = paint;
176         notifyListeners(new RendererChangeEvent(this));
177     }
178
179     /**
180      * Returns the paint used to highlight negative differences.
181      *
182      * @return The paint (never <code>null</code>).
183      */

184     public Paint JavaDoc getNegativePaint() {
185         return this.negativePaint;
186     }
187     
188     /**
189      * Sets the paint used to highlight negative differences.
190      *
191      * @param paint the paint (<code>null</code> not permitted).
192      */

193     public void setNegativePaint(Paint JavaDoc paint) {
194         if (paint == null) {
195             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
196         }
197         this.negativePaint = paint;
198         notifyListeners(new RendererChangeEvent(this));
199     }
200
201     /**
202      * Returns a flag that controls whether or not shapes are drawn for each
203      * data value.
204      *
205      * @return A boolean.
206      */

207     public boolean getShapesVisible() {
208         return this.shapesVisible;
209     }
210
211     /**
212      * Sets a flag that controls whether or not shapes are drawn for each
213      * data value.
214      *
215      * @param flag the flag.
216      */

217     public void setShapesVisible(boolean flag) {
218         this.shapesVisible = flag;
219         notifyListeners(new RendererChangeEvent(this));
220     }
221     
222     /**
223      * Returns the shape used to represent a line in the legend.
224      *
225      * @return The legend line (never <code>null</code>).
226      */

227     public Shape JavaDoc getLegendLine() {
228         return this.legendLine;
229     }
230     
231     /**
232      * Sets the shape used as a line in each legend item and sends a
233      * {@link RendererChangeEvent} to all registered listeners.
234      *
235      * @param line the line (<code>null</code> not permitted).
236      */

237     public void setLegendLine(Shape JavaDoc line) {
238         if (line == null) {
239             throw new IllegalArgumentException JavaDoc("Null 'line' argument.");
240         }
241         this.legendLine = line;
242         notifyListeners(new RendererChangeEvent(this));
243     }
244
245     /**
246      * Initialises the renderer and returns a state object that should be
247      * passed to subsequent calls to the drawItem() method. This method will
248      * be called before the first item is rendered, giving the renderer an
249      * opportunity to initialise any state information it wants to maintain.
250      * The renderer can do nothing if it chooses.
251      *
252      * @param g2 the graphics device.
253      * @param dataArea the area inside the axes.
254      * @param plot the plot.
255      * @param data the data.
256      * @param info an optional info collection object to return data back to
257      * the caller.
258      *
259      * @return A state object.
260      */

261     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
262                                           Rectangle2D JavaDoc dataArea,
263                                           XYPlot plot,
264                                           XYDataset data,
265                                           PlotRenderingInfo info) {
266
267         return super.initialise(g2, dataArea, plot, data, info);
268
269     }
270
271     /**
272      * Returns <code>2</code>, the number of passes required by the renderer.
273      * The {@link XYPlot} will run through the dataset this number of times.
274      *
275      * @return The number of passes required by the renderer.
276      */

277     public int getPassCount() {
278         return 2;
279     }
280     
281     /**
282      * Draws the visual representation of a single data item.
283      *
284      * @param g2 the graphics device.
285      * @param state the renderer state.
286      * @param dataArea the area within which the data is being drawn.
287      * @param info collects information about the drawing.
288      * @param plot the plot (can be used to obtain standard color
289      * information etc).
290      * @param domainAxis the domain (horizontal) axis.
291      * @param rangeAxis the range (vertical) axis.
292      * @param dataset the dataset.
293      * @param series the series index (zero-based).
294      * @param item the item index (zero-based).
295      * @param crosshairState crosshair information for the plot
296      * (<code>null</code> permitted).
297      * @param pass the pass index.
298      */

299     public void drawItem(Graphics2D JavaDoc g2,
300                          XYItemRendererState state,
301                          Rectangle2D JavaDoc dataArea,
302                          PlotRenderingInfo info,
303                          XYPlot plot,
304                          ValueAxis domainAxis,
305                          ValueAxis rangeAxis,
306                          XYDataset dataset,
307                          int series,
308                          int item,
309                          CrosshairState crosshairState,
310                          int pass) {
311
312         if (pass == 0) {
313             drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis,
314                     dataset, series, item, crosshairState);
315         }
316         else if (pass == 1) {
317             drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis,
318                     dataset, series, item, crosshairState);
319         }
320
321     }
322
323     /**
324      * Draws the visual representation of a single data item, first pass.
325      *
326      * @param g2 the graphics device.
327      * @param dataArea the area within which the data is being drawn.
328      * @param info collects information about the drawing.
329      * @param plot the plot (can be used to obtain standard color
330      * information etc).
331      * @param domainAxis the domain (horizontal) axis.
332      * @param rangeAxis the range (vertical) axis.
333      * @param dataset the dataset.
334      * @param series the series index (zero-based).
335      * @param item the item index (zero-based).
336      * @param crosshairState crosshair information for the plot
337      * (<code>null</code> permitted).
338      */

339     protected void drawItemPass0(Graphics2D JavaDoc g2,
340                                  Rectangle2D JavaDoc dataArea,
341                                  PlotRenderingInfo info,
342                                  XYPlot plot,
343                                  ValueAxis domainAxis,
344                                  ValueAxis rangeAxis,
345                                  XYDataset dataset,
346                                  int series,
347                                  int item,
348                                  CrosshairState crosshairState) {
349
350         if (series == 0) {
351
352             PlotOrientation orientation = plot.getOrientation();
353             RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
354             RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
355             
356             double y0 = dataset.getYValue(0, item);
357             double x1 = dataset.getXValue(1, item);
358             double y1 = dataset.getYValue(1, item);
359
360             double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
361                     rangeAxisLocation);
362             double transX1 = domainAxis.valueToJava2D(x1, dataArea,
363                     domainAxisLocation);
364             double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
365                     rangeAxisLocation);
366
367             if (item > 0) {
368                 double prevx0 = dataset.getXValue(0, item - 1);
369                 double prevy0 = dataset.getYValue(0, item - 1);
370                 double prevy1 = dataset.getYValue(1, item - 1);
371
372                 double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea,
373                         domainAxisLocation);
374                 double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea,
375                         rangeAxisLocation);
376                 double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea,
377                         rangeAxisLocation);
378
379                 Shape JavaDoc positive = getPositiveArea((float) prevtransX0,
380                         (float) prevtransY0, (float) prevtransY1,
381                         (float) transX1, (float) transY0, (float) transY1,
382                         orientation);
383                 if (positive != null) {
384                     g2.setPaint(getPositivePaint());
385                     g2.fill(positive);
386                 }
387
388                 Shape JavaDoc negative = getNegativeArea((float) prevtransX0,
389                         (float) prevtransY0, (float) prevtransY1,
390                         (float) transX1, (float) transY0, (float) transY1,
391                         orientation);
392
393                 if (negative != null) {
394                     g2.setPaint(getNegativePaint());
395                     g2.fill(negative);
396                 }
397             }
398         }
399
400     }
401
402     /**
403      * Draws the visual representation of a single data item, second pass. In
404      * the second pass, the renderer draws the lines and shapes for the
405      * individual points in the two series.
406      *
407      * @param g2 the graphics device.
408      * @param dataArea the area within which the data is being drawn.
409      * @param info collects information about the drawing.
410      * @param plot the plot (can be used to obtain standard color information
411      * etc).
412      * @param domainAxis the domain (horizontal) axis.
413      * @param rangeAxis the range (vertical) axis.
414      * @param dataset the dataset.
415      * @param series the series index (zero-based).
416      * @param item the item index (zero-based).
417      * @param crosshairState crosshair information for the plot
418      * (<code>null</code> permitted).
419      */

420     protected void drawItemPass1(Graphics2D JavaDoc g2,
421                                  Rectangle2D JavaDoc dataArea,
422                                  PlotRenderingInfo info,
423                                  XYPlot plot,
424                                  ValueAxis domainAxis,
425                                  ValueAxis rangeAxis,
426                                  XYDataset dataset,
427                                  int series,
428                                  int item,
429                                  CrosshairState crosshairState) {
430
431         Shape JavaDoc entityArea = null;
432         EntityCollection entities = null;
433         if (info != null) {
434             entities = info.getOwner().getEntityCollection();
435         }
436
437         Paint JavaDoc seriesPaint = getItemPaint(series, item);
438         Stroke JavaDoc seriesStroke = getItemStroke(series, item);
439         g2.setPaint(seriesPaint);
440         g2.setStroke(seriesStroke);
441
442         if (series == 0) {
443
444             PlotOrientation orientation = plot.getOrientation();
445             RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
446             RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
447
448             double x0 = dataset.getXValue(0, item);
449             double y0 = dataset.getYValue(0, item);
450             double x1 = dataset.getXValue(1, item);
451             double y1 = dataset.getYValue(1, item);
452
453             double transX0 = domainAxis.valueToJava2D(x0, dataArea,
454                     domainAxisLocation);
455             double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
456                     rangeAxisLocation);
457             double transX1 = domainAxis.valueToJava2D(x1, dataArea,
458                     domainAxisLocation);
459             double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
460                     rangeAxisLocation);
461
462             if (item > 0) {
463                 // get the previous data points...
464
double prevx0 = dataset.getXValue(0, item - 1);
465                 double prevy0 = dataset.getYValue(0, item - 1);
466                 double prevx1 = dataset.getXValue(1, item - 1);
467                 double prevy1 = dataset.getYValue(1, item - 1);
468
469                 double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea,
470                         domainAxisLocation);
471                 double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea,
472                         rangeAxisLocation);
473                 double prevtransX1 = domainAxis.valueToJava2D(prevx1, dataArea,
474                         domainAxisLocation);
475                 double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea,
476                         rangeAxisLocation);
477
478                 Line2D JavaDoc line0 = null;
479                 Line2D JavaDoc line1 = null;
480                 if (orientation == PlotOrientation.HORIZONTAL) {
481                     line0 = new Line2D.Double JavaDoc(transY0, transX0, prevtransY0,
482                             prevtransX0);
483                     line1 = new Line2D.Double JavaDoc(transY1, transX1, prevtransY1,
484                             prevtransX1);
485                 }
486                 else if (orientation == PlotOrientation.VERTICAL) {
487                     line0 = new Line2D.Double JavaDoc(transX0, transY0, prevtransX0,
488                             prevtransY0);
489                     line1 = new Line2D.Double JavaDoc(transX1, transY1, prevtransX1,
490                             prevtransY1);
491                 }
492                 if (line0 != null && line0.intersects(dataArea)) {
493                     g2.setPaint(getItemPaint(series, item));
494                     g2.setStroke(getItemStroke(series, item));
495                     g2.draw(line0);
496                 }
497                 if (line1 != null && line1.intersects(dataArea)) {
498                     g2.setPaint(getItemPaint(1, item));
499                     g2.setStroke(getItemStroke(1, item));
500                     g2.draw(line1);
501                 }
502             }
503
504             if (getShapesVisible()) {
505                 Shape JavaDoc shape0 = getItemShape(series, item);
506                 if (orientation == PlotOrientation.HORIZONTAL) {
507                     shape0 = ShapeUtilities.createTranslatedShape(shape0,
508                             transY0, transX0);
509                 }
510                 else { // vertical
511
shape0 = ShapeUtilities.createTranslatedShape(shape0,
512                             transX0, transY0);
513                 }
514                 if (shape0.intersects(dataArea)) {
515                     g2.setPaint(getItemPaint(series, item));
516                     g2.fill(shape0);
517                 }
518                 entityArea = shape0;
519
520                 // add an entity for the item...
521
if (entities != null) {
522                     if (entityArea == null) {
523                         entityArea = new Rectangle2D.Double JavaDoc(transX0 - 2,
524                                 transY0 - 2, 4, 4);
525                     }
526                     String JavaDoc tip = null;
527                     XYToolTipGenerator generator = getToolTipGenerator(series,
528                             item);
529                     if (generator != null) {
530                         tip = generator.generateToolTip(dataset, series, item);
531                     }
532                     String JavaDoc url = null;
533                     if (getURLGenerator() != null) {
534                         url = getURLGenerator().generateURL(dataset, series,
535                                 item);
536                     }
537                     XYItemEntity entity = new XYItemEntity(entityArea, dataset,
538                             series, item, tip, url);
539                     entities.add(entity);
540                 }
541
542                 Shape JavaDoc shape1 = getItemShape(series + 1, item);
543                 if (orientation == PlotOrientation.HORIZONTAL) {
544                     shape1 = ShapeUtilities.createTranslatedShape(shape1,
545                             transY1, transX1);
546                 }
547                 else { // vertical
548
shape1 = ShapeUtilities.createTranslatedShape(shape1,
549                             transX1, transY1);
550                 }
551                 if (shape1.intersects(dataArea)) {
552                     g2.setPaint(getItemPaint(series + 1, item));
553                     g2.fill(shape1);
554                 }
555                 entityArea = shape1;
556
557                 // add an entity for the item...
558
if (entities != null) {
559                     if (entityArea == null) {
560                         entityArea = new Rectangle2D.Double JavaDoc(transX1 - 2,
561                                 transY1 - 2, 4, 4);
562                     }
563                     String JavaDoc tip = null;
564                     XYToolTipGenerator generator = getToolTipGenerator(series,
565                             item);
566                     if (generator != null) {
567                         tip = generator.generateToolTip(dataset, series + 1,
568                                 item);
569                     }
570                     String JavaDoc url = null;
571                     if (getURLGenerator() != null) {
572                         url = getURLGenerator().generateURL(dataset,
573                                 series + 1, item);
574                     }
575                     XYItemEntity entity = new XYItemEntity(entityArea, dataset,
576                             series + 1, item, tip, url);
577                     entities.add(entity);
578                 }
579             }
580             updateCrosshairValues(crosshairState, x1, y1, transX1, transY1,
581                     orientation);
582         }
583
584     }
585
586     /**
587      * Returns the positive area for a crossover point.
588      *
589      * @param x0 x coordinate.
590      * @param y0A y coordinate A.
591      * @param y0B y coordinate B.
592      * @param x1 x coordinate.
593      * @param y1A y coordinate A.
594      * @param y1B y coordinate B.
595      * @param orientation the plot orientation.
596      *
597      * @return The positive area.
598      */

599     protected Shape JavaDoc getPositiveArea(float x0, float y0A, float y0B,
600                                     float x1, float y1A, float y1B,
601                                     PlotOrientation orientation) {
602
603         Shape JavaDoc result = null;
604
605         boolean startsNegative = (y0A >= y0B);
606         boolean endsNegative = (y1A >= y1B);
607         if (orientation == PlotOrientation.HORIZONTAL) {
608             startsNegative = (y0B >= y0A);
609             endsNegative = (y1B >= y1A);
610         }
611         
612         if (startsNegative) { // starts negative
613
if (endsNegative) {
614                 // all negative - return null
615
result = null;
616             }
617             else {
618                 // changed from negative to positive
619
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
620                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
621                 if (orientation == PlotOrientation.HORIZONTAL) {
622                     area.moveTo(y1A, x1);
623                     area.lineTo(p[1], p[0]);
624                     area.lineTo(y1B, x1);
625                     area.closePath();
626                 }
627                 else if (orientation == PlotOrientation.VERTICAL) {
628                     area.moveTo(x1, y1A);
629                     area.lineTo(p[0], p[1]);
630                     area.lineTo(x1, y1B);
631                     area.closePath();
632                 }
633                 result = area;
634             }
635         }
636         else { // starts positive
637
if (endsNegative) {
638                 // changed from positive to negative
639
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
640                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
641                 if (orientation == PlotOrientation.HORIZONTAL) {
642                     area.moveTo(y0A, x0);
643                     area.lineTo(p[1], p[0]);
644                     area.lineTo(y0B, x0);
645                     area.closePath();
646                 }
647                 else if (orientation == PlotOrientation.VERTICAL) {
648                     area.moveTo(x0, y0A);
649                     area.lineTo(p[0], p[1]);
650                     area.lineTo(x0, y0B);
651                     area.closePath();
652                 }
653                 result = area;
654
655             }
656             else {
657                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
658                 if (orientation == PlotOrientation.HORIZONTAL) {
659                     area.moveTo(y0A, x0);
660                     area.lineTo(y1A, x1);
661                     area.lineTo(y1B, x1);
662                     area.lineTo(y0B, x0);
663                     area.closePath();
664                 }
665                 else if (orientation == PlotOrientation.VERTICAL) {
666                     area.moveTo(x0, y0A);
667                     area.lineTo(x1, y1A);
668                     area.lineTo(x1, y1B);
669                     area.lineTo(x0, y0B);
670                     area.closePath();
671                 }
672                 result = area;
673             }
674
675         }
676
677         return result;
678
679     }
680
681     /**
682      * Returns the negative area for a cross-over section.
683      *
684      * @param x0 x coordinate.
685      * @param y0A y coordinate A.
686      * @param y0B y coordinate B.
687      * @param x1 x coordinate.
688      * @param y1A y coordinate A.
689      * @param y1B y coordinate B.
690      * @param orientation the plot orientation.
691      *
692      * @return The negative area.
693      */

694     protected Shape JavaDoc getNegativeArea(float x0, float y0A, float y0B,
695                                     float x1, float y1A, float y1B,
696                                     PlotOrientation orientation) {
697
698         Shape JavaDoc result = null;
699
700         boolean startsNegative = (y0A >= y0B);
701         boolean endsNegative = (y1A >= y1B);
702         if (orientation == PlotOrientation.HORIZONTAL) {
703             startsNegative = (y0B >= y0A);
704             endsNegative = (y1B >= y1A);
705         }
706         if (startsNegative) { // starts negative
707
if (endsNegative) { // all negative
708
GeneralPath JavaDoc area = new GeneralPath JavaDoc();
709                 if (orientation == PlotOrientation.HORIZONTAL) {
710                     area.moveTo(y0A, x0);
711                     area.lineTo(y1A, x1);
712                     area.lineTo(y1B, x1);
713                     area.lineTo(y0B, x0);
714                     area.closePath();
715                 }
716                 else if (orientation == PlotOrientation.VERTICAL) {
717                     area.moveTo(x0, y0A);
718                     area.lineTo(x1, y1A);
719                     area.lineTo(x1, y1B);
720                     area.lineTo(x0, y0B);
721                     area.closePath();
722                 }
723                 result = area;
724             }
725             else { // changed from negative to positive
726
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
727                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
728                 if (orientation == PlotOrientation.HORIZONTAL) {
729                     area.moveTo(y0A, x0);
730                     area.lineTo(p[1], p[0]);
731                     area.lineTo(y0B, x0);
732                     area.closePath();
733                 }
734                 else if (orientation == PlotOrientation.VERTICAL) {
735                     area.moveTo(x0, y0A);
736                     area.lineTo(p[0], p[1]);
737                     area.lineTo(x0, y0B);
738                     area.closePath();
739                 }
740                 result = area;
741             }
742         }
743         else {
744             if (endsNegative) {
745                 // changed from positive to negative
746
float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
747                 GeneralPath JavaDoc area = new GeneralPath JavaDoc();
748                 if (orientation == PlotOrientation.HORIZONTAL) {
749                     area.moveTo(y1A, x1);
750                     area.lineTo(p[1], p[0]);
751                     area.lineTo(y1B, x1);
752                     area.closePath();
753                 }
754                 else if (orientation == PlotOrientation.VERTICAL) {
755                     area.moveTo(x1, y1A);
756                     area.lineTo(p[0], p[1]);
757                     area.lineTo(x1, y1B);
758                     area.closePath();
759                 }
760                 result = area;
761             }
762             else {
763                 // all negative - return null
764
}
765
766         }
767
768         return result;
769
770     }
771
772     /**
773      * Returns the intersection point of two lines.
774      *
775      * @param x1 x1
776      * @param y1 y1
777      * @param x2 x2
778      * @param y2 y2
779      * @param x3 x3
780      * @param y3 y3
781      * @param x4 x4
782      * @param y4 y4
783      *
784      * @return The intersection point.
785      */

786     private float[] getIntersection(float x1, float y1, float x2, float y2,
787                                     float x3, float y3, float x4, float y4) {
788
789         float n = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
790         float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
791         float u = n / d;
792
793         float[] result = new float[2];
794         result[0] = x1 + u * (x2 - x1);
795         result[1] = y1 + u * (y2 - y1);
796         return result;
797
798     }
799     
800     /**
801      * Returns a default legend item for the specified series. Subclasses
802      * should override this method to generate customised items.
803      *
804      * @param datasetIndex the dataset index (zero-based).
805      * @param series the series index (zero-based).
806      *
807      * @return A legend item for the series.
808      */

809     public LegendItem getLegendItem(int datasetIndex, int series) {
810         LegendItem result = null;
811         XYPlot p = getPlot();
812         if (p != null) {
813             XYDataset dataset = p.getDataset(datasetIndex);
814             if (dataset != null) {
815                 if (getItemVisible(series, 0)) {
816                     String JavaDoc label = getLegendItemLabelGenerator().generateLabel(
817                             dataset, series);
818                     String JavaDoc description = label;
819                     String JavaDoc toolTipText = null;
820                     if (getLegendItemToolTipGenerator() != null) {
821                         toolTipText
822                             = getLegendItemToolTipGenerator().generateLabel(
823                                     dataset, series);
824                     }
825                     String JavaDoc urlText = null;
826                     if (getLegendItemURLGenerator() != null) {
827                         urlText = getLegendItemURLGenerator().generateLabel(
828                                 dataset, series);
829                     }
830                     Paint JavaDoc paint = getSeriesPaint(series);
831                     Stroke JavaDoc stroke = getSeriesStroke(series);
832                     // TODO: the following hard-coded line needs generalising
833
Line2D JavaDoc line = new Line2D.Double JavaDoc(-7.0, 0.0, 7.0, 0.0);
834                     result = new LegendItem(label, description,
835                             toolTipText, urlText, line, stroke, paint);
836                 }
837             }
838
839         }
840
841         return result;
842
843     }
844
845     /**
846      * Tests this renderer for equality with an arbitrary object.
847      *
848      * @param obj the object (<code>null</code> permitted).
849      *
850      * @return A boolean.
851      */

852     public boolean equals(Object JavaDoc obj) {
853         if (obj == this) {
854             return true;
855         }
856         if (!(obj instanceof XYDifferenceRenderer)) {
857             return false;
858         }
859         if (!super.equals(obj)) {
860             return false;
861         }
862         XYDifferenceRenderer that = (XYDifferenceRenderer) obj;
863         if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
864             return false;
865         }
866         if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
867             return false;
868         }
869         if (this.shapesVisible != that.shapesVisible) {
870             return false;
871         }
872         if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
873             return false;
874         }
875         return true;
876     }
877     
878     /**
879      * Returns a clone of the renderer.
880      *
881      * @return A clone.
882      *
883      * @throws CloneNotSupportedException if the renderer cannot be cloned.
884      */

885     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
886         return super.clone();
887     }
888
889     /**
890      * Provides serialization support.
891      *
892      * @param stream the output stream.
893      *
894      * @throws IOException if there is an I/O error.
895      */

896     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
897         stream.defaultWriteObject();
898         SerialUtilities.writePaint(this.positivePaint, stream);
899         SerialUtilities.writePaint(this.negativePaint, stream);
900         SerialUtilities.writeShape(this.legendLine, stream);
901     }
902
903     /**
904      * Provides serialization support.
905      *
906      * @param stream the input stream.
907      *
908      * @throws IOException if there is an I/O error.
909      * @throws ClassNotFoundException if there is a classpath problem.
910      */

911     private void readObject(ObjectInputStream JavaDoc stream)
912         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
913         stream.defaultReadObject();
914         this.positivePaint = SerialUtilities.readPaint(stream);
915         this.negativePaint = SerialUtilities.readPaint(stream);
916         this.legendLine = SerialUtilities.readShape(stream);
917     }
918
919 }
920
Popular Tags