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