001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, 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     * 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    
042    package org.jfree.chart.renderer.xy;
043    
044    import java.awt.Color;
045    import java.awt.Graphics2D;
046    import java.awt.Paint;
047    import java.awt.Shape;
048    import java.io.IOException;
049    import java.io.ObjectInputStream;
050    import java.io.ObjectOutputStream;
051    import java.io.Serializable;
052    
053    import org.jfree.chart.Effect3D;
054    import org.jfree.chart.event.RendererChangeEvent;
055    import org.jfree.io.SerialUtilities;
056    import org.jfree.util.PaintUtilities;
057    
058    /**
059     * A XYLineAndShapeRenderer that adds a shadow line to the graph
060     * to emulate a 3D-effect.
061     */
062    public 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        public double getXOffset() {
101            return this.xOffset;
102        }
103    
104        /**
105         * Returns the y-offset for the 3D effect.
106         *
107         * @return The 3D effect.
108         */
109        public double getYOffset() {
110            return this.yOffset;
111        }
112    
113        /**
114         * Sets the x-offset and sends a {@link RendererChangeEvent} to all
115         * registered listeners.
116         *
117         * @param xOffset  the x-offset.
118         */
119        public void setXOffset(double xOffset) {
120            this.xOffset = xOffset;
121            fireChangeEvent();
122        }
123    
124        /**
125         * Sets the y-offset and sends a {@link RendererChangeEvent} to all
126         * registered listeners.
127         *
128         * @param yOffset  the y-offset.
129         */
130        public void setYOffset(double yOffset) {
131            this.yOffset = yOffset;
132            fireChangeEvent();
133        }
134    
135        /**
136         * Returns the paint used to highlight the left and bottom wall in the plot
137         * background.
138         *
139         * @return The paint.
140         */
141        public Paint getWallPaint() {
142            return this.wallPaint;
143        }
144    
145        /**
146         * Sets the paint used to hightlight the left and bottom walls in the plot
147         * background and sends a {@link RendererChangeEvent} to all registered
148         * listeners.
149         *
150         * @param paint  the paint.
151         */
152        public void setWallPaint(Paint paint) {
153            this.wallPaint = paint;
154            fireChangeEvent();
155        }
156    
157        /**
158         * Returns the number of passes through the data that the renderer requires
159         * in order to draw the chart.  Most charts will require a single pass,
160         * but some require two passes.
161         *
162         * @return The pass count.
163         */
164        public int getPassCount() {
165            return 3;
166        }
167    
168        /**
169         * Returns <code>true</code> if the specified pass involves drawing lines.
170         *
171         * @param pass  the pass.
172         *
173         * @return A boolean.
174         */
175        protected boolean isLinePass(int pass) {
176            return pass == 0 || pass == 1;
177        }
178    
179        /**
180         * Returns <code>true</code> if the specified pass involves drawing items.
181         *
182         * @param pass  the pass.
183         *
184         * @return A boolean.
185         */
186        protected boolean isItemPass(int pass) {
187            return pass == 2;
188        }
189    
190        /**
191         * Returns <code>true</code> if the specified pass involves drawing shadows.
192         *
193         * @param pass  the pass.
194         *
195         * @return A boolean.
196         */
197        protected boolean isShadowPass (int pass) {
198            return pass == 0;
199        }
200    
201        /**
202         * Overrides the method in the subclass to draw a shadow in the first pass.
203         *
204         * @param g2  the graphics device.
205         * @param pass  the pass.
206         * @param series  the series index (zero-based).
207         * @param item  the item index (zero-based).
208         * @param shape  the shape.
209         */
210        protected void drawFirstPassShape(Graphics2D g2,
211                                          int pass,
212                                          int series,
213                                          int item,
214                                          Shape shape) {
215            if (isShadowPass(pass)) {
216                if (getWallPaint() != null) {
217                    g2.setStroke(getItemStroke(series, item));
218                    g2.setPaint(getWallPaint());
219                    g2.translate(getXOffset(), getYOffset());
220                    g2.draw(shape);
221                    g2.translate(-getXOffset(), -getYOffset());
222                }
223            }
224            else {
225                // now draw the real shape
226                super.drawFirstPassShape(g2, pass, series, item, shape);
227            }
228        }
229    
230        /**
231         * Tests this renderer for equality with an arbitrary object.
232         *
233         * @param obj  the object (<code>null</code> permitted).
234         *
235         * @return A boolean.
236         */
237        public boolean equals(Object obj) {
238            if (obj == this) {
239                return true;
240            }
241            if (!(obj instanceof XYLine3DRenderer)) {
242                return false;
243            }
244            XYLine3DRenderer that = (XYLine3DRenderer) obj;
245            if (this.xOffset != that.xOffset) {
246                return false;
247            }
248            if (this.yOffset != that.yOffset) {
249                return false;
250            }
251            if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
252                return false;
253            }
254            return super.equals(obj);
255        }
256    
257        /**
258         * Provides serialization support.
259         *
260         * @param stream  the input stream.
261         *
262         * @throws IOException  if there is an I/O error.
263         * @throws ClassNotFoundException  if there is a classpath problem.
264         */
265        private void readObject(ObjectInputStream stream)
266                throws IOException, ClassNotFoundException {
267            stream.defaultReadObject();
268            this.wallPaint = SerialUtilities.readPaint(stream);
269        }
270    
271        /**
272         * Provides serialization support.
273         *
274         * @param stream  the output stream.
275         *
276         * @throws IOException  if there is an I/O error.
277         */
278        private void writeObject(ObjectOutputStream stream) throws IOException {
279            stream.defaultWriteObject();
280            SerialUtilities.writePaint(this.wallPaint, stream);
281        }
282    
283    }