1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, 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 License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * ----------
  27. * Range.java
  28. * ----------
  29. * (C) Copyright 2002-2005, by Object Refinery Limited and Contributors.
  30. *
  31. * Original Author: David Gilbert (for Object Refinery Limited);
  32. * Contributor(s): Chuanhao Chiu;
  33. * Bill Kelemen;
  34. * Nicolas Brodu;
  35. *
  36. * $Id: Range.java,v 1.5 2005/03/04 11:44:24 mungady Exp $
  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. *
  56. */
  57. package org.jfree.data;
  58. import java.io.Serializable;
  59. /**
  60. * Represents an immutable range of values.
  61. */
  62. public strictfp class Range implements Serializable {
  63. /** The lower bound of the range. */
  64. private double lower;
  65. /** The upper bound of the range. */
  66. private double upper;
  67. /**
  68. * Creates a new range.
  69. *
  70. * @param lower the lower bound (must be <= upper bound).
  71. * @param upper the upper bound (must be >= lower bound).
  72. */
  73. public Range(double lower, double upper) {
  74. if (lower > upper) {
  75. String msg = "Range(double, double): require lower (" + lower
  76. + ") <= upper (" + upper + ").";
  77. throw new IllegalArgumentException(msg);
  78. }
  79. this.lower = lower;
  80. this.upper = upper;
  81. }
  82. /**
  83. * Returns the lower bound for the range.
  84. *
  85. * @return The lower bound.
  86. */
  87. public double getLowerBound() {
  88. return this.lower;
  89. }
  90. /**
  91. * Returns the upper bound for the range.
  92. *
  93. * @return The upper bound.
  94. */
  95. public double getUpperBound() {
  96. return this.upper;
  97. }
  98. /**
  99. * Returns the length of the range.
  100. *
  101. * @return The length.
  102. */
  103. public double getLength() {
  104. return this.upper - this.lower;
  105. }
  106. /**
  107. * Returns the central value for the range.
  108. *
  109. * @return The central value.
  110. */
  111. public double getCentralValue() {
  112. return this.lower / 2.0 + this.upper / 2.0;
  113. }
  114. /**
  115. * Returns <code>true</code> if the range contains the specified value and
  116. * <code>false</code> otherwise.
  117. *
  118. * @param value the value to lookup.
  119. *
  120. * @return <code>true</code> if the range contains the specified value.
  121. */
  122. public boolean contains(double value) {
  123. return (value >= this.lower && value <= this.upper);
  124. }
  125. /**
  126. * Returns <code>true</code> if the range intersects with the specified
  127. * range, and <code>false</code> otherwise.
  128. *
  129. * @param b0 the lower bound (should be <= b1).
  130. * @param b1 the upper bound (should be >= b0).
  131. *
  132. * @return A boolean.
  133. */
  134. public boolean intersects(double b0, double b1) {
  135. if (b0 <= this.lower) {
  136. return (b1 > this.lower);
  137. }
  138. else {
  139. return (b0 < this.upper && b1 >= b0);
  140. }
  141. }
  142. /**
  143. * Returns the value within the range that is closest to the specified
  144. * value.
  145. *
  146. * @param value the value.
  147. *
  148. * @return The constrained value.
  149. */
  150. public double constrain(double value) {
  151. double result = value;
  152. if (!contains(value)) {
  153. if (value > this.upper) {
  154. result = this.upper;
  155. }
  156. else if (value < this.lower) {
  157. result = this.lower;
  158. }
  159. }
  160. return result;
  161. }
  162. /**
  163. * Creates a new range by combining two existing ranges.
  164. * <P>
  165. * Note that:
  166. * <ul>
  167. * <li>either range can be <code>null</code>, in which case the other
  168. * range is returned;</li>
  169. * <li>if both ranges are <code>null</code> the return value is
  170. * <code>null</code>.</li>
  171. * </ul>
  172. *
  173. * @param range1 the first range (<code>null</code> permitted).
  174. * @param range2 the second range (<code>null</code> permitted).
  175. *
  176. * @return A new range (possibly <code>null</code>).
  177. */
  178. public static Range combine(Range range1, Range range2) {
  179. if (range1 == null) {
  180. return range2;
  181. }
  182. else {
  183. if (range2 == null) {
  184. return range1;
  185. }
  186. else {
  187. double l = Math.min(
  188. range1.getLowerBound(), range2.getLowerBound()
  189. );
  190. double u = Math.max(
  191. range1.getUpperBound(), range2.getUpperBound()
  192. );
  193. return new Range(l, u);
  194. }
  195. }
  196. }
  197. /**
  198. * Creates a new range by adding margins to an existing range.
  199. *
  200. * @param range the range (<code>null</code> not permitted).
  201. * @param lowerMargin the lower margin (expressed as a percentage of the
  202. * range length).
  203. * @param upperMargin the upper margin (expressed as a percentage of the
  204. * range length).
  205. *
  206. * @return The expanded range.
  207. */
  208. public static Range expand(Range range,
  209. double lowerMargin, double upperMargin) {
  210. if (range == null) {
  211. throw new IllegalArgumentException("Null 'range' argument.");
  212. }
  213. double length = range.getLength();
  214. double lower = length * lowerMargin;
  215. double upper = length * upperMargin;
  216. return new Range(
  217. range.getLowerBound() - lower, range.getUpperBound() + upper
  218. );
  219. }
  220. /**
  221. * Shifts the range by the specified amount.
  222. *
  223. * @param base the base range.
  224. * @param delta the shift amount.
  225. *
  226. * @return A new range.
  227. */
  228. public static Range shift(Range base, double delta) {
  229. return shift(base, delta, false);
  230. }
  231. /**
  232. * Shifts the range by the specified amount.
  233. *
  234. * @param base the base range.
  235. * @param delta the shift amount.
  236. * @param allowZeroCrossing a flag that determines whether or not the
  237. * bounds of the range are allowed to cross
  238. * zero after adjustment.
  239. *
  240. * @return A new range.
  241. */
  242. public static Range shift(Range base, double delta,
  243. boolean allowZeroCrossing) {
  244. if (allowZeroCrossing) {
  245. return new Range(
  246. base.getLowerBound() + delta, base.getUpperBound() + delta
  247. );
  248. }
  249. else {
  250. return new Range(
  251. shiftWithNoZeroCrossing(base.getLowerBound(), delta),
  252. shiftWithNoZeroCrossing(base.getUpperBound(), delta)
  253. );
  254. }
  255. }
  256. private static double shiftWithNoZeroCrossing(double value, double delta) {
  257. if (value > 0.0) {
  258. return Math.max(value + delta, 0.0);
  259. }
  260. else if (value < 0.0) {
  261. return Math.min(value + delta, 0.0);
  262. }
  263. else {
  264. return value + delta;
  265. }
  266. }
  267. /**
  268. * Tests this object for equality with an arbitrary object.
  269. *
  270. * @param obj the object to test against (<code>null</code> permitted).
  271. *
  272. * @return A boolean.
  273. */
  274. public boolean equals(Object obj) {
  275. if (!(obj instanceof Range)) {
  276. return false;
  277. }
  278. Range range = (Range) obj;
  279. if (!(this.lower == range.lower)) {
  280. return false;
  281. }
  282. if (!(this.upper == range.upper)) {
  283. return false;
  284. }
  285. return true;
  286. }
  287. /**
  288. * Returns a hash code.
  289. *
  290. * @return A hash code.
  291. */
  292. public int hashCode() {
  293. int result;
  294. long temp;
  295. temp = Double.doubleToLongBits(this.lower);
  296. result = (int) (temp ^ (temp >>> 32));
  297. temp = Double.doubleToLongBits(this.upper);
  298. result = 29 * result + (int) (temp ^ (temp >>> 32));
  299. return result;
  300. }
  301. /**
  302. * Returns a string representation of this Range.
  303. *
  304. * @return A String "Range[lower,upper]" where lower=lower range and
  305. * upper=upper range.
  306. */
  307. public String toString() {
  308. return ("Range[" + this.lower + "," + this.upper + "]");
  309. }
  310. }