1 /* 2 * Copyright 2005 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.common.geometry; 17 18 /** 19 * An R1Interval represents a closed, bounded interval on the real line. It is 20 * capable of representing the empty interval (containing no points) and 21 * zero-length intervals (containing a single point). 22 * 23 */ 24 25 public final strictfp class R1Interval { 26 27 private final double lo; 28 private final double hi; 29 30 /** Interval constructor. If lo > hi, the interval is empty. */ R1Interval(double lo, double hi)31 public R1Interval(double lo, double hi) { 32 this.lo = lo; 33 this.hi = hi; 34 } 35 36 /** 37 * Returns an empty interval. (Any interval where lo > hi is considered 38 * empty.) 39 */ empty()40 public static R1Interval empty() { 41 return new R1Interval(1, 0); 42 } 43 44 /** 45 * Convenience method to construct an interval containing a single point. 46 */ fromPoint(double p)47 public static R1Interval fromPoint(double p) { 48 return new R1Interval(p, p); 49 } 50 51 /** 52 * Convenience method to construct the minimal interval containing the two 53 * given points. This is equivalent to starting with an empty interval and 54 * calling AddPoint() twice, but it is more efficient. 55 */ fromPointPair(double p1, double p2)56 public static R1Interval fromPointPair(double p1, double p2) { 57 if (p1 <= p2) { 58 return new R1Interval(p1, p2); 59 } else { 60 return new R1Interval(p2, p1); 61 } 62 } 63 lo()64 public double lo() { 65 return lo; 66 } 67 hi()68 public double hi() { 69 return hi; 70 } 71 72 /** 73 * Return true if the interval is empty, i.e. it contains no points. 74 */ isEmpty()75 public boolean isEmpty() { 76 return lo() > hi(); 77 } 78 79 /** 80 * Return the center of the interval. For empty intervals, the result is 81 * arbitrary. 82 */ getCenter()83 public double getCenter() { 84 return 0.5 * (lo() + hi()); 85 } 86 87 /** 88 * Return the length of the interval. The length of an empty interval is 89 * negative. 90 */ getLength()91 public double getLength() { 92 return hi() - lo(); 93 } 94 contains(double p)95 public boolean contains(double p) { 96 return p >= lo() && p <= hi(); 97 } 98 interiorContains(double p)99 public boolean interiorContains(double p) { 100 return p > lo() && p < hi(); 101 } 102 103 /** Return true if this interval contains the interval 'y'. */ contains(R1Interval y)104 public boolean contains(R1Interval y) { 105 if (y.isEmpty()) { 106 return true; 107 } 108 return y.lo() >= lo() && y.hi() <= hi(); 109 } 110 111 /** 112 * Return true if the interior of this interval contains the entire interval 113 * 'y' (including its boundary). 114 */ interiorContains(R1Interval y)115 public boolean interiorContains(R1Interval y) { 116 if (y.isEmpty()) { 117 return true; 118 } 119 return y.lo() > lo() && y.hi() < hi(); 120 } 121 122 /** 123 * Return true if this interval intersects the given interval, i.e. if they 124 * have any points in common. 125 */ intersects(R1Interval y)126 public boolean intersects(R1Interval y) { 127 if (lo() <= y.lo()) { 128 return y.lo() <= hi() && y.lo() <= y.hi(); 129 } else { 130 return lo() <= y.hi() && lo() <= hi(); 131 } 132 } 133 134 /** 135 * Return true if the interior of this interval intersects any point of the 136 * given interval (including its boundary). 137 */ interiorIntersects(R1Interval y)138 public boolean interiorIntersects(R1Interval y) { 139 return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi(); 140 } 141 142 /** Expand the interval so that it contains the given point "p". */ addPoint(double p)143 public R1Interval addPoint(double p) { 144 if (isEmpty()) { 145 return R1Interval.fromPoint(p); 146 } else if (p < lo()) { 147 return new R1Interval(p, hi()); 148 } else if (p > hi()) { 149 return new R1Interval(lo(), p); 150 } else { 151 return new R1Interval(lo(), hi()); 152 } 153 } 154 155 /** 156 * Return an interval that contains all points with a distance "radius" of a 157 * point in this interval. Note that the expansion of an empty interval is 158 * always empty. 159 */ expanded(double radius)160 public R1Interval expanded(double radius) { 161 // assert (radius >= 0); 162 if (isEmpty()) { 163 return this; 164 } 165 return new R1Interval(lo() - radius, hi() + radius); 166 } 167 168 /** 169 * Return the smallest interval that contains this interval and the given 170 * interval "y". 171 */ union(R1Interval y)172 public R1Interval union(R1Interval y) { 173 if (isEmpty()) { 174 return y; 175 } 176 if (y.isEmpty()) { 177 return this; 178 } 179 return new R1Interval(Math.min(lo(), y.lo()), Math.max(hi(), y.hi())); 180 } 181 182 /** 183 * Return the intersection of this interval with the given interval. Empty 184 * intervals do not need to be special-cased. 185 */ intersection(R1Interval y)186 public R1Interval intersection(R1Interval y) { 187 return new R1Interval(Math.max(lo(), y.lo()), Math.min(hi(), y.hi())); 188 } 189 190 @Override equals(Object that)191 public boolean equals(Object that) { 192 if (that instanceof R1Interval) { 193 R1Interval y = (R1Interval) that; 194 // Return true if two intervals contain the same set of points. 195 return (lo() == y.lo() && hi() == y.hi()) || (isEmpty() && y.isEmpty()); 196 197 } 198 return false; 199 } 200 201 @Override hashCode()202 public int hashCode() { 203 if (isEmpty()) { 204 return 17; 205 } 206 207 long value = 17; 208 value = 37 * value + Double.doubleToLongBits(lo); 209 value = 37 * value + Double.doubleToLongBits(hi); 210 return (int) (value ^ (value >>> 32)); 211 } 212 approxEquals(R1Interval y)213 public boolean approxEquals(R1Interval y) { 214 return approxEquals(y, 1e-15); 215 } 216 217 /** 218 * Return true if length of the symmetric difference between the two intervals 219 * is at most the given tolerance. 220 * 221 */ approxEquals(R1Interval y, double maxError)222 public boolean approxEquals(R1Interval y, double maxError) { 223 if (isEmpty()) { 224 return y.getLength() <= maxError; 225 } 226 if (y.isEmpty()) { 227 return getLength() <= maxError; 228 } 229 return Math.abs(y.lo() - lo()) + Math.abs(y.hi() - hi()) <= maxError; 230 } 231 232 @Override toString()233 public String toString() { 234 return "[" + lo() + ", " + hi() + "]"; 235 } 236 } 237