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