/* * Copyright 2005 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.geometry; /** * An R1Interval represents a closed, bounded interval on the real line. It is * capable of representing the empty interval (containing no points) and * zero-length intervals (containing a single point). * */ public final strictfp class R1Interval { private final double lo; private final double hi; /** Interval constructor. If lo > hi, the interval is empty. */ public R1Interval(double lo, double hi) { this.lo = lo; this.hi = hi; } /** * Returns an empty interval. (Any interval where lo > hi is considered * empty.) */ public static R1Interval empty() { return new R1Interval(1, 0); } /** * Convenience method to construct an interval containing a single point. */ public static R1Interval fromPoint(double p) { return new R1Interval(p, p); } /** * Convenience method to construct the minimal interval containing the two * given points. This is equivalent to starting with an empty interval and * calling AddPoint() twice, but it is more efficient. */ public static R1Interval fromPointPair(double p1, double p2) { if (p1 <= p2) { return new R1Interval(p1, p2); } else { return new R1Interval(p2, p1); } } public double lo() { return lo; } public double hi() { return hi; } /** * Return true if the interval is empty, i.e. it contains no points. */ public boolean isEmpty() { return lo() > hi(); } /** * Return the center of the interval. For empty intervals, the result is * arbitrary. */ public double getCenter() { return 0.5 * (lo() + hi()); } /** * Return the length of the interval. The length of an empty interval is * negative. */ public double getLength() { return hi() - lo(); } public boolean contains(double p) { return p >= lo() && p <= hi(); } public boolean interiorContains(double p) { return p > lo() && p < hi(); } /** Return true if this interval contains the interval 'y'. */ public boolean contains(R1Interval y) { if (y.isEmpty()) { return true; } return y.lo() >= lo() && y.hi() <= hi(); } /** * Return true if the interior of this interval contains the entire interval * 'y' (including its boundary). */ public boolean interiorContains(R1Interval y) { if (y.isEmpty()) { return true; } return y.lo() > lo() && y.hi() < hi(); } /** * Return true if this interval intersects the given interval, i.e. if they * have any points in common. */ public boolean intersects(R1Interval y) { if (lo() <= y.lo()) { return y.lo() <= hi() && y.lo() <= y.hi(); } else { return lo() <= y.hi() && lo() <= hi(); } } /** * Return true if the interior of this interval intersects any point of the * given interval (including its boundary). */ public boolean interiorIntersects(R1Interval y) { return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi(); } /** Expand the interval so that it contains the given point "p". */ public R1Interval addPoint(double p) { if (isEmpty()) { return R1Interval.fromPoint(p); } else if (p < lo()) { return new R1Interval(p, hi()); } else if (p > hi()) { return new R1Interval(lo(), p); } else { return new R1Interval(lo(), hi()); } } /** * Return an interval that contains all points with a distance "radius" of a * point in this interval. Note that the expansion of an empty interval is * always empty. */ public R1Interval expanded(double radius) { // assert (radius >= 0); if (isEmpty()) { return this; } return new R1Interval(lo() - radius, hi() + radius); } /** * Return the smallest interval that contains this interval and the given * interval "y". */ public R1Interval union(R1Interval y) { if (isEmpty()) { return y; } if (y.isEmpty()) { return this; } return new R1Interval(Math.min(lo(), y.lo()), Math.max(hi(), y.hi())); } /** * Return the intersection of this interval with the given interval. Empty * intervals do not need to be special-cased. */ public R1Interval intersection(R1Interval y) { return new R1Interval(Math.max(lo(), y.lo()), Math.min(hi(), y.hi())); } @Override public boolean equals(Object that) { if (that instanceof R1Interval) { R1Interval y = (R1Interval) that; // Return true if two intervals contain the same set of points. return (lo() == y.lo() && hi() == y.hi()) || (isEmpty() && y.isEmpty()); } return false; } @Override public int hashCode() { if (isEmpty()) { return 17; } long value = 17; value = 37 * value + Double.doubleToLongBits(lo); value = 37 * value + Double.doubleToLongBits(hi); return (int) (value ^ (value >>> 32)); } public boolean approxEquals(R1Interval y) { return approxEquals(y, 1e-15); } /** * Return true if length of the symmetric difference between the two intervals * is at most the given tolerance. * */ public boolean approxEquals(R1Interval y, double maxError) { if (isEmpty()) { return y.getLength() <= maxError; } if (y.isEmpty()) { return getLength() <= maxError; } return Math.abs(y.lo() - lo()) + Math.abs(y.hi() - hi()) <= maxError; } @Override public String toString() { return "[" + lo() + ", " + hi() + "]"; } }