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 * StandardDialRange.java 029 * ---------------------- 030 * (C) Copyright 2006-2013, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 03-Nov-2006 : Version 1 (DG); 038 * 08-Mar-2007 : Fix in hashCode() (DG); 039 * 17-Oct-2007 : Removed increment attribute (DG); 040 * 24-Oct-2007 : Added scaleIndex (DG); 041 * 03-Jul-2013 : Use ParamChecks (DG); 042 * 043 */ 044 045package org.jfree.chart.plot.dial; 046 047import java.awt.BasicStroke; 048import java.awt.Color; 049import java.awt.Graphics2D; 050import java.awt.Paint; 051import java.awt.geom.Arc2D; 052import java.awt.geom.Rectangle2D; 053import java.io.IOException; 054import java.io.ObjectInputStream; 055import java.io.ObjectOutputStream; 056import java.io.Serializable; 057 058import org.jfree.chart.HashUtilities; 059import org.jfree.chart.util.ParamChecks; 060import org.jfree.io.SerialUtilities; 061import org.jfree.util.PaintUtilities; 062import org.jfree.util.PublicCloneable; 063 064/** 065 * A layer that draws a range highlight on a dial plot. 066 * 067 * @since 1.0.7 068 */ 069public class StandardDialRange extends AbstractDialLayer implements DialLayer, 070 Cloneable, PublicCloneable, Serializable { 071 072 /** For serialization. */ 073 static final long serialVersionUID = 345515648249364904L; 074 075 /** The scale index. */ 076 private int scaleIndex; 077 078 /** The minimum data value for the scale. */ 079 private double lowerBound; 080 081 /** The maximum data value for the scale. */ 082 private double upperBound; 083 084 /** 085 * The paint used to draw the range highlight. This field is transient 086 * because it requires special handling for serialization. 087 */ 088 private transient Paint paint; 089 090 /** 091 * The factor (in the range 0.0 to 1.0) that determines the inside limit 092 * of the range highlight. 093 */ 094 private double innerRadius; 095 096 /** 097 * The factor (in the range 0.0 to 1.0) that determines the outside limit 098 * of the range highlight. 099 */ 100 private double outerRadius; 101 102 /** 103 * Creates a new instance of <code>StandardDialRange</code>. 104 */ 105 public StandardDialRange() { 106 this(0.0, 100.0, Color.white); 107 } 108 109 /** 110 * Creates a new instance of <code>StandardDialRange</code>. 111 * 112 * @param lower the lower bound. 113 * @param upper the upper bound. 114 * @param paint the paint (<code>null</code> not permitted). 115 */ 116 public StandardDialRange(double lower, double upper, Paint paint) { 117 ParamChecks.nullNotPermitted(paint, "paint"); 118 this.scaleIndex = 0; 119 this.lowerBound = lower; 120 this.upperBound = upper; 121 this.innerRadius = 0.48; 122 this.outerRadius = 0.52; 123 this.paint = paint; 124 } 125 126 /** 127 * Returns the scale index. 128 * 129 * @return The scale index. 130 * 131 * @see #setScaleIndex(int) 132 */ 133 public int getScaleIndex() { 134 return this.scaleIndex; 135 } 136 137 /** 138 * Sets the scale index and sends a {@link DialLayerChangeEvent} to all 139 * registered listeners. 140 * 141 * @param index the scale index. 142 * 143 * @see #getScaleIndex() 144 */ 145 public void setScaleIndex(int index) { 146 this.scaleIndex = index; 147 notifyListeners(new DialLayerChangeEvent(this)); 148 } 149 150 /** 151 * Returns the lower bound (a data value) of the dial range. 152 * 153 * @return The lower bound of the dial range. 154 * 155 * @see #setLowerBound(double) 156 */ 157 public double getLowerBound() { 158 return this.lowerBound; 159 } 160 161 /** 162 * Sets the lower bound of the dial range and sends a 163 * {@link DialLayerChangeEvent} to all registered listeners. 164 * 165 * @param bound the lower bound. 166 * 167 * @see #getLowerBound() 168 */ 169 public void setLowerBound(double bound) { 170 if (bound >= this.upperBound) { 171 throw new IllegalArgumentException( 172 "Lower bound must be less than upper bound."); 173 } 174 this.lowerBound = bound; 175 notifyListeners(new DialLayerChangeEvent(this)); 176 } 177 178 /** 179 * Returns the upper bound of the dial range. 180 * 181 * @return The upper bound. 182 * 183 * @see #setUpperBound(double) 184 */ 185 public double getUpperBound() { 186 return this.upperBound; 187 } 188 189 /** 190 * Sets the upper bound of the dial range and sends a 191 * {@link DialLayerChangeEvent} to all registered listeners. 192 * 193 * @param bound the upper bound. 194 * 195 * @see #getUpperBound() 196 */ 197 public void setUpperBound(double bound) { 198 if (bound <= this.lowerBound) { 199 throw new IllegalArgumentException( 200 "Lower bound must be less than upper bound."); 201 } 202 this.upperBound = bound; 203 notifyListeners(new DialLayerChangeEvent(this)); 204 } 205 206 /** 207 * Sets the bounds for the range and sends a {@link DialLayerChangeEvent} 208 * to all registered listeners. 209 * 210 * @param lower the lower bound. 211 * @param upper the upper bound. 212 */ 213 public void setBounds(double lower, double upper) { 214 if (lower >= upper) { 215 throw new IllegalArgumentException( 216 "Lower must be less than upper."); 217 } 218 this.lowerBound = lower; 219 this.upperBound = upper; 220 notifyListeners(new DialLayerChangeEvent(this)); 221 } 222 223 /** 224 * Returns the paint used to highlight the range. 225 * 226 * @return The paint (never <code>null</code>). 227 * 228 * @see #setPaint(Paint) 229 */ 230 public Paint getPaint() { 231 return this.paint; 232 } 233 234 /** 235 * Sets the paint used to highlight the range and sends a 236 * {@link DialLayerChangeEvent} to all registered listeners. 237 * 238 * @param paint the paint (<code>null</code> not permitted). 239 * 240 * @see #getPaint() 241 */ 242 public void setPaint(Paint paint) { 243 ParamChecks.nullNotPermitted(paint, "paint"); 244 this.paint = paint; 245 notifyListeners(new DialLayerChangeEvent(this)); 246 } 247 248 /** 249 * Returns the inner radius. 250 * 251 * @return The inner radius. 252 * 253 * @see #setInnerRadius(double) 254 */ 255 public double getInnerRadius() { 256 return this.innerRadius; 257 } 258 259 /** 260 * Sets the inner radius and sends a {@link DialLayerChangeEvent} to all 261 * registered listeners. 262 * 263 * @param radius the radius. 264 * 265 * @see #getInnerRadius() 266 */ 267 public void setInnerRadius(double radius) { 268 this.innerRadius = radius; 269 notifyListeners(new DialLayerChangeEvent(this)); 270 } 271 272 /** 273 * Returns the outer radius. 274 * 275 * @return The outer radius. 276 * 277 * @see #setOuterRadius(double) 278 */ 279 public double getOuterRadius() { 280 return this.outerRadius; 281 } 282 283 /** 284 * Sets the outer radius and sends a {@link DialLayerChangeEvent} to all 285 * registered listeners. 286 * 287 * @param radius the radius. 288 * 289 * @see #getOuterRadius() 290 */ 291 public void setOuterRadius(double radius) { 292 this.outerRadius = radius; 293 notifyListeners(new DialLayerChangeEvent(this)); 294 } 295 296 /** 297 * Returns <code>true</code> to indicate that this layer should be 298 * clipped within the dial window. 299 * 300 * @return <code>true</code>. 301 */ 302 @Override 303 public boolean isClippedToWindow() { 304 return true; 305 } 306 307 /** 308 * Draws the range. 309 * 310 * @param g2 the graphics target. 311 * @param plot the plot. 312 * @param frame the dial's reference frame (in Java2D space). 313 * @param view the dial's view rectangle (in Java2D space). 314 */ 315 @Override 316 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 317 Rectangle2D view) { 318 319 Rectangle2D arcRectInner = DialPlot.rectangleByRadius(frame, 320 this.innerRadius, this.innerRadius); 321 Rectangle2D arcRectOuter = DialPlot.rectangleByRadius(frame, 322 this.outerRadius, this.outerRadius); 323 324 DialScale scale = plot.getScale(this.scaleIndex); 325 if (scale == null) { 326 throw new RuntimeException("No scale for scaleIndex = " 327 + this.scaleIndex); 328 } 329 double angleMin = scale.valueToAngle(this.lowerBound); 330 double angleMax = scale.valueToAngle(this.upperBound); 331 332 Arc2D arcInner = new Arc2D.Double(arcRectInner, angleMin, 333 angleMax - angleMin, Arc2D.OPEN); 334 Arc2D arcOuter = new Arc2D.Double(arcRectOuter, angleMax, 335 angleMin - angleMax, Arc2D.OPEN); 336 337 g2.setPaint(this.paint); 338 g2.setStroke(new BasicStroke(2.0f)); 339 g2.draw(arcInner); 340 g2.draw(arcOuter); 341 } 342 343 /** 344 * Tests this instance for equality with an arbitrary object. 345 * 346 * @param obj the object (<code>null</code> permitted). 347 * 348 * @return A boolean. 349 */ 350 @Override 351 public boolean equals(Object obj) { 352 if (obj == this) { 353 return true; 354 } 355 if (!(obj instanceof StandardDialRange)) { 356 return false; 357 } 358 StandardDialRange that = (StandardDialRange) obj; 359 if (this.scaleIndex != that.scaleIndex) { 360 return false; 361 } 362 if (this.lowerBound != that.lowerBound) { 363 return false; 364 } 365 if (this.upperBound != that.upperBound) { 366 return false; 367 } 368 if (!PaintUtilities.equal(this.paint, that.paint)) { 369 return false; 370 } 371 if (this.innerRadius != that.innerRadius) { 372 return false; 373 } 374 if (this.outerRadius != that.outerRadius) { 375 return false; 376 } 377 return super.equals(obj); 378 } 379 380 /** 381 * Returns a hash code for this instance. 382 * 383 * @return The hash code. 384 */ 385 @Override 386 public int hashCode() { 387 int result = 193; 388 long temp = Double.doubleToLongBits(this.lowerBound); 389 result = 37 * result + (int) (temp ^ (temp >>> 32)); 390 temp = Double.doubleToLongBits(this.upperBound); 391 result = 37 * result + (int) (temp ^ (temp >>> 32)); 392 temp = Double.doubleToLongBits(this.innerRadius); 393 result = 37 * result + (int) (temp ^ (temp >>> 32)); 394 temp = Double.doubleToLongBits(this.outerRadius); 395 result = 37 * result + (int) (temp ^ (temp >>> 32)); 396 result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 397 return result; 398 } 399 400 /** 401 * Returns a clone of this instance. 402 * 403 * @return A clone. 404 * 405 * @throws CloneNotSupportedException if any of the attributes of this 406 * instance cannot be cloned. 407 */ 408 @Override 409 public Object clone() throws CloneNotSupportedException { 410 return super.clone(); 411 } 412 413 /** 414 * Provides serialization support. 415 * 416 * @param stream the output stream. 417 * 418 * @throws IOException if there is an I/O error. 419 */ 420 private void writeObject(ObjectOutputStream stream) throws IOException { 421 stream.defaultWriteObject(); 422 SerialUtilities.writePaint(this.paint, stream); 423 } 424 425 /** 426 * Provides serialization support. 427 * 428 * @param stream the input stream. 429 * 430 * @throws IOException if there is an I/O error. 431 * @throws ClassNotFoundException if there is a classpath problem. 432 */ 433 private void readObject(ObjectInputStream stream) 434 throws IOException, ClassNotFoundException { 435 stream.defaultReadObject(); 436 this.paint = SerialUtilities.readPaint(stream); 437 } 438 439}