001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 *
027 * -------------------
028 * XYStepRenderer.java
029 * -------------------
030 * (C) Copyright 2002-2007, 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 *
037 * Changes
038 * -------
039 * 13-May-2002 : Version 1, contributed by Roger Studner (DG);
040 * 25-Jun-2002 : Updated import statements (DG);
041 * 22-Jul-2002 : Added check for null data items (DG);
042 * 25-Mar-2003 : Implemented Serializable (DG);
043 * 01-May-2003 : Modified drawItem() method signature (DG);
044 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
045 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
046 * 28-Oct-2003 : Added tooltips, code contributed by Matthias Rose 
047 *               (RFE 824857) (DG);
048 * 10-Feb-2004 : Removed working line (use line from state object instead) (DG);
049 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState.  Renamed 
050 *               XYToolTipGenerator --> XYItemLabelGenerator (DG);
051 * 19-Jan-2005 : Now accesses only primitives from dataset (DG);
052 * 15-Mar-2005 : Fix silly bug in drawItem() method (DG);
053 * 19-Sep-2005 : Extend XYLineAndShapeRenderer (fixes legend shapes), added 
054 *               support for series visibility, and use getDefaultEntityRadius()
055 *               for entity hotspot size (DG);
056 * ------------- JFREECHART 1.0.x ---------------------------------------------
057 * 15-Jun-2006 : Added basic support for item labels (DG);
058 * 11-Oct-2006 : Fixed rendering with horizontal orientation (see bug 1569094),
059 *               thanks to Gerald Struck (DG);
060 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
061 *
062 */
063
064package org.jfree.chart.renderer.xy;
065
066import java.awt.Graphics2D;
067import java.awt.Paint;
068import java.awt.Shape;
069import java.awt.Stroke;
070import java.awt.geom.Line2D;
071import java.awt.geom.Rectangle2D;
072import java.io.Serializable;
073
074import org.jfree.chart.axis.ValueAxis;
075import org.jfree.chart.entity.EntityCollection;
076import org.jfree.chart.entity.XYItemEntity;
077import org.jfree.chart.labels.XYToolTipGenerator;
078import org.jfree.chart.plot.CrosshairState;
079import org.jfree.chart.plot.PlotOrientation;
080import org.jfree.chart.plot.PlotRenderingInfo;
081import org.jfree.chart.plot.XYPlot;
082import org.jfree.chart.urls.XYURLGenerator;
083import org.jfree.data.xy.XYDataset;
084import org.jfree.ui.RectangleEdge;
085import org.jfree.util.PublicCloneable;
086
087/**
088 * Line/Step item renderer for an {@link XYPlot}.  This class draws lines 
089 * between data points, only allowing horizontal or vertical lines (steps).
090 */
091public class XYStepRenderer extends XYLineAndShapeRenderer 
092                            implements XYItemRenderer, 
093                                       Cloneable,
094                                       PublicCloneable,
095                                       Serializable {
096
097    /** For serialization. */
098    private static final long serialVersionUID = -8918141928884796108L;
099    
100    /**
101     * Constructs a new renderer with no tooltip or URL generation.
102     */
103    public XYStepRenderer() {
104        this(null, null);
105    }
106
107    /**
108     * Constructs a new renderer with the specified tool tip and URL 
109     * generators.
110     *
111     * @param toolTipGenerator  the item label generator (<code>null</code> 
112     *     permitted).
113     * @param urlGenerator  the URL generator (<code>null</code> permitted).
114     */
115    public XYStepRenderer(XYToolTipGenerator toolTipGenerator,
116                          XYURLGenerator urlGenerator) {
117        super();
118        setBaseToolTipGenerator(toolTipGenerator);
119        setURLGenerator(urlGenerator);
120        setShapesVisible(false);
121    }
122
123    /**
124     * Draws the visual representation of a single data item.
125     *
126     * @param g2  the graphics device.
127     * @param state  the renderer state.
128     * @param dataArea  the area within which the data is being drawn.
129     * @param info  collects information about the drawing.
130     * @param plot  the plot (can be used to obtain standard color 
131     *              information etc).
132     * @param domainAxis  the domain axis.
133     * @param rangeAxis  the vertical axis.
134     * @param dataset  the dataset.
135     * @param series  the series index (zero-based).
136     * @param item  the item index (zero-based).
137     * @param crosshairState  crosshair information for the plot 
138     *                        (<code>null</code> permitted).
139     * @param pass  the pass index (ignored here).
140     */
141    public void drawItem(Graphics2D g2, 
142                         XYItemRendererState state,
143                         Rectangle2D dataArea, 
144                         PlotRenderingInfo info,
145                         XYPlot plot, 
146                         ValueAxis domainAxis, 
147                         ValueAxis rangeAxis,
148                         XYDataset dataset, 
149                         int series, 
150                         int item,
151                         CrosshairState crosshairState, 
152                         int pass) {
153
154        // do nothing if item is not visible
155        if (!getItemVisible(series, item)) {
156            return;   
157        }
158
159        PlotOrientation orientation = plot.getOrientation();
160        
161        Paint seriesPaint = getItemPaint(series, item);
162        Stroke seriesStroke = getItemStroke(series, item);
163        g2.setPaint(seriesPaint);
164        g2.setStroke(seriesStroke);
165
166        // get the data point...
167        double x1 = dataset.getXValue(series, item);
168        double y1 = dataset.getYValue(series, item);
169        if (Double.isNaN(y1)) {
170            return;
171        }
172
173        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
174        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
175        double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
176        double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
177
178        if (item > 0) {
179            // get the previous data point...
180            double x0 = dataset.getXValue(series, item - 1);
181            double y0 = dataset.getYValue(series, item - 1);
182            if (!Double.isNaN(y0)) {
183                double transX0 = domainAxis.valueToJava2D(x0, dataArea, 
184                        xAxisLocation);
185                double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 
186                        yAxisLocation);
187
188                Line2D line = state.workingLine;
189                if (orientation == PlotOrientation.HORIZONTAL) {
190                    if (transY0 == transY1) { //this represents the situation 
191                                              // for drawing a horizontal bar.
192                        line.setLine(transY0, transX0, transY1, transX1);
193                        g2.draw(line);
194                    }
195                    else {  //this handles the need to perform a 'step'.
196                        line.setLine(transY0, transX0, transY0, transX1);
197                        g2.draw(line);
198                        line.setLine(transY0, transX1, transY1, transX1);
199                        g2.draw(line);
200                    }
201                }
202                else if (orientation == PlotOrientation.VERTICAL) {
203                    if (transY0 == transY1) { // this represents the situation 
204                                              // for drawing a horizontal bar.
205                        line.setLine(transX0, transY0, transX1, transY1);
206                        g2.draw(line);
207                    }
208                    else {  //this handles the need to perform a 'step'.
209                        line.setLine(transX0, transY0, transX1, transY0);
210                        g2.draw(line);
211                        line.setLine(transX1, transY0, transX1, transY1);
212                        g2.draw(line);
213                    }
214                }
215
216            }
217        }
218
219        // draw the item label if there is one...
220        if (isItemLabelVisible(series, item)) {
221            double xx = transX1;
222            double yy = transY1;
223            if (orientation == PlotOrientation.HORIZONTAL) {
224                xx = transY1;
225                yy = transX1;
226            }          
227            drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 
228                    (y1 < 0.0));
229        }
230
231        int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
232        int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
233        updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 
234                rangeAxisIndex, transX1, transY1, orientation);
235        
236        // collect entity and tool tip information...
237        if (state.getInfo() != null) {
238            EntityCollection entities = state.getEntityCollection();
239            if (entities != null) {
240                int r = getDefaultEntityRadius();
241                Shape shape = orientation == PlotOrientation.VERTICAL
242                    ? new Rectangle2D.Double(transX1 - r, transY1 - r, 2 * r, 
243                            2 * r)
244                    : new Rectangle2D.Double(transY1 - r, transX1 - r, 2 * r, 
245                            2 * r);           
246                if (shape != null) {
247                    String tip = null;
248                    XYToolTipGenerator generator 
249                        = getToolTipGenerator(series, item);
250                    if (generator != null) {
251                        tip = generator.generateToolTip(dataset, series, item);
252                    }
253                    String url = null;
254                    if (getURLGenerator() != null) {
255                        url = getURLGenerator().generateURL(dataset, series, 
256                                item);
257                    }
258                    XYItemEntity entity = new XYItemEntity(shape, dataset, 
259                            series, item, tip, url);
260                    entities.add(entity);
261                }
262            }
263        }
264    }
265
266    /**
267     * Returns a clone of the renderer.
268     * 
269     * @return A clone.
270     * 
271     * @throws CloneNotSupportedException  if the renderer cannot be cloned.
272     */
273    public Object clone() throws CloneNotSupportedException {
274        return super.clone();
275    }
276
277}