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