comparison artifacts/src/main/java/org/dive4elements/river/jfree/StableXYDifferenceRenderer.java @ 9182:77eb4553245b

Fixed: waterlevels could not show a band any more Correctly implemented 'showarea' on area themes now
author gernotbelger
date Tue, 26 Jun 2018 20:19:16 +0200
parents d9c89651bd67
children eec4df8165a1
comparison
equal deleted inserted replaced
9181:5dacb6ea75a1 9182:77eb4553245b
85 */ 85 */
86 package org.dive4elements.river.jfree; 86 package org.dive4elements.river.jfree;
87 87
88 import java.awt.BasicStroke; 88 import java.awt.BasicStroke;
89 import java.awt.Color; 89 import java.awt.Color;
90 import java.awt.Font;
90 import java.awt.Graphics2D; 91 import java.awt.Graphics2D;
91 import java.awt.Font;
92 import java.awt.Paint; 92 import java.awt.Paint;
93 import java.awt.geom.Point2D;
94 import java.awt.Shape; 93 import java.awt.Shape;
95 import java.awt.Stroke; 94 import java.awt.Stroke;
96 import java.awt.geom.GeneralPath; 95 import java.awt.geom.GeneralPath;
97 import java.awt.geom.Line2D; 96 import java.awt.geom.Line2D;
97 import java.awt.geom.Point2D;
98 import java.awt.geom.Rectangle2D; 98 import java.awt.geom.Rectangle2D;
99 import java.io.IOException; 99 import java.io.IOException;
100 import java.io.ObjectInputStream; 100 import java.io.ObjectInputStream;
101 import java.io.ObjectOutputStream; 101 import java.io.ObjectOutputStream;
102 import java.text.NumberFormat;
102 import java.util.ArrayList; 103 import java.util.ArrayList;
103 import java.util.Collections; 104 import java.util.Collections;
104 import java.util.LinkedList; 105 import java.util.LinkedList;
105 import java.util.List; 106 import java.util.List;
106 107
108 import org.apache.log4j.Logger;
109 import org.dive4elements.river.artifacts.math.Linear;
107 import org.jfree.chart.LegendItem; 110 import org.jfree.chart.LegendItem;
108 import org.jfree.chart.axis.ValueAxis; 111 import org.jfree.chart.axis.ValueAxis;
109 import org.jfree.chart.entity.EntityCollection; 112 import org.jfree.chart.entity.EntityCollection;
110 import org.jfree.chart.entity.XYItemEntity; 113 import org.jfree.chart.entity.XYItemEntity;
111 import org.jfree.chart.event.RendererChangeEvent; 114 import org.jfree.chart.event.RendererChangeEvent;
112 import org.jfree.chart.labels.XYToolTipGenerator; 115 import org.jfree.chart.labels.XYToolTipGenerator;
113 import org.jfree.chart.plot.CrosshairState; 116 import org.jfree.chart.plot.CrosshairState;
114 import org.jfree.chart.plot.PlotOrientation; 117 import org.jfree.chart.plot.PlotOrientation;
115 import org.jfree.chart.plot.PlotRenderingInfo; 118 import org.jfree.chart.plot.PlotRenderingInfo;
116 import org.jfree.chart.plot.XYPlot; 119 import org.jfree.chart.plot.XYPlot;
120 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
121 import org.jfree.chart.renderer.xy.XYItemRendererState;
117 import org.jfree.chart.urls.XYURLGenerator; 122 import org.jfree.chart.urls.XYURLGenerator;
123 import org.jfree.data.xy.DefaultXYDataset;
118 import org.jfree.data.xy.XYDataset; 124 import org.jfree.data.xy.XYDataset;
119 import org.jfree.data.xy.DefaultXYDataset;
120 import org.jfree.io.SerialUtilities; 125 import org.jfree.io.SerialUtilities;
121 import org.jfree.ui.RectangleEdge; 126 import org.jfree.ui.RectangleEdge;
122 import org.jfree.util.PaintUtilities; 127 import org.jfree.util.PaintUtilities;
123 import org.jfree.util.PublicCloneable; 128 import org.jfree.util.PublicCloneable;
124 import org.jfree.util.ShapeUtilities; 129 import org.jfree.util.ShapeUtilities;
125 130
126 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
127 import org.jfree.chart.renderer.xy.XYItemRenderer;
128 import org.jfree.chart.renderer.xy.XYItemRendererState;
129
130 import gnu.trove.TDoubleArrayList; 131 import gnu.trove.TDoubleArrayList;
131
132 import org.dive4elements.river.artifacts.math.Linear;
133
134 import java.text.NumberFormat;
135
136 import org.apache.log4j.Logger;
137 132
138 /** 133 /**
139 * A renderer for an {@link XYPlot} that highlights the differences between two 134 * A renderer for an {@link XYPlot} that highlights the differences between two
140 * series. The example shown here is generated by the 135 * series. The example shown here is generated by the
141 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart 136 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart
142 * demo collection: 137 * demo collection:
143 * <br><br> 138 * <br>
139 * <br>
144 * <img src="../../../../../images/StableXYDifferenceRendererSample.png" 140 * <img src="../../../../../images/StableXYDifferenceRendererSample.png"
145 * alt="StableXYDifferenceRendererSample.png" /> 141 * alt="StableXYDifferenceRendererSample.png" />
146 */ 142 */
147 public class StableXYDifferenceRenderer extends AbstractXYItemRenderer 143 public class StableXYDifferenceRenderer extends AbstractXYItemRenderer implements PublicCloneable {
148 implements XYItemRenderer, PublicCloneable { 144
149 145 private static Logger log = Logger.getLogger(StableXYDifferenceRenderer.class);
150 private static Logger log = Logger.getLogger(
151 StableXYDifferenceRenderer.class);
152 146
153 public static final int CALCULATE_POSITIVE_AREA = 1; 147 public static final int CALCULATE_POSITIVE_AREA = 1;
154 public static final int CALCULATE_NEGATIVE_AREA = 2; 148 public static final int CALCULATE_NEGATIVE_AREA = 2;
155 public static final int CALCULATE_ALL_AREA = 149 public static final int CALCULATE_ALL_AREA = CALCULATE_POSITIVE_AREA | CALCULATE_NEGATIVE_AREA;
156 CALCULATE_POSITIVE_AREA | CALCULATE_NEGATIVE_AREA;
157 150
158 /** For serialization. */ 151 /** For serialization. */
159 private static final long serialVersionUID = -8447915602375584857L; 152 private static final long serialVersionUID = -8447915602375584857L;
160 153
154 private boolean drawArea = true;
155
161 /** The paint used to highlight positive differences (y(0) &gt; y(1)). */ 156 /** The paint used to highlight positive differences (y(0) &gt; y(1)). */
162 private transient Paint positivePaint; 157 private transient Paint positivePaint;
163 158
164 /** The paint used to highlight negative differences (y(0) < y(1)). */ 159 /** The paint used to highlight negative differences (y(0) < y(1)). */
165 private transient Paint negativePaint; 160 private transient Paint negativePaint;
166 161
167 /** Display shapes at each point? */ 162 /** Display shapes at each point? */
168 private boolean shapesVisible; 163 private boolean shapesVisible;
169 164
170 /** Display shapes at each point? */ 165 /** Display shapes at each point? */
171 protected boolean drawOutline; 166 private boolean drawOutline;
172 167
173 /** Which stroke to draw outline with? */ 168 /** Which stroke to draw outline with? */
174 protected Stroke outlineStroke; 169 private Stroke outlineStroke;
175 170
176 /** Which paint to draw outline with? */ 171 /** Which paint to draw outline with? */
177 protected Paint outlinePaint; 172 private Paint outlinePaint;
178 173
179 /** The shape to display in the legend item. */ 174 /** The shape to display in the legend item. */
180 private transient Shape legendShape; 175 private transient Shape legendShape;
181 176
182 protected boolean drawOriginalSeries; 177 private final boolean drawOriginalSeries;
183 178
184 /** The color of the label showing the calculated area. */ 179 /** The color of the label showing the calculated area. */
185 protected Color labelColor; 180 private Color labelColor;
186 181
187 /** The background color of the label showing the calculated area. */ 182 /** The background color of the label showing the calculated area. */
188 protected Color labelBGColor; 183 private Color labelBGColor;
189 184
190 /** Font to draw label of calculated area with. */ 185 /** Font to draw label of calculated area with. */
191 protected Font labelFont; 186 private Font labelFont;
192 187
193 /** Template to create i18ned label for area. */ 188 /** Template to create i18ned label for area. */
194 protected String areaLabelTamplate; 189 private String areaLabelTamplate;
195 190
196 /** NumberFormat to use for area. */ 191 /** NumberFormat to use for area. */
197 protected NumberFormat areaLabelNumberFormat; 192 private NumberFormat areaLabelNumberFormat;
198 193
199 protected int areaCalculationMode; 194 private int areaCalculationMode;
200 195
201 protected double positiveArea; 196 private double positiveArea;
202 protected double negativeArea; 197
198 private double negativeArea;
203 199
204 /** Whether or not to draw a label in the area. */ 200 /** Whether or not to draw a label in the area. */
205 protected boolean labelArea = true; 201 private boolean labelArea = true;
206
207 202
208 /** Arithmetic centroid of drawn polygons. */ 203 /** Arithmetic centroid of drawn polygons. */
209 protected Point2D.Double centroid; 204 private Point2D.Double centroid;
210
211 205
212 /** Number of points that contributed to the centroid. */ 206 /** Number of points that contributed to the centroid. */
213 protected int centroidNPoints = 0; 207 private int centroidNPoints = 0;
214
215 208
216 /** 209 /**
217 * This flag controls whether or not the x-coordinates (in Java2D space) 210 * This flag controls whether or not the x-coordinates (in Java2D space)
218 * are rounded to integers. When set to true, this can avoid the vertical 211 * are rounded to integers. When set to true, this can avoid the vertical
219 * striping that anti-aliasing can generate. However, the rounding may not 212 * striping that anti-aliasing can generate. However, the rounding may not
220 * be appropriate for output in high resolution formats (for example, 213 * be appropriate for output in high resolution formats (for example,
221 * vector graphics formats such as SVG and PDF). 214 * vector graphics formats such as SVG and PDF).
222 * 215 *
223 * @since 1.0.4 216 * @since 1.0.4
224 */ 217 */
225 private boolean roundXCoordinates; 218 private final boolean roundXCoordinates;
226 219
227 /** 220 /**
228 * Creates a new renderer with default attributes. 221 * Creates a new renderer with default attributes.
229 */ 222 */
230 public StableXYDifferenceRenderer() { 223 public StableXYDifferenceRenderer() {
231 this(Color.green, Color.red, false /*, null */); 224 this(Color.green, Color.red, false, CALCULATE_ALL_AREA);
232 }
233
234 public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
235 boolean shapes) {
236 this(positivePaint, negativePaint, shapes, CALCULATE_ALL_AREA);
237 } 225 }
238 226
239 /** 227 /**
240 * Creates a new renderer. 228 * Creates a new renderer.
241 * 229 *
242 * @param positivePaint the highlight color for positive differences 230 * @param positivePaint
243 * (<code>null</code> not permitted). 231 * the highlight color for positive differences
244 * @param negativePaint the highlight color for negative differences 232 * (<code>null</code> not permitted).
245 * (<code>null</code> not permitted). 233 * @param negativePaint
246 * @param shapes draw shapes? 234 * the highlight color for negative differences
247 */ 235 * (<code>null</code> not permitted).
248 public StableXYDifferenceRenderer(Paint positivePaint, Paint negativePaint, 236 * @param shapes
249 boolean shapes, int areaCalculationMode) { 237 * draw shapes?
238 */
239 private StableXYDifferenceRenderer(final Paint positivePaint, final Paint negativePaint, final boolean shapes, final int areaCalculationMode) {
250 if (positivePaint == null) { 240 if (positivePaint == null) {
251 throw new IllegalArgumentException( 241 throw new IllegalArgumentException("Null 'positivePaint' argument.");
252 "Null 'positivePaint' argument.");
253 } 242 }
254 if (negativePaint == null) { 243 if (negativePaint == null) {
255 throw new IllegalArgumentException( 244 throw new IllegalArgumentException("Null 'negativePaint' argument.");
256 "Null 'negativePaint' argument.");
257 } 245 }
258 this.positivePaint = positivePaint; 246 this.positivePaint = positivePaint;
259 this.negativePaint = negativePaint; 247 this.negativePaint = negativePaint;
260 this.shapesVisible = shapes; 248 this.shapesVisible = shapes;
261 this.legendShape = new Rectangle2D.Double(-3.0, -3.0, 10.0, 10.0); 249 this.legendShape = new Rectangle2D.Double(-3.0, -3.0, 10.0, 10.0);
262 this.roundXCoordinates = false; 250 this.roundXCoordinates = false;
263 this.drawOutline = true; 251 this.drawOutline = true;
264 this.outlineStroke = new BasicStroke(1); 252 this.outlineStroke = new BasicStroke(1);
265 this.outlinePaint = Color.black; 253 this.outlinePaint = Color.black;
266 this.drawOriginalSeries = false; 254 this.drawOriginalSeries = false;
267 this.areaCalculationMode = areaCalculationMode; 255 this.areaCalculationMode = areaCalculationMode;
268 this.labelBGColor = null; 256 this.labelBGColor = null;
269 this.centroid = new Point2D.Double(0,0); 257 this.centroid = new Point2D.Double(0, 0);
270 } 258 }
271 259
272 public int getAreaCalculationMode() { 260 public void setAreaCalculationMode(final int areaCalculationMode) {
273 return areaCalculationMode;
274 }
275
276 public void setAreaCalculationMode(int areaCalculationMode) {
277 this.areaCalculationMode = areaCalculationMode; 261 this.areaCalculationMode = areaCalculationMode;
278 } 262 }
279 263
280
281 /** Set template to use to create area label (e.g. 'Area=%dm2'). */ 264 /** Set template to use to create area label (e.g. 'Area=%dm2'). */
282 public void setAreaLabelTemplate(String areaTemplate) { 265 public void setAreaLabelTemplate(final String areaTemplate) {
283 this.areaLabelTamplate = areaTemplate; 266 this.areaLabelTamplate = areaTemplate;
284 } 267 }
285 268
286 269 public void setAreaLabelNumberFormat(final NumberFormat nf) {
287 public void setAreaLabelNumberFormat(NumberFormat nf) {
288 this.areaLabelNumberFormat = nf; 270 this.areaLabelNumberFormat = nf;
289 } 271 }
290 272
291 273 public void setLabelArea(final boolean label) {
292 public boolean isLabelArea() {
293 return this.labelArea;
294 }
295
296 public void setLabelArea(boolean label) {
297 this.labelArea = label; 274 this.labelArea = label;
298 } 275 }
299 276
300
301 /** Set font to paint label with. */ 277 /** Set font to paint label with. */
302 public void setLabelFont(Font font) { 278 public void setLabelFont(final Font font) {
303 this.labelFont = font; 279 this.labelFont = font;
304 } 280 }
305 281
306
307 /** Get font with which label is painted. */
308 public Font getLabelFont() {
309 return this.labelFont;
310 }
311
312
313 /** Set color with which to paint label. */ 282 /** Set color with which to paint label. */
314 public void setLabelColor(Color color) { 283 public void setLabelColor(final Color color) {
315 this.labelColor = color; 284 this.labelColor = color;
316 } 285 }
317 286
318
319 /** Get color with which label is painted. */
320 public Color getLabelColor() {
321 return this.labelColor;
322 }
323
324
325 /** Set color with which to paint label bg. */ 287 /** Set color with which to paint label bg. */
326 public void setLabelBGColor(Color color) { 288 public void setLabelBGColor(final Color color) {
327 this.labelBGColor = color; 289 this.labelBGColor = color;
328 } 290 }
329 291
330
331 /** Get color with which label is painted. */
332 public Color getLabelBGColor() {
333 return this.labelBGColor;
334 }
335
336
337 public double getCalculatedArea() {
338 return positiveArea + negativeArea;
339 }
340
341 /** 292 /**
342 * Sets color that is used if drawOutline is true. 293 * Sets color that is used if drawOutline is true.
343 */ 294 */
344 public void setOutlinePaint(Paint outlinePaint) { 295 @Override
296 public void setOutlinePaint(final Paint outlinePaint) {
345 this.outlinePaint = outlinePaint; 297 this.outlinePaint = outlinePaint;
346 } 298 }
347 299
348
349 /**
350 * Gets color which is used if drawOutline is true.
351 */
352 public Paint getOutlinePaint() {
353 return this.outlinePaint;
354 }
355
356
357 /** 300 /**
358 * Sets Stroke that is used if drawOutline is true. 301 * Sets Stroke that is used if drawOutline is true.
359 */ 302 */
360 public void setOutlineStroke(Stroke stroke) { 303 @Override
304 public void setOutlineStroke(final Stroke stroke) {
361 this.outlineStroke = stroke; 305 this.outlineStroke = stroke;
362 } 306 }
363
364
365 /**
366 * Returns Stroke that is used if drawOutline is true.
367 */
368 public Stroke getOutlineStroke() {
369 return this.outlineStroke;
370 }
371
372 307
373 /** 308 /**
374 * Whether or not to draw the 'Shape' of the area (in contrast to 309 * Whether or not to draw the 'Shape' of the area (in contrast to
375 * shapes at data items). 310 * shapes at data items).
376 */ 311 */
377 public void setDrawOutline(boolean doDrawOutline) { 312 public void setDrawOutline(final boolean doDrawOutline) {
378 this.drawOutline = doDrawOutline; 313 this.drawOutline = doDrawOutline;
379 } 314 }
380 315
381 316 public void setDrawArea(final boolean doDrawArea) {
382 /** 317 this.drawArea = doDrawArea;
383 * Returns whether or not to draw the shape of the outline. 318 }
384 */
385 public boolean getDrawOutline() {
386 return this.drawOutline;
387 }
388
389 319
390 /** 320 /**
391 * Returns the paint used to highlight positive differences. 321 * Returns the paint used to highlight positive differences.
392 * 322 *
393 * @return The paint (never <code>null</code>). 323 * @return The paint (never <code>null</code>).
394 * 324 *
395 * @see #setPositivePaint(Paint) 325 * @see #setPositivePaint(Paint)
396 */ 326 */
397 public Paint getPositivePaint() { 327 private Paint getPositivePaint() {
398 return this.positivePaint; 328 return this.positivePaint;
399 } 329 }
400 330
401 /** 331 /**
402 * Sets the paint used to highlight positive differences and sends a 332 * Sets the paint used to highlight positive differences and sends a
403 * {@link RendererChangeEvent} to all registered listeners. 333 * {@link RendererChangeEvent} to all registered listeners.
404 * 334 *
405 * @param paint the paint (<code>null</code> not permitted). 335 * @param paint
336 * the paint (<code>null</code> not permitted).
406 * 337 *
407 * @see #getPositivePaint() 338 * @see #getPositivePaint()
408 */ 339 */
409 public void setPositivePaint(Paint paint) { 340 public void setPositivePaint(final Paint paint) {
410 this.positivePaint = paint; 341 this.positivePaint = paint;
411 fireChangeEvent(); 342 fireChangeEvent();
412 } 343 }
413 344
414 /** 345 /**
416 * 347 *
417 * @return The paint (never <code>null</code>). 348 * @return The paint (never <code>null</code>).
418 * 349 *
419 * @see #setNegativePaint(Paint) 350 * @see #setNegativePaint(Paint)
420 */ 351 */
421 public Paint getNegativePaint() { 352 private Paint getNegativePaint() {
422 return this.negativePaint; 353 return this.negativePaint;
423 } 354 }
424 355
425 /** 356 /**
426 * Sets the paint used to highlight negative differences. 357 * Sets the paint used to highlight negative differences.
427 * 358 *
428 * @param paint the paint (<code>null</code> not permitted). 359 * @param paint
360 * the paint (<code>null</code> not permitted).
429 * 361 *
430 * @see #getNegativePaint() 362 * @see #getNegativePaint()
431 */ 363 */
432 public void setNegativePaint(Paint paint) { 364 public void setNegativePaint(final Paint paint) {
433 this.negativePaint = paint; 365 this.negativePaint = paint;
434 notifyListeners(new RendererChangeEvent(this)); 366 notifyListeners(new RendererChangeEvent(this));
435 } 367 }
436 368
437 /** 369 /**
440 * 372 *
441 * @return A boolean. 373 * @return A boolean.
442 * 374 *
443 * @see #setShapesVisible(boolean) 375 * @see #setShapesVisible(boolean)
444 */ 376 */
445 public boolean getShapesVisible() { 377 private boolean getShapesVisible() {
446 return this.shapesVisible; 378 return this.shapesVisible;
447 } 379 }
448 380
449 /** 381 /**
450 * Sets a flag that controls whether or not shapes are drawn for each 382 * Sets a flag that controls whether or not shapes are drawn for each
451 * data value, and sends a {@link RendererChangeEvent} to all registered 383 * data value, and sends a {@link RendererChangeEvent} to all registered
452 * listeners. 384 * listeners.
453 * 385 *
454 * @param flag the flag. 386 * @param flag
387 * the flag.
455 * 388 *
456 * @see #getShapesVisible() 389 * @see #getShapesVisible()
457 */ 390 */
458 public void setShapesVisible(boolean flag) { 391 public void setShapesVisible(final boolean flag) {
459 this.shapesVisible = flag; 392 this.shapesVisible = flag;
460 fireChangeEvent(); 393 fireChangeEvent();
461 } 394 }
462 395
463 /** 396 /**
465 * 398 *
466 * @return The legend line (never <code>null</code>). 399 * @return The legend line (never <code>null</code>).
467 * 400 *
468 * @see #setLegendLine(Shape) 401 * @see #setLegendLine(Shape)
469 */ 402 */
470 public Shape getLegendLine() { 403 private Shape getLegendLine() {
471 return this.legendShape; 404 return this.legendShape;
472 } 405 }
473 406
474 /** 407 /**
475 * Sets the shape used as a line in each legend item and sends a 408 * Initializes the renderer and returns a state object that should be
476 * {@link RendererChangeEvent} to all registered listeners. 409 * passed to subsequent calls to the drawItem() method. This method will
477 *
478 * @param line the line (<code>null</code> not permitted).
479 *
480 * @see #getLegendLine()
481 */
482 public void setLegendLine(Shape line) {
483 if (line == null) {
484 throw new IllegalArgumentException("Null 'line' argument.");
485 }
486 this.legendShape = line;
487 fireChangeEvent();
488 }
489
490 /**
491 * Returns the flag that controls whether or not the x-coordinates (in
492 * Java2D space) are rounded to integer values.
493 *
494 * @return The flag.
495 *
496 * @since 1.0.4
497 *
498 * @see #setRoundXCoordinates(boolean)
499 */
500 public boolean getRoundXCoordinates() {
501 return this.roundXCoordinates;
502 }
503
504 /**
505 * Sets the flag that controls whether or not the x-coordinates (in
506 * Java2D space) are rounded to integer values, and sends a
507 * {@link RendererChangeEvent} to all registered listeners.
508 *
509 * @param round the new flag value.
510 *
511 * @since 1.0.4
512 *
513 * @see #getRoundXCoordinates()
514 */
515 public void setRoundXCoordinates(boolean round) {
516 this.roundXCoordinates = round;
517 fireChangeEvent();
518 }
519
520 /**
521 * Initialises the renderer and returns a state object that should be
522 * passed to subsequent calls to the drawItem() method. This method will
523 * be called before the first item is rendered, giving the renderer an 410 * be called before the first item is rendered, giving the renderer an
524 * opportunity to initialise any state information it wants to maintain. 411 * opportunity to initialize any state information it wants to maintain.
525 * The renderer can do nothing if it chooses. 412 * The renderer can do nothing if it chooses.
526 * 413 *
527 * @param g2 the graphics device. 414 * @param g2
528 * @param dataArea the (visible) area inside the axes. 415 * the graphics device.
529 * @param plot the plot. 416 * @param dataArea
530 * @param data the data. 417 * the (visible) area inside the axes.
531 * @param info an optional info collection object to return data back to 418 * @param plot
532 * the caller. 419 * the plot.
420 * @param data
421 * the data.
422 * @param info
423 * an optional info collection object to return data back to
424 * the caller.
533 * 425 *
534 * @return A state object. 426 * @return A state object.
535 */ 427 */
536 public XYItemRendererState initialise(Graphics2D g2, 428 @Override
537 Rectangle2D dataArea, 429 public XYItemRendererState initialise(final Graphics2D g2, final Rectangle2D dataArea, final XYPlot plot, final XYDataset data,
538 XYPlot plot, 430 final PlotRenderingInfo info) {
539 XYDataset data, 431
540 PlotRenderingInfo info) { 432 final XYItemRendererState state = super.initialise(g2, dataArea, plot, data, info);
541
542 XYItemRendererState state = super.initialise(g2, dataArea, plot, data,
543 info);
544 state.setProcessVisibleItemsOnly(false); 433 state.setProcessVisibleItemsOnly(false);
545 return state; 434 return state;
546 } 435 }
547 436
548 /** 437 /**
549 * Returns <code>2</code>, the number of passes required by the renderer. 438 * Returns <code>2</code>, the number of passes required by the renderer.
550 * The {@link XYPlot} will run through the dataset this number of times. 439 * The {@link XYPlot} will run through the dataset this number of times.
551 * 440 *
552 * @return The number of passes required by the renderer. 441 * @return The number of passes required by the renderer.
553 */ 442 */
443 @Override
554 public int getPassCount() { 444 public int getPassCount() {
555 return 2; 445 return 2;
556 } 446 }
557 447
558
559 /** 448 /**
560 * Adds x/y data to series. 449 * Adds x/y data to series.
561 */ 450 */
562 private static final void addSeries( 451 private static final void addSeries(final DefaultXYDataset ds, final Comparable key, final TDoubleArrayList xs, final TDoubleArrayList ys) {
563 DefaultXYDataset ds, 452 ds.addSeries(key, new double[][] { xs.toNativeArray(), ys.toNativeArray() });
564 Comparable key, 453 }
565 TDoubleArrayList xs, 454
566 TDoubleArrayList ys 455 private static List<XYDataset> splitByNaNsOneSeries(final XYDataset dataset) {
567 ) { 456 final List<XYDataset> datasets = new ArrayList<>();
568 ds.addSeries( 457
569 key, 458 final int N = dataset.getItemCount(0);
570 new double [][] { 459 final TDoubleArrayList xs = new TDoubleArrayList(N);
571 xs.toNativeArray(), 460 final TDoubleArrayList ys = new TDoubleArrayList(N);
572 ys.toNativeArray()
573 });
574 }
575
576 protected static List<XYDataset> splitByNaNsOneSeries(
577 XYDataset dataset
578 ) {
579 List<XYDataset> datasets = new ArrayList<XYDataset>();
580
581 int N = dataset.getItemCount(0);
582 TDoubleArrayList xs = new TDoubleArrayList(N);
583 TDoubleArrayList ys = new TDoubleArrayList(N);
584 for (int i = 0; i < N; ++i) { 461 for (int i = 0; i < N; ++i) {
585 double x = dataset.getXValue(0, i); 462 final double x = dataset.getXValue(0, i);
586 double y = dataset.getYValue(0, i); 463 final double y = dataset.getYValue(0, i);
587 if (Double.isNaN(x) || Double.isNaN(y)) { 464 if (Double.isNaN(x) || Double.isNaN(y)) {
588 if (!xs.isEmpty()) { 465 if (!xs.isEmpty()) {
589 DefaultXYDataset ds = new DefaultXYDataset(); 466 final DefaultXYDataset ds = new DefaultXYDataset();
590 addSeries(ds, dataset.getSeriesKey(0), xs, ys); 467 addSeries(ds, dataset.getSeriesKey(0), xs, ys);
591 datasets.add(ds); 468 datasets.add(ds);
592 xs.resetQuick(); 469 xs.resetQuick();
593 ys.resetQuick(); 470 ys.resetQuick();
594 } 471 }
595 } 472 } else {
596 else {
597 xs.add(x); 473 xs.add(x);
598 ys.add(y); 474 ys.add(y);
599 } 475 }
600 } 476 }
601 if (!xs.isEmpty()) { 477 if (!xs.isEmpty()) {
602 DefaultXYDataset ds = new DefaultXYDataset(); 478 final DefaultXYDataset ds = new DefaultXYDataset();
603 addSeries(ds, dataset.getSeriesKey(0), xs, ys); 479 addSeries(ds, dataset.getSeriesKey(0), xs, ys);
604 datasets.add(ds); 480 datasets.add(ds);
605 } 481 }
606 482
607 return datasets; 483 return datasets;
608 } 484 }
609 485
610 private static final boolean add(TDoubleArrayList xs, double x) { 486 private static final boolean add(final TDoubleArrayList xs, final double x) {
611 int N = xs.size(); 487 final int N = xs.size();
612 if (N == 0 || xs.getQuick(N-1) < x) { 488 if (N == 0 || xs.getQuick(N - 1) < x) {
613 xs.add(x); 489 xs.add(x);
614 return true; 490 return true;
615 } 491 }
616 return false; 492 return false;
617 } 493 }
618 494
619 protected static boolean hasNaN(XYDataset dataset) { 495 private static boolean hasNaN(final XYDataset dataset) {
620 int N = dataset.getItemCount(0); 496 final int N = dataset.getItemCount(0);
621 int M = dataset.getItemCount(1); 497 final int M = dataset.getItemCount(1);
622 498
623 for (int i = 0; i < N; ++i) { 499 for (int i = 0; i < N; ++i) {
624 double x = dataset.getXValue(0, i); 500 final double x = dataset.getXValue(0, i);
625 double y = dataset.getYValue(0, i); 501 final double y = dataset.getYValue(0, i);
626 if (Double.isNaN(x) || Double.isNaN(y)) { 502 if (Double.isNaN(x) || Double.isNaN(y)) {
627 return true; 503 return true;
628 } 504 }
629 } 505 }
630 506
631 for (int j = 0; j < M; ++j) { 507 for (int j = 0; j < M; ++j) {
632 double x = dataset.getXValue(1, j); 508 final double x = dataset.getXValue(1, j);
633 double y = dataset.getYValue(1, j); 509 final double y = dataset.getYValue(1, j);
634 if (Double.isNaN(x) || Double.isNaN(y)) { 510 if (Double.isNaN(x) || Double.isNaN(y)) {
635 return true; 511 return true;
636 } 512 }
637 } 513 }
638 return false; 514 return false;
639 } 515 }
640 516
641 517 private static List<XYDataset> splitByNaNsTwoSeries(final XYDataset dataset) {
642 protected static List<XYDataset> splitByNaNsTwoSeries( 518 final boolean debug = log.isDebugEnabled();
643 XYDataset dataset 519
644 ) { 520 final List<XYDataset> datasets = new ArrayList<>();
645 boolean debug = log.isDebugEnabled();
646
647 List<XYDataset> datasets = new ArrayList<XYDataset>();
648 521
649 if (!hasNaN(dataset)) { 522 if (!hasNaN(dataset)) {
650 datasets.add(dataset); 523 datasets.add(dataset);
651 return datasets; 524 return datasets;
652 } 525 }
653 526
654 int N = dataset.getItemCount(0); 527 final int N = dataset.getItemCount(0);
655 int M = dataset.getItemCount(1); 528 final int M = dataset.getItemCount(1);
656 529
657 int i = 0, j = 0; 530 int i = 0, j = 0;
658 // ignore leading NaNs 531 // ignore leading NaNs
659 for (; i < N; ++i) { 532 for (; i < N; ++i) {
660 double x = dataset.getXValue(0, i); 533 final double x = dataset.getXValue(0, i);
661 double y = dataset.getYValue(0, i); 534 final double y = dataset.getYValue(0, i);
662 if (!Double.isNaN(x) && !Double.isNaN(y)) { 535 if (!Double.isNaN(x) && !Double.isNaN(y)) {
663 break; 536 break;
664 } 537 }
665 } 538 }
666 539
667 for (; j < M; ++j) { 540 for (; j < M; ++j) {
668 double x = dataset.getXValue(1, j); 541 final double x = dataset.getXValue(1, j);
669 double y = dataset.getYValue(1, j); 542 final double y = dataset.getYValue(1, j);
670 if (!Double.isNaN(x) && !Double.isNaN(y)) { 543 if (!Double.isNaN(x) && !Double.isNaN(y)) {
671 break; 544 break;
672 } 545 }
673 } 546 }
674 547
675 TDoubleArrayList six = new TDoubleArrayList(); 548 final TDoubleArrayList six = new TDoubleArrayList();
676 TDoubleArrayList siy = new TDoubleArrayList(); 549 final TDoubleArrayList siy = new TDoubleArrayList();
677 TDoubleArrayList sjx = new TDoubleArrayList(); 550 final TDoubleArrayList sjx = new TDoubleArrayList();
678 TDoubleArrayList sjy = new TDoubleArrayList(); 551 final TDoubleArrayList sjy = new TDoubleArrayList();
679 552
680 while (i < N && j < M) { 553 while (i < N && j < M) {
681 int ni = i+1; 554 int ni = i + 1;
682 for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni); 555 for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni)
683 for (; ni < N && Double.isNaN(dataset.getXValue(0, ni)); ++ni); 556 ;
684 557 for (; ni < N && Double.isNaN(dataset.getXValue(0, ni)); ++ni)
685 int nj = j+1; 558 ;
686 for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj); 559
687 for (; nj < M && Double.isNaN(dataset.getXValue(1, nj)); ++nj); 560 int nj = j + 1;
561 for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj)
562 ;
563 for (; nj < M && Double.isNaN(dataset.getXValue(1, nj)); ++nj)
564 ;
688 565
689 if (ni == N && nj == M) { // no more splits 566 if (ni == N && nj == M) { // no more splits
690 log.debug("no more splits ...."); 567 log.debug("no more splits ....");
691 for (; i < ni; ++i) { 568 for (; i < ni; ++i) {
692 double x = dataset.getXValue(0, i); 569 final double x = dataset.getXValue(0, i);
693 double y = dataset.getYValue(0, i); 570 final double y = dataset.getYValue(0, i);
694 if (!Double.isNaN(x) 571 if (!Double.isNaN(x) && !Double.isNaN(y) && add(six, x)) {
695 && !Double.isNaN(y)
696 && add(six, x)) {
697 siy.add(y); 572 siy.add(y);
698 } 573 }
699 } 574 }
700 for (; j < nj; ++j) { 575 for (; j < nj; ++j) {
701 double x = dataset.getXValue(1, j); 576 final double x = dataset.getXValue(1, j);
702 double y = dataset.getYValue(1, j); 577 final double y = dataset.getYValue(1, j);
703 if (!Double.isNaN(x) 578 if (!Double.isNaN(x) && !Double.isNaN(y) && add(sjx, x)) {
704 && !Double.isNaN(y)
705 && add(sjx, x)) {
706 sjy.add(y); 579 sjy.add(y);
707 } 580 }
708 } 581 }
709 if (!six.isEmpty() && !sjx.isEmpty()) { 582 if (!six.isEmpty() && !sjx.isEmpty()) {
710 DefaultXYDataset ds = new DefaultXYDataset(); 583 final DefaultXYDataset ds = new DefaultXYDataset();
711 addSeries(ds, dataset.getSeriesKey(0), six, siy); 584 addSeries(ds, dataset.getSeriesKey(0), six, siy);
712 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); 585 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
713 datasets.add(ds); 586 datasets.add(ds);
714 } 587 }
715 break; 588 break;
718 if (debug) { 591 if (debug) {
719 log.debug("ni: " + ni + " " + N); 592 log.debug("ni: " + ni + " " + N);
720 log.debug("nj: " + nj + " " + M); 593 log.debug("nj: " + nj + " " + M);
721 } 594 }
722 595
723 double xni = ni < N 596 final double xni = ni < N ? dataset.getXValue(0, ni) : Double.MAX_VALUE;
724 ? dataset.getXValue(0, ni) 597
725 : Double.MAX_VALUE; 598 final double xnj = nj < M ? dataset.getXValue(1, nj) : Double.MAX_VALUE;
726 599
727 double xnj = nj < M 600 final double xns = Math.min(xni, xnj);
728 ? dataset.getXValue(1, nj)
729 : Double.MAX_VALUE;
730
731 double xns = Math.min(xni, xnj);
732 601
733 double pushxi = Double.NaN; 602 double pushxi = Double.NaN;
734 double pushyi = Double.NaN; 603 double pushyi = Double.NaN;
735 double pushxj = Double.NaN; 604 double pushxj = Double.NaN;
736 double pushyj = Double.NaN; 605 double pushyj = Double.NaN;
737 606
738 for (; i < ni; ++i) { 607 for (; i < ni; ++i) {
739 double x = dataset.getXValue(0, i); 608 final double x = dataset.getXValue(0, i);
740 double y = dataset.getYValue(0, i); 609 final double y = dataset.getYValue(0, i);
741 if (Double.isNaN(x) || Double.isNaN(y)) { 610 if (Double.isNaN(x) || Double.isNaN(y)) {
742 continue; 611 continue;
743 } 612 }
744 if (x < xns) { 613 if (x < xns) {
745 if (add(six, x)) { 614 if (add(six, x)) {
749 } 618 }
750 if (x == xns) { // exact match 619 if (x == xns) { // exact match
751 if (add(six, x)) { 620 if (add(six, x)) {
752 siy.add(y); 621 siy.add(y);
753 } 622 }
754 pushxi = x; pushyi = y; 623 pushxi = x;
755 } 624 pushyi = y;
756 else { // x > xns: intersection 625 } else { // x > xns: intersection
757 if (debug) { 626 if (debug) {
758 log.debug("xns: " + xns); 627 log.debug("xns: " + xns);
759 log.debug("x/y: " + x + " / " + y); 628 log.debug("x/y: " + x + " / " + y);
760 } 629 }
761 int SIX = six.size(); 630 final int SIX = six.size();
762 if (SIX > 0) { // should always be true 631 if (SIX > 0) { // should always be true
763 double yns = Linear.linear( 632 final double yns = Linear.linear(xns, six.getQuick(SIX - 1), x, siy.getQuick(SIX - 1), y);
764 xns,
765 six.getQuick(SIX-1), x,
766 siy.getQuick(SIX-1), y);
767 if (debug) { 633 if (debug) {
768 log.debug("intersection at: " + yns); 634 log.debug("intersection at: " + yns);
769 } 635 }
770 if (add(six, xns)) { 636 if (add(six, xns)) {
771 siy.add(yns); 637 siy.add(yns);
776 } 642 }
777 break; // Split point reached. 643 break; // Split point reached.
778 } 644 }
779 645
780 for (; j < nj; ++j) { 646 for (; j < nj; ++j) {
781 double x = dataset.getXValue(1, j); 647 final double x = dataset.getXValue(1, j);
782 double y = dataset.getYValue(1, j); 648 final double y = dataset.getYValue(1, j);
783 if (Double.isNaN(x) || Double.isNaN(y)) { 649 if (Double.isNaN(x) || Double.isNaN(y)) {
784 continue; 650 continue;
785 } 651 }
786 if (x < xns) { 652 if (x < xns) {
787 if (add(sjx, x)) { 653 if (add(sjx, x)) {
791 } 657 }
792 if (x == xns) { // exact match 658 if (x == xns) { // exact match
793 if (add(sjx, x)) { 659 if (add(sjx, x)) {
794 sjy.add(y); 660 sjy.add(y);
795 } 661 }
796 pushxj = x; pushyj = y; 662 pushxj = x;
797 } 663 pushyj = y;
798 else { // x > xns: intersection 664 } else { // x > xns: intersection
799 int SJX = sjx.size(); 665 final int SJX = sjx.size();
800 if (SJX > 0) { // should always be true 666 if (SJX > 0) { // should always be true
801 double yns = Linear.linear( 667 final double yns = Linear.linear(xns, sjx.getQuick(SJX - 1), x, sjy.getQuick(SJX - 1), y);
802 xns,
803 sjx.getQuick(SJX-1), x,
804 sjy.getQuick(SJX-1), y);
805 if (debug) { 668 if (debug) {
806 log.debug("intersection at: " + yns); 669 log.debug("intersection at: " + yns);
807 } 670 }
808 if (add(sjx, xns)) { 671 if (add(sjx, xns)) {
809 sjy.add(yns); 672 sjy.add(yns);
810 } 673 }
811 pushxj = xns; pushyj = yns; 674 pushxj = xns;
675 pushyj = yns;
812 } 676 }
813 } 677 }
814 break; // Split point reached. 678 break; // Split point reached.
815 } 679 }
816 680
817 if (!six.isEmpty() && !sjx.isEmpty()) { 681 if (!six.isEmpty() && !sjx.isEmpty()) {
818 DefaultXYDataset ds = new DefaultXYDataset(); 682 final DefaultXYDataset ds = new DefaultXYDataset();
819 addSeries(ds, dataset.getSeriesKey(0), six, siy); 683 addSeries(ds, dataset.getSeriesKey(0), six, siy);
820 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); 684 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
821 datasets.add(ds); 685 datasets.add(ds);
822 } 686 }
823 687
824 six.resetQuick(); siy.resetQuick(); 688 six.resetQuick();
825 sjx.resetQuick(); sjy.resetQuick(); 689 siy.resetQuick();
690 sjx.resetQuick();
691 sjy.resetQuick();
826 692
827 // Push split points. 693 // Push split points.
828 if (!Double.isNaN(pushxi)) { 694 if (!Double.isNaN(pushxi)) {
829 six.add(pushxi); 695 six.add(pushxi);
830 siy.add(pushyi); 696 siy.add(pushyi);
836 } 702 }
837 } 703 }
838 704
839 // Copy the rest. 705 // Copy the rest.
840 for (; i < N; ++i) { 706 for (; i < N; ++i) {
841 double x = dataset.getXValue(0, i); 707 final double x = dataset.getXValue(0, i);
842 double y = dataset.getXValue(0, i); 708 final double y = dataset.getXValue(0, i);
843 if (!Double.isNaN(x) 709 if (!Double.isNaN(x) && !Double.isNaN(y) && add(six, x)) {
844 && !Double.isNaN(y)
845 && add(six, x)) {
846 siy.add(y); 710 siy.add(y);
847 } 711 }
848 } 712 }
849 713
850 for (; j < M; ++j) { 714 for (; j < M; ++j) {
851 double x = dataset.getXValue(1, j); 715 final double x = dataset.getXValue(1, j);
852 double y = dataset.getXValue(1, j); 716 final double y = dataset.getXValue(1, j);
853 if (!Double.isNaN(x) 717 if (!Double.isNaN(x) && !Double.isNaN(y) && add(sjx, x)) {
854 && !Double.isNaN(y)
855 && add(sjx, x)) {
856 sjy.add(y); 718 sjy.add(y);
857 } 719 }
858 } 720 }
859 721
860 // Build final dataset. 722 // Build final dataset.
861 if (!six.isEmpty() && !sjx.isEmpty()) { 723 if (!six.isEmpty() && !sjx.isEmpty()) {
862 DefaultXYDataset ds = new DefaultXYDataset(); 724 final DefaultXYDataset ds = new DefaultXYDataset();
863 addSeries(ds, dataset.getSeriesKey(0), six, siy); 725 addSeries(ds, dataset.getSeriesKey(0), six, siy);
864 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy); 726 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
865 datasets.add(ds); 727 datasets.add(ds);
866 } 728 }
867 729
868 if (debug) { 730 if (debug) {
869 log.debug("datasets after split: " + datasets.size()); 731 log.debug("datasets after split: " + datasets.size());
870 } 732 }
871 733
872 /* 734 /*
873 log.debug("Begin dump datasets: "); 735 * log.debug("Begin dump datasets: ");
874 log.debug("Original"); 736 * log.debug("Original");
875 i=0; 737 * i=0;
876 dumpDataset(dataset, "Original" ); 738 * dumpDataset(dataset, "Original" );
877 for (XYDataset set: datasets) { 739 * for (XYDataset set: datasets) {
878 dumpDataset(dataset, "New: " + i++); 740 * dumpDataset(dataset, "New: " + i++);
879 } 741 * }
880 log.debug("End dump datasets: "); 742 * log.debug("End dump datasets: ");
881 */ 743 */
882 744
883 return datasets; 745 return datasets;
884 } 746 }
885 747
886 /* 748 /*
887 public static void dumpDataset(XYDataset dataset, String msg) { 749 * public static void dumpDataset(XYDataset dataset, String msg) {
888 log.debug("Begin dump Dataset: " + msg); 750 * log.debug("Begin dump Dataset: " + msg);
889 751 * int N = dataset.getItemCount(0);
890 int N = dataset.getItemCount(0); 752 * int M = dataset.getItemCount(1);
891 int M = dataset.getItemCount(1); 753 * int i = 0, j = 0;
892 754 * for (; i < N; ++i) {
893 int i = 0, j = 0; 755 * double x = dataset.getXValue(0, i);
894 for (; i < N; ++i) { 756 * double y = dataset.getYValue(0, i);
895 double x = dataset.getXValue(0, i); 757 * log.debug("0 " + i + " X: "+ x + "Y: "+ y);
896 double y = dataset.getYValue(0, i); 758 * }
897 log.debug("0 " + i + " X: "+ x + "Y: "+ y); 759 * for (; j < M; ++j) {
898 } 760 * double x = dataset.getXValue(1, j);
899 761 * double y = dataset.getYValue(1, j);
900 for (; j < M; ++j) { 762 * log.debug("1 " + i + " X: "+ x + "Y: "+ y);
901 double x = dataset.getXValue(1, j); 763 * }
902 double y = dataset.getYValue(1, j); 764 * log.debug("End dump Dataset: " + msg);
903 log.debug("1 " + i + " X: "+ x + "Y: "+ y); 765 * }
904 } 766 */
905 log.debug("End dump Dataset: " + msg); 767
906 } 768 private static List<XYDataset> splitByNaNs(final XYDataset dataset) {
907 */
908
909
910 public static List<XYDataset> splitByNaNs(XYDataset dataset) {
911 769
912 switch (dataset.getSeriesCount()) { 770 switch (dataset.getSeriesCount()) {
913 case 0: 771 case 0:
914 return Collections.<XYDataset>emptyList(); 772 return Collections.<XYDataset>emptyList();
915 case 1: 773 case 1:
916 return splitByNaNsOneSeries(dataset); 774 return splitByNaNsOneSeries(dataset);
917 default: // two or more 775 default: // two or more
918 return splitByNaNsTwoSeries(dataset); 776 return splitByNaNsTwoSeries(dataset);
919 } 777 }
920 } 778 }
921
922 779
923 /** 780 /**
924 * Draws the visual representation of a single data item. 781 * Draws the visual representation of a single data item.
925 * 782 *
926 * @param g2 the graphics device. 783 * @param g2
927 * @param state the renderer state. 784 * the graphics device.
928 * @param dataArea the area within which the data is being drawn. 785 * @param state
929 * @param info collects information about the drawing. 786 * the renderer state.
930 * @param plot the plot (can be used to obtain standard color 787 * @param dataArea
931 * information etc). 788 * the area within which the data is being drawn.
932 * @param domainAxis the domain (horizontal) axis. 789 * @param info
933 * @param rangeAxis the range (vertical) axis. 790 * collects information about the drawing.
934 * @param dataset the dataset. 791 * @param plot
935 * @param series the series index (zero-based). 792 * the plot (can be used to obtain standard color
936 * @param item the item index (zero-based). 793 * information etc).
937 * @param crosshairState crosshair information for the plot 794 * @param domainAxis
938 * (<code>null</code> permitted). 795 * the domain (horizontal) axis.
939 * @param pass the pass index. 796 * @param rangeAxis
940 */ 797 * the range (vertical) axis.
941 public void drawItem(Graphics2D g2, 798 * @param dataset
942 XYItemRendererState state, 799 * the dataset.
943 Rectangle2D dataArea, 800 * @param series
944 PlotRenderingInfo info, 801 * the series index (zero-based).
945 XYPlot plot, 802 * @param item
946 ValueAxis domainAxis, 803 * the item index (zero-based).
947 ValueAxis rangeAxis, 804 * @param crosshairState
948 XYDataset dataset, 805 * crosshair information for the plot
949 int series, 806 * (<code>null</code> permitted).
950 int item, 807 * @param pass
951 CrosshairState crosshairState, 808 * the pass index.
952 int pass) { 809 */
810 @Override
811 public void drawItem(final Graphics2D g2, final XYItemRendererState state, final Rectangle2D dataArea, final PlotRenderingInfo info, final XYPlot plot,
812 final ValueAxis domainAxis, final ValueAxis rangeAxis, final XYDataset dataset, final int series, final int item,
813 final CrosshairState crosshairState, final int pass) {
953 switch (pass) { 814 switch (pass) {
954 case 0: 815 case 0:
955 for (XYDataset ds: splitByNaNs(dataset)) { 816 for (final XYDataset ds : splitByNaNs(dataset)) {
956 drawItemPass0(g2, dataArea, info, 817 drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, ds, series, item, crosshairState);
957 plot, domainAxis, rangeAxis, 818 }
958 ds, series, item, crosshairState); 819 break;
959 } 820 case 1:
960 break; 821 drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState);
961 case 1:
962 drawItemPass1(g2, dataArea, info,
963 plot, domainAxis, rangeAxis,
964 dataset, series, item, crosshairState);
965 } 822 }
966 823
967 // Find geometric middle, calculate area and paint 824 // Find geometric middle, calculate area and paint
968 // a string with it here. 825 // a string with it here.
969 if (pass == 1 826 if (pass == 1 && this.labelArea && this.areaLabelNumberFormat != null && this.areaLabelTamplate != null) {
970 && this.labelArea 827 double center_x = this.centroid.getX();
971 && areaLabelNumberFormat != null 828 double center_y = this.centroid.getY();
972 && areaLabelTamplate != null 829 center_x = domainAxis.valueToJava2D(center_x, dataArea, plot.getDomainAxisEdge());
973 ) { 830 center_y = rangeAxis.valueToJava2D(center_y, dataArea, plot.getRangeAxisEdge());
974 double center_x = centroid.getX();
975 double center_y = centroid.getY();
976 center_x = domainAxis.valueToJava2D(center_x, dataArea,
977 plot.getDomainAxisEdge());
978 center_y = rangeAxis.valueToJava2D(center_y, dataArea,
979 plot.getRangeAxisEdge());
980 831
981 // Respect text-extend if text should appear really centered. 832 // Respect text-extend if text should appear really centered.
982 833
983 float area = 0f; 834 float area = 0f;
984 if (areaCalculationMode == CALCULATE_POSITIVE_AREA 835 if (this.areaCalculationMode == CALCULATE_POSITIVE_AREA || this.areaCalculationMode == CALCULATE_ALL_AREA) {
985 || areaCalculationMode == CALCULATE_ALL_AREA) { 836 area += Math.abs(this.positiveArea);
986 area += Math.abs(positiveArea); 837 }
987 } 838 if (this.areaCalculationMode == CALCULATE_NEGATIVE_AREA || this.areaCalculationMode == CALCULATE_ALL_AREA) {
988 if (areaCalculationMode == CALCULATE_NEGATIVE_AREA 839 area += Math.abs(this.negativeArea);
989 || areaCalculationMode == CALCULATE_ALL_AREA) {
990 area += Math.abs(negativeArea);
991 } 840 }
992 if (area != 0f) { 841 if (area != 0f) {
993 Color oldColor = g2.getColor(); 842 final Color oldColor = g2.getColor();
994 Font oldFont = g2.getFont(); 843 final Font oldFont = g2.getFont();
995 g2.setFont(labelFont); 844 g2.setFont(this.labelFont);
996 String labelText = String.format(this.areaLabelTamplate, 845 final String labelText = String.format(this.areaLabelTamplate, this.areaLabelNumberFormat.format(area));
997 areaLabelNumberFormat.format(area)); 846 if (this.labelBGColor != null) {
998 if (labelBGColor != null) { 847 EnhancedLineAndShapeRenderer.drawTextBox(g2, labelText, (float) center_x, (float) center_y, this.labelBGColor);
999 EnhancedLineAndShapeRenderer.drawTextBox(g2, labelText,
1000 (float)center_x, (float)center_y, labelBGColor);
1001 } 848 }
1002 g2.setColor(labelColor); 849 g2.setColor(this.labelColor);
1003 g2.drawString(labelText, (float)center_x, (float)center_y); 850 g2.drawString(labelText, (float) center_x, (float) center_y);
1004 g2.setFont(oldFont); 851 g2.setFont(oldFont);
1005 g2.setColor(oldColor); 852 g2.setColor(oldColor);
1006 } 853 }
1007 } 854 }
1008 } 855 }
1009 856
1010 /** 857 /**
1011 * Draws the visual representation of a single data item, first pass. 858 * Draws the visual representation of a single data item, first pass.
1012 * 859 *
1013 * @param x_graphics the graphics device. 860 * @param x_graphics
1014 * @param x_dataArea the area within which the data is being drawn. 861 * the graphics device.
1015 * @param x_info collects information about the drawing. 862 * @param x_dataArea
1016 * @param x_plot the plot (can be used to obtain standard color 863 * the area within which the data is being drawn.
1017 * information etc). 864 * @param x_info
1018 * @param x_domainAxis the domain (horizontal) axis. 865 * collects information about the drawing.
1019 * @param x_rangeAxis the range (vertical) axis. 866 * @param x_plot
1020 * @param x_dataset the dataset. 867 * the plot (can be used to obtain standard color
1021 * @param x_series the series index (zero-based). 868 * information etc).
1022 * @param x_item the item index (zero-based). 869 * @param x_domainAxis
1023 * @param x_crosshairState crosshair information for the plot 870 * the domain (horizontal) axis.
1024 * (<code>null</code> permitted). 871 * @param x_rangeAxis
1025 */ 872 * the range (vertical) axis.
1026 protected void drawItemPass0(Graphics2D x_graphics, 873 * @param x_dataset
1027 Rectangle2D x_dataArea, 874 * the dataset.
1028 PlotRenderingInfo x_info, 875 * @param x_series
1029 XYPlot x_plot, 876 * the series index (zero-based).
1030 ValueAxis x_domainAxis, 877 * @param x_item
1031 ValueAxis x_rangeAxis, 878 * the item index (zero-based).
1032 XYDataset x_dataset, 879 * @param x_crosshairState
1033 int x_series, 880 * crosshair information for the plot
1034 int x_item, 881 * (<code>null</code> permitted).
1035 CrosshairState x_crosshairState) { 882 */
883 private void drawItemPass0(final Graphics2D x_graphics, final Rectangle2D x_dataArea, final PlotRenderingInfo x_info, final XYPlot x_plot,
884 final ValueAxis x_domainAxis, final ValueAxis x_rangeAxis, final XYDataset x_dataset, final int x_series, final int x_item,
885 final CrosshairState x_crosshairState) {
1036 886
1037 if (!((0 == x_series) && (0 == x_item))) { 887 if (!((0 == x_series) && (0 == x_item))) {
1038 return; 888 return;
1039 } 889 }
1040 890
1041 boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount()); 891 final boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount());
1042 892
1043 // check if either series is a degenerate case (i.e. less than 2 points) 893 // check if either series is a degenerate case (i.e. less than 2 points)
1044 if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) { 894 if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) {
1045 return; 895 return;
1046 } 896 }
1049 if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) { 899 if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) {
1050 return; 900 return;
1051 } 901 }
1052 902
1053 // polygon definitions 903 // polygon definitions
1054 LinkedList l_minuendXs = new LinkedList(); 904 final List<Double> l_minuendXs = new LinkedList<>();
1055 LinkedList l_minuendYs = new LinkedList(); 905 final List<Double> l_minuendYs = new LinkedList<>();
1056 LinkedList l_subtrahendXs = new LinkedList(); 906 final List<Double> l_subtrahendXs = new LinkedList<>();
1057 LinkedList l_subtrahendYs = new LinkedList(); 907 final List<Double> l_subtrahendYs = new LinkedList<>();
1058 LinkedList l_polygonXs = new LinkedList(); 908 final List<Double> l_polygonXs = new LinkedList<>();
1059 LinkedList l_polygonYs = new LinkedList(); 909 final List<Double> l_polygonYs = new LinkedList<>();
1060 910
1061 // state 911 // state
1062 int l_minuendItem = 0; 912 int l_minuendItem = 0;
1063 int l_minuendItemCount = x_dataset.getItemCount(0); 913 final int l_minuendItemCount = x_dataset.getItemCount(0);
1064 Double l_minuendCurX = null; 914 Double l_minuendCurX = null;
1065 Double l_minuendNextX = null; 915 Double l_minuendNextX = null;
1066 Double l_minuendCurY = null; 916 Double l_minuendCurY = null;
1067 Double l_minuendNextY = null; 917 Double l_minuendNextY = null;
1068 double l_minuendMaxY = Double.NEGATIVE_INFINITY; 918 double l_minuendMaxY = Double.NEGATIVE_INFINITY;
1069 double l_minuendMinY = Double.POSITIVE_INFINITY; 919 double l_minuendMinY = Double.POSITIVE_INFINITY;
1070 920
1071 int l_subtrahendItem = 0; 921 int l_subtrahendItem = 0;
1072 int l_subtrahendItemCount = 0; // actual value set below 922 int l_subtrahendItemCount = 0; // actual value set below
1073 Double l_subtrahendCurX = null; 923 Double l_subtrahendCurX = null;
1074 Double l_subtrahendNextX = null; 924 Double l_subtrahendNextX = null;
1075 Double l_subtrahendCurY = null; 925 Double l_subtrahendCurY = null;
1076 Double l_subtrahendNextY = null; 926 Double l_subtrahendNextY = null;
1077 double l_subtrahendMaxY = Double.NEGATIVE_INFINITY; 927 double l_subtrahendMaxY = Double.NEGATIVE_INFINITY;
1078 double l_subtrahendMinY = Double.POSITIVE_INFINITY; 928 double l_subtrahendMinY = Double.POSITIVE_INFINITY;
1079 929
1080 // if a subtrahend is not specified, assume it is zero 930 // if a subtrahend is not specified, assume it is zero
1081 if (b_impliedZeroSubtrahend) { 931 if (b_impliedZeroSubtrahend) {
1082 l_subtrahendItem = 0; 932 l_subtrahendItem = 0;
1083 l_subtrahendItemCount = 2; 933 l_subtrahendItemCount = 2;
1084 l_subtrahendCurX = new Double(x_dataset.getXValue(0, 0)); 934 l_subtrahendCurX = new Double(x_dataset.getXValue(0, 0));
1085 l_subtrahendNextX = new Double(x_dataset.getXValue(0, 935 l_subtrahendNextX = new Double(x_dataset.getXValue(0, (l_minuendItemCount - 1)));
1086 (l_minuendItemCount - 1))); 936 l_subtrahendCurY = new Double(0.0);
1087 l_subtrahendCurY = new Double(0.0); 937 l_subtrahendNextY = new Double(0.0);
1088 l_subtrahendNextY = new Double(0.0); 938 l_subtrahendMaxY = 0.0;
1089 l_subtrahendMaxY = 0.0; 939 l_subtrahendMinY = 0.0;
1090 l_subtrahendMinY = 0.0;
1091 940
1092 l_subtrahendXs.add(l_subtrahendCurX); 941 l_subtrahendXs.add(l_subtrahendCurX);
1093 l_subtrahendYs.add(l_subtrahendCurY); 942 l_subtrahendYs.add(l_subtrahendCurY);
1094 } 943 } else {
1095 else {
1096 l_subtrahendItemCount = x_dataset.getItemCount(1); 944 l_subtrahendItemCount = x_dataset.getItemCount(1);
1097 } 945 }
1098 946
1099 boolean b_minuendDone = false; 947 boolean b_minuendDone = false;
1100 boolean b_minuendAdvanced = true; 948 boolean b_minuendAdvanced = true;
1101 boolean b_minuendAtIntersect = false; 949 boolean b_minuendAtIntersect = false;
1102 boolean b_minuendFastForward = false; 950 boolean b_minuendFastForward = false;
1103 boolean b_subtrahendDone = false; 951 boolean b_subtrahendDone = false;
1104 boolean b_subtrahendAdvanced = true; 952 boolean b_subtrahendAdvanced = true;
1105 boolean b_subtrahendAtIntersect = false; 953 boolean b_subtrahendAtIntersect = false;
1106 boolean b_subtrahendFastForward = false; 954 boolean b_subtrahendFastForward = false;
1107 boolean b_colinear = false; 955 boolean b_colinear = false;
1108 956
1109 boolean b_positive; 957 boolean b_positive;
1110 958
1111 // coordinate pairs 959 // coordinate pairs
1112 double l_x1 = 0.0, l_y1 = 0.0; // current minuend point 960 double l_x1 = 0.0, l_y1 = 0.0; // current minuend point
1121 l_x1 = x_dataset.getXValue(0, l_minuendItem); 969 l_x1 = x_dataset.getXValue(0, l_minuendItem);
1122 l_y1 = x_dataset.getYValue(0, l_minuendItem); 970 l_y1 = x_dataset.getYValue(0, l_minuendItem);
1123 l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); 971 l_x2 = x_dataset.getXValue(0, l_minuendItem + 1);
1124 l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); 972 l_y2 = x_dataset.getYValue(0, l_minuendItem + 1);
1125 973
1126 l_minuendCurX = new Double(l_x1); 974 l_minuendCurX = new Double(l_x1);
1127 l_minuendCurY = new Double(l_y1); 975 l_minuendCurY = new Double(l_y1);
1128 l_minuendNextX = new Double(l_x2); 976 l_minuendNextX = new Double(l_x2);
1129 l_minuendNextY = new Double(l_y2); 977 l_minuendNextY = new Double(l_y2);
1130 978
1131 if (b_impliedZeroSubtrahend) { 979 if (b_impliedZeroSubtrahend) {
1132 l_x3 = l_subtrahendCurX.doubleValue(); 980 l_x3 = l_subtrahendCurX.doubleValue();
1133 l_y3 = l_subtrahendCurY.doubleValue(); 981 l_y3 = l_subtrahendCurY.doubleValue();
1134 l_x4 = l_subtrahendNextX.doubleValue(); 982 l_x4 = l_subtrahendNextX.doubleValue();
1135 l_y4 = l_subtrahendNextY.doubleValue(); 983 l_y4 = l_subtrahendNextY.doubleValue();
1136 } 984 } else {
1137 else {
1138 l_x3 = x_dataset.getXValue(1, l_subtrahendItem); 985 l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
1139 l_y3 = x_dataset.getYValue(1, l_subtrahendItem); 986 l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
1140 l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); 987 l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1);
1141 l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); 988 l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1);
1142 989
1143 l_subtrahendCurX = new Double(l_x3); 990 l_subtrahendCurX = new Double(l_x3);
1144 l_subtrahendCurY = new Double(l_y3); 991 l_subtrahendCurY = new Double(l_y3);
1145 l_subtrahendNextX = new Double(l_x4); 992 l_subtrahendNextX = new Double(l_x4);
1146 l_subtrahendNextY = new Double(l_y4); 993 l_subtrahendNextY = new Double(l_y4);
1147 } 994 }
1148 995
1149 if (l_x2 <= l_x3) { 996 if (l_x2 <= l_x3) {
1161 } 1008 }
1162 1009
1163 // check if initial polygon needs to be clipped 1010 // check if initial polygon needs to be clipped
1164 if ((l_x3 < l_x1) && (l_x1 < l_x4)) { 1011 if ((l_x3 < l_x1) && (l_x1 < l_x4)) {
1165 // project onto subtrahend 1012 // project onto subtrahend
1166 double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); 1013 final double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3);
1167 l_subtrahendCurX = l_minuendCurX; 1014 l_subtrahendCurX = l_minuendCurX;
1168 l_subtrahendCurY = new Double((l_slope * l_x1) 1015 l_subtrahendCurY = new Double((l_slope * l_x1) + (l_y3 - (l_slope * l_x3)));
1169 + (l_y3 - (l_slope * l_x3)));
1170 1016
1171 l_subtrahendXs.add(l_subtrahendCurX); 1017 l_subtrahendXs.add(l_subtrahendCurX);
1172 l_subtrahendYs.add(l_subtrahendCurY); 1018 l_subtrahendYs.add(l_subtrahendCurY);
1173 } 1019 }
1174 1020
1175 if ((l_x1 < l_x3) && (l_x3 < l_x2)) { 1021 if ((l_x1 < l_x3) && (l_x3 < l_x2)) {
1176 // project onto minuend 1022 // project onto minuend
1177 double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); 1023 final double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
1178 l_minuendCurX = l_subtrahendCurX; 1024 l_minuendCurX = l_subtrahendCurX;
1179 l_minuendCurY = new Double((l_slope * l_x3) 1025 l_minuendCurY = new Double((l_slope * l_x3) + (l_y1 - (l_slope * l_x1)));
1180 + (l_y1 - (l_slope * l_x1)));
1181 1026
1182 l_minuendXs.add(l_minuendCurX); 1027 l_minuendXs.add(l_minuendCurX);
1183 l_minuendYs.add(l_minuendCurY); 1028 l_minuendYs.add(l_minuendCurY);
1184 } 1029 }
1185 1030
1186 l_minuendMaxY = l_minuendCurY.doubleValue(); 1031 l_minuendMaxY = l_minuendCurY.doubleValue();
1187 l_minuendMinY = l_minuendCurY.doubleValue(); 1032 l_minuendMinY = l_minuendCurY.doubleValue();
1188 l_subtrahendMaxY = l_subtrahendCurY.doubleValue(); 1033 l_subtrahendMaxY = l_subtrahendCurY.doubleValue();
1189 l_subtrahendMinY = l_subtrahendCurY.doubleValue(); 1034 l_subtrahendMinY = l_subtrahendCurY.doubleValue();
1190 1035
1191 b_fastForwardDone = true; 1036 b_fastForwardDone = true;
1192 } 1037 }
1212 l_minuendNextX = new Double(l_x2); 1057 l_minuendNextX = new Double(l_x2);
1213 l_minuendNextY = new Double(l_y2); 1058 l_minuendNextY = new Double(l_y2);
1214 } 1059 }
1215 1060
1216 // never updated the subtrahend if it is implied to be zero 1061 // never updated the subtrahend if it is implied to be zero
1217 if (!b_impliedZeroSubtrahend && !b_subtrahendDone 1062 if (!b_impliedZeroSubtrahend && !b_subtrahendDone && !b_subtrahendFastForward && b_subtrahendAdvanced) {
1218 && !b_subtrahendFastForward && b_subtrahendAdvanced) {
1219 l_x3 = x_dataset.getXValue(1, l_subtrahendItem); 1063 l_x3 = x_dataset.getXValue(1, l_subtrahendItem);
1220 l_y3 = x_dataset.getYValue(1, l_subtrahendItem); 1064 l_y3 = x_dataset.getYValue(1, l_subtrahendItem);
1221 l_subtrahendCurX = new Double(l_x3); 1065 l_subtrahendCurX = new Double(l_x3);
1222 l_subtrahendCurY = new Double(l_y3); 1066 l_subtrahendCurY = new Double(l_y3);
1223 1067
1234 l_subtrahendNextX = new Double(l_x4); 1078 l_subtrahendNextX = new Double(l_x4);
1235 l_subtrahendNextY = new Double(l_y4); 1079 l_subtrahendNextY = new Double(l_y4);
1236 } 1080 }
1237 1081
1238 // deassert b_*FastForward (only matters for 1st time through loop) 1082 // deassert b_*FastForward (only matters for 1st time through loop)
1239 b_minuendFastForward = false; 1083 b_minuendFastForward = false;
1240 b_subtrahendFastForward = false; 1084 b_subtrahendFastForward = false;
1241 1085
1242 Double l_intersectX = null; 1086 Double l_intersectX = null;
1243 Double l_intersectY = null; 1087 Double l_intersectY = null;
1244 boolean b_intersect = false; 1088 boolean b_intersect = false;
1245 1089
1246 b_minuendAtIntersect = false; 1090 b_minuendAtIntersect = false;
1247 b_subtrahendAtIntersect = false; 1091 b_subtrahendAtIntersect = false;
1248 1092
1249 // check for intersect 1093 // check for intersect
1250 if ((l_x2 == l_x4) && (l_y2 == l_y4)) { 1094 if ((l_x2 == l_x4) && (l_y2 == l_y4)) {
1251 // check if line segments are colinear 1095 // check if line segments are colinear
1252 if ((l_x1 == l_x3) && (l_y1 == l_y3)) { 1096 if ((l_x1 == l_x3) && (l_y1 == l_y3)) {
1253 b_colinear = true; 1097 b_colinear = true;
1254 } 1098 } else {
1255 else {
1256 // the intersect is at the next point for both the minuend 1099 // the intersect is at the next point for both the minuend
1257 // and subtrahend 1100 // and subtrahend
1258 l_intersectX = new Double(l_x2); 1101 l_intersectX = new Double(l_x2);
1259 l_intersectY = new Double(l_y2); 1102 l_intersectY = new Double(l_y2);
1260 1103
1261 b_intersect = true; 1104 b_intersect = true;
1262 b_minuendAtIntersect = true; 1105 b_minuendAtIntersect = true;
1263 b_subtrahendAtIntersect = true; 1106 b_subtrahendAtIntersect = true;
1264 } 1107 }
1265 } 1108 } else {
1266 else {
1267 // compute common denominator 1109 // compute common denominator
1268 double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1)) 1110 final double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1)) - ((l_x4 - l_x3) * (l_y2 - l_y1));
1269 - ((l_x4 - l_x3) * (l_y2 - l_y1));
1270 1111
1271 // compute common deltas 1112 // compute common deltas
1272 double l_deltaY = l_y1 - l_y3; 1113 final double l_deltaY = l_y1 - l_y3;
1273 double l_deltaX = l_x1 - l_x3; 1114 final double l_deltaX = l_x1 - l_x3;
1274 1115
1275 // compute numerators 1116 // compute numerators
1276 double l_numeratorA = ((l_x4 - l_x3) * l_deltaY) 1117 final double l_numeratorA = ((l_x4 - l_x3) * l_deltaY) - ((l_y4 - l_y3) * l_deltaX);
1277 - ((l_y4 - l_y3) * l_deltaX); 1118 final double l_numeratorB = ((l_x2 - l_x1) * l_deltaY) - ((l_y2 - l_y1) * l_deltaX);
1278 double l_numeratorB = ((l_x2 - l_x1) * l_deltaY)
1279 - ((l_y2 - l_y1) * l_deltaX);
1280 1119
1281 // check if line segments are colinear 1120 // check if line segments are colinear
1282 if ((0 == l_numeratorA) && (0 == l_numeratorB) 1121 if ((0 == l_numeratorA) && (0 == l_numeratorB) && (0 == l_denominator)) {
1283 && (0 == l_denominator)) {
1284 b_colinear = true; 1122 b_colinear = true;
1285 } 1123 } else {
1286 else {
1287 // check if previously colinear 1124 // check if previously colinear
1288 if (b_colinear) { 1125 if (b_colinear) {
1289 // clear colinear points and flag 1126 // clear colinear points and flag
1290 l_minuendXs.clear(); 1127 l_minuendXs.clear();
1291 l_minuendYs.clear(); 1128 l_minuendYs.clear();
1295 l_polygonYs.clear(); 1132 l_polygonYs.clear();
1296 1133
1297 b_colinear = false; 1134 b_colinear = false;
1298 1135
1299 // set new starting point for the polygon 1136 // set new starting point for the polygon
1300 boolean b_useMinuend = ((l_x3 <= l_x1) 1137 final boolean b_useMinuend = ((l_x3 <= l_x1) && (l_x1 <= l_x4));
1301 && (l_x1 <= l_x4)); 1138 l_polygonXs.add(b_useMinuend ? l_minuendCurX : l_subtrahendCurX);
1302 l_polygonXs.add(b_useMinuend ? l_minuendCurX 1139 l_polygonYs.add(b_useMinuend ? l_minuendCurY : l_subtrahendCurY);
1303 : l_subtrahendCurX);
1304 l_polygonYs.add(b_useMinuend ? l_minuendCurY
1305 : l_subtrahendCurY);
1306 } 1140 }
1307 1141
1308 // compute slope components 1142 // compute slope components
1309 double l_slopeA = l_numeratorA / l_denominator; 1143 final double l_slopeA = l_numeratorA / l_denominator;
1310 double l_slopeB = l_numeratorB / l_denominator; 1144 final double l_slopeB = l_numeratorB / l_denominator;
1311 1145
1312 // check if the line segments intersect 1146 // check if the line segments intersect
1313 if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB) 1147 if ((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB) && (l_slopeB <= 1)) {
1314 && (l_slopeB <= 1)) {
1315 // compute the point of intersection 1148 // compute the point of intersection
1316 double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1)); 1149 final double l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1));
1317 double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1)); 1150 final double l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1));
1318 1151
1319 l_intersectX = new Double(l_xi); 1152 l_intersectX = new Double(l_xi);
1320 l_intersectY = new Double(l_yi); 1153 l_intersectY = new Double(l_yi);
1321 b_intersect = true; 1154 b_intersect = true;
1322 b_minuendAtIntersect = ((l_xi == l_x2) 1155 b_minuendAtIntersect = ((l_xi == l_x2) && (l_yi == l_y2));
1323 && (l_yi == l_y2)); 1156 b_subtrahendAtIntersect = ((l_xi == l_x4) && (l_yi == l_y4));
1324 b_subtrahendAtIntersect = ((l_xi == l_x4)
1325 && (l_yi == l_y4));
1326 1157
1327 // advance minuend and subtrahend to intesect 1158 // advance minuend and subtrahend to intesect
1328 l_minuendCurX = l_intersectX; 1159 l_minuendCurX = l_intersectX;
1329 l_minuendCurY = l_intersectY; 1160 l_minuendCurY = l_intersectY;
1330 l_subtrahendCurX = l_intersectX; 1161 l_subtrahendCurX = l_intersectX;
1331 l_subtrahendCurY = l_intersectY; 1162 l_subtrahendCurY = l_intersectY;
1332 } 1163 }
1333 } 1164 }
1334 } 1165 }
1348 Collections.reverse(l_subtrahendYs); 1179 Collections.reverse(l_subtrahendYs);
1349 l_polygonXs.addAll(l_subtrahendXs); 1180 l_polygonXs.addAll(l_subtrahendXs);
1350 l_polygonYs.addAll(l_subtrahendYs); 1181 l_polygonYs.addAll(l_subtrahendYs);
1351 1182
1352 // create an actual polygon 1183 // create an actual polygon
1353 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) 1184 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) && (l_subtrahendMinY <= l_minuendMinY);
1354 && (l_subtrahendMinY <= l_minuendMinY); 1185 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
1355 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis,
1356 x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
1357 1186
1358 // clear the point vectors 1187 // clear the point vectors
1359 l_minuendXs.clear(); 1188 l_minuendXs.clear();
1360 l_minuendYs.clear(); 1189 l_minuendYs.clear();
1361 l_subtrahendXs.clear(); 1190 l_subtrahendXs.clear();
1362 l_subtrahendYs.clear(); 1191 l_subtrahendYs.clear();
1363 l_polygonXs.clear(); 1192 l_polygonXs.clear();
1364 l_polygonYs.clear(); 1193 l_polygonYs.clear();
1365 1194
1366 // set the maxY and minY values to intersect y-value 1195 // set the maxY and minY values to intersect y-value
1367 double l_y = l_intersectY.doubleValue(); 1196 final double l_y = l_intersectY.doubleValue();
1368 l_minuendMaxY = l_y; 1197 l_minuendMaxY = l_y;
1369 l_subtrahendMaxY = l_y; 1198 l_subtrahendMaxY = l_y;
1370 l_minuendMinY = l_y; 1199 l_minuendMinY = l_y;
1371 l_subtrahendMinY = l_y; 1200 l_subtrahendMinY = l_y;
1372 1201
1373 // add interection point to new polygon 1202 // add interection point to new polygon
1374 l_polygonXs.add(l_intersectX); 1203 l_polygonXs.add(l_intersectX);
1375 l_polygonYs.add(l_intersectY); 1204 l_polygonYs.add(l_intersectY);
1377 1206
1378 // advance the minuend if needed 1207 // advance the minuend if needed
1379 if (l_x2 <= l_x4) { 1208 if (l_x2 <= l_x4) {
1380 l_minuendItem++; 1209 l_minuendItem++;
1381 b_minuendAdvanced = true; 1210 b_minuendAdvanced = true;
1382 } 1211 } else {
1383 else {
1384 b_minuendAdvanced = false; 1212 b_minuendAdvanced = false;
1385 } 1213 }
1386 1214
1387 // advance the subtrahend if needed 1215 // advance the subtrahend if needed
1388 if (l_x4 <= l_x2) { 1216 if (l_x4 <= l_x2) {
1389 l_subtrahendItem++; 1217 l_subtrahendItem++;
1390 b_subtrahendAdvanced = true; 1218 b_subtrahendAdvanced = true;
1391 } 1219 } else {
1392 else {
1393 b_subtrahendAdvanced = false; 1220 b_subtrahendAdvanced = false;
1394 } 1221 }
1395 1222
1396 b_minuendDone = (l_minuendItem == (l_minuendItemCount - 1)); 1223 b_minuendDone = (l_minuendItem == (l_minuendItemCount - 1));
1397 b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount 1224 b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount - 1));
1398 - 1));
1399 } 1225 }
1400 1226
1401 // check if the final polygon needs to be clipped 1227 // check if the final polygon needs to be clipped
1402 if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) { 1228 if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) {
1403 // project onto subtrahend 1229 // project onto subtrahend
1404 double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); 1230 final double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3);
1405 l_subtrahendNextX = l_minuendNextX; 1231 l_subtrahendNextX = l_minuendNextX;
1406 l_subtrahendNextY = new Double((l_slope * l_x2) 1232 l_subtrahendNextY = new Double((l_slope * l_x2) + (l_y3 - (l_slope * l_x3)));
1407 + (l_y3 - (l_slope * l_x3)));
1408 } 1233 }
1409 1234
1410 if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) { 1235 if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) {
1411 // project onto minuend 1236 // project onto minuend
1412 double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); 1237 final double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1);
1413 l_minuendNextX = l_subtrahendNextX; 1238 l_minuendNextX = l_subtrahendNextX;
1414 l_minuendNextY = new Double((l_slope * l_x4) 1239 l_minuendNextY = new Double((l_slope * l_x4) + (l_y1 - (l_slope * l_x1)));
1415 + (l_y1 - (l_slope * l_x1)));
1416 } 1240 }
1417 1241
1418 // consider last point of minuend and subtrahend for determining 1242 // consider last point of minuend and subtrahend for determining
1419 // positivity 1243 // positivity
1420 l_minuendMaxY = Math.max(l_minuendMaxY, 1244 l_minuendMaxY = Math.max(l_minuendMaxY, l_minuendNextY.doubleValue());
1421 l_minuendNextY.doubleValue()); 1245 l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_subtrahendNextY.doubleValue());
1422 l_subtrahendMaxY = Math.max(l_subtrahendMaxY, 1246 l_minuendMinY = Math.min(l_minuendMinY, l_minuendNextY.doubleValue());
1423 l_subtrahendNextY.doubleValue()); 1247 l_subtrahendMinY = Math.min(l_subtrahendMinY, l_subtrahendNextY.doubleValue());
1424 l_minuendMinY = Math.min(l_minuendMinY,
1425 l_minuendNextY.doubleValue());
1426 l_subtrahendMinY = Math.min(l_subtrahendMinY,
1427 l_subtrahendNextY.doubleValue());
1428 1248
1429 // add the last point of the minuned and subtrahend 1249 // add the last point of the minuned and subtrahend
1430 l_minuendXs.add(l_minuendNextX); 1250 l_minuendXs.add(l_minuendNextX);
1431 l_minuendYs.add(l_minuendNextY); 1251 l_minuendYs.add(l_minuendNextY);
1432 l_subtrahendXs.add(l_subtrahendNextX); 1252 l_subtrahendXs.add(l_subtrahendNextX);
1442 Collections.reverse(l_subtrahendYs); 1262 Collections.reverse(l_subtrahendYs);
1443 l_polygonXs.addAll(l_subtrahendXs); 1263 l_polygonXs.addAll(l_subtrahendXs);
1444 l_polygonYs.addAll(l_subtrahendYs); 1264 l_polygonYs.addAll(l_subtrahendYs);
1445 1265
1446 // create an actual polygon 1266 // create an actual polygon
1447 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) 1267 b_positive = (l_subtrahendMaxY <= l_minuendMaxY) && (l_subtrahendMinY <= l_minuendMinY);
1448 && (l_subtrahendMinY <= l_minuendMinY); 1268 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, x_rangeAxis, b_positive, l_polygonXs, l_polygonYs);
1449 createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, 1269 }
1450 x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); 1270
1451 } 1271 /**
1452 1272 * Draws the visual representation of a single data item, second pass. In
1453 /**
1454 * Draws the visual representation of a single data item, second pass. In
1455 * the second pass, the renderer draws the lines and shapes for the 1273 * the second pass, the renderer draws the lines and shapes for the
1456 * individual points in the two series. 1274 * individual points in the two series.
1457 * 1275 *
1458 * @param x_graphics the graphics device. 1276 * @param x_graphics
1459 * @param x_dataArea the area within which the data is being drawn. 1277 * the graphics device.
1460 * @param x_info collects information about the drawing. 1278 * @param x_dataArea
1461 * @param x_plot the plot (can be used to obtain standard color 1279 * the area within which the data is being drawn.
1462 * information etc). 1280 * @param x_info
1463 * @param x_domainAxis the domain (horizontal) axis. 1281 * collects information about the drawing.
1464 * @param x_rangeAxis the range (vertical) axis. 1282 * @param x_plot
1465 * @param x_dataset the dataset. 1283 * the plot (can be used to obtain standard color
1466 * @param x_series the series index (zero-based). 1284 * information etc).
1467 * @param x_item the item index (zero-based). 1285 * @param x_domainAxis
1468 * @param x_crosshairState crosshair information for the plot 1286 * the domain (horizontal) axis.
1469 * (<code>null</code> permitted). 1287 * @param x_rangeAxis
1470 */ 1288 * the range (vertical) axis.
1471 protected void drawItemPass1(Graphics2D x_graphics, 1289 * @param x_dataset
1472 Rectangle2D x_dataArea, 1290 * the dataset.
1473 PlotRenderingInfo x_info, 1291 * @param x_series
1474 XYPlot x_plot, 1292 * the series index (zero-based).
1475 ValueAxis x_domainAxis, 1293 * @param x_item
1476 ValueAxis x_rangeAxis, 1294 * the item index (zero-based).
1477 XYDataset x_dataset, 1295 * @param x_crosshairState
1478 int x_series, 1296 * crosshair information for the plot
1479 int x_item, 1297 * (<code>null</code> permitted).
1480 CrosshairState x_crosshairState) { 1298 */
1299 private void drawItemPass1(final Graphics2D x_graphics, final Rectangle2D x_dataArea, final PlotRenderingInfo x_info, final XYPlot x_plot,
1300 final ValueAxis x_domainAxis, final ValueAxis x_rangeAxis, final XYDataset x_dataset, final int x_series, final int x_item,
1301 final CrosshairState x_crosshairState) {
1481 1302
1482 Shape l_entityArea = null; 1303 Shape l_entityArea = null;
1483 EntityCollection l_entities = null; 1304 EntityCollection l_entities = null;
1484 if (null != x_info) { 1305 if (null != x_info) {
1485 l_entities = x_info.getOwner().getEntityCollection(); 1306 l_entities = x_info.getOwner().getEntityCollection();
1486 } 1307 }
1487 1308
1488 Paint l_seriesPaint = getItemPaint(x_series, x_item); 1309 final Paint l_seriesPaint = getItemPaint(x_series, x_item);
1489 Stroke l_seriesStroke = getItemStroke(x_series, x_item); 1310 final Stroke l_seriesStroke = getItemStroke(x_series, x_item);
1490 x_graphics.setPaint(l_seriesPaint); 1311 x_graphics.setPaint(l_seriesPaint);
1491 x_graphics.setStroke(l_seriesStroke); 1312 x_graphics.setStroke(l_seriesStroke);
1492 1313
1493 PlotOrientation l_orientation = x_plot.getOrientation(); 1314 final PlotOrientation l_orientation = x_plot.getOrientation();
1494 RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); 1315 final RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
1495 RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); 1316 final RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge();
1496 1317
1497 double l_x0 = x_dataset.getXValue(x_series, x_item); 1318 final double l_x0 = x_dataset.getXValue(x_series, x_item);
1498 double l_y0 = x_dataset.getYValue(x_series, x_item); 1319 final double l_y0 = x_dataset.getYValue(x_series, x_item);
1499 double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, 1320 final double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, l_domainAxisLocation);
1500 l_domainAxisLocation); 1321 final double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea, l_rangeAxisLocation);
1501 double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea,
1502 l_rangeAxisLocation);
1503 1322
1504 // These are the shapes of the series items. 1323 // These are the shapes of the series items.
1505 if (getShapesVisible()) { 1324 if (getShapesVisible()) {
1506 Shape l_shape = getItemShape(x_series, x_item); 1325 Shape l_shape = getItemShape(x_series, x_item);
1507 if (l_orientation == PlotOrientation.HORIZONTAL) { 1326 if (l_orientation == PlotOrientation.HORIZONTAL) {
1508 l_shape = ShapeUtilities.createTranslatedShape(l_shape, 1327 l_shape = ShapeUtilities.createTranslatedShape(l_shape, l_y1, l_x1);
1509 l_y1, l_x1); 1328 } else {
1510 } 1329 l_shape = ShapeUtilities.createTranslatedShape(l_shape, l_x1, l_y1);
1511 else {
1512 l_shape = ShapeUtilities.createTranslatedShape(l_shape,
1513 l_x1, l_y1);
1514 } 1330 }
1515 if (l_shape.intersects(x_dataArea)) { 1331 if (l_shape.intersects(x_dataArea)) {
1516 x_graphics.setPaint(getItemPaint(x_series, x_item)); 1332 x_graphics.setPaint(getItemPaint(x_series, x_item));
1517 x_graphics.fill(l_shape); 1333 x_graphics.fill(l_shape);
1518 /* TODO We could draw the shapes of single items here. 1334 /*
1519 if (drawOutline) { 1335 * TODO We could draw the shapes of single items here.
1520 x_graphics.setPaint(this.outlinePaint); 1336 * if (drawOutline) {
1521 x_graphics.setStroke(this.outlineStroke); 1337 * x_graphics.setPaint(this.outlinePaint);
1522 x_graphics.draw(l_shape); 1338 * x_graphics.setStroke(this.outlineStroke);
1523 } 1339 * x_graphics.draw(l_shape);
1524 */ 1340 * }
1341 */
1525 } 1342 }
1526 l_entityArea = l_shape; 1343 l_entityArea = l_shape;
1527 } // if (getShapesVisible()) 1344 } // if (getShapesVisible())
1528 1345
1529 // add an entity for the item... 1346 // add an entity for the item...
1530 if (null != l_entities) { 1347 if (null != l_entities) {
1531 if (null == l_entityArea) { 1348 if (null == l_entityArea) {
1532 l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), 1349 l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), 4, 4);
1533 4, 4);
1534 } 1350 }
1535 String l_tip = null; 1351 String l_tip = null;
1536 XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, 1352 final XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, x_item);
1537 x_item);
1538 if (null != l_tipGenerator) { 1353 if (null != l_tipGenerator) {
1539 l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, 1354 l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, x_item);
1540 x_item);
1541 } 1355 }
1542 String l_url = null; 1356 String l_url = null;
1543 XYURLGenerator l_urlGenerator = getURLGenerator(); 1357 final XYURLGenerator l_urlGenerator = getURLGenerator();
1544 if (null != l_urlGenerator) { 1358 if (null != l_urlGenerator) {
1545 l_url = l_urlGenerator.generateURL(x_dataset, x_series, 1359 l_url = l_urlGenerator.generateURL(x_dataset, x_series, x_item);
1546 x_item); 1360 }
1547 } 1361 final XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset, x_series, x_item, l_tip, l_url);
1548 XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset,
1549 x_series, x_item, l_tip, l_url);
1550 l_entities.add(l_entity); 1362 l_entities.add(l_entity);
1551 } 1363 }
1552 1364
1553 // draw the item label if there is one... 1365 // draw the item label if there is one...
1554 if (isItemLabelVisible(x_series, x_item)) { 1366 if (isItemLabelVisible(x_series, x_item)) {
1555 drawItemLabel(x_graphics, l_orientation, x_dataset, x_series, 1367 drawItemLabel(x_graphics, l_orientation, x_dataset, x_series, x_item, l_x1, l_y1, (l_y1 < 0.0));
1556 x_item, l_x1, l_y1, (l_y1 < 0.0)); 1368 }
1557 } 1369
1558 1370 final int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis);
1559 int l_domainAxisIndex = x_plot.getDomainAxisIndex(x_domainAxis); 1371 final int l_rangeAxisIndex = x_plot.getRangeAxisIndex(x_rangeAxis);
1560 int l_rangeAxisIndex = x_plot.getRangeAxisIndex(x_rangeAxis); 1372 updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex, l_rangeAxisIndex, l_x1, l_y1, l_orientation);
1561 updateCrosshairValues(x_crosshairState, l_x0, l_y0, l_domainAxisIndex,
1562 l_rangeAxisIndex, l_x1, l_y1, l_orientation);
1563 1373
1564 if (0 == x_item) { 1374 if (0 == x_item) {
1565 return; 1375 return;
1566 } 1376 }
1567 1377
1568 double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series, 1378 final double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series, (x_item - 1)), x_dataArea, l_domainAxisLocation);
1569 (x_item - 1)), x_dataArea, l_domainAxisLocation); 1379 final double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series, (x_item - 1)), x_dataArea, l_rangeAxisLocation);
1570 double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series,
1571 (x_item - 1)), x_dataArea, l_rangeAxisLocation);
1572 1380
1573 Line2D l_line = null; 1381 Line2D l_line = null;
1574 if (PlotOrientation.HORIZONTAL == l_orientation) { 1382 if (PlotOrientation.HORIZONTAL == l_orientation) {
1575 l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2); 1383 l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2);
1576 } 1384 } else if (PlotOrientation.VERTICAL == l_orientation) {
1577 else if (PlotOrientation.VERTICAL == l_orientation) {
1578 l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2); 1385 l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2);
1579 } 1386 }
1580 1387
1581 if ((null != l_line) && l_line.intersects(x_dataArea)) { 1388 if ((null != l_line) && l_line.intersects(x_dataArea)) {
1582 x_graphics.setPaint(getItemPaint(x_series, x_item)); 1389 x_graphics.setPaint(getItemPaint(x_series, x_item));
1583 x_graphics.setStroke(getItemStroke(x_series, x_item)); 1390 x_graphics.setStroke(getItemStroke(x_series, x_item));
1584 if (drawOriginalSeries) { 1391 if (this.drawOriginalSeries) {
1585 x_graphics.setPaint(this.outlinePaint); 1392 x_graphics.setPaint(this.outlinePaint);
1586 x_graphics.setStroke(this.outlineStroke); 1393 x_graphics.setStroke(this.outlineStroke);
1587 x_graphics.draw(l_line); 1394 x_graphics.draw(l_line);
1588 } 1395 }
1589 } 1396 }
1590 } 1397 }
1591 1398
1592 /** 1399 /**
1593 * Determines if a dataset is degenerate. A degenerate dataset is a 1400 * Determines if a dataset is degenerate. A degenerate dataset is a
1594 * dataset where either series has less than two (2) points. 1401 * dataset where either series has less than two (2) points.
1595 * 1402 *
1596 * @param x_dataset the dataset. 1403 * @param x_dataset
1597 * @param x_impliedZeroSubtrahend if false, do not check the subtrahend 1404 * the dataset.
1405 * @param x_impliedZeroSubtrahend
1406 * if false, do not check the subtrahend
1598 * 1407 *
1599 * @return true if the dataset is degenerate. 1408 * @return true if the dataset is degenerate.
1600 */ 1409 */
1601 private boolean isEitherSeriesDegenerate(XYDataset x_dataset, 1410 private boolean isEitherSeriesDegenerate(final XYDataset x_dataset, final boolean x_impliedZeroSubtrahend) {
1602 boolean x_impliedZeroSubtrahend) {
1603 1411
1604 if (x_impliedZeroSubtrahend) { 1412 if (x_impliedZeroSubtrahend) {
1605 return (x_dataset.getItemCount(0) < 2); 1413 return (x_dataset.getItemCount(0) < 2);
1606 } 1414 }
1607 1415
1608 return ((x_dataset.getItemCount(0) < 2) 1416 return ((x_dataset.getItemCount(0) < 2) || (x_dataset.getItemCount(1) < 2));
1609 || (x_dataset.getItemCount(1) < 2));
1610 } 1417 }
1611 1418
1612 /** 1419 /**
1613 * Determines if the two (2) series are disjoint. 1420 * Determines if the two (2) series are disjoint.
1614 * Disjoint series do not overlap in the domain space. 1421 * Disjoint series do not overlap in the domain space.
1615 * 1422 *
1616 * @param x_dataset the dataset. 1423 * @param x_dataset
1424 * the dataset.
1617 * 1425 *
1618 * @return true if the dataset is degenerate. 1426 * @return true if the dataset is degenerate.
1619 */ 1427 */
1620 private boolean areSeriesDisjoint(XYDataset x_dataset) { 1428 private boolean areSeriesDisjoint(final XYDataset x_dataset) {
1621 1429
1622 int l_minuendItemCount = x_dataset.getItemCount(0); 1430 final int l_minuendItemCount = x_dataset.getItemCount(0);
1623 double l_minuendFirst = x_dataset.getXValue(0, 0); 1431 final double l_minuendFirst = x_dataset.getXValue(0, 0);
1624 double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1); 1432 final double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1);
1625 1433
1626 int l_subtrahendItemCount = x_dataset.getItemCount(1); 1434 final int l_subtrahendItemCount = x_dataset.getItemCount(1);
1627 double l_subtrahendFirst = x_dataset.getXValue(1, 0); 1435 final double l_subtrahendFirst = x_dataset.getXValue(1, 0);
1628 double l_subtrahendLast = x_dataset.getXValue(1, 1436 final double l_subtrahendLast = x_dataset.getXValue(1, l_subtrahendItemCount - 1);
1629 l_subtrahendItemCount - 1); 1437
1630 1438 return ((l_minuendLast < l_subtrahendFirst) || (l_subtrahendLast < l_minuendFirst));
1631 return ((l_minuendLast < l_subtrahendFirst) 1439 }
1632 || (l_subtrahendLast < l_minuendFirst)); 1440
1633 } 1441 private void updateCentroid(final Object[] xValues, final Object[] yValues) {
1634
1635
1636 public void updateCentroid(Object [] xValues, Object [] yValues) {
1637 double x = 0d, y = 0d; 1442 double x = 0d, y = 0d;
1638 1443
1639 for (int i = 0, N = xValues.length; i < N; ++i) { 1444 for (int i = 0, N = xValues.length; i < N; ++i) {
1640 x += ((Double)xValues[i]).doubleValue(); 1445 x += ((Double) xValues[i]).doubleValue();
1641 y += ((Double)yValues[i]).doubleValue(); 1446 y += ((Double) yValues[i]).doubleValue();
1642 } 1447 }
1643 1448
1644 x /= xValues.length; 1449 x /= xValues.length;
1645 y /= yValues.length; 1450 y /= yValues.length;
1646 1451
1647 centroidNPoints++; 1452 this.centroidNPoints++;
1648 double factorNew = 1d / centroidNPoints; 1453 final double factorNew = 1d / this.centroidNPoints;
1649 double factorOld = 1d - factorNew; 1454 final double factorOld = 1d - factorNew;
1650 1455
1651 centroid = new Point2D.Double((factorNew * x + factorOld * centroid.x), 1456 this.centroid = new Point2D.Double((factorNew * x + factorOld * this.centroid.x), (factorNew * y + factorOld * this.centroid.y));
1652 (factorNew * y + factorOld * centroid.y)); 1457 }
1653 } 1458
1654 1459 private static double calculateArea(final Object[] xValues, final Object[] yValues) {
1655
1656 public static double calculateArea(Object [] xValues, Object [] yValues) {
1657 double area = 0d; 1460 double area = 0d;
1658 1461
1659 for (int i = 0, N = xValues.length; i < N; ++i) { 1462 for (int i = 0, N = xValues.length; i < N; ++i) {
1660 int k = (i + 1) % N; 1463 final int k = (i + 1) % N;
1661 double xi = ((Double)xValues[i]).doubleValue(); 1464 final double xi = ((Double) xValues[i]).doubleValue();
1662 double yi = ((Double)yValues[i]).doubleValue(); 1465 final double yi = ((Double) yValues[i]).doubleValue();
1663 double xk = ((Double)xValues[k]).doubleValue(); 1466 final double xk = ((Double) xValues[k]).doubleValue();
1664 double yk = ((Double)yValues[k]).doubleValue(); 1467 final double yk = ((Double) yValues[k]).doubleValue();
1665 1468
1666 area += xi*yk; 1469 area += xi * yk;
1667 area -= xk*yi; 1470 area -= xk * yi;
1668 // TODO centroid calculation here? 1471 // TODO centroid calculation here?
1669 } 1472 }
1670 1473
1671 return 0.5d*area; 1474 return 0.5d * area;
1672 } 1475 }
1673 1476
1674 /** 1477 /**
1675 * Draws the visual representation of a polygon 1478 * Draws the visual representation of a polygon
1676 * 1479 *
1677 * @param x_graphics the graphics device. 1480 * @param x_graphics
1678 * @param x_dataArea the area within which the data is being drawn. 1481 * the graphics device.
1679 * @param x_plot the plot (can be used to obtain standard color 1482 * @param x_dataArea
1680 * information etc). 1483 * the area within which the data is being drawn.
1681 * @param x_domainAxis the domain (horizontal) axis. 1484 * @param x_plot
1682 * @param x_rangeAxis the range (vertical) axis. 1485 * the plot (can be used to obtain standard color
1683 * @param x_positive indicates if the polygon is positive (true) or 1486 * information etc).
1684 * negative (false). 1487 * @param x_domainAxis
1685 * @param x_xValues a linked list of the x values (expects values to be 1488 * the domain (horizontal) axis.
1686 * of type Double). 1489 * @param x_rangeAxis
1687 * @param x_yValues a linked list of the y values (expects values to be 1490 * the range (vertical) axis.
1688 * of type Double). 1491 * @param x_positive
1689 */ 1492 * indicates if the polygon is positive (true) or
1690 private void createPolygon (Graphics2D x_graphics, 1493 * negative (false).
1691 Rectangle2D x_dataArea, 1494 * @param x_xValues
1692 XYPlot x_plot, 1495 * a linked list of the x values (expects values to be
1693 ValueAxis x_domainAxis, 1496 * of type Double).
1694 ValueAxis x_rangeAxis, 1497 * @param x_yValues
1695 boolean x_positive, 1498 * a linked list of the y values (expects values to be
1696 LinkedList x_xValues, 1499 * of type Double).
1697 LinkedList x_yValues) { 1500 */
1698 1501 private void createPolygon(final Graphics2D x_graphics, final Rectangle2D x_dataArea, final XYPlot x_plot, final ValueAxis x_domainAxis,
1699 PlotOrientation l_orientation = x_plot.getOrientation(); 1502 final ValueAxis x_rangeAxis, final boolean x_positive, final List<Double> x_xValues, final List<Double> x_yValues) {
1700 RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); 1503
1701 RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); 1504 final PlotOrientation l_orientation = x_plot.getOrientation();
1702 1505 final RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge();
1703 Object[] l_xValues = x_xValues.toArray(); 1506 final RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge();
1704 Object[] l_yValues = x_yValues.toArray(); 1507
1705 1508 final Object[] l_xValues = x_xValues.toArray();
1706 double area = calculateArea(l_xValues, l_yValues); 1509 final Object[] l_yValues = x_yValues.toArray();
1707 if (x_positive) positiveArea += area; 1510
1708 else negativeArea += area; 1511 final double area = calculateArea(l_xValues, l_yValues);
1512 if (x_positive)
1513 this.positiveArea += area;
1514 else
1515 this.negativeArea += area;
1709 updateCentroid(l_xValues, l_yValues); 1516 updateCentroid(l_xValues, l_yValues);
1710 1517
1711 GeneralPath l_path = new GeneralPath(); 1518 final GeneralPath l_path = new GeneralPath();
1712 1519
1713 if (PlotOrientation.VERTICAL == l_orientation) { 1520 if (PlotOrientation.VERTICAL == l_orientation) {
1714 double l_x = x_domainAxis.valueToJava2D(( 1521 double l_x = x_domainAxis.valueToJava2D(((Double) l_xValues[0]).doubleValue(), x_dataArea, l_domainAxisLocation);
1715 (Double) l_xValues[0]).doubleValue(), x_dataArea,
1716 l_domainAxisLocation);
1717 if (this.roundXCoordinates) { 1522 if (this.roundXCoordinates) {
1718 l_x = Math.rint(l_x); 1523 l_x = Math.rint(l_x);
1719 } 1524 }
1720 1525
1721 double l_y = x_rangeAxis.valueToJava2D(( 1526 double l_y = x_rangeAxis.valueToJava2D(((Double) l_yValues[0]).doubleValue(), x_dataArea, l_rangeAxisLocation);
1722 (Double) l_yValues[0]).doubleValue(), x_dataArea,
1723 l_rangeAxisLocation);
1724 1527
1725 l_path.moveTo((float) l_x, (float) l_y); 1528 l_path.moveTo((float) l_x, (float) l_y);
1726 for (int i = 1; i < l_xValues.length; i++) { 1529 for (int i = 1; i < l_xValues.length; i++) {
1727 l_x = x_domainAxis.valueToJava2D(( 1530 l_x = x_domainAxis.valueToJava2D(((Double) l_xValues[i]).doubleValue(), x_dataArea, l_domainAxisLocation);
1728 (Double) l_xValues[i]).doubleValue(), x_dataArea,
1729 l_domainAxisLocation);
1730 if (this.roundXCoordinates) { 1531 if (this.roundXCoordinates) {
1731 l_x = Math.rint(l_x); 1532 l_x = Math.rint(l_x);
1732 } 1533 }
1733 1534
1734 l_y = x_rangeAxis.valueToJava2D(( 1535 l_y = x_rangeAxis.valueToJava2D(((Double) l_yValues[i]).doubleValue(), x_dataArea, l_rangeAxisLocation);
1735 (Double) l_yValues[i]).doubleValue(), x_dataArea,
1736 l_rangeAxisLocation);
1737 l_path.lineTo((float) l_x, (float) l_y); 1536 l_path.lineTo((float) l_x, (float) l_y);
1738 } 1537 }
1739 l_path.closePath(); 1538 l_path.closePath();
1740 } 1539 } else {
1741 else { 1540 double l_x = x_domainAxis.valueToJava2D(((Double) l_xValues[0]).doubleValue(), x_dataArea, l_domainAxisLocation);
1742 double l_x = x_domainAxis.valueToJava2D((
1743 (Double) l_xValues[0]).doubleValue(), x_dataArea,
1744 l_domainAxisLocation);
1745 if (this.roundXCoordinates) { 1541 if (this.roundXCoordinates) {
1746 l_x = Math.rint(l_x); 1542 l_x = Math.rint(l_x);
1747 } 1543 }
1748 1544
1749 double l_y = x_rangeAxis.valueToJava2D(( 1545 double l_y = x_rangeAxis.valueToJava2D(((Double) l_yValues[0]).doubleValue(), x_dataArea, l_rangeAxisLocation);
1750 (Double) l_yValues[0]).doubleValue(), x_dataArea,
1751 l_rangeAxisLocation);
1752 1546
1753 l_path.moveTo((float) l_y, (float) l_x); 1547 l_path.moveTo((float) l_y, (float) l_x);
1754 for (int i = 1; i < l_xValues.length; i++) { 1548 for (int i = 1; i < l_xValues.length; i++) {
1755 l_x = x_domainAxis.valueToJava2D(( 1549 l_x = x_domainAxis.valueToJava2D(((Double) l_xValues[i]).doubleValue(), x_dataArea, l_domainAxisLocation);
1756 (Double) l_xValues[i]).doubleValue(), x_dataArea,
1757 l_domainAxisLocation);
1758 if (this.roundXCoordinates) { 1550 if (this.roundXCoordinates) {
1759 l_x = Math.rint(l_x); 1551 l_x = Math.rint(l_x);
1760 } 1552 }
1761 1553
1762 l_y = x_rangeAxis.valueToJava2D(( 1554 l_y = x_rangeAxis.valueToJava2D(((Double) l_yValues[i]).doubleValue(), x_dataArea, l_rangeAxisLocation);
1763 (Double) l_yValues[i]).doubleValue(), x_dataArea,
1764 l_rangeAxisLocation);
1765 l_path.lineTo((float) l_y, (float) l_x); 1555 l_path.lineTo((float) l_y, (float) l_x);
1766 } 1556 }
1767 l_path.closePath(); 1557 l_path.closePath();
1768 } 1558 }
1769 1559
1770 if (l_path.intersects(x_dataArea)) { 1560 if (l_path.intersects(x_dataArea)) {
1771 1561
1772 final Paint paint = x_positive ? getPositivePaint(): getNegativePaint(); 1562 final Paint paint = x_positive ? getPositivePaint() : getNegativePaint();
1773 if( paint != null ) { 1563 if (this.drawArea && paint != null) {
1774 x_graphics.setPaint(paint); 1564 x_graphics.setPaint(paint);
1775 x_graphics.fill(l_path); 1565 x_graphics.fill(l_path);
1776 } 1566 }
1777 1567
1778 if (drawOutline) { 1568 if (this.drawOutline) {
1779 x_graphics.setStroke(this.outlineStroke); 1569 x_graphics.setStroke(this.outlineStroke);
1780 x_graphics.setPaint(this.outlinePaint); 1570 x_graphics.setPaint(this.outlinePaint);
1781 x_graphics.draw(l_path); 1571 x_graphics.draw(l_path);
1782 } 1572 }
1783 } 1573 }
1784 } 1574 }
1785 1575
1786 /** 1576 /**
1787 * Returns a default legend item for the specified series. Subclasses 1577 * Returns a default legend item for the specified series. Subclasses
1788 * should override this method to generate customised items. 1578 * should override this method to generate customised items.
1789 * 1579 *
1790 * @param datasetIndex the dataset index (zero-based). 1580 * @param datasetIndex
1791 * @param series the series index (zero-based). 1581 * the dataset index (zero-based).
1582 * @param series
1583 * the series index (zero-based).
1792 * 1584 *
1793 * @return A legend item for the series. 1585 * @return A legend item for the series.
1794 */ 1586 */
1795 public LegendItem getLegendItem(int datasetIndex, int series) { 1587 @Override
1796 LegendItem result = null; 1588 public LegendItem getLegendItem(final int datasetIndex, final int series) {
1797 XYPlot p = getPlot(); 1589 final XYPlot p = getPlot();
1798 if (p != null) { 1590 if (p == null)
1799 XYDataset dataset = p.getDataset(datasetIndex); 1591 return null;
1800 if (dataset != null) { 1592
1801 if (getItemVisible(series, 0)) { 1593 final XYDataset dataset = p.getDataset(datasetIndex);
1802 String label = getLegendItemLabelGenerator().generateLabel( 1594 if (dataset == null)
1803 dataset, series); 1595 return null;
1804 String description = label; 1596
1805 String toolTipText = null; 1597 if (!getItemVisible(series, 0))
1806 if (getLegendItemToolTipGenerator() != null) { 1598 return null;
1807 toolTipText 1599
1808 = getLegendItemToolTipGenerator().generateLabel( 1600 final String label = getLegendItemLabelGenerator().generateLabel(dataset, series);
1809 dataset, series); 1601 final String description = label;
1810 } 1602 String toolTipText = null;
1811 String urlText = null; 1603 if (getLegendItemToolTipGenerator() != null) {
1812 if (getLegendItemURLGenerator() != null) { 1604 toolTipText = getLegendItemToolTipGenerator().generateLabel(dataset, series);
1813 urlText = getLegendItemURLGenerator().generateLabel( 1605 }
1814 dataset, series); 1606 String urlText = null;
1815 } 1607 if (getLegendItemURLGenerator() != null) {
1816 // Individualized Paints: 1608 urlText = getLegendItemURLGenerator().generateLabel(dataset, series);
1817 //Paint paint = lookupSeriesPaint(series); 1609 }
1818 1610
1819 // "Area-Style"- Paint. 1611 // "Area-Style"- Paint.
1820 Paint paint = getPositivePaint(); 1612 final Paint paint = getPositivePaint();
1821 Stroke stroke = lookupSeriesStroke(series); 1613 final Shape line = getLegendLine();
1822 Shape line = getLegendLine(); 1614
1823 // Not-filled Shape: 1615 // Filled Shape ("Area-Style").
1824 //result = new LegendItem(label, description, 1616 final BasicStroke NULL_STROKE = new BasicStroke(0.0f);
1825 // toolTipText, urlText, line, stroke, paint); 1617 final Shape NULL_SHAPE = new Line2D.Float();
1826 1618
1827 if (drawOutline) { 1619 final Paint outlPaint = this.drawOutline ? this.outlinePaint : Color.black;
1828 // TODO Include outline style in legenditem 1620 final Stroke outlStroke = this.drawOutline ? this.outlineStroke : NULL_STROKE;
1829 // (there is a constructor for that) 1621
1830 } 1622 final LegendItem result = new LegendItem(label, description, toolTipText, urlText, true, line, this.drawArea, paint, this.drawOutline, outlPaint,
1831 1623 outlStroke, false, NULL_SHAPE, NULL_STROKE, Color.black);
1832 // Filled Shape ("Area-Style"). 1624
1833 result = new LegendItem(label, description, 1625 result.setLabelFont(lookupLegendTextFont(series));
1834 toolTipText, urlText, line, paint); 1626 final Paint labelPaint = lookupLegendTextPaint(series);
1835 result.setLabelFont(lookupLegendTextFont(series)); 1627 if (labelPaint != null)
1836 Paint labelPaint = lookupLegendTextPaint(series); 1628 result.setLabelPaint(labelPaint);
1837 if (labelPaint != null) { 1629 result.setDataset(dataset);
1838 result.setLabelPaint(labelPaint); 1630 result.setDatasetIndex(datasetIndex);
1839 } 1631 result.setSeriesKey(dataset.getSeriesKey(series));
1840 result.setDataset(dataset); 1632 result.setSeriesIndex(series);
1841 result.setDatasetIndex(datasetIndex);
1842 result.setSeriesKey(dataset.getSeriesKey(series));
1843 result.setSeriesIndex(series);
1844 }
1845 }
1846
1847 }
1848 1633
1849 return result; 1634 return result;
1850 } 1635 }
1851 1636
1852 /** 1637 /**
1853 * Tests this renderer for equality with an arbitrary object. 1638 * Tests this renderer for equality with an arbitrary object.
1854 * 1639 *
1855 * @param obj the object (<code>null</code> permitted). 1640 * @param obj
1641 * the object (<code>null</code> permitted).
1856 * 1642 *
1857 * @return A boolean. 1643 * @return A boolean.
1858 */ 1644 */
1859 public boolean equals(Object obj) { 1645 @Override
1646 public boolean equals(final Object obj) {
1860 if (obj == this) { 1647 if (obj == this) {
1861 return true; 1648 return true;
1862 } 1649 }
1863 if (!(obj instanceof StableXYDifferenceRenderer)) { 1650 if (!(obj instanceof StableXYDifferenceRenderer)) {
1864 return false; 1651 return false;
1865 } 1652 }
1866 if (!super.equals(obj)) { 1653 if (!super.equals(obj)) {
1867 return false; 1654 return false;
1868 } 1655 }
1869 StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj; 1656 final StableXYDifferenceRenderer that = (StableXYDifferenceRenderer) obj;
1870 if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) { 1657 if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
1871 return false; 1658 return false;
1872 } 1659 }
1873 if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) { 1660 if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
1874 return false; 1661 return false;
1888 /** 1675 /**
1889 * Returns a clone of the renderer. 1676 * Returns a clone of the renderer.
1890 * 1677 *
1891 * @return A clone. 1678 * @return A clone.
1892 * 1679 *
1893 * @throws CloneNotSupportedException if the renderer cannot be cloned. 1680 * @throws CloneNotSupportedException
1894 */ 1681 * if the renderer cannot be cloned.
1682 */
1683 @Override
1895 public Object clone() throws CloneNotSupportedException { 1684 public Object clone() throws CloneNotSupportedException {
1896 StableXYDifferenceRenderer clone = 1685 final StableXYDifferenceRenderer clone = (StableXYDifferenceRenderer) super.clone();
1897 (StableXYDifferenceRenderer) super.clone();
1898 clone.legendShape = ShapeUtilities.clone(this.legendShape); 1686 clone.legendShape = ShapeUtilities.clone(this.legendShape);
1899 return clone; 1687 return clone;
1900 } 1688 }
1901 1689
1902 /** 1690 /**
1903 * Provides serialization support. 1691 * Provides serialization support.
1904 * 1692 *
1905 * @param stream the output stream. 1693 * @param stream
1906 * 1694 * the output stream.
1907 * @throws IOException if there is an I/O error. 1695 *
1908 */ 1696 * @throws IOException
1909 private void writeObject(ObjectOutputStream stream) throws IOException { 1697 * if there is an I/O error.
1698 */
1699 private void writeObject(final ObjectOutputStream stream) throws IOException {
1910 stream.defaultWriteObject(); 1700 stream.defaultWriteObject();
1911 SerialUtilities.writePaint(this.positivePaint, stream); 1701 SerialUtilities.writePaint(this.positivePaint, stream);
1912 SerialUtilities.writePaint(this.negativePaint, stream); 1702 SerialUtilities.writePaint(this.negativePaint, stream);
1913 SerialUtilities.writeShape(this.legendShape, stream); 1703 SerialUtilities.writeShape(this.legendShape, stream);
1914 } 1704 }
1915 1705
1916 /** 1706 /**
1917 * Provides serialization support. 1707 * Provides serialization support.
1918 * 1708 *
1919 * @param stream the input stream. 1709 * @param stream
1920 * 1710 * the input stream.
1921 * @throws IOException if there is an I/O error. 1711 *
1922 * @throws ClassNotFoundException if there is a classpath problem. 1712 * @throws IOException
1923 */ 1713 * if there is an I/O error.
1924 private void readObject(ObjectInputStream stream) 1714 * @throws ClassNotFoundException
1925 throws IOException, ClassNotFoundException { 1715 * if there is a classpath problem.
1716 */
1717 private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
1926 stream.defaultReadObject(); 1718 stream.defaultReadObject();
1927 this.positivePaint = SerialUtilities.readPaint(stream); 1719 this.positivePaint = SerialUtilities.readPaint(stream);
1928 this.negativePaint = SerialUtilities.readPaint(stream); 1720 this.negativePaint = SerialUtilities.readPaint(stream);
1929 this.legendShape = SerialUtilities.readShape(stream); 1721 this.legendShape = SerialUtilities.readShape(stream);
1930 } 1722 }
1931 } 1723 }
1932 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org