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 * XYLineAnnotation.java
029 * ---------------------
030 * (C) Copyright 2003-2013, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   Peter Kolb (see patch 2809117);
034 *
035 * Changes:
036 * --------
037 * 02-Apr-2003 : Version 1 (DG);
038 * 19-Aug-2003 : Added equals method, implemented Cloneable, and applied
039 *               serialization fixes (DG);
040 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
041 * 14-Apr-2004 : Fixed draw() method to handle plot orientation correctly (DG);
042 * 29-Sep-2004 : Added support for tool tips and URLS, now extends
043 *               AbstractXYAnnotation (DG);
044 * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
045 * 08-Jun-2005 : Fixed equals() method to handle GradientPaint() (DG);
046 * 05-Nov-2008 : Added workaround for JRE bug 6574155, see JFreeChart bug
047 *               2221495 (DG);
048 * 02-Jul-2013 : Use ParamChecks (DG);
049 *
050 */
051
052package org.jfree.chart.annotations;
053
054import java.awt.BasicStroke;
055import java.awt.Color;
056import java.awt.Graphics2D;
057import java.awt.Paint;
058import java.awt.Stroke;
059import java.awt.geom.Line2D;
060import java.awt.geom.Rectangle2D;
061import java.io.IOException;
062import java.io.ObjectInputStream;
063import java.io.ObjectOutputStream;
064import java.io.Serializable;
065
066import org.jfree.chart.axis.ValueAxis;
067import org.jfree.chart.plot.Plot;
068import org.jfree.chart.plot.PlotOrientation;
069import org.jfree.chart.plot.PlotRenderingInfo;
070import org.jfree.chart.plot.XYPlot;
071import org.jfree.chart.util.LineUtilities;
072import org.jfree.chart.util.ParamChecks;
073import org.jfree.io.SerialUtilities;
074import org.jfree.ui.RectangleEdge;
075import org.jfree.util.ObjectUtilities;
076import org.jfree.util.PaintUtilities;
077import org.jfree.util.PublicCloneable;
078import org.jfree.util.ShapeUtilities;
079
080/**
081 * A simple line annotation that can be placed on an {@link XYPlot}.
082 */
083public class XYLineAnnotation extends AbstractXYAnnotation
084        implements Cloneable, PublicCloneable, Serializable {
085
086    /** For serialization. */
087    private static final long serialVersionUID = -80535465244091334L;
088
089    /** The x-coordinate. */
090    private double x1;
091
092    /** The y-coordinate. */
093    private double y1;
094
095    /** The x-coordinate. */
096    private double x2;
097
098    /** The y-coordinate. */
099    private double y2;
100
101    /** The line stroke. */
102    private transient Stroke stroke;
103
104    /** The line color. */
105    private transient Paint paint;
106
107    /**
108     * Creates a new annotation that draws a line from (x1, y1) to (x2, y2)
109     * where the coordinates are measured in data space (that is, against the
110     * plot's axes).
111     *
112     * @param x1  the x-coordinate for the start of the line.
113     * @param y1  the y-coordinate for the start of the line.
114     * @param x2  the x-coordinate for the end of the line.
115     * @param y2  the y-coordinate for the end of the line.
116     */
117    public XYLineAnnotation(double x1, double y1, double x2, double y2) {
118        this(x1, y1, x2, y2, new BasicStroke(1.0f), Color.black);
119    }
120
121    /**
122     * Creates a new annotation that draws a line from (x1, y1) to (x2, y2)
123     * where the coordinates are measured in data space (that is, against the
124     * plot's axes).
125     *
126     * @param x1  the x-coordinate for the start of the line.
127     * @param y1  the y-coordinate for the start of the line.
128     * @param x2  the x-coordinate for the end of the line.
129     * @param y2  the y-coordinate for the end of the line.
130     * @param stroke  the line stroke (<code>null</code> not permitted).
131     * @param paint  the line color (<code>null</code> not permitted).
132     */
133    public XYLineAnnotation(double x1, double y1, double x2, double y2,
134                            Stroke stroke, Paint paint) {
135
136        super();
137        ParamChecks.nullNotPermitted(stroke, "stroke");
138        ParamChecks.nullNotPermitted(paint, "paint");
139        this.x1 = x1;
140        this.y1 = y1;
141        this.x2 = x2;
142        this.y2 = y2;
143        this.stroke = stroke;
144        this.paint = paint;
145
146    }
147
148    /**
149     * Draws the annotation.  This method is called by the {@link XYPlot}
150     * class, you won't normally need to call it yourself.
151     *
152     * @param g2  the graphics device.
153     * @param plot  the plot.
154     * @param dataArea  the data area.
155     * @param domainAxis  the domain axis.
156     * @param rangeAxis  the range axis.
157     * @param rendererIndex  the renderer index.
158     * @param info  if supplied, this info object will be populated with
159     *              entity information.
160     */
161    @Override
162    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
163                     ValueAxis domainAxis, ValueAxis rangeAxis,
164                     int rendererIndex,
165                     PlotRenderingInfo info) {
166
167        PlotOrientation orientation = plot.getOrientation();
168        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
169                plot.getDomainAxisLocation(), orientation);
170        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
171                plot.getRangeAxisLocation(), orientation);
172        float j2DX1 = 0.0f;
173        float j2DX2 = 0.0f;
174        float j2DY1 = 0.0f;
175        float j2DY2 = 0.0f;
176        if (orientation == PlotOrientation.VERTICAL) {
177            j2DX1 = (float) domainAxis.valueToJava2D(this.x1, dataArea,
178                    domainEdge);
179            j2DY1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea,
180                    rangeEdge);
181            j2DX2 = (float) domainAxis.valueToJava2D(this.x2, dataArea,
182                    domainEdge);
183            j2DY2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea,
184                    rangeEdge);
185        }
186        else if (orientation == PlotOrientation.HORIZONTAL) {
187            j2DY1 = (float) domainAxis.valueToJava2D(this.x1, dataArea,
188                    domainEdge);
189            j2DX1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea,
190                    rangeEdge);
191            j2DY2 = (float) domainAxis.valueToJava2D(this.x2, dataArea,
192                    domainEdge);
193            j2DX2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea,
194                    rangeEdge);
195        }
196        g2.setPaint(this.paint);
197        g2.setStroke(this.stroke);
198        Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2);
199        // line is clipped to avoid JRE bug 6574155, for more info
200        // see JFreeChart bug 2221495
201        boolean visible = LineUtilities.clipLine(line, dataArea);
202        if (visible) {
203            g2.draw(line);
204        }
205
206        String toolTip = getToolTipText();
207        String url = getURL();
208        if (toolTip != null || url != null) {
209            addEntity(info, ShapeUtilities.createLineRegion(line, 1.0f),
210                    rendererIndex, toolTip, url);
211        }
212    }
213
214    /**
215     * Tests this object for equality with an arbitrary object.
216     *
217     * @param obj  the object to test against (<code>null</code> permitted).
218     *
219     * @return <code>true</code> or <code>false</code>.
220     */
221    @Override
222    public boolean equals(Object obj) {
223        if (obj == this) {
224            return true;
225        }
226        if (!super.equals(obj)) {
227            return false;
228        }
229        if (!(obj instanceof XYLineAnnotation)) {
230            return false;
231        }
232        XYLineAnnotation that = (XYLineAnnotation) obj;
233        if (this.x1 != that.x1) {
234            return false;
235        }
236        if (this.y1 != that.y1) {
237            return false;
238        }
239        if (this.x2 != that.x2) {
240            return false;
241        }
242        if (this.y2 != that.y2) {
243            return false;
244        }
245        if (!PaintUtilities.equal(this.paint, that.paint)) {
246            return false;
247        }
248        if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
249            return false;
250        }
251        // seems to be the same...
252        return true;
253    }
254
255    /**
256     * Returns a hash code.
257     *
258     * @return A hash code.
259     */
260    @Override
261    public int hashCode() {
262        int result;
263        long temp;
264        temp = Double.doubleToLongBits(this.x1);
265        result = (int) (temp ^ (temp >>> 32));
266        temp = Double.doubleToLongBits(this.x2);
267        result = 29 * result + (int) (temp ^ (temp >>> 32));
268        temp = Double.doubleToLongBits(this.y1);
269        result = 29 * result + (int) (temp ^ (temp >>> 32));
270        temp = Double.doubleToLongBits(this.y2);
271        result = 29 * result + (int) (temp ^ (temp >>> 32));
272        return result;
273    }
274
275    /**
276     * Returns a clone of the annotation.
277     *
278     * @return A clone.
279     *
280     * @throws CloneNotSupportedException  if the annotation can't be cloned.
281     */
282    @Override
283    public Object clone() throws CloneNotSupportedException {
284        return super.clone();
285    }
286
287    /**
288     * Provides serialization support.
289     *
290     * @param stream  the output stream.
291     *
292     * @throws IOException  if there is an I/O error.
293     */
294    private void writeObject(ObjectOutputStream stream) throws IOException {
295        stream.defaultWriteObject();
296        SerialUtilities.writePaint(this.paint, stream);
297        SerialUtilities.writeStroke(this.stroke, stream);
298    }
299
300    /**
301     * Provides serialization support.
302     *
303     * @param stream  the input stream.
304     *
305     * @throws IOException  if there is an I/O error.
306     * @throws ClassNotFoundException  if there is a classpath problem.
307     */
308    private void readObject(ObjectInputStream stream)
309        throws IOException, ClassNotFoundException {
310        stream.defaultReadObject();
311        this.paint = SerialUtilities.readPaint(stream);
312        this.stroke = SerialUtilities.readStroke(stream);
313    }
314
315}