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
044 package org.jfree.chart.annotations;
045
046 import java.awt.Graphics2D;
047 import java.awt.geom.AffineTransform;
048 import java.awt.geom.Rectangle2D;
049 import java.io.Serializable;
050
051 import org.jfree.chart.axis.ValueAxis;
052 import org.jfree.chart.plot.Plot;
053 import org.jfree.chart.plot.PlotOrientation;
054 import org.jfree.chart.plot.PlotRenderingInfo;
055 import org.jfree.chart.plot.XYPlot;
056 import org.jfree.ui.Drawable;
057 import org.jfree.ui.RectangleEdge;
058 import org.jfree.util.ObjectUtilities;
059 import org.jfree.util.PublicCloneable;
060
061 /**
062 * A general annotation that can be placed on an {@link XYPlot}.
063 */
064 public 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 }