001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.numbers.angle; 018 019import java.util.function.DoubleSupplier; 020import java.util.function.DoubleUnaryOperator; 021 022/** 023 * Represents the <a href="https://en.wikipedia.org/wiki/Angle">angle</a> concept. 024 */ 025public abstract class Angle implements DoubleSupplier { 026 /** 2π. */ 027 public static final double TWO_PI = 2 * Math.PI; 028 /** π/2. */ 029 public static final double PI_OVER_TWO = 0.5 * Math.PI; 030 /** Turns to degrees conversion factor. */ 031 private static final double TURN_TO_DEG = 360d; 032 /** Radians to degrees conversion factor. */ 033 private static final double RAD_TO_DEG = 180.0 / Math.PI; 034 /** Degrees to radians conversion factor. */ 035 private static final double DEG_TO_RAD = Math.PI / 180.0; 036 037 /** Value (unit depends on concrete instance). */ 038 private final double value; 039 040 /** 041 * @param value Value in turns. 042 */ 043 private Angle(final double value) { 044 this.value = value; 045 } 046 047 /** @return the value. */ 048 @Override 049 public double getAsDouble() { 050 return value; 051 } 052 053 /** {@inheritDoc} */ 054 @Override 055 public int hashCode() { 056 return Double.hashCode(value); 057 } 058 059 /** 060 * Test for equality with another object. 061 * Objects are considered to be equal if their concrete types are 062 * equal and their values are exactly the same (or both are {@code Double.NaN}). 063 * 064 * @param other Object to test for equality with this instance. 065 * @return {@code true} if the objects are equal, {@code false} if 066 * {@code other} is {@code null}, not of the same type as this instance, 067 * or not equal to this instance. 068 */ 069 @Override 070 public boolean equals(final Object other) { 071 return other != null && 072 getClass().equals(other.getClass()) && 073 Double.doubleToLongBits(value) == Double.doubleToLongBits(((Angle) other).value); 074 } 075 076 /** 077 * @return the angle in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>. 078 */ 079 public abstract Turn toTurn(); 080 081 /** 082 * @return the angle in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. 083 */ 084 public abstract Rad toRad(); 085 086 /** 087 * @return the angle in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>. 088 */ 089 public abstract Deg toDeg(); 090 091 /** 092 * Unit: <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>. 093 */ 094 public static final class Turn extends Angle { 095 /** Zero. */ 096 public static final Turn ZERO = Turn.of(0d); 097 /** Normalizing operator (result will be within the {@code [0, 1[} interval). */ 098 public static final DoubleUnaryOperator WITHIN_0_AND_1 = normalizer(0d); 099 100 /** 101 * @param angle (in turns). 102 */ 103 private Turn(final double angle) { 104 super(angle); 105 } 106 107 /** 108 * @param angle (in turns). 109 * @return a new intance. 110 */ 111 public static Turn of(final double angle) { 112 return new Turn(angle); 113 } 114 115 /** {@inheritDoc} */ 116 @Override 117 public Turn toTurn() { 118 return this; 119 } 120 121 /** {@inheritDoc} */ 122 @Override 123 public Rad toRad() { 124 return Rad.of(getAsDouble() * TWO_PI); 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public Deg toDeg() { 130 return Deg.of(getAsDouble() * TURN_TO_DEG); 131 } 132 133 /** 134 * Creates an operator for normalizing/reducing an angle. 135 * The output will be within the {@code [lo, lo + 1[} interval. 136 * 137 * @param lo Lower bound of the normalized interval. 138 * @return the normalization operator. 139 */ 140 public static DoubleUnaryOperator normalizer(final double lo) { 141 return new Normalizer(lo, 1d); 142 } 143 } 144 145 /** 146 * Unit: <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. 147 */ 148 public static final class Rad extends Angle { 149 /** Zero. */ 150 public static final Rad ZERO = Rad.of(0d); 151 /** π. */ 152 public static final Rad PI = Rad.of(Math.PI); 153 /** 2π. */ 154 public static final Rad TWO_PI = Rad.of(Angle.TWO_PI); 155 /** Normalizing operator (result will be within the <code>[0, 2π[</code> interval). */ 156 public static final DoubleUnaryOperator WITHIN_0_AND_2PI = normalizer(0d); 157 /** Normalizing operator (result will be within the <code>[-π, π[</code> interval). */ 158 public static final DoubleUnaryOperator WITHIN_MINUS_PI_AND_PI = normalizer(-Math.PI); 159 160 /** 161 * @param angle (in radians). 162 */ 163 private Rad(final double angle) { 164 super(angle); 165 } 166 167 /** 168 * @param angle (in radians). 169 * @return a new intance. 170 */ 171 public static Rad of(final double angle) { 172 return new Rad(angle); 173 } 174 175 /** {@inheritDoc} */ 176 @Override 177 public Turn toTurn() { 178 return Turn.of(getAsDouble() / Angle.TWO_PI); 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 public Rad toRad() { 184 return this; 185 } 186 187 /** {@inheritDoc} */ 188 @Override 189 public Deg toDeg() { 190 return Deg.of(getAsDouble() * RAD_TO_DEG); 191 } 192 193 /** 194 * Creates an operator for normalizing/reducing an angle. 195 * The output will be within the <code> [lo, lo + 2π[</code> interval. 196 * 197 * @param lo Lower bound of the normalized interval. 198 * @return the normalization operator. 199 */ 200 public static DoubleUnaryOperator normalizer(final double lo) { 201 return new Normalizer(lo, Angle.TWO_PI); 202 } 203 } 204 205 /** 206 * Unit: <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>. 207 */ 208 public static final class Deg extends Angle { 209 /** Zero. */ 210 public static final Deg ZERO = Deg.of(0d); 211 /** Normalizing operator (result will be within the {@code [0, 360[} interval). */ 212 public static final DoubleUnaryOperator WITHIN_0_AND_360 = normalizer(0d); 213 214 /** 215 * @param angle (in degrees). 216 */ 217 private Deg(final double angle) { 218 super(angle); 219 } 220 221 /** 222 * @param angle (in degrees). 223 * @return a new intance. 224 */ 225 public static Deg of(final double angle) { 226 return new Deg(angle); 227 } 228 229 /** {@inheritDoc} */ 230 @Override 231 public Turn toTurn() { 232 return Turn.of(getAsDouble() / TURN_TO_DEG); 233 } 234 235 /** {@inheritDoc} */ 236 @Override 237 public Rad toRad() { 238 return Rad.of(getAsDouble() * DEG_TO_RAD); 239 } 240 241 /** {@inheritDoc} */ 242 @Override 243 public Deg toDeg() { 244 return this; 245 } 246 247 /** 248 * Creates an operator for normalizing/reducing an angle. 249 * The output will be within the {@code [c, c + 360[} interval. 250 * 251 * @param lo Lower bound of the normalized interval. 252 * @return the normalization operator. 253 */ 254 public static DoubleUnaryOperator normalizer(final double lo) { 255 return new Normalizer(lo, TURN_TO_DEG); 256 } 257 } 258 259 /** 260 * Normalizes an angle around a center value. 261 */ 262 private static final class Normalizer implements DoubleUnaryOperator { 263 /** Lower bound. */ 264 private final double lo; 265 /** Upper bound. */ 266 private final double hi; 267 /** Period. */ 268 private final double period; 269 /** Normalizer. */ 270 private final Reduce reduce; 271 272 /** 273 * Note: It is assumed that both arguments have the same unit. 274 * 275 * @param lo Lower bound of the desired interval. 276 * @param period Circonference of the circle. 277 */ 278 Normalizer(final double lo, 279 final double period) { 280 this.period = period; 281 this.lo = lo; 282 this.hi = lo + period; 283 reduce = new Reduce(lo, period); 284 } 285 286 /** 287 * @param a Angle. 288 * @return {@code = a - k} where {@code k} is an integer that satisfies 289 * {@code lo <= a - k < lo + period}. 290 */ 291 @Override 292 public double applyAsDouble(final double a) { 293 if (lo <= a && 294 a < hi) { 295 // Already within the main interval. 296 return a; 297 } 298 299 final double normalized = reduce.applyAsDouble(a) + lo; 300 return normalized < hi ? 301 normalized : 302 // If value is too small to be representable compared to the 303 // floor expression above (i.e. value + x = x), then we may 304 // end up with a number exactly equal to the upper bound. 305 // In that case, subtract one period from the normalized value 306 // so that the result is strictly less than the upper bound. (We also 307 // want to ensure that we do not return anything less than the lower bound.) 308 Math.max(lo, normalized - period); 309 } 310 } 311}