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 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 * Convert to a {@link Turn}. 078 * 079 * @return the angle in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>. 080 */ 081 public abstract Turn toTurn(); 082 083 /** 084 * Convert to a {@link Rad}. 085 * 086 * @return the angle in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. 087 */ 088 public abstract Rad toRad(); 089 090 /** 091 * Convert to a {@link Deg}. 092 * 093 * @return the angle in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>. 094 */ 095 public abstract Deg toDeg(); 096 097 /** 098 * Unit: <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>. 099 */ 100 public static final class Turn extends Angle { 101 /** Zero. */ 102 public static final Turn ZERO = of(0d); 103 /** Normalizing operator (result will be within the {@code [0, 1[} interval). */ 104 public static final DoubleUnaryOperator WITHIN_0_AND_1 = normalizer(0d); 105 106 /** 107 * Create an instance. 108 * 109 * @param angle (in turns). 110 */ 111 private Turn(final double angle) { 112 super(angle); 113 } 114 115 /** 116 * Create an instance. 117 * 118 * @param angle (in turns). 119 * @return a new instance. 120 */ 121 public static Turn of(final double angle) { 122 return new Turn(angle); 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public Turn toTurn() { 128 return this; 129 } 130 131 /** {@inheritDoc} */ 132 @Override 133 public Rad toRad() { 134 return Rad.of(getAsDouble() * TWO_PI); 135 } 136 137 /** {@inheritDoc} */ 138 @Override 139 public Deg toDeg() { 140 return Deg.of(getAsDouble() * TURN_TO_DEG); 141 } 142 143 /** 144 * Creates an operator for normalizing/reducing an angle. 145 * The output will be within the {@code [lo, lo + 1[} interval. 146 * 147 * @param lo Lower bound of the normalized interval. 148 * @return the normalization operator. 149 */ 150 public static DoubleUnaryOperator normalizer(final double lo) { 151 return new Normalizer(lo, 1d); 152 } 153 } 154 155 /** 156 * Unit: <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. 157 */ 158 public static final class Rad extends Angle { 159 /** Zero. */ 160 public static final Rad ZERO = of(0d); 161 /** π. */ 162 public static final Rad PI = of(Math.PI); 163 /** 2π. */ 164 public static final Rad TWO_PI = of(Angle.TWO_PI); 165 /** Normalizing operator (result will be within the <code>[0, 2π[</code> interval). */ 166 public static final DoubleUnaryOperator WITHIN_0_AND_2PI = normalizer(0d); 167 /** Normalizing operator (result will be within the <code>[-π, π[</code> interval). */ 168 public static final DoubleUnaryOperator WITHIN_MINUS_PI_AND_PI = normalizer(-Math.PI); 169 170 /** 171 * Create an instance. 172 * 173 * @param angle (in radians). 174 */ 175 private Rad(final double angle) { 176 super(angle); 177 } 178 179 /** 180 * Create an instance. 181 * 182 * @param angle (in radians). 183 * @return a new instance. 184 */ 185 public static Rad of(final double angle) { 186 return new Rad(angle); 187 } 188 189 /** {@inheritDoc} */ 190 @Override 191 public Turn toTurn() { 192 return Turn.of(getAsDouble() / Angle.TWO_PI); 193 } 194 195 /** {@inheritDoc} */ 196 @Override 197 public Rad toRad() { 198 return this; 199 } 200 201 /** {@inheritDoc} */ 202 @Override 203 public Deg toDeg() { 204 return Deg.of(getAsDouble() * RAD_TO_DEG); 205 } 206 207 /** 208 * Creates an operator for normalizing/reducing an angle. 209 * The output will be within the <code> [lo, lo + 2π[</code> interval. 210 * 211 * @param lo Lower bound of the normalized interval. 212 * @return the normalization operator. 213 */ 214 public static DoubleUnaryOperator normalizer(final double lo) { 215 return new Normalizer(lo, Angle.TWO_PI); 216 } 217 } 218 219 /** 220 * Unit: <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>. 221 */ 222 public static final class Deg extends Angle { 223 /** Zero. */ 224 public static final Deg ZERO = of(0d); 225 /** Normalizing operator (result will be within the {@code [0, 360[} interval). */ 226 public static final DoubleUnaryOperator WITHIN_0_AND_360 = normalizer(0d); 227 228 /** 229 * Create an instance. 230 * 231 * @param angle (in degrees). 232 */ 233 private Deg(final double angle) { 234 super(angle); 235 } 236 237 /** 238 * Create an instance. 239 * 240 * @param angle (in degrees). 241 * @return a new instance. 242 */ 243 public static Deg of(final double angle) { 244 return new Deg(angle); 245 } 246 247 /** {@inheritDoc} */ 248 @Override 249 public Turn toTurn() { 250 return Turn.of(getAsDouble() / TURN_TO_DEG); 251 } 252 253 /** {@inheritDoc} */ 254 @Override 255 public Rad toRad() { 256 return Rad.of(getAsDouble() * DEG_TO_RAD); 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 public Deg toDeg() { 262 return this; 263 } 264 265 /** 266 * Creates an operator for normalizing/reducing an angle. 267 * The output will be within the {@code [c, c + 360[} interval. 268 * 269 * @param lo Lower bound of the normalized interval. 270 * @return the normalization operator. 271 */ 272 public static DoubleUnaryOperator normalizer(final double lo) { 273 return new Normalizer(lo, TURN_TO_DEG); 274 } 275 } 276 277 /** 278 * Normalizes an angle around a center value. 279 */ 280 private static final class Normalizer implements DoubleUnaryOperator { 281 /** Lower bound. */ 282 private final double lo; 283 /** Upper bound. */ 284 private final double hi; 285 /** Period. */ 286 private final double period; 287 /** Normalizer. */ 288 private final Reduce reduce; 289 290 /** 291 * Note: It is assumed that both arguments have the same unit. 292 * 293 * @param lo Lower bound of the desired interval. 294 * @param period Circonference of the circle. 295 */ 296 Normalizer(final double lo, 297 final double period) { 298 this.period = period; 299 this.lo = lo; 300 this.hi = lo + period; 301 reduce = new Reduce(lo, period); 302 } 303 304 /** 305 * @param a Angle. 306 * @return {@code = a - k} where {@code k} is an integer that satisfies 307 * {@code lo <= a - k < lo + period}. 308 */ 309 @Override 310 public double applyAsDouble(final double a) { 311 if (lo <= a && 312 a < hi) { 313 // Already within the main interval. 314 return a; 315 } 316 317 final double normalized = reduce.applyAsDouble(a) + lo; 318 return normalized < hi ? 319 normalized : 320 // If value is too small to be representable compared to the 321 // floor expression above (i.e. value + x = x), then we may 322 // end up with a number exactly equal to the upper bound. 323 // In that case, subtract one period from the normalized value 324 // so that the result is strictly less than the upper bound. (We also 325 // want to ensure that we do not return anything less than the lower bound.) 326 Math.max(lo, normalized - period); 327 } 328 } 329}