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