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 * XYDrawableAnnotation.java
029 * -------------------------
030 * (C) Copyright 2003-2008, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes:
036 * --------
037 * 21-May-2003 : Version 1 (DG);
038 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
039 * 30-Sep-2004 : Added support for tool tips and URLs (DG);
040 * 18-Jun-2008 : Added scaling factor (DG);
041 *
042 */
043
044package org.jfree.chart.annotations;
045
046import java.awt.Graphics2D;
047import java.awt.geom.AffineTransform;
048import java.awt.geom.Rectangle2D;
049import java.io.Serializable;
050
051import org.jfree.chart.axis.ValueAxis;
052import org.jfree.chart.plot.Plot;
053import org.jfree.chart.plot.PlotOrientation;
054import org.jfree.chart.plot.PlotRenderingInfo;
055import org.jfree.chart.plot.XYPlot;
056import org.jfree.ui.Drawable;
057import org.jfree.ui.RectangleEdge;
058import org.jfree.util.ObjectUtilities;
059import org.jfree.util.PublicCloneable;
060
061/**
062 * A general annotation that can be placed on an {@link XYPlot}.
063 */
064public class XYDrawableAnnotation extends AbstractXYAnnotation
065        implements Cloneable, PublicCloneable, Serializable {
066
067    /** For serialization. */
068    private static final long serialVersionUID = -6540812859722691020L;
069
070    /** The scaling factor. */
071    private double drawScaleFactor;
072
073    /** The x-coordinate. */
074    private double x;
075
076    /** The y-coordinate. */
077    private double y;
078
079    /** The width. */
080    private double displayWidth;
081
082    /** The height. */
083    private double displayHeight;
084
085    /** The drawable object. */
086    private Drawable drawable;
087
088    /**
089     * Creates a new annotation to be displayed within the given area.
090     *
091     * @param x  the x-coordinate for the area.
092     * @param y  the y-coordinate for the area.
093     * @param width  the width of the area.
094     * @param height  the height of the area.
095     * @param drawable  the drawable object (<code>null</code> not permitted).
096     */
097    public XYDrawableAnnotation(double x, double y, double width, double height,
098                                Drawable drawable) {
099        this(x, y, width, height, 1.0, drawable);
100    }
101
102    /**
103     * Creates a new annotation to be displayed within the given area.  If you
104     * specify a <code>drawScaleFactor</code> of 2.0, the <code>drawable</code>
105     * will be drawn at twice the requested display size then scaled down to
106     * fit the space.
107     *
108     * @param x  the x-coordinate for the area.
109     * @param y  the y-coordinate for the area.
110     * @param displayWidth  the width of the area.
111     * @param displayHeight  the height of the area.
112     * @param drawScaleFactor  the scaling factor for drawing.
113     * @param drawable  the drawable object (<code>null</code> not permitted).
114     *
115     * @since 1.0.11
116     */
117    public XYDrawableAnnotation(double x, double y, double displayWidth,
118            double displayHeight, double drawScaleFactor, Drawable drawable) {
119
120        if (drawable == null) {
121            throw new IllegalArgumentException("Null 'drawable' argument.");
122        }
123        this.x = x;
124        this.y = y;
125        this.displayWidth = displayWidth;
126        this.displayHeight = displayHeight;
127        this.drawScaleFactor = drawScaleFactor;
128        this.drawable = drawable;
129
130    }
131
132    /**
133     * Draws the annotation.
134     *
135     * @param g2  the graphics device.
136     * @param plot  the plot.
137     * @param dataArea  the data area.
138     * @param domainAxis  the domain axis.
139     * @param rangeAxis  the range axis.
140     * @param rendererIndex  the renderer index.
141     * @param info  if supplied, this info object will be populated with
142     *              entity information.
143     */
144    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
145                     ValueAxis domainAxis, ValueAxis rangeAxis,
146                     int rendererIndex,
147                     PlotRenderingInfo info) {
148
149        PlotOrientation orientation = plot.getOrientation();
150        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
151                plot.getDomainAxisLocation(), orientation);
152        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
153                plot.getRangeAxisLocation(), orientation);
154        float j2DX = (float) domainAxis.valueToJava2D(this.x, dataArea,
155                domainEdge);
156        float j2DY = (float) rangeAxis.valueToJava2D(this.y, dataArea,
157                rangeEdge);
158        Rectangle2D displayArea = new Rectangle2D.Double(
159                j2DX - this.displayWidth / 2.0,
160                j2DY - this.displayHeight / 2.0, this.displayWidth,
161                this.displayHeight);
162
163        // here we change the AffineTransform so we can draw the annotation
164        // to a larger area and scale it down into the display area
165        // afterwards, the original transform is restored
166        AffineTransform savedTransform = g2.getTransform();
167        Rectangle2D drawArea = new Rectangle2D.Double(0.0, 0.0,
168                this.displayWidth * this.drawScaleFactor,
169                this.displayHeight * this.drawScaleFactor);
170
171        g2.scale(1/this.drawScaleFactor, 1/this.drawScaleFactor);
172        g2.translate((j2DX - this.displayWidth / 2.0) * this.drawScaleFactor,
173                (j2DY - this.displayHeight / 2.0) * this.drawScaleFactor);
174        this.drawable.draw(g2, drawArea);
175        g2.setTransform(savedTransform);
176        String toolTip = getToolTipText();
177        String url = getURL();
178        if (toolTip != null || url != null) {
179            addEntity(info, displayArea, rendererIndex, toolTip, url);
180        }
181
182    }
183
184    /**
185     * Tests this annotation for equality with an arbitrary object.
186     *
187     * @param obj  the object to test against.
188     *
189     * @return <code>true</code> or <code>false</code>.
190     */
191    public boolean equals(Object obj) {
192
193        if (obj == this) { // simple case
194            return true;
195        }
196        // now try to reject equality...
197        if (!super.equals(obj)) {
198            return false;
199        }
200        if (!(obj instanceof XYDrawableAnnotation)) {
201            return false;
202        }
203        XYDrawableAnnotation that = (XYDrawableAnnotation) obj;
204        if (this.x != that.x) {
205            return false;
206        }
207        if (this.y != that.y) {
208            return false;
209        }
210        if (this.displayWidth != that.displayWidth) {
211            return false;
212        }
213        if (this.displayHeight != that.displayHeight) {
214            return false;
215        }
216        if (this.drawScaleFactor != that.drawScaleFactor) {
217            return false;
218        }
219        if (!ObjectUtilities.equal(this.drawable, that.drawable)) {
220            return false;
221        }
222        // seem to be the same...
223        return true;
224
225    }
226
227    /**
228     * Returns a hash code.
229     *
230     * @return A hash code.
231     */
232    public int hashCode() {
233        int result;
234        long temp;
235        temp = Double.doubleToLongBits(this.x);
236        result = (int) (temp ^ (temp >>> 32));
237        temp = Double.doubleToLongBits(this.y);
238        result = 29 * result + (int) (temp ^ (temp >>> 32));
239        temp = Double.doubleToLongBits(this.displayWidth);
240        result = 29 * result + (int) (temp ^ (temp >>> 32));
241        temp = Double.doubleToLongBits(this.displayHeight);
242        result = 29 * result + (int) (temp ^ (temp >>> 32));
243        return result;
244    }
245
246    /**
247     * Returns a clone of the annotation.
248     *
249     * @return A clone.
250     *
251     * @throws CloneNotSupportedException  if the annotation can't be cloned.
252     */
253    public Object clone() throws CloneNotSupportedException {
254        return super.clone();
255    }
256
257}