    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     * XYStepRenderer.java
    029     * -------------------
    030     * (C) Copyright 2002-2008, 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     *
    067     */
    068
    069    package org.jfree.chart.renderer.xy;
    070
    071    import java.awt.Graphics2D;
    072    import java.awt.Paint;
    073    import java.awt.Stroke;
    074    import java.awt.geom.Line2D;
    075    import java.awt.geom.Rectangle2D;
    076    import java.io.Serializable;
    077
    078    import org.jfree.chart.HashUtilities;
    079    import org.jfree.chart.axis.ValueAxis;
    080    import org.jfree.chart.entity.EntityCollection;
    081    import org.jfree.chart.event.RendererChangeEvent;
    082    import org.jfree.chart.labels.XYToolTipGenerator;
    083    import org.jfree.chart.plot.CrosshairState;
    084    import org.jfree.chart.plot.PlotOrientation;
    085    import org.jfree.chart.plot.PlotRenderingInfo;
    086    import org.jfree.chart.plot.XYPlot;
    087    import org.jfree.chart.urls.XYURLGenerator;
    088    import org.jfree.data.xy.XYDataset;
    089    import org.jfree.ui.RectangleEdge;
    090    import org.jfree.util.PublicCloneable;
    091
    092    /**
    093     * Line/Step item renderer for an {@link XYPlot}.  This class draws lines
    094     * between data points, only allowing horizontal or vertical lines (steps).
    095     */
    096    public class XYStepRenderer extends XYLineAndShapeRenderer
    097            implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
    098
    099        /** For serialization. */
    100        private static final long serialVersionUID = -8918141928884796108L;
    101
    102        /**
    103         * The factor (from 0.0 to 1.0) that determines the position of the
    104         * step.
    105         *
    106         * @since 1.0.10.
    107         */
    108        private double stepPoint = 1.0d;
    109
    110        /**
    111         * Constructs a new renderer with no tooltip or URL generation.
    112         */
    113        public XYStepRenderer() {
    114            this(null, null);
    115        }
    116
    117        /**
    118         * Constructs a new renderer with the specified tool tip and URL
    119         * generators.
    120         *
    121         * @param toolTipGenerator  the item label generator (<code>null</code>
    122         *     permitted).
    123         * @param urlGenerator  the URL generator (<code>null</code> permitted).
    124         */
    125        public XYStepRenderer(XYToolTipGenerator toolTipGenerator,
    126                              XYURLGenerator urlGenerator) {
    127            super();
    128            setBaseToolTipGenerator(toolTipGenerator);
    129            setURLGenerator(urlGenerator);
    130            setBaseShapesVisible(false);
    131        }
    132
    133        /**
    134         * Returns the fraction of the domain position between two points on which
    135         * the step is drawn.  The default is 1.0d, which means the step is drawn
    136         * at the domain position of the second`point. If the stepPoint is 0.5d the
    137         * step is drawn at half between the two points.
    138         *
    139         * @return The fraction of the domain position between two points where the
    140         *         step is drawn.
    141         *
    142         * @see #setStepPoint(double)
    143         *
    144         * @since 1.0.10
    145         */
    146        public double getStepPoint() {
    147            return this.stepPoint;
    148        }
    149
    150        /**
    151         * Sets the step point and sends a {@link RendererChangeEvent} to all
    152         * registered listeners.
    153         *
    154         * @param stepPoint  the step point (in the range 0.0 to 1.0)
    155         *
    156         * @see #getStepPoint()
    157         *
    158         * @since 1.0.10
    159         */
    160        public void setStepPoint(double stepPoint) {
    161            if (stepPoint < 0.0d || stepPoint > 1.0d) {
    162                throw new IllegalArgumentException(
    163                        "Requires stepPoint in [0.0;1.0]");
    164            }
    165            this.stepPoint = stepPoint;
    166            fireChangeEvent();
    167        }
    168
    169        /**
    170         * Draws the visual representation of a single data item.
    171         *
    172         * @param g2  the graphics device.
    173         * @param state  the renderer state.
    174         * @param dataArea  the area within which the data is being drawn.
    175         * @param info  collects information about the drawing.
    176         * @param plot  the plot (can be used to obtain standard color
    177         *              information etc).
    178         * @param domainAxis  the domain axis.
    179         * @param rangeAxis  the vertical axis.
    180         * @param dataset  the dataset.
    181         * @param series  the series index (zero-based).
    182         * @param item  the item index (zero-based).
    183         * @param crosshairState  crosshair information for the plot
    184         *                        (<code>null</code> permitted).
    185         * @param pass  the pass index (ignored here).
    186         */
    187        public void drawItem(Graphics2D g2,
    188                             XYItemRendererState state,
    189                             Rectangle2D dataArea,
    190                             PlotRenderingInfo info,
    191                             XYPlot plot,
    192                             ValueAxis domainAxis,
    193                             ValueAxis rangeAxis,
    194                             XYDataset dataset,
    195                             int series,
    196                             int item,
    197                             CrosshairState crosshairState,
    198                             int pass) {
    199
    200            // do nothing if item is not visible
    201            if (!getItemVisible(series, item)) {
    202                return;
    203            }
    204
    205            PlotOrientation orientation = plot.getOrientation();
    206
    207            Paint seriesPaint = getItemPaint(series, item);
    208            Stroke seriesStroke = getItemStroke(series, item);
    209            g2.setPaint(seriesPaint);
    210            g2.setStroke(seriesStroke);
    211
    212            // get the data point...
    213            double x1 = dataset.getXValue(series, item);
    214            double y1 = dataset.getYValue(series, item);
    215
    216            RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
    217            RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
    218            double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
    219            double transY1 = (Double.isNaN(y1) ? Double.NaN
    220                    : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation));
    221
    222            if (item > 0) {
    223                // get the previous data point...
    224                double x0 = dataset.getXValue(series, item - 1);
    225                double y0 = dataset.getYValue(series, item - 1);
    226                double transX0 = domainAxis.valueToJava2D(x0, dataArea,
    227                        xAxisLocation);
    228                double transY0 = (Double.isNaN(y0) ? Double.NaN
    229                        : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation));
    230
    231                if (orientation == PlotOrientation.HORIZONTAL) {
    232                    if (transY0 == transY1) {
    233                        // this represents the situation
    234                        // for drawing a horizontal bar.
    235                        drawLine(g2, state.workingLine, transY0, transX0, transY1,
    236                                transX1);
    237                    }
    238                    else {  //this handles the need to perform a 'step'.
    239
    240                        // calculate the step point
    241                        double transXs = transX0 + (getStepPoint()
    242                                * (transX1 - transX0));
    243                        drawLine(g2, state.workingLine, transY0, transX0, transY0,
    244                                transXs);
    245                        drawLine(g2, state.workingLine, transY0, transXs, transY1,
    246                                transXs);
    247                        drawLine(g2, state.workingLine, transY1, transXs, transY1,
    248                                transX1);
    249                    }
    250                }
    251                else if (orientation == PlotOrientation.VERTICAL) {
    252                    if (transY0 == transY1) { // this represents the situation
    253                                              // for drawing a horizontal bar.
    254                        drawLine(g2, state.workingLine, transX0, transY0, transX1,
    255                                transY1);
    256                    }
    257                    else {  //this handles the need to perform a 'step'.
    258                        // calculate the step point
    259                        double transXs = transX0 + (getStepPoint()
    260                                * (transX1 - transX0));
    261                        drawLine(g2, state.workingLine, transX0, transY0, transXs,
    262                                transY0);
    263                        drawLine(g2, state.workingLine, transXs, transY0, transXs,
    264                                transY1);
    265                        drawLine(g2, state.workingLine, transXs, transY1, transX1,
    266                                transY1);
    267                    }
    268                }
    269
    270            }
    271
    272            // draw the item label if there is one...
    273            if (isItemLabelVisible(series, item)) {
    274                double xx = transX1;
    275                double yy = transY1;
    276                if (orientation == PlotOrientation.HORIZONTAL) {
    277                    xx = transY1;
    278                    yy = transX1;
    279                }
    280                drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
    281                        (y1 < 0.0));
    282            }
    283
    284            // submit this data item as a candidate for the crosshair point
    285            int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
    286            int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
    287            updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
    288                    rangeAxisIndex, transX1, transY1, orientation);
    289
    290            // collect entity and tool tip information...
    291            EntityCollection entities = state.getEntityCollection();
    292            if (entities != null) {
    293                addEntity(entities, null, dataset, series, item, transX1, transY1);
    294            }
    295
    296        }
    297
    298        /**
    299         * A utility method that draws a line but only if none of the coordinates
    300         * are NaN values.
    301         *
    302         * @param g2  the graphics target.
    303         * @param line  the line object.
    304         * @param x0  the x-coordinate for the starting point of the line.
    305         * @param y0  the y-coordinate for the starting point of the line.
    306         * @param x1  the x-coordinate for the ending point of the line.
    307         * @param y1  the y-coordinate for the ending point of the line.
    308         */
    309        private void drawLine(Graphics2D g2, Line2D line, double x0, double y0,
    310                double x1, double y1) {
    311            if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0)
    312                    || Double.isNaN(y1)) {
    313                return;
    314            }
    315            line.setLine(x0, y0, x1, y1);
    316            g2.draw(line);
    317        }
    318
    319        /**
    320         * Tests this renderer for equality with an arbitrary object.
    321         *
    322         * @param obj  the object (<code>null</code> permitted).
    323         *
    324         * @return A boolean.
    325         */
    326        public boolean equals(Object obj) {
    327            if (obj == this) {
    328                return true;
    329            }
    330            if (!(obj instanceof XYLineAndShapeRenderer)) {
    331                return false;
    332            }
    333            XYStepRenderer that = (XYStepRenderer) obj;
    334            if (this.stepPoint != that.stepPoint) {
    335                return false;
    336            }
    337            return super.equals(obj);
    338        }
    339
    340        /**
    341         * Returns a hash code for this instance.
    342         *
    343         * @return A hash code.
    344         */
    345        public int hashCode() {
    346            return HashUtilities.hashCode(super.hashCode(), this.stepPoint);
    347        }
    348
    349        /**
    350         * Returns a clone of the renderer.
    351         *
    352         * @return A clone.
    353         *
    354         * @throws CloneNotSupportedException  if the renderer cannot be cloned.
    355         */
    356        public Object clone() throws CloneNotSupportedException {
    357            return super.clone();
    358        }
    359
    360    }