001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2006, 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-2006, 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: * $Id: Range.java,v 1.6.2.2 2006/01/11 11:27:34 mungady Exp $
038: *
039: * Changes (from 23-Jun-2001)
040: * --------------------------
041: * 22-Apr-2002 : Version 1, loosely based by code by Bill Kelemen (DG);
042: * 30-Apr-2002 : Added getLength() and getCentralValue() methods. Changed
043: * argument check in constructor (DG);
044: * 13-Jun-2002 : Added contains(double) method (DG);
045: * 22-Aug-2002 : Added fix to combine method where both ranges are null, thanks
046: * to Chuanhao Chiu for reporting and fixing this (DG);
047: * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
048: * 26-Mar-2003 : Implemented Serializable (DG);
049: * 14-Aug-2003 : Added equals() method (DG);
050: * 27-Aug-2003 : Added toString() method (BK);
051: * 11-Sep-2003 : Added Clone Support (NB);
052: * 23-Sep-2003 : Fixed Checkstyle issues (DG);
053: * 25-Sep-2003 : Oops, Range immutable, clone not necessary (NB);
054: * 05-May-2004 : Added constrain() and intersects() methods (DG);
055: * 18-May-2004 : Added expand() method (DG);
056: * ------------- JFreeChart 1.0.0 ---------------------------------------------
057: * 11-Jan-2006 : Added new method expandToInclude(Range, double) (DG);
058: *
059: */
060:
061: package org.jfree.data;
062:
063: import java.io.Serializable;
064:
065: /**
066: * Represents an immutable range of values.
067: */
068: public strictfp class Range implements Serializable {
069:
070: /** For serialization. */
071: private static final long serialVersionUID = -906333695431863380L;
072:
073: /** The lower bound of the range. */
074: private double lower;
075:
076: /** The upper bound of the range. */
077: private double upper;
078:
079: /**
080: * Creates a new range.
081: *
082: * @param lower the lower bound (must be <= upper bound).
083: * @param upper the upper bound (must be >= lower bound).
084: */
085: public Range(double lower, double upper) {
086: if (lower > upper) {
087: String msg = "Range(double, double): require lower ("
088: + lower + ") <= upper (" + upper + ").";
089: throw new IllegalArgumentException(msg);
090: }
091: this .lower = lower;
092: this .upper = upper;
093: }
094:
095: /**
096: * Returns the lower bound for the range.
097: *
098: * @return The lower bound.
099: */
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: } else {
156: return (b0 < this .upper && b1 >= b0);
157: }
158: }
159:
160: /**
161: * Returns the value within the range that is closest to the specified
162: * value.
163: *
164: * @param value the value.
165: *
166: * @return The constrained value.
167: */
168: public double constrain(double value) {
169: double result = value;
170: if (!contains(value)) {
171: if (value > this .upper) {
172: result = this .upper;
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: } else {
200: if (range2 == null) {
201: return range1;
202: } else {
203: double l = Math.min(range1.getLowerBound(), range2
204: .getLowerBound());
205: double u = Math.max(range1.getUpperBound(), range2
206: .getUpperBound());
207: return new Range(l, u);
208: }
209: }
210: }
211:
212: /**
213: * Returns a range that includes all the values in the specified
214: * <code>range</code> AND the specified <code>value</code>.
215: *
216: * @param range the range (<code>null</code> permitted).
217: * @param value the value that must be included.
218: *
219: * @return A range.
220: *
221: * @since 1.0.1
222: */
223: public static Range expandToInclude(Range range, double value) {
224: if (range == null) {
225: return new Range(value, value);
226: }
227: if (value < range.getLowerBound()) {
228: return new Range(value, range.getUpperBound());
229: } else if (value > range.getUpperBound()) {
230: return new Range(range.getLowerBound(), value);
231: } else {
232: return range;
233: }
234: }
235:
236: /**
237: * Creates a new range by adding margins to an existing range.
238: *
239: * @param range the range (<code>null</code> not permitted).
240: * @param lowerMargin the lower margin (expressed as a percentage of the
241: * range length).
242: * @param upperMargin the upper margin (expressed as a percentage of the
243: * range length).
244: *
245: * @return The expanded range.
246: */
247: public static Range expand(Range range, double lowerMargin,
248: double upperMargin) {
249: if (range == null) {
250: throw new IllegalArgumentException("Null 'range' argument.");
251: }
252: double length = range.getLength();
253: double lower = length * lowerMargin;
254: double upper = length * upperMargin;
255: return new Range(range.getLowerBound() - lower, range
256: .getUpperBound()
257: + upper);
258: }
259:
260: /**
261: * Shifts the range by the specified amount.
262: *
263: * @param base the base range.
264: * @param delta the shift amount.
265: *
266: * @return A new range.
267: */
268: public static Range shift(Range base, double delta) {
269: return shift(base, delta, false);
270: }
271:
272: /**
273: * Shifts the range by the specified amount.
274: *
275: * @param base the base range.
276: * @param delta the shift amount.
277: * @param allowZeroCrossing a flag that determines whether or not the
278: * bounds of the range are allowed to cross
279: * zero after adjustment.
280: *
281: * @return A new range.
282: */
283: public static Range shift(Range base, double delta,
284: boolean allowZeroCrossing) {
285: if (allowZeroCrossing) {
286: return new Range(base.getLowerBound() + delta, base
287: .getUpperBound()
288: + delta);
289: } else {
290: return new Range(shiftWithNoZeroCrossing(base
291: .getLowerBound(), delta), shiftWithNoZeroCrossing(
292: base.getUpperBound(), delta));
293: }
294: }
295:
296: /**
297: * Returns the given <code>value</code> adjusted by <code>delta</code> but
298: * with a check to prevent the result from crossing <code>0.0</code>.
299: *
300: * @param value the value.
301: * @param delta the adjustment.
302: *
303: * @return The adjusted value.
304: */
305: private static double shiftWithNoZeroCrossing(double value,
306: double delta) {
307: if (value > 0.0) {
308: return Math.max(value + delta, 0.0);
309: } else if (value < 0.0) {
310: return Math.min(value + delta, 0.0);
311: } else {
312: return value + delta;
313: }
314: }
315:
316: /**
317: * Tests this object for equality with an arbitrary object.
318: *
319: * @param obj the object to test against (<code>null</code> permitted).
320: *
321: * @return A boolean.
322: */
323: public boolean equals(Object obj) {
324: if (!(obj instanceof Range)) {
325: return false;
326: }
327: Range range = (Range) obj;
328: if (!(this .lower == range.lower)) {
329: return false;
330: }
331: if (!(this .upper == range.upper)) {
332: return false;
333: }
334: return true;
335: }
336:
337: /**
338: * Returns a hash code.
339: *
340: * @return A hash code.
341: */
342: public int hashCode() {
343: int result;
344: long temp;
345: temp = Double.doubleToLongBits(this .lower);
346: result = (int) (temp ^ (temp >>> 32));
347: temp = Double.doubleToLongBits(this .upper);
348: result = 29 * result + (int) (temp ^ (temp >>> 32));
349: return result;
350: }
351:
352: /**
353: * Returns a string representation of this Range.
354: *
355: * @return A String "Range[lower,upper]" where lower=lower range and
356: * upper=upper range.
357: */
358: public String toString() {
359: return ("Range[" + this .lower + "," + this .upper + "]");
360: }
361:
362: }
|