1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors.
6  *
7  * Project Info:  http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
22  * USA.
23  *
24  * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
25  * Other names may be trademarks of their respective owners.]
26  *
27  * ----------------
28  * ContourPlot.java
29  * ----------------
30  * (C) Copyright 2002-2013, by David M. O'Donnell and Contributors.
31  *
32  * Original Author:  David M. O'Donnell;
33  * Contributor(s):   David Gilbert (for Object Refinery Limited);
34  *                   Arnaud Lelievre;
35  *                   Nicolas Brodu;
36  *
37  * Changes
38  * -------
39  * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG);
40  * 14-Jan-2003 : Added crosshair attributes (DG);
41  * 23-Jan-2003 : Removed two constructors (DG);
42  * 21-Mar-2003 : Bug fix 701744 (DG);
43  * 26-Mar-2003 : Implemented Serializable (DG);
44  * 09-Jul-2003 : Changed ColorBar from extending axis classes to enclosing
45  *               them (DG);
46  * 05-Aug-2003 : Applied changes in bug report 780298 (DG);
47  * 08-Sep-2003 : Added internationalization via use of properties
48  *               resourceBundle (RFE 690236) (AL);
49  * 11-Sep-2003 : Cloning support (NB);
50  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
51  * 17-Jan-2004 : Removed references to DefaultContourDataset class, replaced
52  *               with ContourDataset interface (with changes to the interface).
53  *               See bug 741048 (DG);
54  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
55  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
56  * 06-Oct-2004 : Updated for changes in DatasetUtilities class (DG);
57  * 11-Nov-2004 : Renamed zoom methods to match ValueAxisPlot interface (DG);
58  * 25-Nov-2004 : Small update to clone() implementation (DG);
59  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
60  * 05-May-2005 : Updated draw() method parameters (DG);
61  * 16-Jun-2005 : Added default constructor (DG);
62  * 01-Sep-2005 : Moved dataAreaRatio from Plot to here (DG);
63  * ------------- JFREECHART 1.0.x ---------------------------------------------
64  * 31-Jan-2007 : Deprecated (DG);
65  * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by
66  *               Jess Thrysoee (DG);
67  * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG);
68  * 02-Jul-2013 : Fix NB warnings (DG);
69  *
70  */
71 
72 package org.jfree.chart.plot;
73 
74 import java.awt.AlphaComposite;
75 import java.awt.Composite;
76 import java.awt.Graphics2D;
77 import java.awt.Paint;
78 import java.awt.RenderingHints;
79 import java.awt.Shape;
80 import java.awt.Stroke;
81 import java.awt.geom.Ellipse2D;
82 import java.awt.geom.GeneralPath;
83 import java.awt.geom.Line2D;
84 import java.awt.geom.Point2D;
85 import java.awt.geom.Rectangle2D;
86 import java.awt.geom.RectangularShape;
87 import java.beans.PropertyChangeEvent;
88 import java.beans.PropertyChangeListener;
89 import java.io.Serializable;
90 import java.util.Iterator;
91 import java.util.List;
92 import java.util.ResourceBundle;
93 
94 import org.jfree.chart.ClipPath;
95 import org.jfree.chart.annotations.XYAnnotation;
96 import org.jfree.chart.axis.AxisSpace;
97 import org.jfree.chart.axis.ColorBar;
98 import org.jfree.chart.axis.NumberAxis;
99 import org.jfree.chart.axis.ValueAxis;
100 import org.jfree.chart.entity.ContourEntity;
101 import org.jfree.chart.entity.EntityCollection;
102 import org.jfree.chart.event.AxisChangeEvent;
103 import org.jfree.chart.event.PlotChangeEvent;
104 import org.jfree.chart.labels.ContourToolTipGenerator;
105 import org.jfree.chart.labels.StandardContourToolTipGenerator;
106 import org.jfree.chart.renderer.xy.XYBlockRenderer;
107 import org.jfree.chart.urls.XYURLGenerator;
108 import org.jfree.chart.util.ResourceBundleWrapper;
109 import org.jfree.data.Range;
110 import org.jfree.data.contour.ContourDataset;
111 import org.jfree.data.general.DatasetChangeEvent;
112 import org.jfree.data.general.DatasetUtilities;
113 import org.jfree.ui.RectangleEdge;
114 import org.jfree.ui.RectangleInsets;
115 import org.jfree.util.ObjectUtilities;
116 
117 /**
118  * A class for creating shaded contours.
119  *
120  * @deprecated This plot is no longer supported, please use {@link XYPlot} with
121  *     an {@link XYBlockRenderer}.
122  */
123 public class ContourPlot extends Plot implements ContourValuePlot,
124         ValueAxisPlot, PropertyChangeListener, Serializable, Cloneable {
125 
126     /** For serialization. */
127     private static final long serialVersionUID = 7861072556590502247L;
128 
129     /** The default insets. */
130     protected static final RectangleInsets DEFAULT_INSETS
131             = new RectangleInsets(2.0, 2.0, 100.0, 10.0);
132 
133     /** The domain axis (used for the x-values). */
134     private ValueAxis domainAxis;
135 
136     /** The range axis (used for the y-values). */
137     private ValueAxis rangeAxis;
138 
139     /** The dataset. */
140     private ContourDataset dataset;
141 
142     /** The colorbar axis (used for the z-values). */
143     private ColorBar colorBar = null;
144 
145     /** The color bar location. */
146     private RectangleEdge colorBarLocation;
147 
148     /** A flag that controls whether or not a domain crosshair is drawn..*/
149     private boolean domainCrosshairVisible;
150 
151     /** The domain crosshair value. */
152     private double domainCrosshairValue;
153 
154     /** The pen/brush used to draw the crosshair (if any). */
155     private transient Stroke domainCrosshairStroke;
156 
157     /** The color used to draw the crosshair (if any). */
158     private transient Paint domainCrosshairPaint;
159 
160     /**
161      * A flag that controls whether or not the crosshair locks onto actual data
162      * points.
163      */
164     private boolean domainCrosshairLockedOnData = true;
165 
166     /** A flag that controls whether or not a range crosshair is drawn..*/
167     private boolean rangeCrosshairVisible;
168 
169     /** The range crosshair value. */
170     private double rangeCrosshairValue;
171 
172     /** The pen/brush used to draw the crosshair (if any). */
173     private transient Stroke rangeCrosshairStroke;
174 
175     /** The color used to draw the crosshair (if any). */
176     private transient Paint rangeCrosshairPaint;
177 
178     /**
179      * A flag that controls whether or not the crosshair locks onto actual data
180      * points.
181      */
182     private boolean rangeCrosshairLockedOnData = true;
183 
184     /**
185      * Defines dataArea rectangle as the ratio formed from dividing height by
186      * width (of the dataArea).  Modifies plot area calculations.
187      * ratio>0 will attempt to layout the plot so that the
188      * dataArea.height/dataArea.width = ratio.
189      * ratio<0 will attempt to layout the plot so that the
190      * dataArea.height/dataArea.width in plot units (not java2D units as when
191      * ratio>0) = -1.*ratio.
192      */         //dmo
193     private double dataAreaRatio = 0.0;  //zero when the parameter is not set
194 
195     /** A list of markers (optional) for the domain axis. */
196     private List domainMarkers;
197 
198     /** A list of markers (optional) for the range axis. */
199     private List rangeMarkers;
200 
201     /** A list of annotations (optional) for the plot. */
202     private List annotations;
203 
204     /** The tool tip generator. */
205     private ContourToolTipGenerator toolTipGenerator;
206 
207     /** The URL text generator. */
208     private XYURLGenerator urlGenerator;
209 
210     /**
211      * Controls whether data are render as filled rectangles or rendered as
212      * points
213      */
214     private boolean renderAsPoints = false;
215 
216     /**
217      * Size of points rendered when renderAsPoints = true.  Size is relative to
218      * dataArea
219      */
220     private double ptSizePct = 0.05;
221 
222     /** Contains the a ClipPath to "trim" the contours. */
223     private transient ClipPath clipPath = null;
224 
225     /** Set to Paint to represent missing values. */
226     private transient Paint missingPaint = null;
227 
228     /** The resourceBundle for the localization. */
229     protected static ResourceBundle localizationResources
230             = ResourceBundleWrapper.getBundle(
231                     "org.jfree.chart.plot.LocalizationBundle");
232 
233     /**
234      * Creates a new plot with no dataset or axes.
235      */
ContourPlot()236     public ContourPlot() {
237         this(null, null, null, null);
238     }
239 
240     /**
241      * Constructs a contour plot with the specified axes (other attributes take
242      * default values).
243      *
244      * @param dataset  The dataset.
245      * @param domainAxis  The domain axis.
246      * @param rangeAxis  The range axis.
247      * @param colorBar  The z-axis axis.
248     */
ContourPlot(ContourDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, ColorBar colorBar)249     public ContourPlot(ContourDataset dataset,
250                        ValueAxis domainAxis, ValueAxis rangeAxis,
251                        ColorBar colorBar) {
252 
253         super();
254 
255         this.dataset = dataset;
256         if (dataset != null) {
257             dataset.addChangeListener(this);
258         }
259 
260         this.domainAxis = domainAxis;
261         if (domainAxis != null) {
262             domainAxis.setPlot(this);
263             domainAxis.addChangeListener(this);
264         }
265 
266         this.rangeAxis = rangeAxis;
267         if (rangeAxis != null) {
268             rangeAxis.setPlot(this);
269             rangeAxis.addChangeListener(this);
270         }
271 
272         this.colorBar = colorBar;
273         if (colorBar != null) {
274             colorBar.getAxis().setPlot(this);
275             colorBar.getAxis().addChangeListener(this);
276             colorBar.configure(this);
277         }
278         this.colorBarLocation = RectangleEdge.LEFT;
279 
280         this.toolTipGenerator = new StandardContourToolTipGenerator();
281 
282     }
283 
284     /**
285      * Returns the color bar location.
286      *
287      * @return The color bar location.
288      */
getColorBarLocation()289     public RectangleEdge getColorBarLocation() {
290         return this.colorBarLocation;
291     }
292 
293     /**
294      * Sets the color bar location and sends a {@link PlotChangeEvent} to all
295      * registered listeners.
296      *
297      * @param edge  the location.
298      */
setColorBarLocation(RectangleEdge edge)299     public void setColorBarLocation(RectangleEdge edge) {
300         this.colorBarLocation = edge;
301         fireChangeEvent();
302     }
303 
304     /**
305      * Returns the primary dataset for the plot.
306      *
307      * @return The primary dataset (possibly <code>null</code>).
308      */
getDataset()309     public ContourDataset getDataset() {
310         return this.dataset;
311     }
312 
313     /**
314      * Sets the dataset for the plot, replacing the existing dataset if there
315      * is one.
316      *
317      * @param dataset  the dataset (<code>null</code> permitted).
318      */
setDataset(ContourDataset dataset)319     public void setDataset(ContourDataset dataset) {
320 
321         // if there is an existing dataset, remove the plot from the list of
322         // change listeners...
323         ContourDataset existing = this.dataset;
324         if (existing != null) {
325             existing.removeChangeListener(this);
326         }
327 
328         // set the new dataset, and register the chart as a change listener...
329         this.dataset = dataset;
330         if (dataset != null) {
331             setDatasetGroup(dataset.getGroup());
332             dataset.addChangeListener(this);
333         }
334 
335         // send a dataset change event to self...
336         DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
337         datasetChanged(event);
338 
339     }
340 
341     /**
342      * Returns the domain axis for the plot.
343      *
344      * @return The domain axis.
345      */
getDomainAxis()346     public ValueAxis getDomainAxis() {
347 
348         ValueAxis result = this.domainAxis;
349 
350         return result;
351 
352     }
353 
354     /**
355      * Sets the domain axis for the plot (this must be compatible with the plot
356      * type or an exception is thrown).
357      *
358      * @param axis The new axis.
359      */
setDomainAxis(ValueAxis axis)360     public void setDomainAxis(ValueAxis axis) {
361 
362         if (isCompatibleDomainAxis(axis)) {
363 
364             if (axis != null) {
365                 axis.setPlot(this);
366                 axis.addChangeListener(this);
367             }
368 
369             // plot is likely registered as a listener with the existing axis...
370             if (this.domainAxis != null) {
371                 this.domainAxis.removeChangeListener(this);
372             }
373 
374             this.domainAxis = axis;
375             fireChangeEvent();
376 
377         }
378 
379     }
380 
381     /**
382      * Returns the range axis for the plot.
383      *
384      * @return The range axis.
385      */
getRangeAxis()386     public ValueAxis getRangeAxis() {
387 
388         ValueAxis result = this.rangeAxis;
389 
390         return result;
391 
392     }
393 
394     /**
395      * Sets the range axis for the plot.
396      * <P>
397      * An exception is thrown if the new axis and the plot are not mutually
398      * compatible.
399      *
400      * @param axis The new axis (null permitted).
401      */
setRangeAxis(ValueAxis axis)402     public void setRangeAxis(ValueAxis axis) {
403 
404         if (axis != null) {
405             axis.setPlot(this);
406             axis.addChangeListener(this);
407         }
408 
409         // plot is likely registered as a listener with the existing axis...
410         if (this.rangeAxis != null) {
411             this.rangeAxis.removeChangeListener(this);
412         }
413 
414         this.rangeAxis = axis;
415         fireChangeEvent();
416 
417     }
418 
419     /**
420      * Sets the colorbar for the plot.
421      *
422      * @param axis The new axis (null permitted).
423      */
setColorBarAxis(ColorBar axis)424     public void setColorBarAxis(ColorBar axis) {
425 
426         this.colorBar = axis;
427         fireChangeEvent();
428 
429     }
430 
431     /**
432      * Returns the data area ratio.
433      *
434      * @return The ratio.
435      */
getDataAreaRatio()436     public double getDataAreaRatio() {
437         return this.dataAreaRatio;
438     }
439 
440     /**
441      * Sets the data area ratio.
442      *
443      * @param ratio  the ratio.
444      */
setDataAreaRatio(double ratio)445     public void setDataAreaRatio(double ratio) {
446         this.dataAreaRatio = ratio;
447     }
448 
449     /**
450      * Adds a marker for the domain axis.
451      * <P>
452      * Typically a marker will be drawn by the renderer as a line perpendicular
453      * to the range axis, however this is entirely up to the renderer.
454      *
455      * @param marker the marker.
456      */
addDomainMarker(Marker marker)457     public void addDomainMarker(Marker marker) {
458 
459         if (this.domainMarkers == null) {
460             this.domainMarkers = new java.util.ArrayList();
461         }
462         this.domainMarkers.add(marker);
463         fireChangeEvent();
464 
465     }
466 
467     /**
468      * Clears all the domain markers.
469      */
clearDomainMarkers()470     public void clearDomainMarkers() {
471         if (this.domainMarkers != null) {
472             this.domainMarkers.clear();
473             fireChangeEvent();
474         }
475     }
476 
477     /**
478      * Adds a marker for the range axis.
479      * <P>
480      * Typically a marker will be drawn by the renderer as a line perpendicular
481      * to the range axis, however this is entirely up to the renderer.
482      *
483      * @param marker The marker.
484      */
addRangeMarker(Marker marker)485     public void addRangeMarker(Marker marker) {
486 
487         if (this.rangeMarkers == null) {
488             this.rangeMarkers = new java.util.ArrayList();
489         }
490         this.rangeMarkers.add(marker);
491         fireChangeEvent();
492 
493     }
494 
495     /**
496      * Clears all the range markers.
497      */
clearRangeMarkers()498     public void clearRangeMarkers() {
499         if (this.rangeMarkers != null) {
500             this.rangeMarkers.clear();
501             fireChangeEvent();
502         }
503     }
504 
505     /**
506      * Adds an annotation to the plot.
507      *
508      * @param annotation  the annotation.
509      */
addAnnotation(XYAnnotation annotation)510     public void addAnnotation(XYAnnotation annotation) {
511 
512         if (this.annotations == null) {
513             this.annotations = new java.util.ArrayList();
514         }
515         this.annotations.add(annotation);
516         fireChangeEvent();
517 
518     }
519 
520     /**
521      * Clears all the annotations.
522      */
clearAnnotations()523     public void clearAnnotations() {
524         if (this.annotations != null) {
525             this.annotations.clear();
526             fireChangeEvent();
527         }
528     }
529 
530     /**
531      * Checks the compatibility of a domain axis, returning true if the axis is
532      * compatible with the plot, and false otherwise.
533      *
534      * @param axis The proposed axis.
535      *
536      * @return <code>true</code> if the axis is compatible with the plot.
537      */
isCompatibleDomainAxis(ValueAxis axis)538     public boolean isCompatibleDomainAxis(ValueAxis axis) {
539 
540         return true;
541 
542     }
543 
544     /**
545      * Draws the plot on a Java 2D graphics device (such as the screen or a
546      * printer).
547      * <P>
548      * The optional <code>info</code> argument collects information about the
549      * rendering of the plot (dimensions, tooltip information etc).  Just pass
550      * in <code>null</code> if you do not need this information.
551      *
552      * @param g2  the graphics device.
553      * @param area  the area within which the plot (including axis labels)
554      *              should be drawn.
555      * @param anchor  the anchor point (<code>null</code> permitted).
556      * @param parentState  the state from the parent plot, if there is one.
557      * @param info  collects chart drawing information (<code>null</code>
558      *              permitted).
559      */
560     @Override
draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info)561     public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
562                      PlotState parentState, PlotRenderingInfo info) {
563 
564         // if the plot area is too small, just return...
565         boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
566         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
567         if (b1 || b2) {
568             return;
569         }
570 
571         // record the plot area...
572         if (info != null) {
573             info.setPlotArea(area);
574         }
575 
576         // adjust the drawing area for plot insets (if any)...
577         RectangleInsets insets = getInsets();
578         insets.trim(area);
579 
580         AxisSpace space = new AxisSpace();
581 
582         space = this.domainAxis.reserveSpace(g2, this, area,
583                 RectangleEdge.BOTTOM, space);
584         space = this.rangeAxis.reserveSpace(g2, this, area,
585                 RectangleEdge.LEFT, space);
586 
587         Rectangle2D estimatedDataArea = space.shrink(area, null);
588 
589         AxisSpace space2 = new AxisSpace();
590         space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea,
591                 this.colorBarLocation, space2);
592         Rectangle2D adjustedPlotArea = space2.shrink(area, null);
593 
594         Rectangle2D dataArea = space.shrink(adjustedPlotArea, null);
595 
596         Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation);
597 
598         // additional dataArea modifications
599         if (getDataAreaRatio() != 0.0) { //check whether modification is
600             double ratio = getDataAreaRatio();
601             Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone();
602             double h = tmpDataArea.getHeight();
603             double w = tmpDataArea.getWidth();
604 
605             if (ratio > 0) { // ratio represents pixels
606                 if (w * ratio <= h) {
607                     h = ratio * w;
608                 }
609                 else {
610                     w = h / ratio;
611                 }
612             }
613             else {  // ratio represents axis units
614                 ratio *= -1.0;
615                 double xLength = getDomainAxis().getRange().getLength();
616                 double yLength = getRangeAxis().getRange().getLength();
617                 double unitRatio = yLength / xLength;
618 
619                 ratio = unitRatio * ratio;
620 
621                 if (w * ratio <= h) {
622                     h = ratio * w;
623                 }
624                 else {
625                     w = h / ratio;
626                 }
627             }
628 
629             dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2
630                     - w / 2, tmpDataArea.getY(), w, h);
631         }
632 
633         if (info != null) {
634             info.setDataArea(dataArea);
635         }
636 
637         CrosshairState crosshairState = new CrosshairState();
638         crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
639 
640         // draw the plot background...
641         drawBackground(g2, dataArea);
642 
643         double cursor = dataArea.getMaxY();
644         if (this.domainAxis != null) {
645             this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea,
646                     RectangleEdge.BOTTOM, info);
647         }
648 
649         if (this.rangeAxis != null) {
650             cursor = dataArea.getMinX();
651             this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea,
652                     RectangleEdge.LEFT, info);
653         }
654 
655         if (this.colorBar != null) {
656             cursor = 0.0;
657             this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea,
658                     colorBarArea, this.colorBarLocation);
659         }
660         Shape originalClip = g2.getClip();
661         Composite originalComposite = g2.getComposite();
662 
663         g2.clip(dataArea);
664         g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
665                 getForegroundAlpha()));
666         render(g2, dataArea, info, crosshairState);
667 
668         if (this.domainMarkers != null) {
669             Iterator iterator = this.domainMarkers.iterator();
670             while (iterator.hasNext()) {
671                 Marker marker = (Marker) iterator.next();
672                 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea);
673             }
674         }
675 
676         if (this.rangeMarkers != null) {
677             Iterator iterator = this.rangeMarkers.iterator();
678             while (iterator.hasNext()) {
679                 Marker marker = (Marker) iterator.next();
680                 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea);
681             }
682         }
683 
684 // TO DO:  these annotations only work with XYPlot, see if it is possible to
685 // make ContourPlot a subclass of XYPlot (DG);
686 
687 //        // draw the annotations...
688 //        if (this.annotations != null) {
689 //            Iterator iterator = this.annotations.iterator();
690 //            while (iterator.hasNext()) {
691 //                Annotation annotation = (Annotation) iterator.next();
692 //                if (annotation instanceof XYAnnotation) {
693 //                    XYAnnotation xya = (XYAnnotation) annotation;
694 //                    // get the annotation to draw itself...
695 //                    xya.draw(g2, this, dataArea, getDomainAxis(),
696 //                             getRangeAxis());
697 //                }
698 //            }
699 //        }
700 
701         g2.setClip(originalClip);
702         g2.setComposite(originalComposite);
703         drawOutline(g2, dataArea);
704 
705     }
706 
707     /**
708      * Draws a representation of the data within the dataArea region, using the
709      * current renderer.
710      * <P>
711      * The <code>info</code> and <code>crosshairState</code> arguments may be
712      * <code>null</code>.
713      *
714      * @param g2  the graphics device.
715      * @param dataArea  the region in which the data is to be drawn.
716      * @param info  an optional object for collection dimension information.
717      * @param crosshairState  an optional object for collecting crosshair info.
718      */
render(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, CrosshairState crosshairState)719     public void render(Graphics2D g2, Rectangle2D dataArea,
720                        PlotRenderingInfo info, CrosshairState crosshairState) {
721 
722         // now get the data and plot it (the visual representation will depend
723         // on the renderer that has been set)...
724         ContourDataset data = getDataset();
725         if (data != null) {
726 
727             ColorBar zAxis = getColorBar();
728 
729             if (this.clipPath != null) {
730                 GeneralPath clipper = getClipPath().draw(g2, dataArea,
731                         this.domainAxis, this.rangeAxis);
732                 if (this.clipPath.isClip()) {
733                     g2.clip(clipper);
734                 }
735             }
736 
737             if (this.renderAsPoints) {
738                 pointRenderer(g2, dataArea, info, this, this.domainAxis,
739                         this.rangeAxis, zAxis, data, crosshairState);
740             }
741             else {
742                 contourRenderer(g2, dataArea, info, this, this.domainAxis,
743                         this.rangeAxis, zAxis, data, crosshairState);
744             }
745 
746             // draw vertical crosshair if required...
747             setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
748             if (isDomainCrosshairVisible()) {
749                 drawVerticalLine(g2, dataArea,
750                                  getDomainCrosshairValue(),
751                                  getDomainCrosshairStroke(),
752                                  getDomainCrosshairPaint());
753             }
754 
755             // draw horizontal crosshair if required...
756             setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
757             if (isRangeCrosshairVisible()) {
758                 drawHorizontalLine(g2, dataArea,
759                                    getRangeCrosshairValue(),
760                                    getRangeCrosshairStroke(),
761                                    getRangeCrosshairPaint());
762             }
763 
764         }
765         else if (this.clipPath != null) {
766             getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis);
767         }
768 
769     }
770 
771     /**
772      * Fills the plot.
773      *
774      * @param g2  the graphics device.
775      * @param dataArea  the area within which the data is being drawn.
776      * @param info  collects information about the drawing.
777      * @param plot  the plot (can be used to obtain standard color
778      *              information etc).
779      * @param horizontalAxis  the domain (horizontal) axis.
780      * @param verticalAxis  the range (vertical) axis.
781      * @param colorBar  the color bar axis.
782      * @param data  the dataset.
783      * @param crosshairState  information about crosshairs on a plot.
784      */
contourRenderer(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, ContourPlot plot, ValueAxis horizontalAxis, ValueAxis verticalAxis, ColorBar colorBar, ContourDataset data, CrosshairState crosshairState)785     public void contourRenderer(Graphics2D g2,
786                                 Rectangle2D dataArea,
787                                 PlotRenderingInfo info,
788                                 ContourPlot plot,
789                                 ValueAxis horizontalAxis,
790                                 ValueAxis verticalAxis,
791                                 ColorBar colorBar,
792                                 ContourDataset data,
793                                 CrosshairState crosshairState) {
794 
795         // setup for collecting optional entity info...
796         Rectangle2D.Double entityArea;
797         EntityCollection entities = null;
798         if (info != null) {
799             entities = info.getOwner().getEntityCollection();
800         }
801 
802         Rectangle2D.Double rect;
803         rect = new Rectangle2D.Double();
804 
805         //turn off anti-aliasing when filling rectangles
806         Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
807         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
808                 RenderingHints.VALUE_ANTIALIAS_OFF);
809 
810         // get the data points
811         Number[] xNumber = data.getXValues();
812         Number[] yNumber = data.getYValues();
813         Number[] zNumber = data.getZValues();
814 
815         double[] x = new double[xNumber.length];
816         double[] y = new double[yNumber.length];
817 
818         for (int i = 0; i < x.length; i++) {
819             x[i] = xNumber[i].doubleValue();
820             y[i] = yNumber[i].doubleValue();
821         }
822 
823         int[] xIndex = data.indexX();
824         int[] indexX = data.getXIndices();
825         boolean vertInverted = ((NumberAxis) verticalAxis).isInverted();
826         boolean horizInverted = false;
827         if (horizontalAxis instanceof NumberAxis) {
828             horizInverted = ((NumberAxis) horizontalAxis).isInverted();
829         }
830         double transX = 0.0;
831         double transXm1;
832         double transXp1;
833         double transDXm1;
834         double transDXp1 = 0.0;
835         double transDX = 0.0;
836         double transY;
837         double transYm1;
838         double transYp1;
839         double transDYm1;
840         double transDYp1 = 0.0;
841         double transDY;
842         int iMax = xIndex[xIndex.length - 1];
843         for (int k = 0; k < x.length; k++) {
844             int i = xIndex[k];
845             if (indexX[i] == k) { // this is a new column
846                 if (i == 0) {
847                     transX = horizontalAxis.valueToJava2D(x[k], dataArea,
848                             RectangleEdge.BOTTOM);
849                     transXm1 = transX;
850                     transXp1 = horizontalAxis.valueToJava2D(
851                             x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM);
852                     transDXm1 = Math.abs(0.5 * (transX - transXm1));
853                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
854                 }
855                 else if (i == iMax) {
856                     transX = horizontalAxis.valueToJava2D(x[k], dataArea,
857                             RectangleEdge.BOTTOM);
858                     transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]],
859                             dataArea, RectangleEdge.BOTTOM);
860                     transXp1 = transX;
861                     transDXm1 = Math.abs(0.5 * (transX - transXm1));
862                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
863                 }
864                 else {
865                     transX = horizontalAxis.valueToJava2D(x[k], dataArea,
866                             RectangleEdge.BOTTOM);
867                     transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]],
868                             dataArea, RectangleEdge.BOTTOM);
869                     transDXm1 = transDXp1;
870                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
871                 }
872 
873                 if (horizInverted) {
874                     transX -= transDXp1;
875                 }
876                 else {
877                     transX -= transDXm1;
878                 }
879 
880                 transDX = transDXm1 + transDXp1;
881 
882                 transY = verticalAxis.valueToJava2D(y[k], dataArea,
883                         RectangleEdge.LEFT);
884                 transYm1 = transY;
885                 if (k + 1 == y.length) {
886                     continue;
887                 }
888                 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea,
889                         RectangleEdge.LEFT);
890                 transDYm1 = Math.abs(0.5 * (transY - transYm1));
891                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
892             }
893             else if ((i < indexX.length - 1
894                      && indexX[i + 1] - 1 == k) || k == x.length - 1) {
895                 // end of column
896                 transY = verticalAxis.valueToJava2D(y[k], dataArea,
897                         RectangleEdge.LEFT);
898                 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea,
899                         RectangleEdge.LEFT);
900                 transYp1 = transY;
901                 transDYm1 = Math.abs(0.5 * (transY - transYm1));
902                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
903             }
904             else {
905                 transY = verticalAxis.valueToJava2D(y[k], dataArea,
906                         RectangleEdge.LEFT);
907                 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea,
908                         RectangleEdge.LEFT);
909                 transDYm1 = transDYp1;
910                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
911             }
912             if (vertInverted) {
913                 transY -= transDYm1;
914             }
915             else {
916                 transY -= transDYp1;
917             }
918 
919             transDY = transDYm1 + transDYp1;
920 
921             rect.setRect(transX, transY, transDX, transDY);
922             if (zNumber[k] != null) {
923                 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
924                 g2.fill(rect);
925             }
926             else if (this.missingPaint != null) {
927                 g2.setPaint(this.missingPaint);
928                 g2.fill(rect);
929             }
930 
931             entityArea = rect;
932 
933             // add an entity for the item...
934             if (entities != null) {
935                 String tip = "";
936                 if (getToolTipGenerator() != null) {
937                     tip = this.toolTipGenerator.generateToolTip(data, k);
938                 }
939 //              Shape s = g2.getClip();
940 //              if (s.contains(rect) || s.intersects(rect)) {
941                 String url = null;
942                 // if (getURLGenerator() != null) {    //dmo: look at this later
943                 //      url = getURLGenerator().generateURL(data, series, item);
944                 // }
945                 // Unlike XYItemRenderer, we need to clone entityArea since it
946                 // reused.
947                 ContourEntity entity = new ContourEntity(
948                         (Rectangle2D.Double) entityArea.clone(), tip, url);
949                 entity.setIndex(k);
950                 entities.add(entity);
951 //              }
952             }
953 
954             // do we need to update the crosshair values?
955             if (plot.isDomainCrosshairLockedOnData()) {
956                 if (plot.isRangeCrosshairLockedOnData()) {
957                     // both axes
958                     crosshairState.updateCrosshairPoint(x[k], y[k], transX,
959                             transY, PlotOrientation.VERTICAL);
960                 }
961                 else {
962                     // just the horizontal axis...
963                     crosshairState.updateCrosshairX(transX);
964                 }
965             }
966             else {
967                 if (plot.isRangeCrosshairLockedOnData()) {
968                     // just the vertical axis...
969                     crosshairState.updateCrosshairY(transY);
970                 }
971             }
972         }
973 
974         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
975 
976     }
977 
978     /**
979      * Draws the visual representation of a single data item.
980      *
981      * @param g2  the graphics device.
982      * @param dataArea  the area within which the data is being drawn.
983      * @param info  collects information about the drawing.
984      * @param plot  the plot (can be used to obtain standard color
985      *              information etc).
986      * @param domainAxis  the domain (horizontal) axis.
987      * @param rangeAxis  the range (vertical) axis.
988      * @param colorBar  the color bar axis.
989      * @param data  the dataset.
990      * @param crosshairState  information about crosshairs on a plot.
991      */
pointRenderer(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, ContourPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, ColorBar colorBar, ContourDataset data, CrosshairState crosshairState)992     public void pointRenderer(Graphics2D g2,
993                               Rectangle2D dataArea,
994                               PlotRenderingInfo info,
995                               ContourPlot plot,
996                               ValueAxis domainAxis,
997                               ValueAxis rangeAxis,
998                               ColorBar colorBar,
999                               ContourDataset data,
1000                               CrosshairState crosshairState) {
1001 
1002         // setup for collecting optional entity info...
1003         RectangularShape entityArea;
1004         EntityCollection entities = null;
1005         if (info != null) {
1006             entities = info.getOwner().getEntityCollection();
1007         }
1008 
1009 //      Rectangle2D.Double rect = null;
1010 //      rect = new Rectangle2D.Double();
1011         RectangularShape rect = new Ellipse2D.Double();
1012 
1013 
1014         //turn off anti-aliasing when filling rectangles
1015         Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
1016         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1017                 RenderingHints.VALUE_ANTIALIAS_OFF);
1018 
1019         // if (tooltips!=null) tooltips.clearToolTips(); // reset collection
1020         // get the data points
1021         Number[] xNumber = data.getXValues();
1022         Number[] yNumber = data.getYValues();
1023         Number[] zNumber = data.getZValues();
1024 
1025         double[] x = new double[xNumber.length];
1026         double[] y = new double[yNumber.length];
1027 
1028         for (int i = 0; i < x.length; i++) {
1029             x[i] = xNumber[i].doubleValue();
1030             y[i] = yNumber[i].doubleValue();
1031         }
1032 
1033         double transX;
1034         double transDX;
1035         double transY;
1036         double transDY;
1037         double size = dataArea.getWidth() * this.ptSizePct;
1038         for (int k = 0; k < x.length; k++) {
1039 
1040             transX = domainAxis.valueToJava2D(x[k], dataArea,
1041                     RectangleEdge.BOTTOM) - 0.5 * size;
1042             transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT)
1043                      - 0.5 * size;
1044             transDX = size;
1045             transDY = size;
1046 
1047             rect.setFrame(transX, transY, transDX, transDY);
1048 
1049             if (zNumber[k] != null) {
1050                 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
1051                 g2.fill(rect);
1052             }
1053             else if (this.missingPaint != null) {
1054                 g2.setPaint(this.missingPaint);
1055                 g2.fill(rect);
1056             }
1057 
1058 
1059             entityArea = rect;
1060 
1061             // add an entity for the item...
1062             if (entities != null) {
1063                 String tip = null;
1064                 if (getToolTipGenerator() != null) {
1065                     tip = this.toolTipGenerator.generateToolTip(data, k);
1066                 }
1067                 String url = null;
1068                 // if (getURLGenerator() != null) {   //dmo: look at this later
1069                 //   url = getURLGenerator().generateURL(data, series, item);
1070                 // }
1071                 // Unlike XYItemRenderer, we need to clone entityArea since it
1072                 // reused.
1073                 ContourEntity entity = new ContourEntity(
1074                         (RectangularShape) entityArea.clone(), tip, url);
1075                 entity.setIndex(k);
1076                 entities.add(entity);
1077             }
1078 
1079             // do we need to update the crosshair values?
1080             if (plot.isDomainCrosshairLockedOnData()) {
1081                 if (plot.isRangeCrosshairLockedOnData()) {
1082                     // both axes
1083                     crosshairState.updateCrosshairPoint(x[k], y[k], transX,
1084                             transY, PlotOrientation.VERTICAL);
1085                 }
1086                 else {
1087                     // just the horizontal axis...
1088                     crosshairState.updateCrosshairX(transX);
1089                 }
1090             }
1091             else {
1092                 if (plot.isRangeCrosshairLockedOnData()) {
1093                     // just the vertical axis...
1094                     crosshairState.updateCrosshairY(transY);
1095                 }
1096             }
1097         }
1098 
1099 
1100         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
1101 
1102     }
1103 
1104     /**
1105      * Utility method for drawing a crosshair on the chart (if required).
1106      *
1107      * @param g2  The graphics device.
1108      * @param dataArea  The data area.
1109      * @param value  The coordinate, where to draw the line.
1110      * @param stroke  The stroke to use.
1111      * @param paint  The paint to use.
1112      */
drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint)1113     protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea,
1114                                     double value, Stroke stroke, Paint paint) {
1115 
1116         double xx = getDomainAxis().valueToJava2D(value, dataArea,
1117                 RectangleEdge.BOTTOM);
1118         Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx,
1119                 dataArea.getMaxY());
1120         g2.setStroke(stroke);
1121         g2.setPaint(paint);
1122         g2.draw(line);
1123 
1124     }
1125 
1126     /**
1127      * Utility method for drawing a crosshair on the chart (if required).
1128      *
1129      * @param g2  The graphics device.
1130      * @param dataArea  The data area.
1131      * @param value  The coordinate, where to draw the line.
1132      * @param stroke  The stroke to use.
1133      * @param paint  The paint to use.
1134      */
drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint)1135     protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea,
1136                                       double value, Stroke stroke,
1137                                       Paint paint) {
1138 
1139         double yy = getRangeAxis().valueToJava2D(value, dataArea,
1140                 RectangleEdge.LEFT);
1141         Line2D line = new Line2D.Double(dataArea.getMinX(), yy,
1142                 dataArea.getMaxX(), yy);
1143         g2.setStroke(stroke);
1144         g2.setPaint(paint);
1145         g2.draw(line);
1146 
1147     }
1148 
1149     /**
1150      * Handles a 'click' on the plot by updating the anchor values...
1151      *
1152      * @param x  x-coordinate, where the click occured.
1153      * @param y  y-coordinate, where the click occured.
1154      * @param info  An object for collection dimension information.
1155      */
1156     @Override
handleClick(int x, int y, PlotRenderingInfo info)1157     public void handleClick(int x, int y, PlotRenderingInfo info) {
1158 
1159 /*        // set the anchor value for the horizontal axis...
1160         ValueAxis hva = getDomainAxis();
1161         if (hva != null) {
1162             double hvalue = hva.translateJava2DtoValue(
1163                 (float) x, info.getDataArea()
1164             );
1165 
1166             hva.setAnchorValue(hvalue);
1167             setDomainCrosshairValue(hvalue);
1168         }
1169 
1170         // set the anchor value for the vertical axis...
1171         ValueAxis vva = getRangeAxis();
1172         if (vva != null) {
1173             double vvalue = vva.translateJava2DtoValue(
1174                 (float) y, info.getDataArea()
1175             );
1176             vva.setAnchorValue(vvalue);
1177             setRangeCrosshairValue(vvalue);
1178         }
1179 */
1180     }
1181 
1182     /**
1183      * Zooms the axis ranges by the specified percentage about the anchor point.
1184      *
1185      * @param percent  The amount of the zoom.
1186      */
1187     @Override
zoom(double percent)1188     public void zoom(double percent) {
1189 
1190         if (percent > 0) {
1191           //  double range = this.domainAxis.getRange().getLength();
1192           //  double scaledRange = range * percent;
1193           //  domainAxis.setAnchoredRange(scaledRange);
1194 
1195           //  range = this.rangeAxis.getRange().getLength();
1196          //  scaledRange = range * percent;
1197          //   rangeAxis.setAnchoredRange(scaledRange);
1198         }
1199         else {
1200             getRangeAxis().setAutoRange(true);
1201             getDomainAxis().setAutoRange(true);
1202         }
1203 
1204     }
1205 
1206     /**
1207      * Returns the plot type as a string.
1208      *
1209      * @return A short string describing the type of plot.
1210      */
1211     @Override
getPlotType()1212     public String getPlotType() {
1213         return localizationResources.getString("Contour_Plot");
1214     }
1215 
1216     /**
1217      * Returns the range for an axis.
1218      *
1219      * @param axis  the axis.
1220      *
1221      * @return The range for an axis.
1222      */
1223     @Override
getDataRange(ValueAxis axis)1224     public Range getDataRange(ValueAxis axis) {
1225 
1226         if (this.dataset == null) {
1227             return null;
1228         }
1229 
1230         Range result = null;
1231 
1232         if (axis == getDomainAxis()) {
1233             result = DatasetUtilities.findDomainBounds(this.dataset);
1234         }
1235         else if (axis == getRangeAxis()) {
1236             result = DatasetUtilities.findRangeBounds(this.dataset);
1237         }
1238         return result;
1239     }
1240 
1241     /**
1242      * Returns the range for the Contours.
1243      *
1244      * @return The range for the Contours (z-axis).
1245      */
1246     @Override
getContourDataRange()1247     public Range getContourDataRange() {
1248         Range result = null;
1249         ContourDataset data = getDataset();
1250         if (data != null) {
1251             Range h = getDomainAxis().getRange();
1252             Range v = getRangeAxis().getRange();
1253             result = this.visibleRange(data, h, v);
1254         }
1255         return result;
1256     }
1257 
1258     /**
1259      * Notifies all registered listeners of a property change.
1260      * <P>
1261      * One source of property change events is the plot's renderer.
1262      *
1263      * @param event  Information about the property change.
1264      */
1265     @Override
propertyChange(PropertyChangeEvent event)1266     public void propertyChange(PropertyChangeEvent event) {
1267         fireChangeEvent();
1268     }
1269 
1270     /**
1271      * Receives notification of a change to the plot's dataset.
1272      * <P>
1273      * The chart reacts by passing on a chart change event to all registered
1274      * listeners.
1275      *
1276      * @param event  Information about the event (not used here).
1277      */
1278     @Override
datasetChanged(DatasetChangeEvent event)1279     public void datasetChanged(DatasetChangeEvent event) {
1280         if (this.domainAxis != null) {
1281             this.domainAxis.configure();
1282         }
1283         if (this.rangeAxis != null) {
1284             this.rangeAxis.configure();
1285         }
1286         if (this.colorBar != null) {
1287             this.colorBar.configure(this);
1288         }
1289         super.datasetChanged(event);
1290     }
1291 
1292     /**
1293      * Returns the colorbar.
1294      *
1295      * @return The colorbar.
1296      */
getColorBar()1297     public ColorBar getColorBar() {
1298         return this.colorBar;
1299     }
1300 
1301     /**
1302      * Returns a flag indicating whether or not the domain crosshair is visible.
1303      *
1304      * @return The flag.
1305      */
isDomainCrosshairVisible()1306     public boolean isDomainCrosshairVisible() {
1307         return this.domainCrosshairVisible;
1308     }
1309 
1310     /**
1311      * Sets the flag indicating whether or not the domain crosshair is visible.
1312      *
1313      * @param flag  the new value of the flag.
1314      */
setDomainCrosshairVisible(boolean flag)1315     public void setDomainCrosshairVisible(boolean flag) {
1316 
1317         if (this.domainCrosshairVisible != flag) {
1318             this.domainCrosshairVisible = flag;
1319             fireChangeEvent();
1320         }
1321 
1322     }
1323 
1324     /**
1325      * Returns a flag indicating whether or not the crosshair should "lock-on"
1326      * to actual data values.
1327      *
1328      * @return The flag.
1329      */
isDomainCrosshairLockedOnData()1330     public boolean isDomainCrosshairLockedOnData() {
1331         return this.domainCrosshairLockedOnData;
1332     }
1333 
1334     /**
1335      * Sets the flag indicating whether or not the domain crosshair should
1336      * "lock-on" to actual data values.
1337      *
1338      * @param flag  the flag.
1339      */
setDomainCrosshairLockedOnData(boolean flag)1340     public void setDomainCrosshairLockedOnData(boolean flag) {
1341         if (this.domainCrosshairLockedOnData != flag) {
1342             this.domainCrosshairLockedOnData = flag;
1343             fireChangeEvent();
1344         }
1345     }
1346 
1347     /**
1348      * Returns the domain crosshair value.
1349      *
1350      * @return The value.
1351      */
getDomainCrosshairValue()1352     public double getDomainCrosshairValue() {
1353         return this.domainCrosshairValue;
1354     }
1355 
1356     /**
1357      * Sets the domain crosshair value.
1358      * <P>
1359      * Registered listeners are notified that the plot has been modified, but
1360      * only if the crosshair is visible.
1361      *
1362      * @param value  the new value.
1363      */
setDomainCrosshairValue(double value)1364     public void setDomainCrosshairValue(double value) {
1365         setDomainCrosshairValue(value, true);
1366     }
1367 
1368     /**
1369      * Sets the domain crosshair value.
1370      * <P>
1371      * Registered listeners are notified that the axis has been modified, but
1372      * only if the crosshair is visible.
1373      *
1374      * @param value  the new value.
1375      * @param notify  a flag that controls whether or not listeners are
1376      *                notified.
1377      */
setDomainCrosshairValue(double value, boolean notify)1378     public void setDomainCrosshairValue(double value, boolean notify) {
1379         this.domainCrosshairValue = value;
1380         if (isDomainCrosshairVisible() && notify) {
1381             fireChangeEvent();
1382         }
1383     }
1384 
1385     /**
1386      * Returns the Stroke used to draw the crosshair (if visible).
1387      *
1388      * @return The crosshair stroke.
1389      */
getDomainCrosshairStroke()1390     public Stroke getDomainCrosshairStroke() {
1391         return this.domainCrosshairStroke;
1392     }
1393 
1394     /**
1395      * Sets the Stroke used to draw the crosshairs (if visible) and notifies
1396      * registered listeners that the axis has been modified.
1397      *
1398      * @param stroke  the new crosshair stroke.
1399      */
setDomainCrosshairStroke(Stroke stroke)1400     public void setDomainCrosshairStroke(Stroke stroke) {
1401         this.domainCrosshairStroke = stroke;
1402         fireChangeEvent();
1403     }
1404 
1405     /**
1406      * Returns the domain crosshair color.
1407      *
1408      * @return The crosshair color.
1409      */
getDomainCrosshairPaint()1410     public Paint getDomainCrosshairPaint() {
1411         return this.domainCrosshairPaint;
1412     }
1413 
1414     /**
1415      * Sets the Paint used to color the crosshairs (if visible) and notifies
1416      * registered listeners that the axis has been modified.
1417      *
1418      * @param paint the new crosshair paint.
1419      */
setDomainCrosshairPaint(Paint paint)1420     public void setDomainCrosshairPaint(Paint paint) {
1421         this.domainCrosshairPaint = paint;
1422         fireChangeEvent();
1423     }
1424 
1425     /**
1426      * Returns a flag indicating whether or not the range crosshair is visible.
1427      *
1428      * @return The flag.
1429      */
isRangeCrosshairVisible()1430     public boolean isRangeCrosshairVisible() {
1431         return this.rangeCrosshairVisible;
1432     }
1433 
1434     /**
1435      * Sets the flag indicating whether or not the range crosshair is visible.
1436      *
1437      * @param flag  the new value of the flag.
1438      */
setRangeCrosshairVisible(boolean flag)1439     public void setRangeCrosshairVisible(boolean flag) {
1440         if (this.rangeCrosshairVisible != flag) {
1441             this.rangeCrosshairVisible = flag;
1442             fireChangeEvent();
1443         }
1444     }
1445 
1446     /**
1447      * Returns a flag indicating whether or not the crosshair should "lock-on"
1448      * to actual data values.
1449      *
1450      * @return The flag.
1451      */
isRangeCrosshairLockedOnData()1452     public boolean isRangeCrosshairLockedOnData() {
1453         return this.rangeCrosshairLockedOnData;
1454     }
1455 
1456     /**
1457      * Sets the flag indicating whether or not the range crosshair should
1458      * "lock-on" to actual data values.
1459      *
1460      * @param flag  the flag.
1461      */
setRangeCrosshairLockedOnData(boolean flag)1462     public void setRangeCrosshairLockedOnData(boolean flag) {
1463         if (this.rangeCrosshairLockedOnData != flag) {
1464             this.rangeCrosshairLockedOnData = flag;
1465             fireChangeEvent();
1466         }
1467     }
1468 
1469     /**
1470      * Returns the range crosshair value.
1471      *
1472      * @return The value.
1473      */
getRangeCrosshairValue()1474     public double getRangeCrosshairValue() {
1475         return this.rangeCrosshairValue;
1476     }
1477 
1478     /**
1479      * Sets the domain crosshair value.
1480      * <P>
1481      * Registered listeners are notified that the plot has been modified, but
1482      * only if the crosshair is visible.
1483      *
1484      * @param value  the new value.
1485      */
setRangeCrosshairValue(double value)1486     public void setRangeCrosshairValue(double value) {
1487         setRangeCrosshairValue(value, true);
1488     }
1489 
1490     /**
1491      * Sets the range crosshair value.
1492      * <P>
1493      * Registered listeners are notified that the axis has been modified, but
1494      * only if the crosshair is visible.
1495      *
1496      * @param value  the new value.
1497      * @param notify  a flag that controls whether or not listeners are
1498      *                notified.
1499      */
setRangeCrosshairValue(double value, boolean notify)1500     public void setRangeCrosshairValue(double value, boolean notify) {
1501         this.rangeCrosshairValue = value;
1502         if (isRangeCrosshairVisible() && notify) {
1503             fireChangeEvent();
1504         }
1505     }
1506 
1507     /**
1508      * Returns the Stroke used to draw the crosshair (if visible).
1509      *
1510      * @return The crosshair stroke.
1511      */
getRangeCrosshairStroke()1512     public Stroke getRangeCrosshairStroke() {
1513         return this.rangeCrosshairStroke;
1514     }
1515 
1516     /**
1517      * Sets the Stroke used to draw the crosshairs (if visible) and notifies
1518      * registered listeners that the axis has been modified.
1519      *
1520      * @param stroke  the new crosshair stroke.
1521      */
setRangeCrosshairStroke(Stroke stroke)1522     public void setRangeCrosshairStroke(Stroke stroke) {
1523         this.rangeCrosshairStroke = stroke;
1524         fireChangeEvent();
1525     }
1526 
1527     /**
1528      * Returns the range crosshair color.
1529      *
1530      * @return The crosshair color.
1531      */
getRangeCrosshairPaint()1532     public Paint getRangeCrosshairPaint() {
1533         return this.rangeCrosshairPaint;
1534     }
1535 
1536     /**
1537      * Sets the Paint used to color the crosshairs (if visible) and notifies
1538      * registered listeners that the axis has been modified.
1539      *
1540      * @param paint the new crosshair paint.
1541      */
setRangeCrosshairPaint(Paint paint)1542     public void setRangeCrosshairPaint(Paint paint) {
1543         this.rangeCrosshairPaint = paint;
1544         fireChangeEvent();
1545     }
1546 
1547     /**
1548      * Returns the tool tip generator.
1549      *
1550      * @return The tool tip generator (possibly null).
1551      */
getToolTipGenerator()1552     public ContourToolTipGenerator getToolTipGenerator() {
1553         return this.toolTipGenerator;
1554     }
1555 
1556     /**
1557      * Sets the tool tip generator.
1558      *
1559      * @param generator  the tool tip generator (null permitted).
1560      */
setToolTipGenerator(ContourToolTipGenerator generator)1561     public void setToolTipGenerator(ContourToolTipGenerator generator) {
1562         //Object oldValue = this.toolTipGenerator;
1563         this.toolTipGenerator = generator;
1564     }
1565 
1566     /**
1567      * Returns the URL generator for HTML image maps.
1568      *
1569      * @return The URL generator (possibly null).
1570      */
getURLGenerator()1571     public XYURLGenerator getURLGenerator() {
1572         return this.urlGenerator;
1573     }
1574 
1575     /**
1576      * Sets the URL generator for HTML image maps.
1577      *
1578      * @param urlGenerator  the URL generator (null permitted).
1579      */
setURLGenerator(XYURLGenerator urlGenerator)1580     public void setURLGenerator(XYURLGenerator urlGenerator) {
1581         //Object oldValue = this.urlGenerator;
1582         this.urlGenerator = urlGenerator;
1583     }
1584 
1585     /**
1586      * Draws a vertical line on the chart to represent a 'range marker'.
1587      *
1588      * @param g2  the graphics device.
1589      * @param plot  the plot.
1590      * @param domainAxis  the domain axis.
1591      * @param marker  the marker line.
1592      * @param dataArea  the axis data area.
1593      */
drawDomainMarker(Graphics2D g2, ContourPlot plot, ValueAxis domainAxis, Marker marker, Rectangle2D dataArea)1594     public void drawDomainMarker(Graphics2D g2,
1595                                  ContourPlot plot,
1596                                  ValueAxis domainAxis,
1597                                  Marker marker,
1598                                  Rectangle2D dataArea) {
1599 
1600         if (marker instanceof ValueMarker) {
1601             ValueMarker vm = (ValueMarker) marker;
1602             double value = vm.getValue();
1603             Range range = domainAxis.getRange();
1604             if (!range.contains(value)) {
1605                 return;
1606             }
1607 
1608             double x = domainAxis.valueToJava2D(value, dataArea,
1609                     RectangleEdge.BOTTOM);
1610             Line2D line = new Line2D.Double(x, dataArea.getMinY(), x,
1611                     dataArea.getMaxY());
1612             Paint paint = marker.getOutlinePaint();
1613             Stroke stroke = marker.getOutlineStroke();
1614             g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
1615             g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
1616             g2.draw(line);
1617         }
1618 
1619     }
1620 
1621     /**
1622      * Draws a horizontal line across the chart to represent a 'range marker'.
1623      *
1624      * @param g2  the graphics device.
1625      * @param plot  the plot.
1626      * @param rangeAxis  the range axis.
1627      * @param marker  the marker line.
1628      * @param dataArea  the axis data area.
1629      */
drawRangeMarker(Graphics2D g2, ContourPlot plot, ValueAxis rangeAxis, Marker marker, Rectangle2D dataArea)1630     public void drawRangeMarker(Graphics2D g2,
1631                                 ContourPlot plot,
1632                                 ValueAxis rangeAxis,
1633                                 Marker marker,
1634                                 Rectangle2D dataArea) {
1635 
1636         if (marker instanceof ValueMarker) {
1637             ValueMarker vm = (ValueMarker) marker;
1638             double value = vm.getValue();
1639             Range range = rangeAxis.getRange();
1640             if (!range.contains(value)) {
1641                 return;
1642             }
1643 
1644             double y = rangeAxis.valueToJava2D(value, dataArea,
1645                     RectangleEdge.LEFT);
1646             Line2D line = new Line2D.Double(dataArea.getMinX(), y,
1647                     dataArea.getMaxX(), y);
1648             Paint paint = marker.getOutlinePaint();
1649             Stroke stroke = marker.getOutlineStroke();
1650             g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
1651             g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
1652             g2.draw(line);
1653         }
1654 
1655     }
1656 
1657     /**
1658      * Returns the clipPath.
1659      * @return ClipPath
1660      */
getClipPath()1661     public ClipPath getClipPath() {
1662         return this.clipPath;
1663     }
1664 
1665     /**
1666      * Sets the clipPath.
1667      * @param clipPath The clipPath to set
1668      */
setClipPath(ClipPath clipPath)1669     public void setClipPath(ClipPath clipPath) {
1670         this.clipPath = clipPath;
1671     }
1672 
1673     /**
1674      * Returns the ptSizePct.
1675      * @return double
1676      */
getPtSizePct()1677     public double getPtSizePct() {
1678         return this.ptSizePct;
1679     }
1680 
1681     /**
1682      * Returns the renderAsPoints.
1683      * @return boolean
1684      */
isRenderAsPoints()1685     public boolean isRenderAsPoints() {
1686         return this.renderAsPoints;
1687     }
1688 
1689     /**
1690      * Sets the ptSizePct.
1691      * @param ptSizePct The ptSizePct to set
1692      */
setPtSizePct(double ptSizePct)1693     public void setPtSizePct(double ptSizePct) {
1694         this.ptSizePct = ptSizePct;
1695     }
1696 
1697     /**
1698      * Sets the renderAsPoints.
1699      * @param renderAsPoints The renderAsPoints to set
1700      */
setRenderAsPoints(boolean renderAsPoints)1701     public void setRenderAsPoints(boolean renderAsPoints) {
1702         this.renderAsPoints = renderAsPoints;
1703     }
1704 
1705     /**
1706      * Receives notification of a change to one of the plot's axes.
1707      *
1708      * @param event  information about the event.
1709      */
1710     @Override
axisChanged(AxisChangeEvent event)1711     public void axisChanged(AxisChangeEvent event) {
1712         Object source = event.getSource();
1713         if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) {
1714             ColorBar cba = this.colorBar;
1715             if (this.colorBar.getAxis().isAutoRange()) {
1716                 cba.getAxis().configure();
1717             }
1718 
1719         }
1720         super.axisChanged(event);
1721     }
1722 
1723     /**
1724      * Returns the visible z-range.
1725      *
1726      * @param data  the dataset.
1727      * @param x  the x range.
1728      * @param y  the y range.
1729      *
1730      * @return The range.
1731      */
visibleRange(ContourDataset data, Range x, Range y)1732     public Range visibleRange(ContourDataset data, Range x, Range y) {
1733         Range range = data.getZValueRange(x, y);
1734         return range;
1735     }
1736 
1737     /**
1738      * Returns the missingPaint.
1739      * @return Paint
1740      */
getMissingPaint()1741     public Paint getMissingPaint() {
1742         return this.missingPaint;
1743     }
1744 
1745     /**
1746      * Sets the missingPaint.
1747      *
1748      * @param paint  the missingPaint to set.
1749      */
setMissingPaint(Paint paint)1750     public void setMissingPaint(Paint paint) {
1751         this.missingPaint = paint;
1752     }
1753 
1754     /**
1755      * Multiplies the range on the domain axis/axes by the specified factor
1756      * (to be implemented).
1757      *
1758      * @param x  the x-coordinate (in Java2D space).
1759      * @param y  the y-coordinate (in Java2D space).
1760      * @param factor  the zoom factor.
1761      */
zoomDomainAxes(double x, double y, double factor)1762     public void zoomDomainAxes(double x, double y, double factor) {
1763         // TODO: to be implemented
1764     }
1765 
1766     /**
1767      * Zooms the domain axes (not yet implemented).
1768      *
1769      * @param x  the x-coordinate (in Java2D space).
1770      * @param y  the y-coordinate (in Java2D space).
1771      * @param lowerPercent  the new lower bound.
1772      * @param upperPercent  the new upper bound.
1773      */
zoomDomainAxes(double x, double y, double lowerPercent, double upperPercent)1774     public void zoomDomainAxes(double x, double y, double lowerPercent,
1775                                double upperPercent) {
1776         // TODO: to be implemented
1777     }
1778 
1779     /**
1780      * Multiplies the range on the range axis/axes by the specified factor.
1781      *
1782      * @param x  the x-coordinate (in Java2D space).
1783      * @param y  the y-coordinate (in Java2D space).
1784      * @param factor  the zoom factor.
1785      */
zoomRangeAxes(double x, double y, double factor)1786     public void zoomRangeAxes(double x, double y, double factor) {
1787         // TODO: to be implemented
1788     }
1789 
1790     /**
1791      * Zooms the range axes (not yet implemented).
1792      *
1793      * @param x  the x-coordinate (in Java2D space).
1794      * @param y  the y-coordinate (in Java2D space).
1795      * @param lowerPercent  the new lower bound.
1796      * @param upperPercent  the new upper bound.
1797      */
zoomRangeAxes(double x, double y, double lowerPercent, double upperPercent)1798     public void zoomRangeAxes(double x, double y, double lowerPercent,
1799                               double upperPercent) {
1800         // TODO: to be implemented
1801     }
1802 
1803     /**
1804      * Returns <code>false</code>.
1805      *
1806      * @return A boolean.
1807      */
isDomainZoomable()1808     public boolean isDomainZoomable() {
1809         return false;
1810     }
1811 
1812     /**
1813      * Returns <code>false</code>.
1814      *
1815      * @return A boolean.
1816      */
isRangeZoomable()1817     public boolean isRangeZoomable() {
1818         return false;
1819     }
1820 
1821     /**
1822      * Extends plot cloning to this plot type
1823      * @see org.jfree.chart.plot.Plot#clone()
1824      */
1825     @Override
clone()1826     public Object clone() throws CloneNotSupportedException {
1827         ContourPlot clone = (ContourPlot) super.clone();
1828 
1829         if (this.domainAxis != null) {
1830             clone.domainAxis = (ValueAxis) this.domainAxis.clone();
1831             clone.domainAxis.setPlot(clone);
1832             clone.domainAxis.addChangeListener(clone);
1833         }
1834         if (this.rangeAxis != null) {
1835             clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
1836             clone.rangeAxis.setPlot(clone);
1837             clone.rangeAxis.addChangeListener(clone);
1838         }
1839 
1840         if (clone.dataset != null) {
1841             clone.dataset.addChangeListener(clone);
1842         }
1843 
1844         if (this.colorBar != null) {
1845             clone.colorBar = (ColorBar) this.colorBar.clone();
1846         }
1847 
1848         clone.domainMarkers = (List) ObjectUtilities.deepClone(
1849                 this.domainMarkers);
1850         clone.rangeMarkers = (List) ObjectUtilities.deepClone(
1851                 this.rangeMarkers);
1852         clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
1853 
1854         if (this.clipPath != null) {
1855             clone.clipPath = (ClipPath) this.clipPath.clone();
1856         }
1857 
1858         return clone;
1859     }
1860 
1861 }
1862