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