View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.numbers.angle;
18  
19  import java.util.function.DoubleSupplier;
20  import java.util.function.DoubleUnaryOperator;
21  
22  /**
23   * Represents the <a href="https://en.wikipedia.org/wiki/Angle">angle</a> concept.
24   */
25  public abstract class Angle implements DoubleSupplier {
26      /** 2&pi;. */
27      public static final double TWO_PI = 2 * Math.PI;
28      /** &pi;/2. */
29      public static final double PI_OVER_TWO = 0.5 * Math.PI;
30      /** Turns to degrees conversion factor. */
31      private static final double TURN_TO_DEG = 360d;
32      /** Radians to degrees conversion factor. */
33      private static final double RAD_TO_DEG = 180.0 / Math.PI;
34      /** Degrees to radians conversion factor. */
35      private static final double DEG_TO_RAD = Math.PI / 180.0;
36  
37      /** Value (unit depends on concrete instance). */
38      private final double value;
39  
40      /**
41       * @param value Value in turns.
42       */
43      private Angle(final double value) {
44          this.value = value;
45      }
46  
47      /** @return the value. */
48      @Override
49      public double getAsDouble() {
50          return value;
51      }
52  
53      /** {@inheritDoc} */
54      @Override
55      public int hashCode() {
56          return Double.hashCode(value);
57      }
58  
59      /**
60       * Test for equality with another object.
61       * Objects are considered to be equal if their concrete types are
62       * equal and their values are exactly the same (or both are {@code Double.NaN}).
63       *
64       * @param other Object to test for equality with this instance.
65       * @return {@code true} if the objects are equal, {@code false} if
66       * {@code other} is {@code null}, not of the same type as this instance,
67       * or not equal to this instance.
68       */
69      @Override
70      public boolean equals(final Object other) {
71          return other != null &&
72                  getClass().equals(other.getClass()) &&
73                  Double.doubleToLongBits(value) == Double.doubleToLongBits(((Angle) other).value);
74      }
75  
76      /**
77       * @return the angle in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>.
78       */
79      public abstract Turn toTurn();
80  
81      /**
82       * @return the angle in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>.
83       */
84      public abstract Rad toRad();
85  
86      /**
87       * @return the angle in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>.
88       */
89      public abstract Deg toDeg();
90  
91      /**
92       * Unit: <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>.
93       */
94      public static final class Turn extends Angle {
95          /** Zero. */
96          public static final Turn ZERO = Turn.of(0d);
97          /** Normalizing operator (result will be within the {@code [0, 1[} interval). */
98          public static final DoubleUnaryOperator WITHIN_0_AND_1 = normalizer(0d);
99  
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         /** &pi;. */
152         public static final Rad PI = Rad.of(Math.PI);
153         /** 2&pi;. */
154         public static final Rad TWO_PI = Rad.of(Angle.TWO_PI);
155         /** Normalizing operator (result will be within the <code>[0, 2&pi;[</code> interval). */
156         public static final DoubleUnaryOperator WITHIN_0_AND_2PI = normalizer(0d);
157         /** Normalizing operator (result will be within the <code>[-&pi;, &pi;[</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&pi;[</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 }