001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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 * XYLine3DRenderer.java
029 * ---------------------
030 * (C) Copyright 2005-2008, by Object Refinery Limited.
031 *
032 * Original Author:  Thomas Morgner;
033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
034 *
035 * Changes
036 * -------
037 * 14-Jan-2005 : Added standard header (DG);
038 * 01-May-2007 : Fixed equals() and serialization bugs (DG);
039 *
040 */
041
042package org.jfree.chart.renderer.xy;
043
044import java.awt.Color;
045import java.awt.Graphics2D;
046import java.awt.Paint;
047import java.awt.Shape;
048import java.io.IOException;
049import java.io.ObjectInputStream;
050import java.io.ObjectOutputStream;
051import java.io.Serializable;
052
053import org.jfree.chart.Effect3D;
054import org.jfree.chart.event.RendererChangeEvent;
055import org.jfree.io.SerialUtilities;
056import org.jfree.util.PaintUtilities;
057
058/**
059 * A XYLineAndShapeRenderer that adds a shadow line to the graph
060 * to emulate a 3D-effect.
061 */
062public class XYLine3DRenderer extends XYLineAndShapeRenderer
063                              implements Effect3D, Serializable {
064
065    /** For serialization. */
066    private static final long serialVersionUID = 588933208243446087L;
067
068    /** The default x-offset for the 3D effect. */
069    public static final double DEFAULT_X_OFFSET = 12.0;
070
071    /** The default y-offset for the 3D effect. */
072    public static final double DEFAULT_Y_OFFSET = 8.0;
073
074    /** The default wall paint. */
075    public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
076
077    /** The size of x-offset for the 3D effect. */
078    private double xOffset;
079
080    /** The size of y-offset for the 3D effect. */
081    private double yOffset;
082
083    /** The paint used to shade the left and lower 3D wall. */
084    private transient Paint wallPaint;
085
086    /**
087     * Creates a new renderer.
088     */
089    public XYLine3DRenderer() {
090        this.wallPaint = DEFAULT_WALL_PAINT;
091        this.xOffset = DEFAULT_X_OFFSET;
092        this.yOffset = DEFAULT_Y_OFFSET;
093    }
094
095    /**
096     * Returns the x-offset for the 3D effect.
097     *
098     * @return The 3D effect.
099     */
100    @Override
101    public double getXOffset() {
102        return this.xOffset;
103    }
104
105    /**
106     * Returns the y-offset for the 3D effect.
107     *
108     * @return The 3D effect.
109     */
110    @Override
111    public double getYOffset() {
112        return this.yOffset;
113    }
114
115    /**
116     * Sets the x-offset and sends a {@link RendererChangeEvent} to all
117     * registered listeners.
118     *
119     * @param xOffset  the x-offset.
120     */
121    public void setXOffset(double xOffset) {
122        this.xOffset = xOffset;
123        fireChangeEvent();
124    }
125
126    /**
127     * Sets the y-offset and sends a {@link RendererChangeEvent} to all
128     * registered listeners.
129     *
130     * @param yOffset  the y-offset.
131     */
132    public void setYOffset(double yOffset) {
133        this.yOffset = yOffset;
134        fireChangeEvent();
135    }
136
137    /**
138     * Returns the paint used to highlight the left and bottom wall in the plot
139     * background.
140     *
141     * @return The paint.
142     */
143    public Paint getWallPaint() {
144        return this.wallPaint;
145    }
146
147    /**
148     * Sets the paint used to hightlight the left and bottom walls in the plot
149     * background and sends a {@link RendererChangeEvent} to all registered
150     * listeners.
151     *
152     * @param paint  the paint.
153     */
154    public void setWallPaint(Paint paint) {
155        this.wallPaint = paint;
156        fireChangeEvent();
157    }
158
159    /**
160     * Returns the number of passes through the data that the renderer requires
161     * in order to draw the chart.  Most charts will require a single pass,
162     * but some require two passes.
163     *
164     * @return The pass count.
165     */
166    @Override
167    public int getPassCount() {
168        return 3;
169    }
170
171    /**
172     * Returns <code>true</code> if the specified pass involves drawing lines.
173     *
174     * @param pass  the pass.
175     *
176     * @return A boolean.
177     */
178    @Override
179    protected boolean isLinePass(int pass) {
180        return pass == 0 || pass == 1;
181    }
182
183    /**
184     * Returns <code>true</code> if the specified pass involves drawing items.
185     *
186     * @param pass  the pass.
187     *
188     * @return A boolean.
189     */
190    @Override
191    protected boolean isItemPass(int pass) {
192        return pass == 2;
193    }
194
195    /**
196     * Returns <code>true</code> if the specified pass involves drawing shadows.
197     *
198     * @param pass  the pass.
199     *
200     * @return A boolean.
201     */
202    protected boolean isShadowPass (int pass) {
203        return pass == 0;
204    }
205
206    /**
207     * Overrides the method in the subclass to draw a shadow in the first pass.
208     *
209     * @param g2  the graphics device.
210     * @param pass  the pass.
211     * @param series  the series index (zero-based).
212     * @param item  the item index (zero-based).
213     * @param shape  the shape.
214     */
215    @Override
216    protected void drawFirstPassShape(Graphics2D g2, int pass, int series,
217            int item, Shape shape) {
218        if (isShadowPass(pass)) {
219            if (getWallPaint() != null) {
220                g2.setStroke(getItemStroke(series, item));
221                g2.setPaint(getWallPaint());
222                g2.translate(getXOffset(), getYOffset());
223                g2.draw(shape);
224                g2.translate(-getXOffset(), -getYOffset());
225            }
226        }
227        else {
228            // now draw the real shape
229            super.drawFirstPassShape(g2, pass, series, item, shape);
230        }
231    }
232
233    /**
234     * Tests this renderer for equality with an arbitrary object.
235     *
236     * @param obj  the object (<code>null</code> permitted).
237     *
238     * @return A boolean.
239     */
240    @Override
241    public boolean equals(Object obj) {
242        if (obj == this) {
243            return true;
244        }
245        if (!(obj instanceof XYLine3DRenderer)) {
246            return false;
247        }
248        XYLine3DRenderer that = (XYLine3DRenderer) obj;
249        if (this.xOffset != that.xOffset) {
250            return false;
251        }
252        if (this.yOffset != that.yOffset) {
253            return false;
254        }
255        if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
256            return false;
257        }
258        return super.equals(obj);
259    }
260
261    /**
262     * Provides serialization support.
263     *
264     * @param stream  the input stream.
265     *
266     * @throws IOException  if there is an I/O error.
267     * @throws ClassNotFoundException  if there is a classpath problem.
268     */
269    private void readObject(ObjectInputStream stream)
270            throws IOException, ClassNotFoundException {
271        stream.defaultReadObject();
272        this.wallPaint = SerialUtilities.readPaint(stream);
273    }
274
275    /**
276     * Provides serialization support.
277     *
278     * @param stream  the output stream.
279     *
280     * @throws IOException  if there is an I/O error.
281     */
282    private void writeObject(ObjectOutputStream stream) throws IOException {
283        stream.defaultWriteObject();
284        SerialUtilities.writePaint(this.wallPaint, stream);
285    }
286
287}