001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2011, 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 * XYDrawableAnnotation.java
029 * -------------------------
030 * (C) Copyright 2003-2009, 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        super();
121        if (drawable == null) {
122            throw new IllegalArgumentException("Null 'drawable' argument.");
123        }
124        this.x = x;
125        this.y = y;
126        this.displayWidth = displayWidth;
127        this.displayHeight = displayHeight;
128        this.drawScaleFactor = drawScaleFactor;
129        this.drawable = drawable;
130
131    }
132
133    /**
134     * Draws the annotation.
135     *
136     * @param g2  the graphics device.
137     * @param plot  the plot.
138     * @param dataArea  the data area.
139     * @param domainAxis  the domain axis.
140     * @param rangeAxis  the range axis.
141     * @param rendererIndex  the renderer index.
142     * @param info  if supplied, this info object will be populated with
143     *              entity information.
144     */
145    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
146                     ValueAxis domainAxis, ValueAxis rangeAxis,
147                     int rendererIndex,
148                     PlotRenderingInfo info) {
149
150        PlotOrientation orientation = plot.getOrientation();
151        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
152                plot.getDomainAxisLocation(), orientation);
153        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
154                plot.getRangeAxisLocation(), orientation);
155        float j2DX = (float) domainAxis.valueToJava2D(this.x, dataArea,
156                domainEdge);
157        float j2DY = (float) rangeAxis.valueToJava2D(this.y, dataArea,
158                rangeEdge);
159        Rectangle2D displayArea = new Rectangle2D.Double(
160                j2DX - this.displayWidth / 2.0,
161                j2DY - this.displayHeight / 2.0, this.displayWidth,
162                this.displayHeight);
163
164        // here we change the AffineTransform so we can draw the annotation
165        // to a larger area and scale it down into the display area
166        // afterwards, the original transform is restored
167        AffineTransform savedTransform = g2.getTransform();
168        Rectangle2D drawArea = new Rectangle2D.Double(0.0, 0.0,
169                this.displayWidth * this.drawScaleFactor,
170                this.displayHeight * this.drawScaleFactor);
171
172        g2.scale(1 / this.drawScaleFactor, 1 / this.drawScaleFactor);
173        g2.translate((j2DX - this.displayWidth / 2.0) * this.drawScaleFactor,
174                (j2DY - this.displayHeight / 2.0) * this.drawScaleFactor);
175        this.drawable.draw(g2, drawArea);
176        g2.setTransform(savedTransform);
177        String toolTip = getToolTipText();
178        String url = getURL();
179        if (toolTip != null || url != null) {
180            addEntity(info, displayArea, rendererIndex, toolTip, url);
181        }
182
183    }
184
185    /**
186     * Tests this annotation for equality with an arbitrary object.
187     *
188     * @param obj  the object to test against.
189     *
190     * @return <code>true</code> or <code>false</code>.
191     */
192    public boolean equals(Object obj) {
193
194        if (obj == this) { // simple case
195            return true;
196        }
197        // now try to reject equality...
198        if (!super.equals(obj)) {
199            return false;
200        }
201        if (!(obj instanceof XYDrawableAnnotation)) {
202            return false;
203        }
204        XYDrawableAnnotation that = (XYDrawableAnnotation) obj;
205        if (this.x != that.x) {
206            return false;
207        }
208        if (this.y != that.y) {
209            return false;
210        }
211        if (this.displayWidth != that.displayWidth) {
212            return false;
213        }
214        if (this.displayHeight != that.displayHeight) {
215            return false;
216        }
217        if (this.drawScaleFactor != that.drawScaleFactor) {
218            return false;
219        }
220        if (!ObjectUtilities.equal(this.drawable, that.drawable)) {
221            return false;
222        }
223        // seem to be the same...
224        return true;
225
226    }
227
228    /**
229     * Returns a hash code.
230     *
231     * @return A hash code.
232     */
233    public int hashCode() {
234        int result;
235        long temp;
236        temp = Double.doubleToLongBits(this.x);
237        result = (int) (temp ^ (temp >>> 32));
238        temp = Double.doubleToLongBits(this.y);
239        result = 29 * result + (int) (temp ^ (temp >>> 32));
240        temp = Double.doubleToLongBits(this.displayWidth);
241        result = 29 * result + (int) (temp ^ (temp >>> 32));
242        temp = Double.doubleToLongBits(this.displayHeight);
243        result = 29 * result + (int) (temp ^ (temp >>> 32));
244        return result;
245    }
246
247    /**
248     * Returns a clone of the annotation.
249     *
250     * @return A clone.
251     *
252     * @throws CloneNotSupportedException  if the annotation can't be cloned.
253     */
254    public Object clone() throws CloneNotSupportedException {
255        return super.clone();
256    }
257
258}