001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------- 028 * Range.java 029 * ---------- 030 * (C) Copyright 2002-2013, 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 * Sergei Ivanov; 037 * 038 * Changes (from 23-Jun-2001) 039 * -------------------------- 040 * 22-Apr-2002 : Version 1, loosely based by code by Bill Kelemen (DG); 041 * 30-Apr-2002 : Added getLength() and getCentralValue() methods. Changed 042 * argument check in constructor (DG); 043 * 13-Jun-2002 : Added contains(double) method (DG); 044 * 22-Aug-2002 : Added fix to combine method where both ranges are null, thanks 045 * to Chuanhao Chiu for reporting and fixing this (DG); 046 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG); 047 * 26-Mar-2003 : Implemented Serializable (DG); 048 * 14-Aug-2003 : Added equals() method (DG); 049 * 27-Aug-2003 : Added toString() method (BK); 050 * 11-Sep-2003 : Added Clone Support (NB); 051 * 23-Sep-2003 : Fixed Checkstyle issues (DG); 052 * 25-Sep-2003 : Oops, Range immutable, clone not necessary (NB); 053 * 05-May-2004 : Added constrain() and intersects() methods (DG); 054 * 18-May-2004 : Added expand() method (DG); 055 * ------------- JFreeChart 1.0.x --------------------------------------------- 056 * 11-Jan-2006 : Added new method expandToInclude(Range, double) (DG); 057 * 18-Dec-2007 : New methods intersects(Range) and scale(...) thanks to Sergei 058 * Ivanov (DG); 059 * 08-Jan-2012 : New method combineIgnoringNaN() (DG); 060 * 061 */ 062 063package org.jfree.data; 064 065import java.io.Serializable; 066import org.jfree.chart.util.ParamChecks; 067 068/** 069 * Represents an immutable range of values. 070 */ 071public strictfp class Range implements Serializable { 072 073 /** For serialization. */ 074 private static final long serialVersionUID = -906333695431863380L; 075 076 /** The lower bound of the range. */ 077 private double lower; 078 079 /** The upper bound of the range. */ 080 private double upper; 081 082 /** 083 * Creates a new range. 084 * 085 * @param lower the lower bound (must be <= upper bound). 086 * @param upper the upper bound (must be >= lower bound). 087 */ 088 public Range(double lower, double upper) { 089 if (lower > upper) { 090 String msg = "Range(double, double): require lower (" + lower 091 + ") <= upper (" + upper + ")."; 092 throw new IllegalArgumentException(msg); 093 } 094 this.lower = lower; 095 this.upper = upper; 096 } 097 098 /** 099 * Returns the lower bound for the range. 100 * 101 * @return The lower bound. 102 */ 103 public double getLowerBound() { 104 return this.lower; 105 } 106 107 /** 108 * Returns the upper bound for the range. 109 * 110 * @return The upper bound. 111 */ 112 public double getUpperBound() { 113 return this.upper; 114 } 115 116 /** 117 * Returns the length of the range. 118 * 119 * @return The length. 120 */ 121 public double getLength() { 122 return this.upper - this.lower; 123 } 124 125 /** 126 * Returns the central value for the range. 127 * 128 * @return The central value. 129 */ 130 public double getCentralValue() { 131 return this.lower / 2.0 + this.upper / 2.0; 132 } 133 134 /** 135 * Returns <code>true</code> if the range contains the specified value and 136 * <code>false</code> otherwise. 137 * 138 * @param value the value to lookup. 139 * 140 * @return <code>true</code> if the range contains the specified value. 141 */ 142 public boolean contains(double value) { 143 return (value >= this.lower && value <= this.upper); 144 } 145 146 /** 147 * Returns <code>true</code> if the range intersects with the specified 148 * range, and <code>false</code> otherwise. 149 * 150 * @param b0 the lower bound (should be <= b1). 151 * @param b1 the upper bound (should be >= b0). 152 * 153 * @return A boolean. 154 */ 155 public boolean intersects(double b0, double b1) { 156 if (b0 <= this.lower) { 157 return (b1 > this.lower); 158 } 159 else { 160 return (b0 < this.upper && b1 >= b0); 161 } 162 } 163 164 /** 165 * Returns <code>true</code> if the range intersects with the specified 166 * range, and <code>false</code> otherwise. 167 * 168 * @param range another range (<code>null</code> not permitted). 169 * 170 * @return A boolean. 171 * 172 * @since 1.0.9 173 */ 174 public boolean intersects(Range range) { 175 return intersects(range.getLowerBound(), range.getUpperBound()); 176 } 177 178 /** 179 * Returns the value within the range that is closest to the specified 180 * value. 181 * 182 * @param value the value. 183 * 184 * @return The constrained value. 185 */ 186 public double constrain(double value) { 187 double result = value; 188 if (!contains(value)) { 189 if (value > this.upper) { 190 result = this.upper; 191 } 192 else if (value < this.lower) { 193 result = this.lower; 194 } 195 } 196 return result; 197 } 198 199 /** 200 * Creates a new range by combining two existing ranges. 201 * <P> 202 * Note that: 203 * <ul> 204 * <li>either range can be <code>null</code>, in which case the other 205 * range is returned;</li> 206 * <li>if both ranges are <code>null</code> the return value is 207 * <code>null</code>.</li> 208 * </ul> 209 * 210 * @param range1 the first range (<code>null</code> permitted). 211 * @param range2 the second range (<code>null</code> permitted). 212 * 213 * @return A new range (possibly <code>null</code>). 214 */ 215 public static Range combine(Range range1, Range range2) { 216 if (range1 == null) { 217 return range2; 218 } 219 if (range2 == null) { 220 return range1; 221 } 222 double l = Math.min(range1.getLowerBound(), range2.getLowerBound()); 223 double u = Math.max(range1.getUpperBound(), range2.getUpperBound()); 224 return new Range(l, u); 225 } 226 227 /** 228 * Combines two ranges. This method has a special handling for Double.NaN. 229 * 230 * @param range1 the first range (<code>null</code> permitted). 231 * @param range2 the second range (<code>null</code> permitted). 232 * 233 * @return A new range (possibly <code>null</code>). 234 * 235 * @since 1.0.15 236 */ 237 public static Range combineIgnoringNaN(Range range1, Range range2) { 238 if (range1 == null) { 239 return range2; 240 } 241 if (range2 == null) { 242 return range1; 243 } 244 double l = min(range1.getLowerBound(), range2.getLowerBound()); 245 double u = max(range1.getUpperBound(), range2.getUpperBound()); 246 return new Range(l, u); 247 } 248 249 private static double min(double d1, double d2) { 250 if (Double.isNaN(d1)) { 251 return d2; 252 } 253 if (Double.isNaN(d2)) { 254 return d1; 255 } 256 return Math.min(d1, d2); 257 } 258 259 private static double max(double d1, double d2) { 260 if (Double.isNaN(d1)) { 261 return d2; 262 } 263 if (Double.isNaN(d2)) { 264 return d1; 265 } 266 return Math.max(d1, d2); 267 } 268 269 /** 270 * Returns a range that includes all the values in the specified 271 * <code>range</code> AND the specified <code>value</code>. 272 * 273 * @param range the range (<code>null</code> permitted). 274 * @param value the value that must be included. 275 * 276 * @return A range. 277 * 278 * @since 1.0.1 279 */ 280 public static Range expandToInclude(Range range, double value) { 281 if (range == null) { 282 return new Range(value, value); 283 } 284 if (value < range.getLowerBound()) { 285 return new Range(value, range.getUpperBound()); 286 } 287 else if (value > range.getUpperBound()) { 288 return new Range(range.getLowerBound(), value); 289 } 290 else { 291 return range; 292 } 293 } 294 295 /** 296 * Creates a new range by adding margins to an existing range. 297 * 298 * @param range the range (<code>null</code> not permitted). 299 * @param lowerMargin the lower margin (expressed as a percentage of the 300 * range length). 301 * @param upperMargin the upper margin (expressed as a percentage of the 302 * range length). 303 * 304 * @return The expanded range. 305 */ 306 public static Range expand(Range range, 307 double lowerMargin, double upperMargin) { 308 ParamChecks.nullNotPermitted(range, "range"); 309 double length = range.getLength(); 310 double lower = range.getLowerBound() - length * lowerMargin; 311 double upper = range.getUpperBound() + length * upperMargin; 312 if (lower > upper) { 313 lower = lower / 2.0 + upper / 2.0; 314 upper = lower; 315 } 316 return new Range(lower, upper); 317 } 318 319 /** 320 * Shifts the range by the specified amount. 321 * 322 * @param base the base range (<code>null</code> not permitted). 323 * @param delta the shift amount. 324 * 325 * @return A new range. 326 */ 327 public static Range shift(Range base, double delta) { 328 return shift(base, delta, false); 329 } 330 331 /** 332 * Shifts the range by the specified amount. 333 * 334 * @param base the base range (<code>null</code> not permitted). 335 * @param delta the shift amount. 336 * @param allowZeroCrossing a flag that determines whether or not the 337 * bounds of the range are allowed to cross 338 * zero after adjustment. 339 * 340 * @return A new range. 341 */ 342 public static Range shift(Range base, double delta, 343 boolean allowZeroCrossing) { 344 ParamChecks.nullNotPermitted(base, "base"); 345 if (allowZeroCrossing) { 346 return new Range(base.getLowerBound() + delta, 347 base.getUpperBound() + delta); 348 } 349 else { 350 return new Range(shiftWithNoZeroCrossing(base.getLowerBound(), 351 delta), shiftWithNoZeroCrossing(base.getUpperBound(), 352 delta)); 353 } 354 } 355 356 /** 357 * Returns the given <code>value</code> adjusted by <code>delta</code> but 358 * with a check to prevent the result from crossing <code>0.0</code>. 359 * 360 * @param value the value. 361 * @param delta the adjustment. 362 * 363 * @return The adjusted value. 364 */ 365 private static double shiftWithNoZeroCrossing(double value, double delta) { 366 if (value > 0.0) { 367 return Math.max(value + delta, 0.0); 368 } 369 else if (value < 0.0) { 370 return Math.min(value + delta, 0.0); 371 } 372 else { 373 return value + delta; 374 } 375 } 376 377 /** 378 * Scales the range by the specified factor. 379 * 380 * @param base the base range (<code>null</code> not permitted). 381 * @param factor the scaling factor (must be non-negative). 382 * 383 * @return A new range. 384 * 385 * @since 1.0.9 386 */ 387 public static Range scale(Range base, double factor) { 388 ParamChecks.nullNotPermitted(base, "base"); 389 if (factor < 0) { 390 throw new IllegalArgumentException("Negative 'factor' argument."); 391 } 392 return new Range(base.getLowerBound() * factor, 393 base.getUpperBound() * factor); 394 } 395 396 /** 397 * Tests this object for equality with an arbitrary object. 398 * 399 * @param obj the object to test against (<code>null</code> permitted). 400 * 401 * @return A boolean. 402 */ 403 @Override 404 public boolean equals(Object obj) { 405 if (!(obj instanceof Range)) { 406 return false; 407 } 408 Range range = (Range) obj; 409 if (!(this.lower == range.lower)) { 410 return false; 411 } 412 if (!(this.upper == range.upper)) { 413 return false; 414 } 415 return true; 416 } 417 418 /** 419 * Returns a hash code. 420 * 421 * @return A hash code. 422 */ 423 @Override 424 public int hashCode() { 425 int result; 426 long temp; 427 temp = Double.doubleToLongBits(this.lower); 428 result = (int) (temp ^ (temp >>> 32)); 429 temp = Double.doubleToLongBits(this.upper); 430 result = 29 * result + (int) (temp ^ (temp >>> 32)); 431 return result; 432 } 433 434 /** 435 * Returns a string representation of this Range. 436 * 437 * @return A String "Range[lower,upper]" where lower=lower range and 438 * upper=upper range. 439 */ 440 @Override 441 public String toString() { 442 return ("Range[" + this.lower + "," + this.upper + "]"); 443 } 444 445}