    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    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
    071package org.jfree.chart.renderer.xy;
    072
    073import java.awt.Graphics2D;
    074import java.awt.Paint;
    075import java.awt.Stroke;
    076import java.awt.geom.Line2D;
    077import java.awt.geom.Rectangle2D;
    078import java.io.Serializable;
    079
    080import org.jfree.chart.HashUtilities;
    081import org.jfree.chart.axis.ValueAxis;
    082import org.jfree.chart.entity.EntityCollection;
    083import org.jfree.chart.event.RendererChangeEvent;
    084import org.jfree.chart.labels.XYToolTipGenerator;
    085import org.jfree.chart.plot.CrosshairState;
    086import org.jfree.chart.plot.PlotOrientation;
    087import org.jfree.chart.plot.PlotRenderingInfo;
    088import org.jfree.chart.plot.XYPlot;
    089import org.jfree.chart.urls.XYURLGenerator;
    090import org.jfree.data.xy.XYDataset;
    091import org.jfree.ui.RectangleEdge;
    092import 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 */
    104public 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}