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: public class StandardDialRange extends AbstractDialLayer implements DialLayer, 66: Cloneable, PublicCloneable, Serializable { 67: 68: /** For serialization. */ 69: static final long serialVersionUID = 345515648249364904L; 70: 71: /** The scale index. */ 72: private int scaleIndex; 73: 74: /** The minimum data value for the scale. */ 75: private double lowerBound; 76: 77: /** The maximum data value for the scale. */ 78: private double upperBound; 79: 80: /** 81: * The paint used to draw the range highlight. This field is transient 82: * because it requires special handling for serialization. 83: */ 84: private transient Paint paint; 85: 86: /** 87: * The factor (in the range 0.0 to 1.0) that determines the inside limit 88: * of the range highlight. 89: */ 90: private double innerRadius; 91: 92: /** 93: * The factor (in the range 0.0 to 1.0) that determines the outside limit 94: * of the range highlight. 95: */ 96: private double outerRadius; 97: 98: /** 99: * Creates a new instance of <code>StandardDialRange</code>. 100: */ 101: public StandardDialRange() { 102: this(0.0, 100.0, Color.white); 103: } 104: 105: /** 106: * Creates a new instance of <code>StandardDialRange</code>. 107: * 108: * @param lower the lower bound. 109: * @param upper the upper bound. 110: * @param paint the paint (<code>null</code> not permitted). 111: */ 112: public StandardDialRange(double lower, double upper, Paint paint) { 113: if (paint == null) { 114: throw new IllegalArgumentException("Null 'paint' argument."); 115: } 116: this.scaleIndex = 0; 117: this.lowerBound = lower; 118: this.upperBound = upper; 119: this.innerRadius = 0.48; 120: this.outerRadius = 0.52; 121: this.paint = paint; 122: } 123: 124: /** 125: * Returns the scale index. 126: * 127: * @return The scale index. 128: * 129: * @see #setScaleIndex(int) 130: */ 131: public int getScaleIndex() { 132: return this.scaleIndex; 133: } 134: 135: /** 136: * Sets the scale index and sends a {@link DialLayerChangeEvent} to all 137: * registered listeners. 138: * 139: * @param index the scale index. 140: * 141: * @see #getScaleIndex() 142: */ 143: public void setScaleIndex(int index) { 144: this.scaleIndex = index; 145: notifyListeners(new DialLayerChangeEvent(this)); 146: } 147: 148: /** 149: * Returns the lower bound (a data value) of the dial range. 150: * 151: * @return The lower bound of the dial range. 152: * 153: * @see #setLowerBound(double) 154: */ 155: public double getLowerBound() { 156: return this.lowerBound; 157: } 158: 159: /** 160: * Sets the lower bound of the dial range and sends a 161: * {@link DialLayerChangeEvent} to all registered listeners. 162: * 163: * @param bound the lower bound. 164: * 165: * @see #getLowerBound() 166: */ 167: public void setLowerBound(double bound) { 168: if (bound >= this.upperBound) { 169: throw new IllegalArgumentException( 170: "Lower bound must be less than upper bound."); 171: } 172: this.lowerBound = bound; 173: notifyListeners(new DialLayerChangeEvent(this)); 174: } 175: 176: /** 177: * Returns the upper bound of the dial range. 178: * 179: * @return The upper bound. 180: * 181: * @see #setUpperBound(double) 182: */ 183: public double getUpperBound() { 184: return this.upperBound; 185: } 186: 187: /** 188: * Sets the upper bound of the dial range and sends a 189: * {@link DialLayerChangeEvent} to all registered listeners. 190: * 191: * @param bound the upper bound. 192: * 193: * @see #getUpperBound() 194: */ 195: public void setUpperBound(double bound) { 196: if (bound <= this.lowerBound) { 197: throw new IllegalArgumentException( 198: "Lower bound must be less than upper bound."); 199: } 200: this.upperBound = bound; 201: notifyListeners(new DialLayerChangeEvent(this)); 202: } 203: 204: /** 205: * Sets the bounds for the range and sends a {@link DialLayerChangeEvent} 206: * to all registered listeners. 207: * 208: * @param lower the lower bound. 209: * @param upper the upper bound. 210: */ 211: public void setBounds(double lower, double upper) { 212: if (lower >= upper) { 213: throw new IllegalArgumentException( 214: "Lower must be less than upper."); 215: } 216: this.lowerBound = lower; 217: this.upperBound = upper; 218: notifyListeners(new DialLayerChangeEvent(this)); 219: } 220: 221: /** 222: * Returns the paint used to highlight the range. 223: * 224: * @return The paint (never <code>null</code>). 225: * 226: * @see #setPaint(Paint) 227: */ 228: public Paint getPaint() { 229: return this.paint; 230: } 231: 232: /** 233: * Sets the paint used to highlight the range and sends a 234: * {@link DialLayerChangeEvent} to all registered listeners. 235: * 236: * @param paint the paint (<code>null</code> not permitted). 237: * 238: * @see #getPaint() 239: */ 240: public void setPaint(Paint paint) { 241: if (paint == null) { 242: throw new IllegalArgumentException("Null 'paint' argument."); 243: } 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: public boolean isClippedToWindow() { 303: return true; 304: } 305: 306: /** 307: * Draws the range. 308: * 309: * @param g2 the graphics target. 310: * @param plot the plot. 311: * @param frame the dial's reference frame (in Java2D space). 312: * @param view the dial's view rectangle (in Java2D space). 313: */ 314: public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 315: Rectangle2D view) { 316: 317: Rectangle2D arcRectInner = DialPlot.rectangleByRadius(frame, 318: this.innerRadius, this.innerRadius); 319: Rectangle2D arcRectOuter = DialPlot.rectangleByRadius(frame, 320: this.outerRadius, this.outerRadius); 321: 322: DialScale scale = plot.getScale(this.scaleIndex); 323: if (scale == null) { 324: throw new RuntimeException("No scale for scaleIndex = " 325: + this.scaleIndex); 326: } 327: double angleMin = scale.valueToAngle(this.lowerBound); 328: double angleMax = scale.valueToAngle(this.upperBound); 329: 330: Arc2D arcInner = new Arc2D.Double(arcRectInner, angleMin, 331: angleMax - angleMin, Arc2D.OPEN); 332: Arc2D arcOuter = new Arc2D.Double(arcRectOuter, angleMax, 333: angleMin - angleMax, Arc2D.OPEN); 334: 335: g2.setPaint(this.paint); 336: g2.setStroke(new BasicStroke(2.0f)); 337: g2.draw(arcInner); 338: g2.draw(arcOuter); 339: } 340: 341: /** 342: * Tests this instance for equality with an arbitrary object. 343: * 344: * @param obj the object (<code>null</code> permitted). 345: * 346: * @return A boolean. 347: */ 348: public boolean equals(Object obj) { 349: if (obj == this) { 350: return true; 351: } 352: if (!(obj instanceof StandardDialRange)) { 353: return false; 354: } 355: StandardDialRange that = (StandardDialRange) obj; 356: if (this.scaleIndex != that.scaleIndex) { 357: return false; 358: } 359: if (this.lowerBound != that.lowerBound) { 360: return false; 361: } 362: if (this.upperBound != that.upperBound) { 363: return false; 364: } 365: if (!PaintUtilities.equal(this.paint, that.paint)) { 366: return false; 367: } 368: if (this.innerRadius != that.innerRadius) { 369: return false; 370: } 371: if (this.outerRadius != that.outerRadius) { 372: return false; 373: } 374: return super.equals(obj); 375: } 376: 377: /** 378: * Returns a hash code for this instance. 379: * 380: * @return The hash code. 381: */ 382: public int hashCode() { 383: int result = 193; 384: long temp = Double.doubleToLongBits(this.lowerBound); 385: result = 37 * result + (int) (temp ^ (temp >>> 32)); 386: temp = Double.doubleToLongBits(this.upperBound); 387: result = 37 * result + (int) (temp ^ (temp >>> 32)); 388: temp = Double.doubleToLongBits(this.innerRadius); 389: result = 37 * result + (int) (temp ^ (temp >>> 32)); 390: temp = Double.doubleToLongBits(this.outerRadius); 391: result = 37 * result + (int) (temp ^ (temp >>> 32)); 392: result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 393: return result; 394: } 395: 396: /** 397: * Returns a clone of this instance. 398: * 399: * @return A clone. 400: * 401: * @throws CloneNotSupportedException if any of the attributes of this 402: * instance cannot be cloned. 403: */ 404: public Object clone() throws CloneNotSupportedException { 405: return super.clone(); 406: } 407: 408: /** 409: * Provides serialization support. 410: * 411: * @param stream the output stream. 412: * 413: * @throws IOException if there is an I/O error. 414: */ 415: private void writeObject(ObjectOutputStream stream) throws IOException { 416: stream.defaultWriteObject(); 417: SerialUtilities.writePaint(this.paint, stream); 418: } 419: 420: /** 421: * Provides serialization support. 422: * 423: * @param stream the input stream. 424: * 425: * @throws IOException if there is an I/O error. 426: * @throws ClassNotFoundException if there is a classpath problem. 427: */ 428: private void readObject(ObjectInputStream stream) 429: throws IOException, ClassNotFoundException { 430: stream.defaultReadObject(); 431: this.paint = SerialUtilities.readPaint(stream); 432: } 433: 434: }