View Javadoc
1   package org.djutils.base;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertTrue;
6   
7   import org.junit.Test;
8   
9   /**
10   * AngleUtilTest tests the angle normalization methods in AngleUtil. <br>
11   * <br>
12   * Copyright (c) 2020-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
13   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
14   * distributed under a three-clause BSD-style license, which can be found at
15   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
16   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
17   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
18   */
19  public class AngleUtilTest
20  {
21      /**
22       * Test angle normalization.
23       */
24      @Test
25      public void testAngleNormalization()
26      {
27          final double pi = Math.PI;
28          final double pi05 = 0.5 * pi;
29          final double pi15 = 1.5 * pi;
30          final double pi20 = 2.0 * pi;
31          final double pi100 = 10.0 * pi;
32  
33          // center
34          assertEquals(pi, AngleUtil.normalizeAroundPi(pi), 1E-6);
35          assertEquals(0.0, AngleUtil.normalizeAroundZero(0.0), 1E-6);
36  
37          assertEquals(pi, AngleUtil.normalizeAroundPi(pi + pi20), 1E-6);
38          assertEquals(0.0, AngleUtil.normalizeAroundZero(0.0 + pi20), 1E-6);
39          assertEquals(pi, AngleUtil.normalizeAroundPi(pi - pi20), 1E-6);
40          assertEquals(0.0, AngleUtil.normalizeAroundZero(0.0 - pi20), 1E-6);
41  
42          assertEquals(pi, AngleUtil.normalizeAroundPi(pi + pi100), 1E-6);
43          assertEquals(0.0, AngleUtil.normalizeAroundZero(0.0 + pi100), 1E-6);
44          assertEquals(pi, AngleUtil.normalizeAroundPi(pi - pi100), 1E-6);
45          assertEquals(0.0, AngleUtil.normalizeAroundZero(0.0 - pi100), 1E-6);
46  
47          // quart
48          assertEquals(pi05, AngleUtil.normalizeAroundPi(pi05), 1E-6);
49          assertEquals(pi15, AngleUtil.normalizeAroundPi(pi15), 1E-6);
50          assertEquals(pi05, AngleUtil.normalizeAroundZero(pi05), 1E-6);
51          assertEquals(-pi05, AngleUtil.normalizeAroundZero(-pi05), 1E-6);
52          assertEquals(pi05, AngleUtil.normalizeAroundZero(-pi15), 1E-6);
53          assertEquals(-pi05, AngleUtil.normalizeAroundZero(pi15), 1E-6);
54  
55          assertEquals(pi05, AngleUtil.normalizeAroundPi(pi05 + pi20), 1E-6);
56          assertEquals(pi05, AngleUtil.normalizeAroundPi(pi05 - pi20), 1E-6);
57          assertEquals(pi15, AngleUtil.normalizeAroundPi(pi15 + pi20), 1E-6);
58          assertEquals(pi15, AngleUtil.normalizeAroundPi(pi15 - pi20), 1E-6);
59          assertEquals(pi05, AngleUtil.normalizeAroundZero(pi05 + pi20), 1E-6);
60          assertEquals(pi05, AngleUtil.normalizeAroundZero(pi05 - pi20), 1E-6);
61          assertEquals(-pi05, AngleUtil.normalizeAroundZero(pi20 - pi05), 1E-6);
62          assertEquals(-pi05, AngleUtil.normalizeAroundZero(-pi20 - pi05), 1E-6);
63  
64          assertEquals(pi05, AngleUtil.normalizeAroundPi(pi05 + pi100), 1E-6);
65          assertEquals(pi05, AngleUtil.normalizeAroundPi(pi05 - pi100), 1E-6);
66          assertEquals(pi15, AngleUtil.normalizeAroundPi(pi15 + pi100), 1E-6);
67          assertEquals(pi15, AngleUtil.normalizeAroundPi(pi15 - pi100), 1E-6);
68          assertEquals(pi05, AngleUtil.normalizeAroundZero(pi05 + pi100), 1E-6);
69          assertEquals(pi05, AngleUtil.normalizeAroundZero(pi05 - pi100), 1E-6);
70          assertEquals(-pi05, AngleUtil.normalizeAroundZero(pi100 - pi05), 1E-6);
71          assertEquals(-pi05, AngleUtil.normalizeAroundZero(-pi100 - pi05), 1E-6);
72  
73          // edges plus or minus a small number
74          final double eps = 1E-8;
75          assertEquals(pi, AngleUtil.normalizeAroundZero(pi - eps), 1E-6);
76          assertEquals(-pi, AngleUtil.normalizeAroundZero(pi + eps), 1E-6);
77          assertEquals(-pi, AngleUtil.normalizeAroundZero(-pi + eps), 1E-6);
78          assertEquals(pi, AngleUtil.normalizeAroundZero(-pi - eps), 1E-6);
79  
80          assertEquals(0.0, AngleUtil.normalizeAroundPi(eps), 1E-6);
81          assertEquals(pi20, AngleUtil.normalizeAroundPi(-eps), 1E-6);
82          assertEquals(pi20, AngleUtil.normalizeAroundPi(pi20 - eps), 1E-6);
83          assertEquals(0.0, AngleUtil.normalizeAroundPi(pi20 + eps), 1E-6);
84  
85          // NaN and Infinity should result in NaN
86          assertTrue(Double.isNaN(AngleUtil.normalizeAroundPi(Double.NaN)));
87          assertTrue(Double.isNaN(AngleUtil.normalizeAroundZero(Double.NaN)));
88          assertTrue(Double.isNaN(AngleUtil.normalizeAroundPi(Double.POSITIVE_INFINITY)));
89          assertTrue(Double.isNaN(AngleUtil.normalizeAroundZero(Double.POSITIVE_INFINITY)));
90          assertTrue(Double.isNaN(AngleUtil.normalizeAroundPi(Double.NEGATIVE_INFINITY)));
91          assertTrue(Double.isNaN(AngleUtil.normalizeAroundZero(Double.NEGATIVE_INFINITY)));
92      }
93  
94      /**
95       * Test angle epsilonEquals.
96       */
97      @Test
98      public void testAngleEpsilonEquals()
99      {
100         final double pi = Math.PI;
101         final double pi05 = 0.5 * pi;
102         final double pi15 = 1.5 * pi;
103         final double pi20 = 2.0 * pi;
104 
105         assertTrue(AngleUtil.epsilonEquals(pi, 3, 0.2));
106         assertTrue(AngleUtil.epsilonEquals(pi, 3.14, 0.01));
107         assertFalse(AngleUtil.epsilonEquals(pi, 3.14, 0.001));
108         assertTrue(AngleUtil.epsilonEquals(3, pi, 0.2));
109         assertTrue(AngleUtil.epsilonEquals(3.14, pi, 0.01));
110         assertFalse(AngleUtil.epsilonEquals(3.14, pi, 0.001));
111 
112         assertTrue(AngleUtil.epsilonEquals(pi, -pi, 1E-6));
113         assertTrue(AngleUtil.epsilonEquals(0.0, pi20, 1E-6));
114         assertTrue(AngleUtil.epsilonEquals(-pi20, 5.0 * pi20, 1E-6));
115         assertFalse(AngleUtil.epsilonEquals(pi05, pi15, 1E-6));
116         assertTrue(AngleUtil.epsilonEquals(pi - 1E-7, pi + 1E-7, 1E-6));
117         assertTrue(AngleUtil.epsilonEquals(pi20 - 1E-7, pi20 + 1E-7, 1E-6));
118         for (double s1 : new double[] {-1E-7, 0.0, 1E-7})
119         {
120             for (double s2 : new double[] {-1E-7, 0.0, 1E-7})
121             {
122                 assertTrue(AngleUtil.epsilonEquals(s1, pi20 + s2, 1E-6));
123                 assertTrue(AngleUtil.epsilonEquals(s1, -pi20 + s2, 1E-6));
124                 assertTrue(AngleUtil.epsilonEquals(-pi + s1, pi + s2, 1E-6));
125                 for (double a : new double[] {0.0, pi05, pi15, pi20})
126                 {
127                     assertTrue(AngleUtil.epsilonEquals(a + s1, a + s2, 1E-6));
128                 }
129             }
130         }
131     }
132 
133     /**
134      * Test angle interpolation.
135      */
136     @Test
137     public void testAngleInterpolate()
138     {
139         assertEquals(0.1, AngleUtil.interpolateClockwise(0.1, 0.2, 0.0), 1E-6);
140         assertEquals(0.2, AngleUtil.interpolateClockwise(0.1, 0.2, 1.0), 1E-6);
141         assertEquals(0.15, AngleUtil.interpolateClockwise(0.1, 0.2, 0.5), 1E-6);
142         assertEquals(0.3, AngleUtil.interpolateClockwise(0.1, 0.2, 2.0), 1E-6);
143         assertEquals(0.05, AngleUtil.interpolateClockwise(0.1, 0.2, -0.5), 1E-6);
144 
145         assertEquals(0.1, AngleUtil.interpolateClockwise(0.1, 0.1, 0.0), 1E-6);
146         assertEquals(0.1, AngleUtil.interpolateClockwise(0.1, 0.1, 1.0), 1E-6);
147         assertEquals(0.1, AngleUtil.interpolateClockwise(0.1, 0.1, -1.0), 1E-6);
148         assertEquals(0.1, AngleUtil.interpolateClockwise(0.1, 0.1, 10.0), 1E-6);
149 
150         final double pi = Math.PI;
151         final double pi05 = 0.5 * pi;
152         final double pi15 = 1.5 * pi;
153         final double pi20 = 2.0 * pi;
154 
155         assertEquals(0.0, AngleUtil.interpolateClockwise(0, pi20, 0.0), 1E-6);
156         assertEquals(0.0, AngleUtil.interpolateClockwise(0, pi20, 1.0), 1E-6);
157         assertEquals(0.0, AngleUtil.interpolateClockwise(0, pi20, 0.5), 1E-6);
158         assertEquals(0.0, AngleUtil.interpolateClockwise(pi20, 0, 0.5), 1E-6);
159         assertEquals(pi, Math.abs(AngleUtil.interpolateClockwise(-pi, pi, 0.5)), 1E-6);
160         assertEquals(pi, Math.abs(AngleUtil.interpolateClockwise(pi, -pi, 0.5)), 1E-6);
161 
162         assertEquals(pi, Math.abs(AngleUtil.interpolateClockwise(pi05, pi15, 0.5)), 1E-6);
163         assertEquals(0.0, AngleUtil.interpolateClockwise(pi15, pi05, 0.5), 1E-6);
164 
165         assertEquals(pi / 4, AngleUtil.interpolateClockwise(0.0, pi05, 0.5), 1E-6);
166         assertEquals(-0.75 * pi, AngleUtil.interpolateClockwise(pi05, 0.0, 0.5), 1E-6);
167     }
168 
169     /**
170      * Test the interpolateShortest method.
171      */
172     @Test
173     public void testInterpolateShortest()
174     {
175         for (double angle1 = -15 * Math.PI / 6; angle1 < 50; angle1 += Math.PI / 300)
176         {
177             for (double angle2 = -15 * Math.PI / 6; angle2 < 50; angle2 += Math.PI / 300)
178             {
179                 for (double fraction : new double[] {0, 1, 0.5, 0.1, 0.9})
180                 {
181                     double angleFraction = AngleUtil.interpolateShortest(angle1, angle2, fraction);
182                     double check1 = Math.abs(AngleUtil.normalizeAroundZero(angle1 - angleFraction));
183                     double check2 = Math.abs(AngleUtil.normalizeAroundZero(angleFraction - angle2));
184                     // System.out.println(String.format(
185                     // "angle1 %s, normalized %s, angle2 %s, normalized %s, frac %s, check1 %s, check2 %s, fraction %.1f",
186                     // printAngle(angle1), printAngle(AngleUtil.normalizeAroundZero(angle1)), printAngle(angle2),
187                     // printAngle(AngleUtil.normalizeAroundZero(angle2)), printAngle(angleFraction), printAngle(check1),
188                     // printAngle(check2), fraction));
189                     if (check1 > Math.PI * fraction)
190                     {
191                         AngleUtil.interpolateShortest(angle1, angle2, fraction);
192                     }
193                     assertTrue("diff of normalized is less than Pi * fraction", check1 <= Math.PI * fraction + 0.000001);
194                     assertTrue("diff of normalized complement is less than 2*Pi/3",
195                             check2 <= Math.PI * (1 - fraction) + 0.000001);
196                     if (angle1 != angle2)
197                     {
198                         if (Math.abs(check1) > 0.00001 && Math.abs(check2) > 0.00001)
199                         {
200                             assertEquals("angle should be divided in parts with ratio fraction", fraction / (1 - fraction),
201                                     check1 / check2, 0.0001);
202                         }
203                     }
204                     else
205                     {
206                         assertEquals("if angles are identical; both parts are zero ", 0, check1, 1e-10);
207                         assertEquals("if angles are identical; both parts are zero ", 0, check2, 1e-10);
208                     }
209                 }
210             }
211         }
212         // Test extrapolation lightly
213         assertEquals("extrapolate at 2 of 0.2, 0.3", 0.4, AngleUtil.interpolateShortest(0.2, 0.3, 2), 0.00001);
214         assertEquals("extrapolate at 01 of 0.2, 0.3", 0.1, AngleUtil.interpolateShortest(0.2, 0.3, -1), 0.00001);
215     }
216 
217     /**
218      * Format an angle.
219      * @param angle double; angle in radians
220      * @return String; fixed width text representation of angle
221      */
222     public static String printAngle(final double angle)
223     {
224         return String.format("%9.6f (%4.0f\u00b0)", angle, Math.toDegrees(angle));
225     }
226 
227 }