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 }