    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2009, 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     * XYStepRenderer.java
    029     * -------------------
    030     * (C) Copyright 2002-2009, by Roger Studner and Contributors.
    031     *
    032     * Original Author:  Roger Studner;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *                   Matthias Rose;
    035     *                   Gerald Struck (fix for bug 1569094);
    036     *                   Ulrich Voigt (patch 1874890);
    037     *                   Martin Hoeller (contribution to patch 1874890);
    038     *
    039     * Changes
    040     * -------
    041     * 13-May-2002 : Version 1, contributed by Roger Studner (DG);
    042     * 25-Jun-2002 : Updated import statements (DG);
    043     * 22-Jul-2002 : Added check for null data items (DG);
    044     * 25-Mar-2003 : Implemented Serializable (DG);
    045     * 01-May-2003 : Modified drawItem() method signature (DG);
    046     * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
    047     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    048     * 28-Oct-2003 : Added tooltips, code contributed by Matthias Rose
    049     *               (RFE 824857) (DG);
    050     * 10-Feb-2004 : Removed working line (use line from state object instead) (DG);
    051     * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState.  Renamed
    052     *               XYToolTipGenerator --> XYItemLabelGenerator (DG);
    053     * 19-Jan-2005 : Now accesses only primitives from dataset (DG);
    054     * 15-Mar-2005 : Fix silly bug in drawItem() method (DG);
    055     * 19-Sep-2005 : Extend XYLineAndShapeRenderer (fixes legend shapes), added
    056     *               support for series visibility, and use getDefaultEntityRadius()
    057     *               for entity hotspot size (DG);
    058     * ------------- JFREECHART 1.0.x ---------------------------------------------
    059     * 15-Jun-2006 : Added basic support for item labels (DG);
    060     * 11-Oct-2006 : Fixed rendering with horizontal orientation (see bug 1569094),
    061     *               thanks to Gerald Struck (DG);
    062     * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
    063     * 14-Feb-2008 : Applied patch 1874890 by Ulrich Voigt (with contribution from
    064     *               Martin Hoeller) (DG);
    065     * 14-May-2008 : Call addEntity() in drawItem() (DG);
    066     * 24-Sep-2008 : Fixed bug 2113627 by utilising second pass to draw item
    067     *               labels (DG);
    068     *
    069     */
    070
    071    package org.jfree.chart.renderer.xy;
    072
    073    import java.awt.Graphics2D;
    074    import java.awt.Paint;
    075    import java.awt.Stroke;
    076    import java.awt.geom.Line2D;
    077    import java.awt.geom.Rectangle2D;
    078    import java.io.Serializable;
    079
    080    import org.jfree.chart.HashUtilities;
    081    import org.jfree.chart.axis.ValueAxis;
    082    import org.jfree.chart.entity.EntityCollection;
    083    import org.jfree.chart.event.RendererChangeEvent;
    084    import org.jfree.chart.labels.XYToolTipGenerator;
    085    import org.jfree.chart.plot.CrosshairState;
    086    import org.jfree.chart.plot.PlotOrientation;
    087    import org.jfree.chart.plot.PlotRenderingInfo;
    088    import org.jfree.chart.plot.XYPlot;
    089    import org.jfree.chart.urls.XYURLGenerator;
    090    import org.jfree.data.xy.XYDataset;
    091    import org.jfree.ui.RectangleEdge;
    092    import org.jfree.util.PublicCloneable;
    093
    094    /**
    095     * Line/Step item renderer for an {@link XYPlot}.  This class draws lines
    096     * between data points, only allowing horizontal or vertical lines (steps).
    097     * The example shown here is generated by the
    098     * <code>XYStepRendererDemo1.java</code> program included in the JFreeChart
    099     * demo collection:
    100     * <br><br>
    101     * <img src="../../../../../images/XYStepRendererSample.png"
    102     * alt="XYStepRendererSample.png" />
    103     */
    104    public class XYStepRenderer extends XYLineAndShapeRenderer
    105            implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
    106
    107        /** For serialization. */
    108        private static final long serialVersionUID = -8918141928884796108L;
    109
    110        /**
    111         * The factor (from 0.0 to 1.0) that determines the position of the
    112         * step.
    113         *
    114         * @since 1.0.10.
    115         */
    116        private double stepPoint = 1.0d;
    117
    118        /**
    119         * Constructs a new renderer with no tooltip or URL generation.
    120         */
    121        public XYStepRenderer() {
    122            this(null, null);
    123        }
    124
    125        /**
    126         * Constructs a new renderer with the specified tool tip and URL
    127         * generators.
    128         *
    129         * @param toolTipGenerator  the item label generator (<code>null</code>
    130         *     permitted).
    131         * @param urlGenerator  the URL generator (<code>null</code> permitted).
    132         */
    133        public XYStepRenderer(XYToolTipGenerator toolTipGenerator,
    134                              XYURLGenerator urlGenerator) {
    135            super();
    136            setBaseToolTipGenerator(toolTipGenerator);
    137            setURLGenerator(urlGenerator);
    138            setBaseShapesVisible(false);
    139        }
    140
    141        /**
    142         * Returns the fraction of the domain position between two points on which
    143         * the step is drawn.  The default is 1.0d, which means the step is drawn
    144         * at the domain position of the second`point. If the stepPoint is 0.5d the
    145         * step is drawn at half between the two points.
    146         *
    147         * @return The fraction of the domain position between two points where the
    148         *         step is drawn.
    149         *
    150         * @see #setStepPoint(double)
    151         *
    152         * @since 1.0.10
    153         */
    154        public double getStepPoint() {
    155            return this.stepPoint;
    156        }
    157
    158        /**
    159         * Sets the step point and sends a {@link RendererChangeEvent} to all
    160         * registered listeners.
    161         *
    162         * @param stepPoint  the step point (in the range 0.0 to 1.0)
    163         *
    164         * @see #getStepPoint()
    165         *
    166         * @since 1.0.10
    167         */
    168        public void setStepPoint(double stepPoint) {
    169            if (stepPoint < 0.0d || stepPoint > 1.0d) {
    170                throw new IllegalArgumentException(
    171                        "Requires stepPoint in [0.0;1.0]");
    172            }
    173            this.stepPoint = stepPoint;
    174            fireChangeEvent();
    175        }
    176
    177        /**
    178         * Draws the visual representation of a single data item.
    179         *
    180         * @param g2  the graphics device.
    181         * @param state  the renderer state.
    182         * @param dataArea  the area within which the data is being drawn.
    183         * @param info  collects information about the drawing.
    184         * @param plot  the plot (can be used to obtain standard color
    185         *              information etc).
    186         * @param domainAxis  the domain axis.
    187         * @param rangeAxis  the vertical axis.
    188         * @param dataset  the dataset.
    189         * @param series  the series index (zero-based).
    190         * @param item  the item index (zero-based).
    191         * @param crosshairState  crosshair information for the plot
    192         *                        (<code>null</code> permitted).
    193         * @param pass  the pass index.
    194         */
    195        public void drawItem(Graphics2D g2,
    196                             XYItemRendererState state,
    197                             Rectangle2D dataArea,
    198                             PlotRenderingInfo info,
    199                             XYPlot plot,
    200                             ValueAxis domainAxis,
    201                             ValueAxis rangeAxis,
    202                             XYDataset dataset,
    203                             int series,
    204                             int item,
    205                             CrosshairState crosshairState,
    206                             int pass) {
    207
    208            // do nothing if item is not visible
    209            if (!getItemVisible(series, item)) {
    210                return;
    211            }
    212
    213            PlotOrientation orientation = plot.getOrientation();
    214
    215            Paint seriesPaint = getItemPaint(series, item);
    216            Stroke seriesStroke = getItemStroke(series, item);
    217            g2.setPaint(seriesPaint);
    218            g2.setStroke(seriesStroke);
    219
    220            // get the data point...
    221            double x1 = dataset.getXValue(series, item);
    222            double y1 = dataset.getYValue(series, item);
    223
    224            RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    225            RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
    226            double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
    227            double transY1 = (Double.isNaN(y1) ? Double.NaN
    228                    : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation));
    229
    230            if (pass == 0 && item > 0) {
    231                // get the previous data point...
    232                double x0 = dataset.getXValue(series, item - 1);
    233                double y0 = dataset.getYValue(series, item - 1);
    234                double transX0 = domainAxis.valueToJava2D(x0, dataArea,
    235                        xAxisLocation);
    236                double transY0 = (Double.isNaN(y0) ? Double.NaN
    237                        : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation));
    238
    239                if (orientation == PlotOrientation.HORIZONTAL) {
    240                    if (transY0 == transY1) {
    241                        // this represents the situation
    242                        // for drawing a horizontal bar.
    243                        drawLine(g2, state.workingLine, transY0, transX0, transY1,
    244                                transX1);
    245                    }
    246                    else {  //this handles the need to perform a 'step'.
    247
    248                        // calculate the step point
    249                        double transXs = transX0 + (getStepPoint()
    250                                * (transX1 - transX0));
    251                        drawLine(g2, state.workingLine, transY0, transX0, transY0,
    252                                transXs);
    253                        drawLine(g2, state.workingLine, transY0, transXs, transY1,
    254                                transXs);
    255                        drawLine(g2, state.workingLine, transY1, transXs, transY1,
    256                                transX1);
    257                    }
    258                }
    259                else if (orientation == PlotOrientation.VERTICAL) {
    260                    if (transY0 == transY1) { // this represents the situation
    261                                              // for drawing a horizontal bar.
    262                        drawLine(g2, state.workingLine, transX0, transY0, transX1,
    263                                transY1);
    264                    }
    265                    else {  //this handles the need to perform a 'step'.
    266                        // calculate the step point
    267                        double transXs = transX0 + (getStepPoint()
    268                                * (transX1 - transX0));
    269                        drawLine(g2, state.workingLine, transX0, transY0, transXs,
    270                                transY0);
    271                        drawLine(g2, state.workingLine, transXs, transY0, transXs,
    272                                transY1);
    273                        drawLine(g2, state.workingLine, transXs, transY1, transX1,
    274                                transY1);
    275                    }
    276                }
    277
    278                // submit this data item as a candidate for the crosshair point
    279                int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
    280                int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
    281                updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
    282                        rangeAxisIndex, transX1, transY1, orientation);
    283
    284                // collect entity and tool tip information...
    285                EntityCollection entities = state.getEntityCollection();
    286                if (entities != null) {
    287                    addEntity(entities, null, dataset, series, item, transX1,
    288                            transY1);
    289                }
    290
    291            }
    292
    293            if (pass == 1) {
    294                // draw the item label if there is one...
    295                if (isItemLabelVisible(series, item)) {
    296                    double xx = transX1;
    297                    double yy = transY1;
    298                    if (orientation == PlotOrientation.HORIZONTAL) {
    299                        xx = transY1;
    300                        yy = transX1;
    301                    }
    302                    drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
    303                            (y1 < 0.0));
    304                }
    305            }
    306        }
    307
    308        /**
    309         * A utility method that draws a line but only if none of the coordinates
    310         * are NaN values.
    311         *
    312         * @param g2  the graphics target.
    313         * @param line  the line object.
    314         * @param x0  the x-coordinate for the starting point of the line.
    315         * @param y0  the y-coordinate for the starting point of the line.
    316         * @param x1  the x-coordinate for the ending point of the line.
    317         * @param y1  the y-coordinate for the ending point of the line.
    318         */
    319        private void drawLine(Graphics2D g2, Line2D line, double x0, double y0,
    320                double x1, double y1) {
    321            if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0)
    322                    || Double.isNaN(y1)) {
    323                return;
    324            }
    325            line.setLine(x0, y0, x1, y1);
    326            g2.draw(line);
    327        }
    328
    329        /**
    330         * Tests this renderer for equality with an arbitrary object.
    331         *
    332         * @param obj  the object (<code>null</code> permitted).
    333         *
    334         * @return A boolean.
    335         */
    336        public boolean equals(Object obj) {
    337            if (obj == this) {
    338                return true;
    339            }
    340            if (!(obj instanceof XYLineAndShapeRenderer)) {
    341                return false;
    342            }
    343            XYStepRenderer that = (XYStepRenderer) obj;
    344            if (this.stepPoint != that.stepPoint) {
    345                return false;
    346            }
    347            return super.equals(obj);
    348        }
    349
    350        /**
    351         * Returns a hash code for this instance.
    352         *
    353         * @return A hash code.
    354         */
    355        public int hashCode() {
    356            return HashUtilities.hashCode(super.hashCode(), this.stepPoint);
    357        }
    358
    359        /**
    360         * Returns a clone of the renderer.
    361         *
    362         * @return A clone.
    363         *
    364         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    365         */
    366        public Object clone() throws CloneNotSupportedException {
    367            return super.clone();
    368        }
    369
    370    }