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}