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    /**
016     * Constructs a new {@code Range}.
017     * @param lower Lower bound. Must be positive or zero
018     * @param upper Upper bound
019     * @throws IllegalArgumentException if the range is invalid ({@code lower < 0 || lower >= upper})
020     */
021    public Range(double lower, double upper) {
022        if (lower < 0 || lower >= upper)
023            throw new IllegalArgumentException("Invalid range: "+lower+"-"+upper);
024        this.lower = lower;
025        this.upper = upper;
026    }
027
028    public boolean contains(double x) {
029        return lower < x && x <= upper;
030    }
031
032    /**
033     * provides the intersection of 2 overlapping ranges
034     */
035    public static Range cut(Range a, Range b) {
036        if (b.lower >= a.upper || b.upper <= a.lower)
037            throw new IllegalArgumentException("Ranges do not overlap: "+a+" - "+b);
038        return new Range(Math.max(a.lower, b.lower), Math.min(a.upper, b.upper));
039    }
040
041    /**
042     * under the premise, that x is within this range,
043     * and not within the other range, it shrinks this range in a way
044     * to exclude the other range, but still contain x.
045     *
046     * x                  |
047     *
048     * this   (------------------------------]
049     *
050     * other                   (-------]  or
051     *                         (-----------------]
052     *
053     * result (----------------]
054     */
055    public Range reduceAround(double x, Range other) {
056        if (!contains(x))
057            throw new IllegalArgumentException(x+" is not inside "+this);
058        if (other.contains(x))
059            throw new IllegalArgumentException(x+" is inside "+other);
060
061        if (x < other.lower && other.lower < upper)
062            return new Range(lower, other.lower);
063
064        if (this.lower < other.upper && other.upper < x)
065            return new Range(other.upper, this.upper);
066
067        return this;
068    }
069
070    public double getLower() {
071        return lower;
072    }
073
074    public double getUpper() {
075        return upper;
076    }
077
078    @Override
079    public String toString() {
080        return String.format("|s%s-%s", lower, upper);
081    }
082
083    @Override
084    public boolean equals(Object o) {
085        if (this == o) return true;
086        if (o == null || getClass() != o.getClass()) return false;
087
088        Range range = (Range) o;
089
090        if (Double.compare(range.lower, lower) != 0) return false;
091        if (Double.compare(range.upper, upper) != 0) return false;
092
093        return true;
094    }
095
096    @Override
097    public int hashCode() {
098        int result;
099        long temp;
100        temp = Double.doubleToLongBits(lower);
101        result = (int) (temp ^ (temp >>> 32));
102        temp = Double.doubleToLongBits(upper);
103        result = 31 * result + (int) (temp ^ (temp >>> 32));
104        return result;
105    }
106}