001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert 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-2022, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Peter Kolb (see patch 2809117); 034 * 035 */ 036 037package org.jfree.chart.annotations; 038 039import java.awt.BasicStroke; 040import java.awt.Color; 041import java.awt.Graphics2D; 042import java.awt.Paint; 043import java.awt.Stroke; 044import java.awt.geom.Line2D; 045import java.awt.geom.Rectangle2D; 046import java.io.IOException; 047import java.io.ObjectInputStream; 048import java.io.ObjectOutputStream; 049import java.io.Serializable; 050import java.util.Objects; 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.api.RectangleEdge; 058import org.jfree.chart.internal.LineUtils; 059import org.jfree.chart.internal.PaintUtils; 060import org.jfree.chart.internal.Args; 061import org.jfree.chart.api.PublicCloneable; 062import org.jfree.chart.internal.SerialUtils; 063import org.jfree.chart.internal.ShapeUtils; 064 065/** 066 * A simple line annotation that can be placed on an {@link XYPlot}. 067 * Instances of this class are immutable. 068 */ 069public class XYLineAnnotation extends AbstractXYAnnotation 070 implements Cloneable, PublicCloneable, Serializable { 071 072 /** For serialization. */ 073 private static final long serialVersionUID = -80535465244091334L; 074 075 /** The x-coordinate. */ 076 private double x1; 077 078 /** The y-coordinate. */ 079 private double y1; 080 081 /** The x-coordinate. */ 082 private double x2; 083 084 /** The y-coordinate. */ 085 private double y2; 086 087 /** The line stroke. */ 088 private transient Stroke stroke; 089 090 /** The line color. */ 091 private transient Paint paint; 092 093 /** 094 * Creates a new annotation that draws a line from (x1, y1) to (x2, y2) 095 * where the coordinates are measured in data space (that is, against the 096 * plot's axes). All the line coordinates are required to be finite values. 097 * 098 * @param x1 the x-coordinate for the start of the line. 099 * @param y1 the y-coordinate for the start of the line. 100 * @param x2 the x-coordinate for the end of the line. 101 * @param y2 the y-coordinate for the end of the line. 102 */ 103 public XYLineAnnotation(double x1, double y1, double x2, double y2) { 104 this(x1, y1, x2, y2, new BasicStroke(1.0f), Color.BLACK); 105 } 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 (must be finite). 113 * @param y1 the y-coordinate for the start of the line (must be finite). 114 * @param x2 the x-coordinate for the end of the line (must be finite). 115 * @param y2 the y-coordinate for the end of the line (must be finite). 116 * @param stroke the line stroke ({@code null} not permitted). 117 * @param paint the line color ({@code null} not permitted). 118 */ 119 public XYLineAnnotation(double x1, double y1, double x2, double y2, 120 Stroke stroke, Paint paint) { 121 super(); 122 Args.nullNotPermitted(stroke, "stroke"); 123 Args.nullNotPermitted(paint, "paint"); 124 Args.requireFinite(x1, "x1"); 125 Args.requireFinite(y1, "y1"); 126 Args.requireFinite(x2, "x2"); 127 Args.requireFinite(y2, "y2"); 128 this.x1 = x1; 129 this.y1 = y1; 130 this.x2 = x2; 131 this.y2 = y2; 132 this.stroke = stroke; 133 this.paint = paint; 134 } 135 136 /** 137 * Returns the x-coordinate for the starting point of the line (set in the 138 * constructor). 139 * 140 * @return The x-coordinate for the starting point of the line. 141 */ 142 public double getX1() { 143 return x1; 144 } 145 146 /** 147 * Returns the y-coordinate for the starting point of the line (set in the 148 * constructor). 149 * 150 * @return The y-coordinate for the starting point of the line. 151 */ 152 public double getY1() { 153 return y1; 154 } 155 156 /** 157 * Returns the x-coordinate for the ending point of the line (set in the 158 * constructor). 159 * 160 * @return The x-coordinate for the ending point of the line. 161 */ 162 public double getX2() { 163 return x2; 164 } 165 166 /** 167 * Returns the y-coordinate for the ending point of the line (set in the 168 * constructor). 169 * 170 * @return The y-coordinate for the ending point of the line. 171 */ 172 public double getY2() { 173 return y2; 174 } 175 176 /** 177 * Returns the stroke used for drawing the line (set in the constructor). 178 * 179 * @return The stroke (never {@code null}). 180 */ 181 public Stroke getStroke() { 182 return stroke; 183 } 184 185 /** 186 * Returns the paint used for drawing the line (set in the constructor). 187 * 188 * @return The paint (never {@code null}). 189 */ 190 public Paint getPaint() { 191 return paint; 192 } 193 194 /** 195 * Draws the annotation. This method is called by the {@link XYPlot} 196 * class, you won't normally need to call it yourself. 197 * 198 * @param g2 the graphics device. 199 * @param plot the plot. 200 * @param dataArea the data area. 201 * @param domainAxis the domain axis. 202 * @param rangeAxis the range axis. 203 * @param rendererIndex the renderer index. 204 * @param info if supplied, this info object will be populated with 205 * entity information. 206 */ 207 @Override 208 public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, 209 ValueAxis domainAxis, ValueAxis rangeAxis, 210 int rendererIndex, 211 PlotRenderingInfo info) { 212 213 PlotOrientation orientation = plot.getOrientation(); 214 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 215 plot.getDomainAxisLocation(), orientation); 216 RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 217 plot.getRangeAxisLocation(), orientation); 218 float j2DX1 = 0.0f; 219 float j2DX2 = 0.0f; 220 float j2DY1 = 0.0f; 221 float j2DY2 = 0.0f; 222 if (orientation == PlotOrientation.VERTICAL) { 223 j2DX1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, 224 domainEdge); 225 j2DY1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, 226 rangeEdge); 227 j2DX2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, 228 domainEdge); 229 j2DY2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, 230 rangeEdge); 231 } else if (orientation == PlotOrientation.HORIZONTAL) { 232 j2DY1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, 233 domainEdge); 234 j2DX1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, 235 rangeEdge); 236 j2DY2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, 237 domainEdge); 238 j2DX2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, 239 rangeEdge); 240 } 241 g2.setPaint(this.paint); 242 g2.setStroke(this.stroke); 243 Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2); 244 // line is clipped to avoid JRE bug 6574155, for more info 245 // see JFreeChart bug 2221495 246 boolean visible = LineUtils.clipLine(line, dataArea); 247 if (visible) { 248 g2.draw(line); 249 } 250 251 String toolTip = getToolTipText(); 252 String url = getURL(); 253 if (toolTip != null || url != null) { 254 addEntity(info, ShapeUtils.createLineRegion(line, 1.0f), 255 rendererIndex, toolTip, url); 256 } 257 } 258 259 /** 260 * Tests this object for equality with an arbitrary object. 261 * 262 * @param obj the object to test against ({@code null} permitted). 263 * 264 * @return {@code true} or {@code false}. 265 */ 266 @Override 267 public boolean equals(Object obj) { 268 if (obj == this) { 269 return true; 270 } 271 if (!super.equals(obj)) { 272 return false; 273 } 274 if (!(obj instanceof XYLineAnnotation)) { 275 return false; 276 } 277 XYLineAnnotation that = (XYLineAnnotation) obj; 278 if (this.x1 != that.x1) { 279 return false; 280 } 281 if (this.y1 != that.y1) { 282 return false; 283 } 284 if (this.x2 != that.x2) { 285 return false; 286 } 287 if (this.y2 != that.y2) { 288 return false; 289 } 290 if (!PaintUtils.equal(this.paint, that.paint)) { 291 return false; 292 } 293 if (!Objects.equals(this.stroke, that.stroke)) { 294 return false; 295 } 296 return true; 297 } 298 299 /** 300 * Returns a hash code. 301 * 302 * @return A hash code. 303 */ 304 @Override 305 public int hashCode() { 306 int result; 307 long temp; 308 temp = Double.doubleToLongBits(this.x1); 309 result = (int) (temp ^ (temp >>> 32)); 310 temp = Double.doubleToLongBits(this.x2); 311 result = 29 * result + (int) (temp ^ (temp >>> 32)); 312 temp = Double.doubleToLongBits(this.y1); 313 result = 29 * result + (int) (temp ^ (temp >>> 32)); 314 temp = Double.doubleToLongBits(this.y2); 315 result = 29 * result + (int) (temp ^ (temp >>> 32)); 316 return result; 317 } 318 319 /** 320 * Returns a clone of the annotation. 321 * 322 * @return A clone. 323 * 324 * @throws CloneNotSupportedException if the annotation can't be cloned. 325 */ 326 @Override 327 public Object clone() throws CloneNotSupportedException { 328 return super.clone(); 329 } 330 331 /** 332 * Provides serialization support. 333 * 334 * @param stream the output stream. 335 * 336 * @throws IOException if there is an I/O error. 337 */ 338 private void writeObject(ObjectOutputStream stream) throws IOException { 339 stream.defaultWriteObject(); 340 SerialUtils.writePaint(this.paint, stream); 341 SerialUtils.writeStroke(this.stroke, stream); 342 } 343 344 /** 345 * Provides serialization support. 346 * 347 * @param stream the input stream. 348 * 349 * @throws IOException if there is an I/O error. 350 * @throws ClassNotFoundException if there is a classpath problem. 351 */ 352 private void readObject(ObjectInputStream stream) 353 throws IOException, ClassNotFoundException { 354 stream.defaultReadObject(); 355 this.paint = SerialUtils.readPaint(stream); 356 this.stroke = SerialUtils.readStroke(stream); 357 } 358 359}