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