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: * DeviationRenderer.java
029: * ----------------------
030: * (C) Copyright 2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: DeviationRenderer.java,v 1.1.2.3 2007/05/04 11:12:16 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 21-Feb-2007 : Version 1 (DG);
040: * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG);
041: *
042: */
043:
044: package org.jfree.chart.renderer.xy;
045:
046: import java.awt.AlphaComposite;
047: import java.awt.Composite;
048: import java.awt.Graphics2D;
049: import java.awt.geom.GeneralPath;
050: import java.awt.geom.Rectangle2D;
051: import java.util.List;
052:
053: import org.jfree.chart.axis.ValueAxis;
054: import org.jfree.chart.entity.EntityCollection;
055: import org.jfree.chart.event.RendererChangeEvent;
056: import org.jfree.chart.plot.CrosshairState;
057: import org.jfree.chart.plot.PlotOrientation;
058: import org.jfree.chart.plot.PlotRenderingInfo;
059: import org.jfree.chart.plot.XYPlot;
060: import org.jfree.data.xy.IntervalXYDataset;
061: import org.jfree.data.xy.XYDataset;
062: import org.jfree.ui.RectangleEdge;
063:
064: /**
065: * A specialised subclass of the {@link XYLineAndShapeRenderer} that requires
066: * an {@link IntervalXYDataset} and represents the y-interval by shading an
067: * area behind the y-values on the chart.
068: *
069: * @since 1.0.5
070: */
071: public class DeviationRenderer extends XYLineAndShapeRenderer {
072:
073: /**
074: * A state object that is passed to each call to <code>drawItem</code>.
075: */
076: public static class State extends XYLineAndShapeRenderer.State {
077:
078: /**
079: * A list of coordinates for the upper y-values in the current series
080: * (after translation into Java2D space).
081: */
082: public List upperCoordinates;
083:
084: /**
085: * A list of coordinates for the lower y-values in the current series
086: * (after translation into Java2D space).
087: */
088: public List lowerCoordinates;
089:
090: /**
091: * Creates a new state instance.
092: *
093: * @param info the plot rendering info.
094: */
095: public State(PlotRenderingInfo info) {
096: super (info);
097: this .lowerCoordinates = new java.util.ArrayList();
098: this .upperCoordinates = new java.util.ArrayList();
099: }
100:
101: }
102:
103: /** The alpha transparency for the interval shading. */
104: private float alpha;
105:
106: /**
107: * Creates a new renderer that displays lines and shapes for the data
108: * items, as well as the shaded area for the y-interval.
109: */
110: public DeviationRenderer() {
111: this (true, true);
112: }
113:
114: /**
115: * Creates a new renderer.
116: *
117: * @param lines show lines between data items?
118: * @param shapes show a shape for each data item?
119: */
120: public DeviationRenderer(boolean lines, boolean shapes) {
121: super (lines, shapes);
122: super .setDrawSeriesLineAsPath(true);
123: this .alpha = 0.5f;
124: }
125:
126: /**
127: * Returns the alpha transparency for the background shading.
128: *
129: * @return The alpha transparency.
130: *
131: * @see #setAlpha(float)
132: */
133: public float getAlpha() {
134: return this .alpha;
135: }
136:
137: /**
138: * Sets the alpha transparency for the background shading, and sends a
139: * {@link RendererChangeEvent} to all registered listeners.
140: *
141: * @param alpha the alpha (in the range 0.0f to 1.0f).
142: *
143: * @see #getAlpha()
144: */
145: public void setAlpha(float alpha) {
146: if (alpha < 0.0f || alpha > 1.0f) {
147: throw new IllegalArgumentException(
148: "Requires 'alpha' in the range 0.0 to 1.0.");
149: }
150: this .alpha = alpha;
151: notifyListeners(new RendererChangeEvent(this ));
152: }
153:
154: /**
155: * This method is overridden so that this flag cannot be changed---it is
156: * set to <code>true</code> for this renderer.
157: *
158: * @param flag ignored.
159: */
160: public void setDrawSeriesLineAsPath(boolean flag) {
161: // ignore
162: }
163:
164: /**
165: * Initialises and returns a state object that can be passed to each
166: * invocation of the {@link #drawItem} method.
167: *
168: * @param g2 the graphics target.
169: * @param dataArea the data area.
170: * @param plot the plot.
171: * @param dataset the dataset.
172: * @param info the plot rendering info.
173: *
174: * @return A newly initialised state object.
175: */
176: public XYItemRendererState initialise(Graphics2D g2,
177: Rectangle2D dataArea, XYPlot plot, XYDataset dataset,
178: PlotRenderingInfo info) {
179: State state = new State(info);
180: state.seriesPath = new GeneralPath();
181: state.setProcessVisibleItemsOnly(false);
182: return state;
183: }
184:
185: /**
186: * Returns the number of passes (through the dataset) used by this
187: * renderer.
188: *
189: * @return <code>3</code>.
190: */
191: public int getPassCount() {
192: return 3;
193: }
194:
195: /**
196: * Returns <code>true</code> if this is the pass where the shapes are
197: * drawn.
198: *
199: * @param pass the pass index.
200: *
201: * @return A boolean.
202: *
203: * @see #isLinePass(int)
204: */
205: protected boolean isItemPass(int pass) {
206: return (pass == 2);
207: }
208:
209: /**
210: * Returns <code>true</code> if this is the pass where the lines are
211: * drawn.
212: *
213: * @param pass the pass index.
214: *
215: * @return A boolean.
216: *
217: * @see #isItemPass(int)
218: */
219: protected boolean isLinePass(int pass) {
220: return (pass == 1);
221: }
222:
223: /**
224: * Draws the visual representation of a single data item.
225: *
226: * @param g2 the graphics device.
227: * @param state the renderer state.
228: * @param dataArea the area within which the data is being drawn.
229: * @param info collects information about the drawing.
230: * @param plot the plot (can be used to obtain standard color
231: * information etc).
232: * @param domainAxis the domain axis.
233: * @param rangeAxis the range axis.
234: * @param dataset the dataset.
235: * @param series the series index (zero-based).
236: * @param item the item index (zero-based).
237: * @param crosshairState crosshair information for the plot
238: * (<code>null</code> permitted).
239: * @param pass the pass index.
240: */
241: public void drawItem(Graphics2D g2, XYItemRendererState state,
242: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
243: ValueAxis domainAxis, ValueAxis rangeAxis,
244: XYDataset dataset, int series, int item,
245: CrosshairState crosshairState, int pass) {
246:
247: // do nothing if item is not visible
248: if (!getItemVisible(series, item)) {
249: return;
250: }
251:
252: // first pass draws the shading
253: if (pass == 0) {
254: IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
255: State drState = (State) state;
256:
257: double x = intervalDataset.getXValue(series, item);
258: double yLow = intervalDataset.getStartYValue(series, item);
259: double yHigh = intervalDataset.getEndYValue(series, item);
260:
261: RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
262: RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
263:
264: double xx = domainAxis.valueToJava2D(x, dataArea,
265: xAxisLocation);
266: double yyLow = rangeAxis.valueToJava2D(yLow, dataArea,
267: yAxisLocation);
268: double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea,
269: yAxisLocation);
270:
271: PlotOrientation orientation = plot.getOrientation();
272: if (orientation == PlotOrientation.HORIZONTAL) {
273: drState.lowerCoordinates
274: .add(new double[] { yyLow, xx });
275: drState.upperCoordinates
276: .add(new double[] { yyHigh, xx });
277: } else if (orientation == PlotOrientation.VERTICAL) {
278: drState.lowerCoordinates
279: .add(new double[] { xx, yyLow });
280: drState.upperCoordinates
281: .add(new double[] { xx, yyHigh });
282: }
283:
284: if (item == (dataset.getItemCount(series) - 1)) {
285: // last item in series, draw the lot...
286: // set up the alpha-transparency...
287: Composite originalComposite = g2.getComposite();
288: g2.setComposite(AlphaComposite.getInstance(
289: AlphaComposite.SRC_OVER, this .alpha));
290: g2.setPaint(getItemFillPaint(series, item));
291: GeneralPath area = new GeneralPath();
292: double[] coords = (double[]) drState.lowerCoordinates
293: .get(0);
294: area.moveTo((float) coords[0], (float) coords[1]);
295: for (int i = 1; i < drState.lowerCoordinates.size(); i++) {
296: coords = (double[]) drState.lowerCoordinates.get(i);
297: area.lineTo((float) coords[0], (float) coords[1]);
298: }
299: int count = drState.upperCoordinates.size();
300: coords = (double[]) drState.upperCoordinates
301: .get(count - 1);
302: area.lineTo((float) coords[0], (float) coords[1]);
303: for (int i = count - 2; i >= 0; i--) {
304: coords = (double[]) drState.upperCoordinates.get(i);
305: area.lineTo((float) coords[0], (float) coords[1]);
306: }
307: area.closePath();
308: g2.fill(area);
309: g2.setComposite(originalComposite);
310:
311: drState.lowerCoordinates.clear();
312: drState.upperCoordinates.clear();
313: }
314: }
315: if (isLinePass(pass)) {
316:
317: // the following code handles the line for the y-values...it's
318: // all done by code in the super class
319: if (item == 0) {
320: State s = (State) state;
321: s.seriesPath.reset();
322: s.setLastPointGood(false);
323: }
324:
325: if (getItemLineVisible(series, item)) {
326: drawPrimaryLineAsPath(state, g2, plot, dataset, pass,
327: series, item, domainAxis, rangeAxis, dataArea);
328: }
329: }
330:
331: // second pass adds shapes where the items are ..
332: else if (isItemPass(pass)) {
333:
334: // setup for collecting optional entity info...
335: EntityCollection entities = null;
336: if (info != null) {
337: entities = info.getOwner().getEntityCollection();
338: }
339:
340: drawSecondaryPass(g2, plot, dataset, pass, series, item,
341: domainAxis, dataArea, rangeAxis, crosshairState,
342: entities);
343: }
344: }
345:
346: /**
347: * Tests this renderer for equality with an arbitrary object.
348: *
349: * @param obj the object (<code>null</code> permitted).
350: *
351: * @return A boolean.
352: */
353: public boolean equals(Object obj) {
354: if (obj == this ) {
355: return true;
356: }
357: if (!(obj instanceof DeviationRenderer)) {
358: return false;
359: }
360: DeviationRenderer that = (DeviationRenderer) obj;
361: if (this .alpha != that.alpha) {
362: return false;
363: }
364: return super.equals(obj);
365: }
366:
367: }
|