Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2006, 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: * Range.java 29: * ---------- 30: * (C) Copyright 2002-2006, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Chuanhao Chiu; 34: * Bill Kelemen; 35: * Nicolas Brodu; 36: * 37: * $Id: Range.java,v 1.6.2.2 2006/01/11 11:27:34 mungady Exp $ 38: * 39: * Changes (from 23-Jun-2001) 40: * -------------------------- 41: * 22-Apr-2002 : Version 1, loosely based by code by Bill Kelemen (DG); 42: * 30-Apr-2002 : Added getLength() and getCentralValue() methods. Changed 43: * argument check in constructor (DG); 44: * 13-Jun-2002 : Added contains(double) method (DG); 45: * 22-Aug-2002 : Added fix to combine method where both ranges are null, thanks 46: * to Chuanhao Chiu for reporting and fixing this (DG); 47: * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 48: * 26-Mar-2003 : Implemented Serializable (DG); 49: * 14-Aug-2003 : Added equals() method (DG); 50: * 27-Aug-2003 : Added toString() method (BK); 51: * 11-Sep-2003 : Added Clone Support (NB); 52: * 23-Sep-2003 : Fixed Checkstyle issues (DG); 53: * 25-Sep-2003 : Oops, Range immutable, clone not necessary (NB); 54: * 05-May-2004 : Added constrain() and intersects() methods (DG); 55: * 18-May-2004 : Added expand() method (DG); 56: * ------------- JFreeChart 1.0.0 --------------------------------------------- 57: * 11-Jan-2006 : Added new method expandToInclude(Range, double) (DG); 58: * 59: */ 60: 61: package org.jfree.data; 62: 63: import java.io.Serializable; 64: 65: /** 66: * Represents an immutable range of values. 67: */ 68: public strictfp class Range implements Serializable { 69: 70: /** For serialization. */ 71: private static final long serialVersionUID = -906333695431863380L; 72: 73: /** The lower bound of the range. */ 74: private double lower; 75: 76: /** The upper bound of the range. */ 77: private double upper; 78: 79: /** 80: * Creates a new range. 81: * 82: * @param lower the lower bound (must be <= upper bound). 83: * @param upper the upper bound (must be >= lower bound). 84: */ 85: public Range(double lower, double upper) { 86: if (lower > upper) { 87: String msg = "Range(double, double): require lower (" + lower 88: + ") <= upper (" + upper + ")."; 89: throw new IllegalArgumentException(msg); 90: } 91: this.lower = lower; 92: this.upper = upper; 93: } 94: 95: /** 96: * Returns the lower bound for the range. 97: * 98: * @return The lower bound. 99: */ 100: public double getLowerBound() { 101: return this.lower; 102: } 103: 104: /** 105: * Returns the upper bound for the range. 106: * 107: * @return The upper bound. 108: */ 109: public double getUpperBound() { 110: return this.upper; 111: } 112: 113: /** 114: * Returns the length of the range. 115: * 116: * @return The length. 117: */ 118: public double getLength() { 119: return this.upper - this.lower; 120: } 121: 122: /** 123: * Returns the central value for the range. 124: * 125: * @return The central value. 126: */ 127: public double getCentralValue() { 128: return this.lower / 2.0 + this.upper / 2.0; 129: } 130: 131: /** 132: * Returns <code>true</code> if the range contains the specified value and 133: * <code>false</code> otherwise. 134: * 135: * @param value the value to lookup. 136: * 137: * @return <code>true</code> if the range contains the specified value. 138: */ 139: public boolean contains(double value) { 140: return (value >= this.lower && value <= this.upper); 141: } 142: 143: /** 144: * Returns <code>true</code> if the range intersects with the specified 145: * range, and <code>false</code> otherwise. 146: * 147: * @param b0 the lower bound (should be <= b1). 148: * @param b1 the upper bound (should be >= b0). 149: * 150: * @return A boolean. 151: */ 152: public boolean intersects(double b0, double b1) { 153: if (b0 <= this.lower) { 154: return (b1 > this.lower); 155: } 156: else { 157: return (b0 < this.upper && b1 >= b0); 158: } 159: } 160: 161: /** 162: * Returns the value within the range that is closest to the specified 163: * value. 164: * 165: * @param value the value. 166: * 167: * @return The constrained value. 168: */ 169: public double constrain(double value) { 170: double result = value; 171: if (!contains(value)) { 172: if (value > this.upper) { 173: result = this.upper; 174: } 175: else if (value < this.lower) { 176: result = this.lower; 177: } 178: } 179: return result; 180: } 181: 182: /** 183: * Creates a new range by combining two existing ranges. 184: * <P> 185: * Note that: 186: * <ul> 187: * <li>either range can be <code>null</code>, in which case the other 188: * range is returned;</li> 189: * <li>if both ranges are <code>null</code> the return value is 190: * <code>null</code>.</li> 191: * </ul> 192: * 193: * @param range1 the first range (<code>null</code> permitted). 194: * @param range2 the second range (<code>null</code> permitted). 195: * 196: * @return A new range (possibly <code>null</code>). 197: */ 198: public static Range combine(Range range1, Range range2) { 199: if (range1 == null) { 200: return range2; 201: } 202: else { 203: if (range2 == null) { 204: return range1; 205: } 206: else { 207: double l = Math.min(range1.getLowerBound(), 208: range2.getLowerBound()); 209: double u = Math.max(range1.getUpperBound(), 210: range2.getUpperBound()); 211: return new Range(l, u); 212: } 213: } 214: } 215: 216: /** 217: * Returns a range that includes all the values in the specified 218: * <code>range</code> AND the specified <code>value</code>. 219: * 220: * @param range the range (<code>null</code> permitted). 221: * @param value the value that must be included. 222: * 223: * @return A range. 224: * 225: * @since 1.0.1 226: */ 227: public static Range expandToInclude(Range range, double value) { 228: if (range == null) { 229: return new Range(value, value); 230: } 231: if (value < range.getLowerBound()) { 232: return new Range(value, range.getUpperBound()); 233: } 234: else if (value > range.getUpperBound()) { 235: return new Range(range.getLowerBound(), value); 236: } 237: else { 238: return range; 239: } 240: } 241: 242: /** 243: * Creates a new range by adding margins to an existing range. 244: * 245: * @param range the range (<code>null</code> not permitted). 246: * @param lowerMargin the lower margin (expressed as a percentage of the 247: * range length). 248: * @param upperMargin the upper margin (expressed as a percentage of the 249: * range length). 250: * 251: * @return The expanded range. 252: */ 253: public static Range expand(Range range, 254: double lowerMargin, double upperMargin) { 255: if (range == null) { 256: throw new IllegalArgumentException("Null 'range' argument."); 257: } 258: double length = range.getLength(); 259: double lower = length * lowerMargin; 260: double upper = length * upperMargin; 261: return new Range(range.getLowerBound() - lower, 262: range.getUpperBound() + upper); 263: } 264: 265: /** 266: * Shifts the range by the specified amount. 267: * 268: * @param base the base range. 269: * @param delta the shift amount. 270: * 271: * @return A new range. 272: */ 273: public static Range shift(Range base, double delta) { 274: return shift(base, delta, false); 275: } 276: 277: /** 278: * Shifts the range by the specified amount. 279: * 280: * @param base the base range. 281: * @param delta the shift amount. 282: * @param allowZeroCrossing a flag that determines whether or not the 283: * bounds of the range are allowed to cross 284: * zero after adjustment. 285: * 286: * @return A new range. 287: */ 288: public static Range shift(Range base, double delta, 289: boolean allowZeroCrossing) { 290: if (allowZeroCrossing) { 291: return new Range(base.getLowerBound() + delta, 292: base.getUpperBound() + delta); 293: } 294: else { 295: return new Range(shiftWithNoZeroCrossing(base.getLowerBound(), 296: delta), shiftWithNoZeroCrossing(base.getUpperBound(), 297: delta)); 298: } 299: } 300: 301: /** 302: * Returns the given <code>value</code> adjusted by <code>delta</code> but 303: * with a check to prevent the result from crossing <code>0.0</code>. 304: * 305: * @param value the value. 306: * @param delta the adjustment. 307: * 308: * @return The adjusted value. 309: */ 310: private static double shiftWithNoZeroCrossing(double value, double delta) { 311: if (value > 0.0) { 312: return Math.max(value + delta, 0.0); 313: } 314: else if (value < 0.0) { 315: return Math.min(value + delta, 0.0); 316: } 317: else { 318: return value + delta; 319: } 320: } 321: 322: /** 323: * Tests this object for equality with an arbitrary object. 324: * 325: * @param obj the object to test against (<code>null</code> permitted). 326: * 327: * @return A boolean. 328: */ 329: public boolean equals(Object obj) { 330: if (!(obj instanceof Range)) { 331: return false; 332: } 333: Range range = (Range) obj; 334: if (!(this.lower == range.lower)) { 335: return false; 336: } 337: if (!(this.upper == range.upper)) { 338: return false; 339: } 340: return true; 341: } 342: 343: /** 344: * Returns a hash code. 345: * 346: * @return A hash code. 347: */ 348: public int hashCode() { 349: int result; 350: long temp; 351: temp = Double.doubleToLongBits(this.lower); 352: result = (int) (temp ^ (temp >>> 32)); 353: temp = Double.doubleToLongBits(this.upper); 354: result = 29 * result + (int) (temp ^ (temp >>> 32)); 355: return result; 356: } 357: 358: /** 359: * Returns a string representation of this Range. 360: * 361: * @return A String "Range[lower,upper]" where lower=lower range and 362: * upper=upper range. 363: */ 364: public String toString() { 365: return ("Range[" + this.lower + "," + this.upper + "]"); 366: } 367: 368: }