001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------- 028 * Range.java 029 * ---------- 030 * (C) Copyright 2002-2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Chuanhao Chiu; 034 * Bill Kelemen; 035 * Nicolas Brodu; 036 * 037 * Changes (from 23-Jun-2001) 038 * -------------------------- 039 * 22-Apr-2002 : Version 1, loosely based by code by Bill Kelemen (DG); 040 * 30-Apr-2002 : Added getLength() and getCentralValue() methods. Changed 041 * argument check in constructor (DG); 042 * 13-Jun-2002 : Added contains(double) method (DG); 043 * 22-Aug-2002 : Added fix to combine method where both ranges are null, thanks 044 * to Chuanhao Chiu for reporting and fixing this (DG); 045 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 046 * 26-Mar-2003 : Implemented Serializable (DG); 047 * 14-Aug-2003 : Added equals() method (DG); 048 * 27-Aug-2003 : Added toString() method (BK); 049 * 11-Sep-2003 : Added Clone Support (NB); 050 * 23-Sep-2003 : Fixed Checkstyle issues (DG); 051 * 25-Sep-2003 : Oops, Range immutable, clone not necessary (NB); 052 * 05-May-2004 : Added constrain() and intersects() methods (DG); 053 * 18-May-2004 : Added expand() method (DG); 054 * ------------- JFreeChart 1.0.x --------------------------------------------- 055 * 11-Jan-2006 : Added new method expandToInclude(Range, double) (DG); 056 * 057 */ 058 059package org.jfree.data; 060 061import java.io.Serializable; 062 063/** 064 * Represents an immutable range of values. 065 */ 066public strictfp class Range implements Serializable { 067 068 /** For serialization. */ 069 private static final long serialVersionUID = -906333695431863380L; 070 071 /** The lower bound of the range. */ 072 private double lower; 073 074 /** The upper bound of the range. */ 075 private double upper; 076 077 /** 078 * Creates a new range. 079 * 080 * @param lower the lower bound (must be <= upper bound). 081 * @param upper the upper bound (must be >= lower bound). 082 */ 083 public Range(double lower, double upper) { 084 if (lower > upper) { 085 String msg = "Range(double, double): require lower (" + lower 086 + ") <= upper (" + upper + ")."; 087 throw new IllegalArgumentException(msg); 088 } 089 this.lower = lower; 090 this.upper = upper; 091 } 092 093 /** 094 * Returns the lower bound for the range. 095 * 096 * @return The lower bound. 097 */ 098 public double getLowerBound() { 099 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}