    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}