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 * XYBoxAnnotation.java 029 * -------------------- 030 * (C) Copyright 2005-2022, by David Gilbert and Contributors. 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.Rectangle2D; 045import java.io.IOException; 046import java.io.ObjectInputStream; 047import java.io.ObjectOutputStream; 048import java.io.Serializable; 049import java.util.Objects; 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.chart.api.RectangleEdge; 057import org.jfree.chart.internal.PaintUtils; 058import org.jfree.chart.api.PublicCloneable; 059import org.jfree.chart.internal.Args; 060import org.jfree.chart.internal.SerialUtils; 061 062/** 063 * A box annotation that can be placed on an {@link XYPlot}. The 064 * box coordinates are specified in data space. 065 */ 066public class XYBoxAnnotation extends AbstractXYAnnotation 067 implements Cloneable, PublicCloneable, Serializable { 068 069 /** For serialization. */ 070 private static final long serialVersionUID = 6764703772526757457L; 071 072 /** The lower x-coordinate. */ 073 private double x0; 074 075 /** The lower y-coordinate. */ 076 private double y0; 077 078 /** The upper x-coordinate. */ 079 private double x1; 080 081 /** The upper y-coordinate. */ 082 private double y1; 083 084 /** The stroke used to draw the box outline. */ 085 private transient Stroke stroke; 086 087 /** The paint used to draw the box outline. */ 088 private transient Paint outlinePaint; 089 090 /** The paint used to fill the box. */ 091 private transient Paint fillPaint; 092 093 /** 094 * Creates a new annotation (where, by default, the box is drawn 095 * with a black outline). 096 * 097 * @param x0 the lower x-coordinate of the box (in data space). 098 * @param y0 the lower y-coordinate of the box (in data space). 099 * @param x1 the upper x-coordinate of the box (in data space). 100 * @param y1 the upper y-coordinate of the box (in data space). 101 */ 102 public XYBoxAnnotation(double x0, double y0, double x1, double y1) { 103 this(x0, y0, x1, y1, new BasicStroke(1.0f), Color.BLACK); 104 } 105 106 /** 107 * Creates a new annotation where the box is drawn as an outline using 108 * the specified {@code stroke} and {@code outlinePaint}. 109 * 110 * @param x0 the lower x-coordinate of the box (in data space). 111 * @param y0 the lower y-coordinate of the box (in data space). 112 * @param x1 the upper x-coordinate of the box (in data space). 113 * @param y1 the upper y-coordinate of the box (in data space). 114 * @param stroke the shape stroke ({@code null} permitted). 115 * @param outlinePaint the shape color ({@code null} permitted). 116 */ 117 public XYBoxAnnotation(double x0, double y0, double x1, double y1, 118 Stroke stroke, Paint outlinePaint) { 119 this(x0, y0, x1, y1, stroke, outlinePaint, null); 120 } 121 122 /** 123 * Creates a new annotation. 124 * 125 * @param x0 the lower x-coordinate of the box (in data space, must be finite). 126 * @param y0 the lower y-coordinate of the box (in data space, must be finite). 127 * @param x1 the upper x-coordinate of the box (in data space, must be finite). 128 * @param y1 the upper y-coordinate of the box (in data space, must be finite). 129 * @param stroke the shape stroke ({@code null} permitted). 130 * @param outlinePaint the shape color ({@code null} permitted). 131 * @param fillPaint the paint used to fill the shape ({@code null} 132 * permitted). 133 */ 134 public XYBoxAnnotation(double x0, double y0, double x1, double y1, 135 Stroke stroke, Paint outlinePaint, Paint fillPaint) { 136 super(); 137 Args.requireFinite(x0, "x0"); 138 Args.requireFinite(y0, "y0"); 139 Args.requireFinite(x1, "x1"); 140 Args.requireFinite(y1, "y1"); 141 this.x0 = x0; 142 this.y0 = y0; 143 this.x1 = x1; 144 this.y1 = y1; 145 this.stroke = stroke; 146 this.outlinePaint = outlinePaint; 147 this.fillPaint = fillPaint; 148 } 149 150 /** 151 * Returns the x-coordinate for the bottom left corner of the box (set in the 152 * constructor). 153 * 154 * @return The x-coordinate for the bottom left corner of the box. 155 */ 156 public double getX0() { 157 return x0; 158 } 159 160 /** 161 * Returns the y-coordinate for the bottom left corner of the box (set in the 162 * constructor). 163 * 164 * @return The y-coordinate for the bottom left corner of the box. 165 */ 166 public double getY0() { 167 return y0; 168 } 169 170 /** 171 * Returns the x-coordinate for the top right corner of the box (set in the 172 * constructor). 173 * 174 * @return The x-coordinate for the top right corner of the box. 175 */ 176 public double getX1() { 177 return x1; 178 } 179 180 /** 181 * Returns the y-coordinate for the top right corner of the box (set in the 182 * constructor). 183 * 184 * @return The y-coordinate for the top right corner of the box. 185 */ 186 public double getY1() { 187 return y1; 188 } 189 190 /** 191 * Returns the stroke used for the box outline. 192 * 193 * @return The stroke (possibly {@code null}). 194 */ 195 public Stroke getStroke() { 196 return stroke; 197 } 198 199 /** 200 * Returns the paint used for the box outline. 201 * 202 * @return The paint (possibly {@code null}). 203 */ 204 public Paint getOutlinePaint() { 205 return outlinePaint; 206 } 207 208 /** 209 * Returns the paint used for the box fill. 210 * 211 * @return The paint (possibly {@code null}). 212 */ 213 public Paint getFillPaint() { 214 return fillPaint; 215 } 216 217 /** 218 * Draws the annotation. This method is usually called by the 219 * {@link XYPlot} class, you shouldn't need to call it directly. 220 * 221 * @param g2 the graphics device. 222 * @param plot the plot. 223 * @param dataArea the data area. 224 * @param domainAxis the domain axis. 225 * @param rangeAxis the range axis. 226 * @param rendererIndex the renderer index. 227 * @param info the plot rendering info. 228 */ 229 @Override 230 public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, 231 ValueAxis domainAxis, ValueAxis rangeAxis, 232 int rendererIndex, PlotRenderingInfo info) { 233 234 PlotOrientation orientation = plot.getOrientation(); 235 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 236 plot.getDomainAxisLocation(), orientation); 237 RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 238 plot.getRangeAxisLocation(), orientation); 239 240 double transX0 = domainAxis.valueToJava2D(this.x0, dataArea, 241 domainEdge); 242 double transY0 = rangeAxis.valueToJava2D(this.y0, dataArea, rangeEdge); 243 double transX1 = domainAxis.valueToJava2D(this.x1, dataArea, 244 domainEdge); 245 double transY1 = rangeAxis.valueToJava2D(this.y1, dataArea, rangeEdge); 246 247 Rectangle2D box = null; 248 if (orientation == PlotOrientation.HORIZONTAL) { 249 box = new Rectangle2D.Double(transY0, transX1, transY1 - transY0, 250 transX0 - transX1); 251 } else if (orientation == PlotOrientation.VERTICAL) { 252 box = new Rectangle2D.Double(transX0, transY1, transX1 - transX0, 253 transY0 - transY1); 254 } 255 256 if (this.fillPaint != null) { 257 g2.setPaint(this.fillPaint); 258 g2.fill(box); 259 } 260 261 if (this.stroke != null && this.outlinePaint != null) { 262 g2.setPaint(this.outlinePaint); 263 g2.setStroke(this.stroke); 264 g2.draw(box); 265 } 266 addEntity(info, box, rendererIndex, getToolTipText(), getURL()); 267 } 268 269 /** 270 * Tests this annotation for equality with an arbitrary object. 271 * 272 * @param obj the object ({@code null} permitted). 273 * 274 * @return A boolean. 275 */ 276 @Override 277 public boolean equals(Object obj) { 278 if (obj == this) { 279 return true; 280 } 281 // now try to reject equality 282 if (!super.equals(obj)) { 283 return false; 284 } 285 if (!(obj instanceof XYBoxAnnotation)) { 286 return false; 287 } 288 XYBoxAnnotation that = (XYBoxAnnotation) obj; 289 if (!(this.x0 == that.x0)) { 290 return false; 291 } 292 if (!(this.y0 == that.y0)) { 293 return false; 294 } 295 if (!(this.x1 == that.x1)) { 296 return false; 297 } 298 if (!(this.y1 == that.y1)) { 299 return false; 300 } 301 if (!Objects.equals(this.stroke, that.stroke)) { 302 return false; 303 } 304 if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { 305 return false; 306 } 307 if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { 308 return false; 309 } 310 return true; 311 } 312 313 /** 314 * Returns a hash code. 315 * 316 * @return A hash code. 317 */ 318 @Override 319 public int hashCode() { 320 int result; 321 long temp; 322 temp = Double.doubleToLongBits(this.x0); 323 result = (int) (temp ^ (temp >>> 32)); 324 temp = Double.doubleToLongBits(this.x1); 325 result = 29 * result + (int) (temp ^ (temp >>> 32)); 326 temp = Double.doubleToLongBits(this.y0); 327 result = 29 * result + (int) (temp ^ (temp >>> 32)); 328 temp = Double.doubleToLongBits(this.y1); 329 result = 29 * result + (int) (temp ^ (temp >>> 32)); 330 return result; 331 } 332 333 /** 334 * Returns a clone. 335 * 336 * @return A clone. 337 * 338 * @throws CloneNotSupportedException not thrown by this class, but may be 339 * by subclasses. 340 */ 341 @Override 342 public Object clone() throws CloneNotSupportedException { 343 return super.clone(); 344 } 345 346 /** 347 * Provides serialization support. 348 * 349 * @param stream the output stream ({@code null} not permitted). 350 * 351 * @throws IOException if there is an I/O error. 352 */ 353 private void writeObject(ObjectOutputStream stream) throws IOException { 354 stream.defaultWriteObject(); 355 SerialUtils.writeStroke(this.stroke, stream); 356 SerialUtils.writePaint(this.outlinePaint, stream); 357 SerialUtils.writePaint(this.fillPaint, stream); 358 } 359 360 /** 361 * Provides serialization support. 362 * 363 * @param stream the input stream ({@code null} not permitted). 364 * 365 * @throws IOException if there is an I/O error. 366 * @throws ClassNotFoundException if there is a classpath problem. 367 */ 368 private void readObject(ObjectInputStream stream) 369 throws IOException, ClassNotFoundException { 370 stream.defaultReadObject(); 371 this.stroke = SerialUtils.readStroke(stream); 372 this.outlinePaint = SerialUtils.readPaint(stream); 373 this.fillPaint = SerialUtils.readPaint(stream); 374 } 375 376}