001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004/**
005 * An interval of the form "lower < x <= upper" where 0 <= lower < upper.
006 * (upper can be Double.POSITIVE_INFINITY)
007 * immutable class
008 */
009public class Range {
010    private final double lower;
011    private final double upper;
012
013    public static final Range ZERO_TO_INFINITY = new Range(0.0, Double.POSITIVE_INFINITY);
014
015    public Range(double lower, double upper) {
016        if (lower < 0 || lower >= upper)
017            throw new IllegalArgumentException();
018        this.lower = lower;
019        this.upper = upper;
020    }
021
022    public boolean contains(double x) {
023        return lower < x && x <= upper;
024    }
025
026    /**
027     * provides the intersection of 2 overlapping ranges
028     */
029    public static Range cut(Range a, Range b) {
030        if (b.lower >= a.upper || b.upper <= a.lower)
031            throw new IllegalArgumentException();
032        return new Range(Math.max(a.lower, b.lower), Math.min(a.upper, b.upper));
033    }
034
035    /**
036     * under the premise, that x is within this range,
037     * and not within the other range, it shrinks this range in a way
038     * to exclude the other range, but still contain x.
039     *
040     * x                  |
041     *
042     * this   (------------------------------]
043     *
044     * other                   (-------]  or
045     *                         (-----------------]
046     *
047     * result (----------------]
048     */
049    public Range reduceAround(double x, Range other) {
050        if (!contains(x))
051            throw new IllegalArgumentException();
052        if (other.contains(x))
053            throw new IllegalArgumentException();
054
055        if (x < other.lower && other.lower < upper)
056            return new Range(lower, other.lower);
057
058        if (this.lower < other.upper && other.upper < x)
059            return new Range(other.upper, this.upper);
060
061        return this;
062    }
063
064    public double getLower() {
065        return lower;
066    }
067
068    public double getUpper() {
069        return upper;
070    }
071
072    @Override
073    public String toString() {
074        return String.format("|s%s-%s", lower, upper);
075    }
076
077    @Override
078    public boolean equals(Object o) {
079        if (this == o) return true;
080        if (o == null || getClass() != o.getClass()) return false;
081
082        Range range = (Range) o;
083
084        if (Double.compare(range.lower, lower) != 0) return false;
085        if (Double.compare(range.upper, upper) != 0) return false;
086
087        return true;
088    }
089
090    @Override
091    public int hashCode() {
092        int result;
093        long temp;
094        temp = Double.doubleToLongBits(lower);
095        result = (int) (temp ^ (temp >>> 32));
096        temp = Double.doubleToLongBits(upper);
097        result = 31 * result + (int) (temp ^ (temp >>> 32));
098        return result;
099    }
100}