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