001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------------------- 028 * StandardDialRange.java 029 * ---------------------- 030 * (C) Copyright 2006-2008, 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 * 042 */ 043 044 package org.jfree.chart.plot.dial; 045 046 import java.awt.BasicStroke; 047 import java.awt.Color; 048 import java.awt.Graphics2D; 049 import java.awt.Paint; 050 import java.awt.geom.Arc2D; 051 import java.awt.geom.Rectangle2D; 052 import java.io.IOException; 053 import java.io.ObjectInputStream; 054 import java.io.ObjectOutputStream; 055 import java.io.Serializable; 056 057 import org.jfree.chart.HashUtilities; 058 import org.jfree.io.SerialUtilities; 059 import org.jfree.util.PaintUtilities; 060 import org.jfree.util.PublicCloneable; 061 062 /** 063 * A layer that draws a range highlight on a dial plot. 064 * 065 * @since 1.0.7 066 */ 067 public class StandardDialRange extends AbstractDialLayer implements DialLayer, 068 Cloneable, PublicCloneable, Serializable { 069 070 /** For serialization. */ 071 static final long serialVersionUID = 345515648249364904L; 072 073 /** The scale index. */ 074 private int scaleIndex; 075 076 /** The minimum data value for the scale. */ 077 private double lowerBound; 078 079 /** The maximum data value for the scale. */ 080 private double upperBound; 081 082 /** 083 * The paint used to draw the range highlight. This field is transient 084 * because it requires special handling for serialization. 085 */ 086 private transient Paint paint; 087 088 /** 089 * The factor (in the range 0.0 to 1.0) that determines the inside limit 090 * of the range highlight. 091 */ 092 private double innerRadius; 093 094 /** 095 * The factor (in the range 0.0 to 1.0) that determines the outside limit 096 * of the range highlight. 097 */ 098 private double outerRadius; 099 100 /** 101 * Creates a new instance of <code>StandardDialRange</code>. 102 */ 103 public StandardDialRange() { 104 this(0.0, 100.0, Color.white); 105 } 106 107 /** 108 * Creates a new instance of <code>StandardDialRange</code>. 109 * 110 * @param lower the lower bound. 111 * @param upper the upper bound. 112 * @param paint the paint (<code>null</code> not permitted). 113 */ 114 public StandardDialRange(double lower, double upper, Paint paint) { 115 if (paint == null) { 116 throw new IllegalArgumentException("Null 'paint' argument."); 117 } 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 if (paint == null) { 244 throw new IllegalArgumentException("Null 'paint' argument."); 245 } 246 this.paint = paint; 247 notifyListeners(new DialLayerChangeEvent(this)); 248 } 249 250 /** 251 * Returns the inner radius. 252 * 253 * @return The inner radius. 254 * 255 * @see #setInnerRadius(double) 256 */ 257 public double getInnerRadius() { 258 return this.innerRadius; 259 } 260 261 /** 262 * Sets the inner radius and sends a {@link DialLayerChangeEvent} to all 263 * registered listeners. 264 * 265 * @param radius the radius. 266 * 267 * @see #getInnerRadius() 268 */ 269 public void setInnerRadius(double radius) { 270 this.innerRadius = radius; 271 notifyListeners(new DialLayerChangeEvent(this)); 272 } 273 274 /** 275 * Returns the outer radius. 276 * 277 * @return The outer radius. 278 * 279 * @see #setOuterRadius(double) 280 */ 281 public double getOuterRadius() { 282 return this.outerRadius; 283 } 284 285 /** 286 * Sets the outer radius and sends a {@link DialLayerChangeEvent} to all 287 * registered listeners. 288 * 289 * @param radius the radius. 290 * 291 * @see #getOuterRadius() 292 */ 293 public void setOuterRadius(double radius) { 294 this.outerRadius = radius; 295 notifyListeners(new DialLayerChangeEvent(this)); 296 } 297 298 /** 299 * Returns <code>true</code> to indicate that this layer should be 300 * clipped within the dial window. 301 * 302 * @return <code>true</code>. 303 */ 304 public boolean isClippedToWindow() { 305 return true; 306 } 307 308 /** 309 * Draws the range. 310 * 311 * @param g2 the graphics target. 312 * @param plot the plot. 313 * @param frame the dial's reference frame (in Java2D space). 314 * @param view the dial's view rectangle (in Java2D space). 315 */ 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 public boolean equals(Object obj) { 351 if (obj == this) { 352 return true; 353 } 354 if (!(obj instanceof StandardDialRange)) { 355 return false; 356 } 357 StandardDialRange that = (StandardDialRange) obj; 358 if (this.scaleIndex != that.scaleIndex) { 359 return false; 360 } 361 if (this.lowerBound != that.lowerBound) { 362 return false; 363 } 364 if (this.upperBound != that.upperBound) { 365 return false; 366 } 367 if (!PaintUtilities.equal(this.paint, that.paint)) { 368 return false; 369 } 370 if (this.innerRadius != that.innerRadius) { 371 return false; 372 } 373 if (this.outerRadius != that.outerRadius) { 374 return false; 375 } 376 return super.equals(obj); 377 } 378 379 /** 380 * Returns a hash code for this instance. 381 * 382 * @return The hash code. 383 */ 384 public int hashCode() { 385 int result = 193; 386 long temp = Double.doubleToLongBits(this.lowerBound); 387 result = 37 * result + (int) (temp ^ (temp >>> 32)); 388 temp = Double.doubleToLongBits(this.upperBound); 389 result = 37 * result + (int) (temp ^ (temp >>> 32)); 390 temp = Double.doubleToLongBits(this.innerRadius); 391 result = 37 * result + (int) (temp ^ (temp >>> 32)); 392 temp = Double.doubleToLongBits(this.outerRadius); 393 result = 37 * result + (int) (temp ^ (temp >>> 32)); 394 result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 395 return result; 396 } 397 398 /** 399 * Returns a clone of this instance. 400 * 401 * @return A clone. 402 * 403 * @throws CloneNotSupportedException if any of the attributes of this 404 * instance cannot be cloned. 405 */ 406 public Object clone() throws CloneNotSupportedException { 407 return super.clone(); 408 } 409 410 /** 411 * Provides serialization support. 412 * 413 * @param stream the output stream. 414 * 415 * @throws IOException if there is an I/O error. 416 */ 417 private void writeObject(ObjectOutputStream stream) throws IOException { 418 stream.defaultWriteObject(); 419 SerialUtilities.writePaint(this.paint, stream); 420 } 421 422 /** 423 * Provides serialization support. 424 * 425 * @param stream the input stream. 426 * 427 * @throws IOException if there is an I/O error. 428 * @throws ClassNotFoundException if there is a classpath problem. 429 */ 430 private void readObject(ObjectInputStream stream) 431 throws IOException, ClassNotFoundException { 432 stream.defaultReadObject(); 433 this.paint = SerialUtilities.readPaint(stream); 434 } 435 436 }