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