    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     * XYLine3DRenderer.java
    029     * ---------------------
    030     * (C) Copyright 2005, 2007, 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    }