Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java @ 1823:7a11b37d8594
Added a copy of JFreeChart's XYDifferenceRenderer
flys-artifacts/trunk@3152 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Thu, 03 Nov 2011 10:32:17 +0000 |
parents | |
children | 982956bde69e |
comparison
equal
deleted
inserted
replaced
1822:6ed439ff61bf | 1823:7a11b37d8594 |
---|---|
1 /* =========================================================== | |
2 * JFreeChart : a free chart library for the Java(tm) platform | |
3 * =========================================================== | |
4 * | |
5 * (C) Copyright 2000-2008, 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 * StableXYDifferenceRenderer.java | |
29 * ------------------------- | |
30 * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors. | |
31 * | |
32 * Original Author: David Gilbert (for Object Refinery Limited); | |
33 * Contributor(s): Richard West, Advanced Micro Devices, Inc. (major rewrite | |
34 * of difference drawing algorithm); | |
35 * | |
36 * Changes: | |
37 * -------- | |
38 * 30-Apr-2003 : Version 1 (DG); | |
39 * 30-Jul-2003 : Modified entity constructor (CZ); | |
40 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); | |
41 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); | |
42 * 09-Feb-2004 : Updated to support horizontal plot orientation (DG); | |
43 * 10-Feb-2004 : Added default constructor, setter methods and updated | |
44 * Javadocs (DG); | |
45 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); | |
46 * 30-Mar-2004 : Fixed bug in getNegativePaint() method (DG); | |
47 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with | |
48 * getYValue() (DG); | |
49 * 25-Aug-2004 : Fixed a bug preventing the use of crosshairs (DG); | |
50 * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); | |
51 * 19-Jan-2005 : Now accesses only primitive values from dataset (DG); | |
52 * 22-Feb-2005 : Override getLegendItem(int, int) to return "line" items (DG); | |
53 * 13-Apr-2005 : Fixed shape positioning bug (id = 1182062) (DG); | |
54 * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); | |
55 * 04-May-2005 : Override equals() method, renamed get/setPlotShapes() --> | |
56 * get/setShapesVisible (DG); | |
57 * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG); | |
58 * 16-Jun-2005 : Fix bug (1221021) affecting stroke used for each series (DG); | |
59 * ------------- JFREECHART 1.0.x --------------------------------------------- | |
60 * 24-Jan-2007 : Added flag to allow rounding of x-coordinates, and fixed | |
61 * bug in clone() (DG); | |
62 * 05-Feb-2007 : Added an extra call to updateCrosshairValues() in | |
63 * drawItemPass1(), to fix bug 1564967 (DG); | |
64 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); | |
65 * 08-Mar-2007 : Fixed entity generation (DG); | |
66 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); | |
67 * 23-Apr-2007 : Rewrite of difference drawing algorithm to allow use of | |
68 * series with disjoint x-values (RW); | |
69 * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG); | |
70 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); | |
71 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); | |
72 * 05-Nov-2007 : Draw item labels if visible (RW); | |
73 * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG); | |
74 * | |
75 */ | |
76 | |
77 package org.jfree.chart.renderer.xy; | |
78 | |
79 import java.awt.Color; | |
80 import java.awt.Graphics2D; | |
81 import java.awt.Paint; | |
82 import java.awt.Shape; | |
83 import java.awt.Stroke; | |
84 import java.awt.geom.GeneralPath; | |
85 import java.awt.geom.Line2D; | |
86 import java.awt.geom.Rectangle2D; | |
87 import java.io.IOException; | |
88 import java.io.ObjectInputStream; | |
89 import java.io.ObjectOutputStream; | |
90 import java.util.Collections; | |
91 import java.util.LinkedList; | |
92 | |
93 import org.jfree.chart.LegendItem; | |
94 import org.jfree.chart.axis.ValueAxis; | |
95 import org.jfree.chart.entity.EntityCollection; | |
96 import org.jfree.chart.entity.XYItemEntity; | |
97 import org.jfree.chart.event.RendererChangeEvent; | |
98 import org.jfree.chart.labels.XYToolTipGenerator; | |
99 import org.jfree.chart.plot.CrosshairState; | |
100 import org.jfree.chart.plot.PlotOrientation; | |
101 import org.jfree.chart.plot.PlotRenderingInfo; | |
102 import org.jfree.chart.plot.XYPlot; | |
103 import org.jfree.chart.urls.XYURLGenerator; | |
104 import org.jfree.data.xy.XYDataset; | |
105 import org.jfree.io.SerialUtilities; | |
106 import org.jfree.ui.RectangleEdge; | |
107 import org.jfree.util.PaintUtilities; | |
108 import org.jfree.util.PublicCloneable; | |
109 import org.jfree.util.ShapeUtilities; | |
110 | |
111 /** | |
112 * A renderer for an {@link XYPlot} that highlights the differences between two | |
113 * series. The example shown here is generated by the | |
114 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart | |
115 * demo collection: | |
116 * <br><br> | |
117 * <img src="../../../../../images/StableXYDifferenceRendererSample.png" | |
118 * alt="StableXYDifferenceRendererSample.png" /> | |
119 */ | |
120 public class StableXYDifferenceRenderer extends AbstractXYItemRenderer | |
121 implements XYItemRenderer, PublicCloneable { | |
122 | |
123 /** For serialization. */ | |
124 private static final long serialVersionUID = -8447915602375584857L; | |
125 | |
126 /** The paint used to highlight positive differences (y(0) > y(1)). */ | |
127 private transient Paint positivePaint; | |
128 | |
129 /** The paint used to highlight negative differences (y(0) < y(1)). */ | |
130 private transient Paint negativePaint; | |
131 | |
132 /** Display shapes at each point? */ | |
133 private boolean shapesVisible; | |
134 | |
135 /** The shape to display in the legend item. */ | |
136 private transient Shape legendLine; | |
137 | |
138 /** | |
139 * This flag controls whether or not the x-coordinates (in Java2D space) | |
140 * are rounded to integers. When set to true, this can avoid the vertical | |
141 * striping that anti-aliasing can generate. However, the rounding may not | |
142 * be appropriate for output in high resolution formats (for example, | |
143 * vector graphics formats such as SVG and PDF). | |
144 * | |
145 * @since 1.0.4 | |
146 */ | |
147 private boolean roundXCoordinates; | |
148 | |
149 /** | |
150 * Creates a new renderer with default attributes. | |
151 */ | |
152 public StableXYDifferenceRenderer() { | |
153 this(Color.green, Color.red, false); | |
154 } | |
155 | |
156 /** | |
157 * Creates a new renderer. | |
158 * | |
159 * @param positivePaint the highlight color for positive differences | |
160 * (<code>null</code> not permitted). | |
161 * @param negativePaint the highlight color for negative differences | |
162 * (<code>null</code> not permitted). | |
163 * @param shapes draw shapes? | |
164 */ | |
165 public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint, | |
166 boolean shapes) { | |
167 if (positivePaint == null) { | |
168 throw new IllegalArgumentException( | |
169 "Null 'positivePaint' argument."); | |
170 } | |
171 if (negativePaint == null) { | |
172 throw new IllegalArgumentException( | |
173 "Null 'negativePaint' argument."); | |
174 } | |
175 this.positivePaint = positivePaint; | |
176 this.negativePaint = negativePaint; | |
177 this.shapesVisible = shapes; | |
178 this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); | |
179 this.roundXCoordinates = false; | |
180 } | |
181 | |
182 /** | |
183 * Returns the paint used to highlight positive differences. | |
184 * | |
185 * @return The paint (never <code>null</code>). | |
186 * | |
187 * @see #setPositivePaint(Paint) | |
188 */ | |
189 public Paint getPositivePaint() { | |
190 return this.positivePaint; | |
191 } | |
192 | |
193 /** | |
194 * Sets the paint used to highlight positive differences and sends a | |
195 * {@link RendererChangeEvent} to all registered listeners. | |
196 * | |
197 * @param paint the paint (<code>null</code> not permitted). | |
198 * | |
199 * @see #getPositivePaint() | |
200 */ | |
201 public void setPositivePaint(Paint paint) { | |
202 if (paint == null) { | |
203 throw new IllegalArgumentException("Null 'paint' argument."); | |
204 } | |
205 this.positivePaint = paint; | |
206 fireChangeEvent(); | |
207 } | |
208 | |
209 /** | |
210 * Returns the paint used to highlight negative differences. | |
211 * | |
212 * @return The paint (never <code>null</code>). | |
213 * | |
214 * @see #setNegativePaint(Paint) | |
215 */ | |
216 public Paint getNegativePaint() { | |
217 return this.negativePaint; | |
218 } | |
219 | |
220 /** | |
221 * Sets the paint used to highlight negative differences. | |
222 * | |
223 * @param paint the paint (<code>null</code> not permitted). | |
224 * | |
225 * @see #getNegativePaint() | |
226 */ | |
227 public void setNegativePaint(Paint paint) { | |
228 if (paint == null) { | |
229 throw new IllegalArgumentException("Null 'paint' argument."); | |
230 } | |
231 this.negativePaint = paint; | |
232 notifyListeners(new RendererChangeEvent(this)); | |
233 } | |
234 | |
235 /** | |
236 * Returns a flag that controls whether or not shapes are drawn for each | |
237 * data value. | |
238 * | |
239 * @return A boolean. | |
240 * | |
241 * @see #setShapesVisible(boolean) | |
242 */ | |
243 public boolean getShapesVisible() { | |
244 return this.shapesVisible; | |
245 } | |
246 | |
247 /** | |
248 * Sets a flag that controls whether or not shapes are drawn for each | |
249 * data value, and sends a {@link RendererChangeEvent} to all registered | |
250 * listeners. | |
251 * | |
252 * @param flag the flag. | |
253 * | |
254 * @see #getShapesVisible() | |
255 */ | |
256 public void setShapesVisible(boolean flag) { | |
257 this.shapesVisible = flag; | |
258 fireChangeEvent(); | |
259 } | |
260 | |
261 /** | |
262 * Returns the shape used to represent a line in the legend. | |
263 * | |
264 * @return The legend line (never <code>null</code>). | |
265 * | |
266 * @see #setLegendLine(Shape) | |
267 */ | |
268 public Shape getLegendLine() { | |
269 return this.legendLine; | |
270 } | |
271 | |
272 /** | |
273 * Sets the shape used as a line in each legend item and sends a | |
274 * {@link RendererChangeEvent} to all registered listeners. | |
275 * | |
276 * @param line the line (<code>null</code> not permitted). | |
277 * | |
278 * @see #getLegendLine() | |
279 */ | |
280 public void setLegendLine(Shape line) { | |
281 if (line == null) { | |
282 throw new IllegalArgumentException("Null 'line' argument."); | |
283 } | |
284 this.legendLine = line; | |
285 fireChangeEvent(); | |
286 } | |
287 | |
288 /** | |
289 * Returns the flag that controls whether or not the x-coordinates (in | |
290 * Java2D space) are rounded to integer values. | |
291 * | |
292 * @return The flag. | |
293 * | |
294 * @since 1.0.4 | |
295 * | |
296 * @see #setRoundXCoordinates(boolean) | |
297 */ | |
298 public boolean getRoundXCoordinates() { | |
299 return this.roundXCoordinates; | |
300 } | |
301 | |
302 /** | |
303 * Sets the flag that controls whether or not the x-coordinates (in | |
304 * Java2D space) are rounded to integer values, and sends a | |
305 * {@link RendererChangeEvent} to all registered listeners. | |
306 * | |
307 * @param round the new flag value. | |
308 * | |
309 * @since 1.0.4 | |
310 * | |
311 * @see #getRoundXCoordinates() | |
312 */ | |
313 public void setRoundXCoordinates(boolean round) { | |
314 this.roundXCoordinates = round; | |
315 fireChangeEvent(); | |
316 } | |
317 | |
318 /** | |
319 * Initialises the renderer and returns a state object that should be | |
320 * passed to subsequent calls to the drawItem() method. This method will | |
321 * be called before the first item is rendered, giving the renderer an | |
322 * opportunity to initialise any state information it wants to maintain. | |
323 * The renderer can do nothing if it chooses. | |
324 * | |
325 * @param g2 the graphics device. | |
326 * @param dataArea the area inside the axes. | |
327 * @param plot the plot. | |
328 * @param data the data. | |
329 * @param info an optional info collection object to return data back to | |
330 * the caller. | |
331 * | |
332 * @return A state object. | |
333 */ | |
334 public XYItemRendererState initialise(Graphics2D g2, | |
335 Rectangle2D dataArea, | |
336 XYPlot plot, | |
337 XYDataset data, | |
338 PlotRenderingInfo info) { | |
339 | |
340 XYItemRendererState state = super.initialise(g2, dataArea, plot, data, | |
341 info); | |
342 state.setProcessVisibleItemsOnly(false); | |
343 return state; | |
344 | |
345 } | |
346 | |
347 /** | |
348 * Returns <code>2</code>, the number of passes required by the renderer. | |
349 * The {@link XYPlot} will run through the dataset this number of times. | |
350 * | |
351 * @return The number of passes required by the renderer. | |
352 */ | |
353 public int getPassCount() { | |
354 return 2; | |
355 } | |
356 | |
357 /** | |
358 * Draws the visual representation of a single data item. | |
359 * | |
360 * @param g2 the graphics device. | |
361 * @param state the renderer state. | |
362 * @param dataArea the area within which the data is being drawn. | |
363 * @param info collects information about the drawing. | |
364 * @param plot the plot (can be used to obtain standard color | |
365 * information etc). | |
366 * @param domainAxis the domain (horizontal) axis. | |
367 * @param rangeAxis the range (vertical) axis. | |
368 * @param dataset the dataset. | |
369 * @param series the series index (zero-based). | |
370 * @param item the item index (zero-based). | |
371 * @param crosshairState crosshair information for the plot | |
372 * (<code>null</code> permitted). | |
373 * @param pass the pass index. | |
374 */ | |
375 public void drawItem(Graphics2D g2, | |
376 XYItemRendererState state, | |
377 Rectangle2D dataArea, | |
378 PlotRenderingInfo info, | |
379 XYPlot plot, | |
380 ValueAxis domainAxis, | |
381 ValueAxis rangeAxis, | |
382 XYDataset dataset, | |
383 int series, | |
384 int item, | |
385 CrosshairState crosshairState, | |
386 int pass) { | |
387 | |
388 if (pass == 0) { | |
389 drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, | |
390 dataset, series, item, crosshairState); | |
391 } | |
392 else if (pass == 1) { | |
393 drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis, | |
394 dataset, series, item, crosshairState); | |
395 } | |
396 | |
397 } | |
398 | |
399 /** | |
400 * Draws the visual representation of a single data item, first pass. | |
401 * | |
402 * @param x_graphics the graphics device. | |
403 * @param x_dataArea the area within which the data is being drawn. | |
404 * @param x_info collects information about the drawing. | |
405 * @param x_plot the plot (can be used to obtain standard color | |
406 * information etc). | |
407 * @param x_domainAxis the domain (horizontal) axis. | |
408 * @param x_rangeAxis the range (vertical) axis. | |
409 * @param x_dataset the dataset. | |
410 * @param x_series the series index (zero-based). | |
411 * @param x_item the item index (zero-based). | |
412 * @param x_crosshairState crosshair information for the plot | |
413 * (<code>null</code> permitted). | |
414 */ | |
415 protected void drawItemPass0(Graphics2D x_graphics, | |
416 Rectangle2D x_dataArea, | |
417 PlotRenderingInfo x_info, | |
418 XYPlot x_plot, | |
419 ValueAxis x_domainAxis, | |
420 ValueAxis x_rangeAxis, | |
421 XYDataset x_dataset, | |
422 int x_series, | |
423 int x_item, | |
424 CrosshairState x_crosshairState) { | |
425 | |
426 if (!((0 == x_series) && (0 == x_item))) { | |
427 return; | |
428 } | |
429 | |
430 boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount()); | |
431 | |
432 // check if either series is a degenerate case (i.e. less than 2 points) | |
433 if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) { | |
434 return; | |
435 } | |
436 | |
437 // check if series are disjoint (i.e. domain-spans do not overlap) | |
438 if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) { | |
439 return; | |
440 } | |
441 | |
442 // polygon definitions | |
443 LinkedList l_minuendXs = new LinkedList(); | |
444 LinkedList l_minuendYs = new LinkedList(); | |
445 LinkedList l_subtrahendXs = new LinkedList(); | |
446 LinkedList l_subtrahendYs = new LinkedList(); | |
447 LinkedList l_polygonXs = new LinkedList(); | |
448 LinkedList l_polygonYs = new LinkedList(); | |
449 | |
450 // state | |
451 int l_minuendItem = 0; | |
452 int l_minuendItemCount = x_dataset.getItemCount(0); | |
453 Double l_minuendCurX = null; | |
454 Double l_minuendNextX = null; | |
455 Double l_minuendCurY = null; | |
456 Double l_minuendNextY = null; | |
457 double l_minuendMaxY = Double.NEGATIVE_INFINITY; | |
458 double l_minuendMinY = Double.POSITIVE_INFINITY; | |
459 | |
460 int l_subtrahendItem = 0; | |
461 int l_subtrahendItemCount = 0; // actual value set below | |
462 Double l_subtrahendCurX = null; | |
463 Double l_subtrahendNextX = null; | |
464 Double l_subtrahendCurY = null; | |
465 Double l_subtrahendNextY = null; | |
466 double l_subtrahendMaxY = Double.NEGATIVE_INFINITY; | |
467 double l_subtrahendMinY = Double.POSITIVE_INFINITY; | |
468 | |
469 // if a subtrahend is not specified, assume it is zero | |
470 if (b_impliedZeroSubtrahend) { | |
471 l_subtrahendItem = 0; | |
472 l_subtrahendItemCount = 2; | |
473 l_subtrahendCurX = new Double(x_dataset.getXValue(0, 0)); | |
474 l_subtrahendNextX = new Double(x_dataset.getXValue(0, | |
475 (l_minuendItemCount - 1))); | |
476 l_subtrahendCurY = new Double(0.0); | |
477 l_subtrahendNextY = new Double(0.0); | |
478 l_subtrahendMaxY = 0.0; | |
479 l_subtrahendMinY = 0.0; | |
480 | |
481 l_subtrahendXs.add(l_subtrahendCurX); | |
482 l_subtrahendYs.add(l_subtrahendCurY); | |
483 } | |
484 else { | |
485 l_subtrahendItemCount = x_dataset.getItemCount(1); | |
486 } | |
487 | |
488 boolean b_minuendDone = false; | |
489 boolean b_minuendAdvanced = true; | |
490 boolean b_minuendAtIntersect = false; | |
491 boolean b_minuendFastForward = false; | |
492 boolean b_subtrahendDone = false; | |
493 boolean b_subtrahendAdvanced = true; | |
494 boolean b_subtrahendAtIntersect = false; | |
495 boolean b_subtrahendFastForward = false; | |
496 boolean b_colinear = false; | |
497 | |
498 boolean b_positive; | |
499 | |
500 // coordinate pairs | |
501 double l_x1 = 0.0, l_y1 = 0.0; // current minuend point | |
502 double l_x2 = 0.0, l_y2 = 0.0; // next minuend point | |
503 double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point | |
504 double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point | |
505 | |
506 // fast-forward through leading tails | |
507 boolean b_fastForwardDone = false; | |
508 while (!b_fastForwardDone) { | |
509 // get the x and y coordinates | |
510 l_x1 = x_dataset.getXValue(0, l_minuendItem); | |
511 l_y1 = x_dataset.getYValue(0, l_minuendItem); | |
512 l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); | |
513 l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); | |
514 | |
515 l_minuendCurX = new Double(l_x1); | |
516 l_minuendCurY = new Double(l_y1); | |
517 l_minuendNextX = new Double(l_x2); | |
518 l_minuendNextY = new Double(l_y2); | |
519 | |
520 if (b_impliedZeroSubtrahend) { | |
521 l_x3 = l_subtrahendCurX.doubleValue(); | |
522 l_y3 = l_subtrahendCurY.doubleValue(); | |
523 l_x4 = l_subtrahendNextX.doubleValue(); | |
524 l_y4 = l_subtrahendNextY.doubleValue(); | |
525 } | |
526 else { | |
527 l_x3 = x_dataset.getXValue(1, l_subtrahendItem); | |
528 l_y3 = x_dataset.getYValue(1, l_subtrahendItem); | |
529 l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); | |
530 l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); | |
531 | |
532 l_subtrahendCurX = new Double(l_x3); | |
533 l_subtrahendCurY = new Double(l_y3); | |
534 l_subtrahendNextX = new Double(l_x4); | |
535 l_subtrahendNextY = new Double(l_y4); | |
536 } | |
537 | |
538 if (l_x2 <= l_x3) { | |
539 // minuend needs to be fast forwarded | |
540 l_minuendItem++; | |
541 b_minuendFastForward = true; | |
542 continue; | |
543 } | |
544 | |
545 if (l_x4 <= l_x1) { | |
546 // subtrahend needs to be fast forwarded | |
547 l_subtrahendItem++; | |
548 b_subtrahendFastForward = true; | |
549 continue; | |
550 } | |
551 | |
552 // check if initial polygon needs to be clipped | |
553 if ((l_x3 < l_x1) && (l_x1 < l_x4)) { | |
554 // project onto subtrahend | |
555 double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); | |
556 l_subtrahendCurX = l_minuendCurX; | |
557 l_subtrahendCurY = new Double((l_slope * l_x1) | |
558 + (l_y3 - (l_slope * l_x3))); | |
559 | |
560 l_subtrahendXs.add(l_subtrahendCurX); | |
561 l_subtrahendYs.add(l_subtrahendCurY); | |
562 } | |
563 | |
564 if ((l_x1 < l_x3) && (l_x3 < l_x2)) { | |
565 // project onto minuend | |
566 double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); | |
567 l_minuendCurX = l_subtrahendCurX; | |
568 l_minuendCurY = new Double((l_slope * l_x3) | |
569 + (l_y1 - (l_slope * l_x1))); | |
570 | |
571 l_minuendXs.add(l_minuendCurX); | |
572 l_minuendYs.add(l_minuendCurY); | |
573 } | |
574 | |
575 l_minuendMaxY = l_minuendCurY.doubleValue(); | |
576 l_minuendMinY = l_minuendCurY.doubleValue(); | |
577 l_subtrahendMaxY = l_subtrahendCurY.doubleValue(); | |
578 l_subtrahendMinY = l_subtrahendCurY.doubleValue(); | |
579 | |
580 b_fastForwardDone = true; | |
581 } | |
582 | |
583 // start of algorithm | |
584 while (!b_minuendDone && !b_subtrahendDone) { | |
585 if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) { | |
586 l_x1 = x_dataset.getXValue(0, l_minuendItem); | |
587 l_y1 = x_dataset.getYValue(0, l_minuendItem); | |
588 l_minuendCurX = new Double(l_x1); | |
589 l_minuendCurY = new Double(l_y1); | |
590 | |
591 if (!b_minuendAtIntersect) { | |
592 l_minuendXs.add(l_minuendCurX); | |
593 l_minuendYs.add(l_minuendCurY); | |
594 } | |
595 | |
596 l_minuendMaxY = Math.max(l_minuendMaxY, l_y1); | |
597 l_minuendMinY = Math.min(l_minuendMinY, l_y1); | |
598 | |
599 l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); | |
600 l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); | |
601 l_minuendNextX = new Double(l_x2); | |
602 l_minuendNextY = new Double(l_y2); | |
603 } | |
604 | |
605 // never updated the subtrahend if it is implied to be zero | |
606 if (!b_impliedZeroSubtrahend && !b_subtrahendDone | |
607 && !b_subtrahendFastForward && b_subtrahendAdvanced) { | |
608 l_x3 = x_dataset.getXValue(1, l_subtrahendItem); | |
609 l_y3 = x_dataset.getYValue(1, l_subtrahendItem); | |
610 l_subtrahendCurX = new Double(l_x3); | |
611 l_subtrahendCurY = new Double(l_y3); | |
612 | |
613 if (!b_subtrahendAtIntersect) { | |
614 l_subtrahendXs.add(l_subtrahendCurX); | |
615 l_subtrahendYs.add(l_subtrahendCurY); | |
616 } | |
617 | |
618 l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3); | |
619 l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3); | |
620 | |
621 l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); | |
622 l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); | |
623 l_subtrahendNextX = new Double(l_x4); | |
624 l_subtrahendNextY = new Double(l_y4); | |
625 } | |
626 | |
627 // deassert b_*FastForward (only matters for 1st time through loop) | |
628 b_minuendFastForward = false; | |
629 b_subtrahendFastForward = false; | |
630 | |
631 Double l_intersectX = null; | |
632 Double l_intersectY = null; | |
633 boolean b_intersect = false; | |
634 | |
635 b_minuendAtIntersect = false; | |
636 b_subtrahendAtIntersect = false; | |
637 | |
638 // check for intersect | |
639 if ((l_x2 == l_x4) && (l_y2 == l_y4)) { | |
640 // check if line segments are colinear | |
641 if ((l_x1 == l_x3) && (l_y1 == l_y3)) { | |
642 b_colinear = true; | |
643 } | |
644 else { | |
645 // the intersect is at the next point for both the minuend | |
646 // and subtrahend | |
647 l_intersectX = new Double(l_x2); | |
648 l_intersectY = new Double(l_y2); | |
649 | |
650 b_intersect = true; | |
651 b_minuendAtIntersect = true; | |
652 b_subtrahendAtIntersect = true; | |
653 } | |
654 } | |
655 else { | |
656 // compute common denominator | |
657 double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1)) | |
658 - ((l_x4 - l_x3) * (l_y2 - l_y1)); | |
659 | |
660 // compute common deltas | |
661 double l_deltaY = l_y1 - l_y3; | |
662 double l_deltaX = l_x1 - l_x3; | |
663 | |
664 // compute numerators | |
665 double l_numeratorA = ((l_x4 - l_x3) * l_deltaY) | |
666 - ((l_y4 - l_y3) * l_deltaX); | |
667 double l_numeratorB = ((l_x2 - l_x1) * l_deltaY) | |
668 - ((l_y2 - l_y1) * l_deltaX); | |
669 | |
670 // check if line segments are colinear | |
671 if ((0 == l_numeratorA) && (0 == l_numeratorB) | |
672 && (0 == l_denominator)) { | |
673 b_colinear = true; | |
674 } | |
675 else { | |
676 // check if previously colinear | |
677 if (b_colinear) { | |
678 // clear colinear points and flag | |
679 l_minuendXs.clear(); | |
680 l_minuendYs.clear(); | |
681 l_subtrahendXs.clear(); | |
682 l_subtrahendYs.clear(); | |
683 l_polygonXs.clear(); | |
684 l_polygonYs.clear(); | |
685 | |
686 b_colinear = false; | |
687 | |
688 // set new starting point for the polygon | |
689 boolean b_useMinuend = ((l_x3 <= l_x1) | |
690 && (l_x1 <= l_x4)); | |
691 l_polygonXs.add(b_useMinuend ? l_minuendCurX | |
692 : l_subtrahendCurX); | |
693 l_polygonYs.add(b_useMinuend ? l_minuendCurY | |
694 : l_subtrahendCurY); | |
695 } | |
696 | |
697 // compute slope components | |
698 double l_slopeA = l_numeratorA / l_denominator; | |
699 double l_slopeB = l_numeratorB / l_denominator; | |
700 | |
701 // check if the line segments intersect | |
702 if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB) | |
703 && (l_slopeB <= 1)) { | |
704 // compute the point of intersection | |
705 double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1)); | |
706 double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1)); | |
707 | |
708 l_intersectX = new Double(l_xi); | |
709 l_intersectY = new Double(l_yi); | |
710 b_intersect = true; | |
711 b_minuendAtIntersect = ((l_xi == l_x2) | |
712 && (l_yi == l_y2)); | |
713 b_subtrahendAtIntersect = ((l_xi == l_x4) | |
714 && (l_yi == l_y4)); | |
715 | |
716 // advance minuend and subtrahend to intesect | |
717 l_minuendCurX = l_intersectX; | |
718 l_minuendCurY = l_intersectY; | |
719 l_subtrahendCurX = l_intersectX; | |
720 l_subtrahendCurY = l_intersectY; | |
721 } | |
722 } | |
723 } | |
724 | |
725 if (b_intersect) { | |
726 // create the polygon | |
727 // add the minuend's points to polygon | |
728 l_polygonXs.addAll(l_minuendXs); | |
729 l_polygonYs.addAll(l_minuendYs); | |
730 | |
731 // add intersection point to the polygon | |
732 l_polygonXs.add(l_intersectX); | |
733 l_polygonYs.add(l_intersectY); | |
734 | |
735 // add the subtrahend's points to the polygon in reverse | |
736 Collections.reverse(l_subtrahendXs); | |
737 Collections.reverse(l_subtrahendYs); | |
738 l_polygonXs.addAll(l_subtrahendXs); | |
739 l_polygonYs.addAll(l_subtrahendYs); | |
740 | |
741 // create an actual polygon | |
742 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) | |
743 && (l_subtrahendMinY <= l_minuendMinY); | |
744 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, | |
745 x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); | |
746 | |
747 // clear the point vectors | |
748 l_minuendXs.clear(); | |
749 l_minuendYs.clear(); | |
750 l_subtrahendXs.clear(); | |
751 l_subtrahendYs.clear(); | |
752 l_polygonXs.clear(); | |
753 l_polygonYs.clear(); | |
754 | |
755 // set the maxY and minY values to intersect y-value | |
756 double l_y = l_intersectY.doubleValue(); | |
757 l_minuendMaxY = l_y; | |
758 l_subtrahendMaxY = l_y; | |
759 l_minuendMinY = l_y; | |
760 l_subtrahendMinY = l_y; | |
761 | |
762 // add interection point to new polygon | |
763 l_polygonXs.add(l_intersectX); | |
764 l_polygonYs.add(l_intersectY); | |
765 } | |
766 | |
767 // advance the minuend if needed | |
768 if (l_x2 <= l_x4) { | |
769 l_minuendItem++; | |
770 b_minuendAdvanced = true; | |
771 } | |
772 else { | |
773 b_minuendAdvanced = false; | |
774 } | |
775 | |
776 // advance the subtrahend if needed | |
777 if (l_x4 <= l_x2) { | |
778 l_subtrahendItem++; | |
779 b_subtrahendAdvanced = true; | |
780 } | |
781 else { | |
782 b_subtrahendAdvanced = false; | |
783 } | |
784 | |
785 b_minuendDone = (l_minuendItem == (l_minuendItemCount - 1)); | |
786 b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount | |
787 - 1)); | |
788 } | |
789 | |
790 // check if the final polygon needs to be clipped | |
791 if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) { | |
792 // project onto subtrahend | |
793 double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); | |
794 l_subtrahendNextX = l_minuendNextX; | |
795 l_subtrahendNextY = new Double((l_slope * l_x2) | |
796 + (l_y3 - (l_slope * l_x3))); | |
797 } | |
798 | |
799 if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) { | |
800 // project onto minuend | |
801 double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); | |
802 l_minuendNextX = l_subtrahendNextX; | |
803 l_minuendNextY = new Double((l_slope * l_x4) | |
804 + (l_y1 - (l_slope * l_x1))); | |
805 } | |
806 | |
807 // consider last point of minuend and subtrahend for determining | |
808 // positivity | |
809 l_minuendMaxY = Math.max(l_minuendMaxY, | |
810 l_minuendNextY.doubleValue()); | |
811 l_subtrahendMaxY = Math.max(l_subtrahendMaxY, | |
812 l_subtrahendNextY.doubleValue()); | |
813 l_minuendMinY = Math.min(l_minuendMinY, | |
814 l_minuendNextY.doubleValue()); | |
815 l_subtrahendMinY = Math.min(l_subtrahendMinY, | |
816 l_subtrahendNextY.doubleValue()); | |
817 | |
818 // add the last point of the minuned and subtrahend | |
819 l_minuendXs.add(l_minuendNextX); | |
820 l_minuendYs.add(l_minuendNextY); | |
821 l_subtrahendXs.add(l_subtrahendNextX); | |
822 l_subtrahendYs.add(l_subtrahendNextY); | |
823 | |
824 // create the polygon | |
825 // add the minuend's points to polygon | |
826 l_polygonXs.addAll(l_minuendXs); | |
827 l_polygonYs.addAll(l_minuendYs); | |
828 | |
829 // add the subtrahend's points to the polygon in reverse | |
830 Collections.reverse(l_subtrahendXs); | |
831 Collections.reverse(l_subtrahendYs); | |
832 l_polygonXs.addAll(l_subtrahendXs); | |
833 l_polygonYs.addAll(l_subtrahendYs); | |
834 | |
835 // create an actual polygon | |
836 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) | |
837 && (l_subtrahendMinY <= l_minuendMinY); | |
838 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, | |
839 x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); | |
840 } | |
841 | |
842 /** | |
843 * Draws the visual representation of a single data item, second pass. In | |
844 * the second pass, the renderer draws the lines and shapes for the | |
845 * individual points in the two series. | |
846 * | |
847 * @param x_graphics the graphics device. | |
848 * @param x_dataArea the area within which the data is being drawn. | |
849 * @param x_info collects information about the drawing. | |
850 * @param x_plot the plot (can be used to obtain standard color | |
851 * information etc). | |
852 * @param x_domainAxis the domain (horizontal) axis. | |
853 * @param x_rangeAxis the range (vertical) axis. | |
854 * @param x_dataset the dataset. | |
855 * @param x_series the series index (zero-based). | |
856 * @param x_item the item index (zero-based). | |
857 * @param x_crosshairState crosshair information for the plot | |
858 * (<code>null</code> permitted). | |
859 */ | |
860 protected void drawItemPass1(Graphics2D x_graphics, | |
861 Rectangle2D x_dataArea, | |
862 PlotRenderingInfo x_info, | |
863 XYPlot x_plot, | |
864 ValueAxis x_domainAxis, | |
865 ValueAxis x_rangeAxis, | |
866 XYDataset x_dataset, | |
867 int x_series, | |
868 int x_item, | |
869 CrosshairState x_crosshairState) { | |
870 | |
871 Shape l_entityArea = null; | |
872 EntityCollection l_entities = null; | |
873 if (null != x_info) { | |
874 l_entities = x_info.getOwner().getEntityCollection(); | |
875 } | |
876 | |
877 Paint l_seriesPaint = getItemPaint(x_series, x_item); | |
878 Stroke l_seriesStroke = getItemStroke(x_series, x_item); | |
879 x_graphics.setPaint(l_seriesPaint); | |
880 x_graphics.setStroke(l_seriesStroke); | |
881 | |
882 PlotOrientation l_orientation = x_plot.getOrientation(); | |
883 RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); | |
884 RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); | |
885 | |
886 double l_x0 = x_dataset.getXValue(x_series, x_item); | |
887 double l_y0 = x_dataset.getYValue(x_series, x_item); | |
888 double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, | |
889 l_domainAxisLocation); | |
890 double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea, | |
891 l_rangeAxisLocation); | |
892 | |
893 if (getShapesVisible()) { | |
894 Shape l_shape = getItemShape(x_series, x_item); | |
895 if (l_orientation == PlotOrientation.HORIZONTAL) { | |
896 l_shape = ShapeUtilities.createTranslatedShape(l_shape, | |
897 l_y1, l_x1); | |
898 } | |
899 else { | |
900 l_shape = ShapeUtilities.createTranslatedShape(l_shape, | |
901 l_x1, l_y1); | |
902 } | |
903 if (l_shape.intersects(x_dataArea)) { | |
904 x_graphics.setPaint(getItemPaint(x_series, x_item)); | |
905 x_graphics.fill(l_shape); | |
906 } | |
907 l_entityArea = l_shape; | |
908 } | |
909 | |
910 // add an entity for the item... | |
911 if (null != l_entities) { | |
912 if (null == l_entityArea) { | |
913 l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), | |
914 4, 4); | |
915 } | |
916 String l_tip = null; | |
917 XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, | |
918 x_item); | |
919 if (null != l_tipGenerator) { | |
920 l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, | |
921 x_item); | |
922 } | |
923 String l_url = null; | |
924 XYURLGenerator l_urlGenerator = getURLGenerator(); | |
925 if (null != l_urlGenerator) { | |
926 l_url = l_urlGenerator.generateURL(x_dataset, x_series, | |
927 x_item); | |
928 } | |
929 XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset, | |
930 x_series, x_item, l_tip, l_url); | |
931 l_entities.add(l_entity); | |
932 } | |
933 | |
934 // draw the item label if there is one... | |
935 if (isItemLabelVisible(x_series, x_item)) { | |
936 drawItemLabel(x_graphics, l_orientation, x_dataset, x_series, | |
937 x_item, l_x1, l_y1, (l_y1 < 0.0)); | |
938 } | |
939 | |
940 int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis); | |
941 int l_rangeAxisIndex = x_plot.getRangeAxisIndex(x_rangeAxis); | |
942 updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex, | |
943 l_rangeAxisIndex, l_x1, l_y1, l_orientation); | |
944 | |
945 if (0 == x_item) { | |
946 return; | |
947 } | |
948 | |
949 double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series, | |
950 (x_item - 1)), x_dataArea, l_domainAxisLocation); | |
951 double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series, | |
952 (x_item - 1)), x_dataArea, l_rangeAxisLocation); | |
953 | |
954 Line2D l_line = null; | |
955 if (PlotOrientation.HORIZONTAL == l_orientation) { | |
956 l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2); | |
957 } | |
958 else if (PlotOrientation.VERTICAL == l_orientation) { | |
959 l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2); | |
960 } | |
961 | |
962 if ((null != l_line) && l_line.intersects(x_dataArea)) { | |
963 x_graphics.setPaint(getItemPaint(x_series, x_item)); | |
964 x_graphics.setStroke(getItemStroke(x_series, x_item)); | |
965 x_graphics.draw(l_line); | |
966 } | |
967 } | |
968 | |
969 /** | |
970 * Determines if a dataset is degenerate. A degenerate dataset is a | |
971 * dataset where either series has less than two (2) points. | |
972 * | |
973 * @param x_dataset the dataset. | |
974 * @param x_impliedZeroSubtrahend if false, do not check the subtrahend | |
975 * | |
976 * @return true if the dataset is degenerate. | |
977 */ | |
978 private boolean isEitherSeriesDegenerate(XYDataset x_dataset, | |
979 boolean x_impliedZeroSubtrahend) { | |
980 | |
981 if (x_impliedZeroSubtrahend) { | |
982 return (x_dataset.getItemCount(0) < 2); | |
983 } | |
984 | |
985 return ((x_dataset.getItemCount(0) < 2) | |
986 || (x_dataset.getItemCount(1) < 2)); | |
987 } | |
988 | |
989 /** | |
990 * Determines if the two (2) series are disjoint. | |
991 * Disjoint series do not overlap in the domain space. | |
992 * | |
993 * @param x_dataset the dataset. | |
994 * | |
995 * @return true if the dataset is degenerate. | |
996 */ | |
997 private boolean areSeriesDisjoint(XYDataset x_dataset) { | |
998 | |
999 int l_minuendItemCount = x_dataset.getItemCount(0); | |
1000 double l_minuendFirst = x_dataset.getXValue(0, 0); | |
1001 double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1); | |
1002 | |
1003 int l_subtrahendItemCount = x_dataset.getItemCount(1); | |
1004 double l_subtrahendFirst = x_dataset.getXValue(1, 0); | |
1005 double l_subtrahendLast = x_dataset.getXValue(1, | |
1006 l_subtrahendItemCount - 1); | |
1007 | |
1008 return ((l_minuendLast < l_subtrahendFirst) | |
1009 || (l_subtrahendLast < l_minuendFirst)); | |
1010 } | |
1011 | |
1012 /** | |
1013 * Draws the visual representation of a polygon | |
1014 * | |
1015 * @param x_graphics the graphics device. | |
1016 * @param x_dataArea the area within which the data is being drawn. | |
1017 * @param x_plot the plot (can be used to obtain standard color | |
1018 * information etc). | |
1019 * @param x_domainAxis the domain (horizontal) axis. | |
1020 * @param x_rangeAxis the range (vertical) axis. | |
1021 * @param x_positive indicates if the polygon is positive (true) or | |
1022 * negative (false). | |
1023 * @param x_xValues a linked list of the x values (expects values to be | |
1024 * of type Double). | |
1025 * @param x_yValues a linked list of the y values (expects values to be | |
1026 * of type Double). | |
1027 */ | |
1028 private void createPolygon (Graphics2D x_graphics, | |
1029 Rectangle2D x_dataArea, | |
1030 XYPlot x_plot, | |
1031 ValueAxis x_domainAxis, | |
1032 ValueAxis x_rangeAxis, | |
1033 boolean x_positive, | |
1034 LinkedList x_xValues, | |
1035 LinkedList x_yValues) { | |
1036 | |
1037 PlotOrientation l_orientation = x_plot.getOrientation(); | |
1038 RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); | |
1039 RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); | |
1040 | |
1041 Object[] l_xValues = x_xValues.toArray(); | |
1042 Object[] l_yValues = x_yValues.toArray(); | |
1043 | |
1044 GeneralPath l_path = new GeneralPath(); | |
1045 | |
1046 if (PlotOrientation.VERTICAL == l_orientation) { | |
1047 double l_x = x_domainAxis.valueToJava2D(( | |
1048 (Double) l_xValues[0]).doubleValue(), x_dataArea, | |
1049 l_domainAxisLocation); | |
1050 if (this.roundXCoordinates) { | |
1051 l_x = Math.rint(l_x); | |
1052 } | |
1053 | |
1054 double l_y = x_rangeAxis.valueToJava2D(( | |
1055 (Double) l_yValues[0]).doubleValue(), x_dataArea, | |
1056 l_rangeAxisLocation); | |
1057 | |
1058 l_path.moveTo((float) l_x, (float) l_y); | |
1059 for (int i = 1; i < l_xValues.length; i++) { | |
1060 l_x = x_domainAxis.valueToJava2D(( | |
1061 (Double) l_xValues[i]).doubleValue(), x_dataArea, | |
1062 l_domainAxisLocation); | |
1063 if (this.roundXCoordinates) { | |
1064 l_x = Math.rint(l_x); | |
1065 } | |
1066 | |
1067 l_y = x_rangeAxis.valueToJava2D(( | |
1068 (Double) l_yValues[i]).doubleValue(), x_dataArea, | |
1069 l_rangeAxisLocation); | |
1070 l_path.lineTo((float) l_x, (float) l_y); | |
1071 } | |
1072 l_path.closePath(); | |
1073 } | |
1074 else { | |
1075 double l_x = x_domainAxis.valueToJava2D(( | |
1076 (Double) l_xValues[0]).doubleValue(), x_dataArea, | |
1077 l_domainAxisLocation); | |
1078 if (this.roundXCoordinates) { | |
1079 l_x = Math.rint(l_x); | |
1080 } | |
1081 | |
1082 double l_y = x_rangeAxis.valueToJava2D(( | |
1083 (Double) l_yValues[0]).doubleValue(), x_dataArea, | |
1084 l_rangeAxisLocation); | |
1085 | |
1086 l_path.moveTo((float) l_y, (float) l_x); | |
1087 for (int i = 1; i < l_xValues.length; i++) { | |
1088 l_x = x_domainAxis.valueToJava2D(( | |
1089 (Double) l_xValues[i]).doubleValue(), x_dataArea, | |
1090 l_domainAxisLocation); | |
1091 if (this.roundXCoordinates) { | |
1092 l_x = Math.rint(l_x); | |
1093 } | |
1094 | |
1095 l_y = x_rangeAxis.valueToJava2D(( | |
1096 (Double) l_yValues[i]).doubleValue(), x_dataArea, | |
1097 l_rangeAxisLocation); | |
1098 l_path.lineTo((float) l_y, (float) l_x); | |
1099 } | |
1100 l_path.closePath(); | |
1101 } | |
1102 | |
1103 if (l_path.intersects(x_dataArea)) { | |
1104 x_graphics.setPaint(x_positive ? getPositivePaint() | |
1105 : getNegativePaint()); | |
1106 x_graphics.fill(l_path); | |
1107 } | |
1108 } | |
1109 | |
1110 /** | |
1111 * Returns a default legend item for the specified series. Subclasses | |
1112 * should override this method to generate customised items. | |
1113 * | |
1114 * @param datasetIndex the dataset index (zero-based). | |
1115 * @param series the series index (zero-based). | |
1116 * | |
1117 * @return A legend item for the series. | |
1118 */ | |
1119 public LegendItem getLegendItem(int datasetIndex, int series) { | |
1120 LegendItem result = null; | |
1121 XYPlot p = getPlot(); | |
1122 if (p != null) { | |
1123 XYDataset dataset = p.getDataset(datasetIndex); | |
1124 if (dataset != null) { | |
1125 if (getItemVisible(series, 0)) { | |
1126 String label = getLegendItemLabelGenerator().generateLabel( | |
1127 dataset, series); | |
1128 String description = label; | |
1129 String toolTipText = null; | |
1130 if (getLegendItemToolTipGenerator() != null) { | |
1131 toolTipText | |
1132 = getLegendItemToolTipGenerator().generateLabel( | |
1133 dataset, series); | |
1134 } | |
1135 String urlText = null; | |
1136 if (getLegendItemURLGenerator() != null) { | |
1137 urlText = getLegendItemURLGenerator().generateLabel( | |
1138 dataset, series); | |
1139 } | |
1140 Paint paint = lookupSeriesPaint(series); | |
1141 Stroke stroke = lookupSeriesStroke(series); | |
1142 Shape line = getLegendLine(); | |
1143 result = new LegendItem(label, description, | |
1144 toolTipText, urlText, line, stroke, paint); | |
1145 result.setLabelFont(lookupLegendTextFont(series)); | |
1146 Paint labelPaint = lookupLegendTextPaint(series); | |
1147 if (labelPaint != null) { | |
1148 result.setLabelPaint(labelPaint); | |
1149 } | |
1150 result.setDataset(dataset); | |
1151 result.setDatasetIndex(datasetIndex); | |
1152 result.setSeriesKey(dataset.getSeriesKey(series)); | |
1153 result.setSeriesIndex(series); | |
1154 } | |
1155 } | |
1156 | |
1157 } | |
1158 | |
1159 return result; | |
1160 | |
1161 } | |
1162 | |
1163 /** | |
1164 * Tests this renderer for equality with an arbitrary object. | |
1165 * | |
1166 * @param obj the object (<code>null</code> permitted). | |
1167 * | |
1168 * @return A boolean. | |
1169 */ | |
1170 public boolean equals(Object obj) { | |
1171 if (obj == this) { | |
1172 return true; | |
1173 } | |
1174 if (!(obj instanceof StableXYDifferenceRenderer)) { | |
1175 return false; | |
1176 } | |
1177 if (!super.equals(obj)) { | |
1178 return false; | |
1179 } | |
1180 StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj; | |
1181 if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) { | |
1182 return false; | |
1183 } | |
1184 if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) { | |
1185 return false; | |
1186 } | |
1187 if (this.shapesVisible != that.shapesVisible) { | |
1188 return false; | |
1189 } | |
1190 if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) { | |
1191 return false; | |
1192 } | |
1193 if (this.roundXCoordinates != that.roundXCoordinates) { | |
1194 return false; | |
1195 } | |
1196 return true; | |
1197 } | |
1198 | |
1199 /** | |
1200 * Returns a clone of the renderer. | |
1201 * | |
1202 * @return A clone. | |
1203 * | |
1204 * @throws CloneNotSupportedException if the renderer cannot be cloned. | |
1205 */ | |
1206 public Object clone() throws CloneNotSupportedException { | |
1207 StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone(); | |
1208 clone.legendLine = ShapeUtilities.clone(this.legendLine); | |
1209 return clone; | |
1210 } | |
1211 | |
1212 /** | |
1213 * Provides serialization support. | |
1214 * | |
1215 * @param stream the output stream. | |
1216 * | |
1217 * @throws IOException if there is an I/O error. | |
1218 */ | |
1219 private void writeObject(ObjectOutputStream stream) throws IOException { | |
1220 stream.defaultWriteObject(); | |
1221 SerialUtilities.writePaint(this.positivePaint, stream); | |
1222 SerialUtilities.writePaint(this.negativePaint, stream); | |
1223 SerialUtilities.writeShape(this.legendLine, stream); | |
1224 } | |
1225 | |
1226 /** | |
1227 * Provides serialization support. | |
1228 * | |
1229 * @param stream the input stream. | |
1230 * | |
1231 * @throws IOException if there is an I/O error. | |
1232 * @throws ClassNotFoundException if there is a classpath problem. | |
1233 */ | |
1234 private void readObject(ObjectInputStream stream) | |
1235 throws IOException, ClassNotFoundException { | |
1236 stream.defaultReadObject(); | |
1237 this.positivePaint = SerialUtilities.readPaint(stream); | |
1238 this.negativePaint = SerialUtilities.readPaint(stream); | |
1239 this.legendLine = SerialUtilities.readShape(stream); | |
1240 } | |
1241 } | |
1242 |