Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ---------------------- 28: * StandardDialRange.java 29: * ---------------------- 30: * (C) Copyright 2006, 2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 03-Nov-2006 : Version 1 (DG); 38: * 08-Mar-2007 : Fix in hashCode() (DG); 39: * 17-Oct-2007 : Removed increment attribute (DG); 40: * 24-Oct-2007 : Added scaleIndex (DG); 41: * 42: */ 43: 44: package org.jfree.chart.plot.dial; 45: 46: import java.awt.BasicStroke; 47: import java.awt.Color; 48: import java.awt.Graphics2D; 49: import java.awt.Paint; 50: import java.awt.geom.Arc2D; 51: import java.awt.geom.Rectangle2D; 52: import java.io.IOException; 53: import java.io.ObjectInputStream; 54: import java.io.ObjectOutputStream; 55: import java.io.Serializable; 56: 57: import org.jfree.chart.HashUtilities; 58: import org.jfree.io.SerialUtilities; 59: import org.jfree.util.PaintUtilities; 60: import org.jfree.util.PublicCloneable; 61: 62: /** 63: * A layer that draws a range highlight on a dial plot. 64: * 65: * @since 1.0.7 66: */ 67: public class StandardDialRange extends AbstractDialLayer implements DialLayer, 68: Cloneable, PublicCloneable, Serializable { 69: 70: /** For serialization. */ 71: static final long serialVersionUID = 345515648249364904L; 72: 73: /** The scale index. */ 74: private int scaleIndex; 75: 76: /** The minimum data value for the scale. */ 77: private double lowerBound; 78: 79: /** The maximum data value for the scale. */ 80: private double upperBound; 81: 82: /** 83: * The paint used to draw the range highlight. This field is transient 84: * because it requires special handling for serialization. 85: */ 86: private transient Paint paint; 87: 88: /** 89: * The factor (in the range 0.0 to 1.0) that determines the inside limit 90: * of the range highlight. 91: */ 92: private double innerRadius; 93: 94: /** 95: * The factor (in the range 0.0 to 1.0) that determines the outside limit 96: * of the range highlight. 97: */ 98: private double outerRadius; 99: 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: }