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