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.geometry.euclidean.threed;
18  
19  import java.util.regex.Pattern;
20  
21  import org.apache.commons.geometry.core.GeometryTestUtils;
22  import org.apache.commons.numbers.angle.Angle;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.Test;
25  
26  class SphericalCoordinatesTest {
27  
28      private static final double EPS = 1e-10;
29  
30      private static final double THREE_PI_OVER_TWO = 3 * Math.PI / 2;
31  
32      private static final double QUARTER_PI = 0.25 * Math.PI;
33      private static final double MINUS_QUARTER_PI = -0.25 * Math.PI;
34      private static final double THREE_QUARTER_PI = 0.75 * Math.PI;
35      private static final double MINUS_THREE_QUARTER_PI = -0.75 * Math.PI;
36  
37      @Test
38      void testOf() {
39          // act/assert
40          checkSpherical(SphericalCoordinates.of(0, 0, 0), 0, 0, 0);
41          checkSpherical(SphericalCoordinates.of(0.1, 0.2, 0.3), 0.1, 0.2, 0.3);
42  
43          checkSpherical(SphericalCoordinates.of(1, Angle.PI_OVER_TWO, Math.PI),
44                  1, Angle.PI_OVER_TWO, Math.PI);
45          checkSpherical(SphericalCoordinates.of(1, -Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
46                  1, THREE_PI_OVER_TWO, Angle.PI_OVER_TWO);
47      }
48  
49      @Test
50      void testOf_normalizesAzimuthAngle() {
51          // act/assert
52          checkSpherical(SphericalCoordinates.of(2, Angle.TWO_PI, 0), 2, 0, 0);
53          checkSpherical(SphericalCoordinates.of(2, Angle.PI_OVER_TWO + Angle.TWO_PI, 0), 2, Angle.PI_OVER_TWO, 0);
54          checkSpherical(SphericalCoordinates.of(2, -Math.PI, 0), 2, Math.PI, 0);
55          checkSpherical(SphericalCoordinates.of(2, THREE_PI_OVER_TWO, 0), 2, THREE_PI_OVER_TWO, 0);
56      }
57  
58      @Test
59      void testOf_normalizesPolarAngle() {
60          // act/assert
61          checkSpherical(SphericalCoordinates.of(1, 0, 0), 1, 0, 0);
62  
63          checkSpherical(SphericalCoordinates.of(1, 0, QUARTER_PI), 1, 0, QUARTER_PI);
64          checkSpherical(SphericalCoordinates.of(1, 0, MINUS_QUARTER_PI), 1, 0, QUARTER_PI);
65  
66          checkSpherical(SphericalCoordinates.of(1, 0, Angle.PI_OVER_TWO), 1, 0, Angle.PI_OVER_TWO);
67          checkSpherical(SphericalCoordinates.of(1, 0, -Angle.PI_OVER_TWO), 1, 0, Angle.PI_OVER_TWO);
68  
69          checkSpherical(SphericalCoordinates.of(1, 0, THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
70          checkSpherical(SphericalCoordinates.of(1, 0, MINUS_THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
71  
72          checkSpherical(SphericalCoordinates.of(1, 0, Angle.TWO_PI), 1, 0, 0);
73          checkSpherical(SphericalCoordinates.of(1, 0, -Angle.TWO_PI), 1, 0, 0);
74      }
75  
76      @Test
77      void testOf_angleWrapAround() {
78          // act/assert
79          checkOfWithAngleWrapAround(1, 0, 0);
80          checkOfWithAngleWrapAround(1, QUARTER_PI, QUARTER_PI);
81          checkOfWithAngleWrapAround(1, Angle.PI_OVER_TWO, Angle.PI_OVER_TWO);
82          checkOfWithAngleWrapAround(1, THREE_QUARTER_PI, THREE_QUARTER_PI);
83          checkOfWithAngleWrapAround(1, Math.PI, Math.PI);
84      }
85  
86      private void checkOfWithAngleWrapAround(final double radius, final double azimuth, final double polar) {
87          for (int i = -4; i <= 4; ++i) {
88              checkSpherical(
89                      SphericalCoordinates.of(radius, azimuth + (i * Angle.TWO_PI), polar + (-i * Angle.TWO_PI)),
90                      radius, azimuth, polar);
91          }
92      }
93  
94      @Test
95      void testOf_negativeRadius() {
96          // act/assert
97          checkSpherical(SphericalCoordinates.of(-2, 0, 0), 2, Math.PI, Math.PI);
98          checkSpherical(SphericalCoordinates.of(-2, Math.PI, Math.PI), 2, 0, 0);
99  
100         checkSpherical(SphericalCoordinates.of(-3, Angle.PI_OVER_TWO, QUARTER_PI), 3, THREE_PI_OVER_TWO, THREE_QUARTER_PI);
101         checkSpherical(SphericalCoordinates.of(-3, -Angle.PI_OVER_TWO, THREE_QUARTER_PI), 3, Angle.PI_OVER_TWO, QUARTER_PI);
102 
103         checkSpherical(SphericalCoordinates.of(-4, QUARTER_PI, Angle.PI_OVER_TWO), 4, Math.PI + QUARTER_PI, Angle.PI_OVER_TWO);
104         checkSpherical(SphericalCoordinates.of(-4, MINUS_THREE_QUARTER_PI, Angle.PI_OVER_TWO), 4, QUARTER_PI, Angle.PI_OVER_TWO);
105     }
106 
107     @Test
108     void testOf_NaNAndInfinite() {
109         // act/assert
110         checkSpherical(SphericalCoordinates.of(Double.NaN, Double.NaN, Double.NaN),
111                 Double.NaN, Double.NaN, Double.NaN);
112         checkSpherical(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY),
113                 Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
114         checkSpherical(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
115                 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
116     }
117 
118     @Test
119     void testFromCartesian_coordinates() {
120         // arrange
121         final double sqrt3 = Math.sqrt(3);
122 
123         // act/assert
124         checkSpherical(SphericalCoordinates.fromCartesian(0, 0, 0), 0, 0, 0);
125 
126         checkSpherical(SphericalCoordinates.fromCartesian(0.1, 0, 0), 0.1, 0, Angle.PI_OVER_TWO);
127         checkSpherical(SphericalCoordinates.fromCartesian(-0.1, 0, 0), 0.1, Math.PI, Angle.PI_OVER_TWO);
128 
129         checkSpherical(SphericalCoordinates.fromCartesian(0, 0.1, 0), 0.1, Angle.PI_OVER_TWO, Angle.PI_OVER_TWO);
130         checkSpherical(SphericalCoordinates.fromCartesian(0, -0.1, 0), 0.1, THREE_PI_OVER_TWO, Angle.PI_OVER_TWO);
131 
132         checkSpherical(SphericalCoordinates.fromCartesian(0, 0, 0.1), 0.1, 0, 0);
133         checkSpherical(SphericalCoordinates.fromCartesian(0, 0, -0.1), 0.1, 0, Math.PI);
134 
135         checkSpherical(SphericalCoordinates.fromCartesian(1, 1, 1), sqrt3, QUARTER_PI, Math.acos(1 / sqrt3));
136         checkSpherical(SphericalCoordinates.fromCartesian(-1, -1, -1), sqrt3, 1.25 * Math.PI, Math.acos(-1 / sqrt3));
137     }
138 
139     @Test
140     void testFromCartesian_vector() {
141         // arrange
142         final double sqrt3 = Math.sqrt(3);
143 
144         // act/assert
145         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0, 0, 0)), 0, 0, 0);
146 
147         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0.1, 0, 0)), 0.1, 0, Angle.PI_OVER_TWO);
148         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(-0.1, 0, 0)), 0.1, Math.PI, Angle.PI_OVER_TWO);
149 
150         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0, 0.1, 0)), 0.1, Angle.PI_OVER_TWO, Angle.PI_OVER_TWO);
151         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0, -0.1, 0)), 0.1, THREE_PI_OVER_TWO, Angle.PI_OVER_TWO);
152 
153         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0, 0, 0.1)), 0.1, 0, 0);
154         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(0, 0, -0.1)), 0.1, 0, Math.PI);
155 
156         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(1, 1, 1)), sqrt3, QUARTER_PI, Math.acos(1 / sqrt3));
157         checkSpherical(SphericalCoordinates.fromCartesian(Vector3D.of(-1, -1, -1)), sqrt3, 1.25 * Math.PI, Math.acos(-1 / sqrt3));
158     }
159 
160     @Test
161     void testToVector() {
162         // arrange
163         final double sqrt3 = Math.sqrt(3);
164 
165         // act/assert
166         checkVector(SphericalCoordinates.of(0, 0, 0).toVector(), 0, 0, 0);
167 
168         checkVector(SphericalCoordinates.of(1, 0, Angle.PI_OVER_TWO).toVector(), 1, 0, 0);
169         checkVector(SphericalCoordinates.of(1, Math.PI, Angle.PI_OVER_TWO).toVector(), -1, 0, 0);
170 
171         checkVector(SphericalCoordinates.of(2, Angle.PI_OVER_TWO, Angle.PI_OVER_TWO).toVector(), 0, 2, 0);
172         checkVector(SphericalCoordinates.of(2, -Angle.PI_OVER_TWO, Angle.PI_OVER_TWO).toVector(), 0, -2, 0);
173 
174         checkVector(SphericalCoordinates.of(3, 0, 0).toVector(), 0, 0, 3);
175         checkVector(SphericalCoordinates.of(3, 0, Math.PI).toVector(), 0, 0, -3);
176 
177         checkVector(SphericalCoordinates.of(sqrt3, QUARTER_PI, Math.acos(1 / sqrt3)).toVector(), 1, 1, 1);
178         checkVector(SphericalCoordinates.of(sqrt3, MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toVector(), -1, -1, -1);
179     }
180 
181     @Test
182     void testToCartesian_static() {
183         // arrange
184         final double sqrt3 = Math.sqrt(3);
185 
186         // act/assert
187         checkVector(SphericalCoordinates.toCartesian(0, 0, 0), 0, 0, 0);
188 
189         checkVector(SphericalCoordinates.toCartesian(1, 0, Angle.PI_OVER_TWO), 1, 0, 0);
190         checkVector(SphericalCoordinates.toCartesian(1, Math.PI, Angle.PI_OVER_TWO), -1, 0, 0);
191 
192         checkVector(SphericalCoordinates.toCartesian(2, Angle.PI_OVER_TWO, Angle.PI_OVER_TWO), 0, 2, 0);
193         checkVector(SphericalCoordinates.toCartesian(2, -Angle.PI_OVER_TWO, Angle.PI_OVER_TWO), 0, -2, 0);
194 
195         checkVector(SphericalCoordinates.toCartesian(3, 0, 0), 0, 0, 3);
196         checkVector(SphericalCoordinates.toCartesian(3, 0, Math.PI), 0, 0, -3);
197 
198         checkVector(SphericalCoordinates.toCartesian(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)), 1, 1, 1);
199         checkVector(SphericalCoordinates.toCartesian(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)), -1, -1, -1);
200     }
201 
202     @Test
203     void testGetDimension() {
204         // arrange
205         final SphericalCoordinates s = SphericalCoordinates.of(0, 0, 0);
206 
207         // act/assert
208         Assertions.assertEquals(3, s.getDimension());
209     }
210 
211     @Test
212     void testNaN() {
213         // act/assert
214         Assertions.assertTrue(SphericalCoordinates.of(0, 0, Double.NaN).isNaN());
215         Assertions.assertTrue(SphericalCoordinates.of(0, Double.NaN, 0).isNaN());
216         Assertions.assertTrue(SphericalCoordinates.of(Double.NaN, 0, 0).isNaN());
217 
218         Assertions.assertFalse(SphericalCoordinates.of(1, 1, 1).isNaN());
219         Assertions.assertFalse(SphericalCoordinates.of(1, 1, Double.NEGATIVE_INFINITY).isNaN());
220         Assertions.assertFalse(SphericalCoordinates.of(1, Double.POSITIVE_INFINITY, 1).isNaN());
221         Assertions.assertFalse(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 1, 1).isNaN());
222     }
223 
224     @Test
225     void testInfinite() {
226         // act/assert
227         Assertions.assertTrue(SphericalCoordinates.of(0, 0, Double.NEGATIVE_INFINITY).isInfinite());
228         Assertions.assertTrue(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, 0).isInfinite());
229         Assertions.assertTrue(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 0, 0).isInfinite());
230         Assertions.assertTrue(SphericalCoordinates.of(0, 0, Double.POSITIVE_INFINITY).isInfinite());
231         Assertions.assertTrue(SphericalCoordinates.of(0, Double.POSITIVE_INFINITY, 0).isInfinite());
232         Assertions.assertTrue(SphericalCoordinates.of(Double.POSITIVE_INFINITY, 0, 0).isInfinite());
233 
234         Assertions.assertFalse(SphericalCoordinates.of(1, 1, 1).isInfinite());
235         Assertions.assertFalse(SphericalCoordinates.of(0, 0, Double.NaN).isInfinite());
236         Assertions.assertFalse(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
237         Assertions.assertFalse(SphericalCoordinates.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isInfinite());
238         Assertions.assertFalse(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isInfinite());
239         Assertions.assertFalse(SphericalCoordinates.of(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
240     }
241 
242     @Test
243     void testFinite() {
244         // act/assert
245         Assertions.assertTrue(SphericalCoordinates.of(1, 1, 1).isFinite());
246 
247         Assertions.assertFalse(SphericalCoordinates.of(0, 0, Double.NEGATIVE_INFINITY).isFinite());
248         Assertions.assertFalse(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, 0).isFinite());
249         Assertions.assertFalse(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 0, 0).isFinite());
250         Assertions.assertFalse(SphericalCoordinates.of(0, 0, Double.POSITIVE_INFINITY).isFinite());
251         Assertions.assertFalse(SphericalCoordinates.of(0, Double.POSITIVE_INFINITY, 0).isFinite());
252         Assertions.assertFalse(SphericalCoordinates.of(Double.POSITIVE_INFINITY, 0, 0).isFinite());
253 
254         Assertions.assertFalse(SphericalCoordinates.of(0, 0, Double.NaN).isFinite());
255         Assertions.assertFalse(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isFinite());
256         Assertions.assertFalse(SphericalCoordinates.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isFinite());
257         Assertions.assertFalse(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isFinite());
258         Assertions.assertFalse(SphericalCoordinates.of(0, Double.NaN, Double.POSITIVE_INFINITY).isFinite());
259     }
260 
261     @Test
262     void testHashCode() {
263         // arrange
264         final SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
265         final SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
266         final SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
267         final SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
268 
269         final SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
270 
271         // act/assert
272         Assertions.assertEquals(a.hashCode(), a.hashCode());
273         Assertions.assertEquals(a.hashCode(), e.hashCode());
274 
275         Assertions.assertNotEquals(a.hashCode(), b.hashCode());
276         Assertions.assertNotEquals(a.hashCode(), c.hashCode());
277         Assertions.assertNotEquals(a.hashCode(), d.hashCode());
278     }
279 
280     @Test
281     void testHashCode_NaNInstancesHaveSameHashCode() {
282         // arrange
283         final SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
284         final SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
285         final SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
286 
287         // act/assert
288         Assertions.assertEquals(a.hashCode(), b.hashCode());
289         Assertions.assertEquals(b.hashCode(), c.hashCode());
290     }
291 
292     @Test
293     void testEquals() {
294         // arrange
295         final SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
296         final SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
297         final SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
298         final SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
299 
300         final SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
301 
302         // act/assert
303         GeometryTestUtils.assertSimpleEqualsCases(a);
304         Assertions.assertEquals(a, e);
305 
306         Assertions.assertNotEquals(a, b);
307         Assertions.assertNotEquals(a, c);
308         Assertions.assertNotEquals(a, d);
309     }
310 
311     @Test
312     void testEquals_NaNInstancesEqual() {
313         // arrange
314         final SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
315         final SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
316         final SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
317 
318         // act/assert
319         Assertions.assertEquals(a, b);
320         Assertions.assertEquals(b, c);
321     }
322 
323     @Test
324     void testEqualsAndHashCode_signedZeroConsistency() {
325         // arrange
326         final SphericalCoordinates a = SphericalCoordinates.of(0.0, -0.0, 0.0);
327         final SphericalCoordinates b = SphericalCoordinates.of(-0.0, 0.0, -0.0);
328         final SphericalCoordinates c = SphericalCoordinates.of(0.0, -0.0, 0.0);
329         final SphericalCoordinates d = SphericalCoordinates.of(-0.0, 0.0, -0.0);
330 
331         // act/assert
332         Assertions.assertFalse(a.equals(b));
333         Assertions.assertNotEquals(a.hashCode(), b.hashCode());
334 
335         Assertions.assertTrue(a.equals(c));
336         Assertions.assertEquals(a.hashCode(), c.hashCode());
337 
338         Assertions.assertTrue(b.equals(d));
339         Assertions.assertEquals(b.hashCode(), d.hashCode());
340     }
341 
342     @Test
343     void testToString() {
344         // arrange
345         final SphericalCoordinates sph = SphericalCoordinates.of(1, 2, 3);
346         final Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
347 
348         // act
349         final String str = sph.toString();
350 
351         // assert
352         Assertions.assertTrue(pattern.matcher(str).matches(), "Expected string " + str + " to match regex " + pattern);
353     }
354 
355     @Test
356     void testParse() {
357         // act/assert
358         checkSpherical(SphericalCoordinates.parse("(1, 2, 3)"), 1, 2, 3);
359         checkSpherical(SphericalCoordinates.parse("(  -2.0 , 1 , -5e-1)"), 2, 1 + Math.PI, Math.PI - 0.5);
360         checkSpherical(SphericalCoordinates.parse("(NaN,Infinity,-Infinity)"), Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
361     }
362 
363     @Test
364     void testParse_failure() {
365         // act/assert
366         Assertions.assertThrows(IllegalArgumentException.class, () -> SphericalCoordinates.parse("abc"));
367     }
368 
369     @Test
370     void testNormalizeAzimuth() {
371         // act/assert
372         Assertions.assertEquals(0.0, SphericalCoordinates.normalizeAzimuth(0), EPS);
373 
374         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizeAzimuth(Angle.PI_OVER_TWO), EPS);
375         Assertions.assertEquals(Math.PI, SphericalCoordinates.normalizeAzimuth(Math.PI), EPS);
376         Assertions.assertEquals(THREE_PI_OVER_TWO, SphericalCoordinates.normalizeAzimuth(THREE_PI_OVER_TWO), EPS);
377         Assertions.assertEquals(0.0, SphericalCoordinates.normalizeAzimuth(Angle.TWO_PI), EPS);
378 
379         Assertions.assertEquals(THREE_PI_OVER_TWO, SphericalCoordinates.normalizeAzimuth(-Angle.PI_OVER_TWO), EPS);
380         Assertions.assertEquals(Math.PI, SphericalCoordinates.normalizeAzimuth(-Math.PI), EPS);
381         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizeAzimuth(-Math.PI - Angle.PI_OVER_TWO), EPS);
382         Assertions.assertEquals(0.0, SphericalCoordinates.normalizeAzimuth(-Angle.TWO_PI), EPS);
383     }
384 
385     @Test
386     void testNormalizeAzimuth_NaNAndInfinite() {
387         // act/assert
388         Assertions.assertEquals(Double.NaN, SphericalCoordinates.normalizeAzimuth(Double.NaN), EPS);
389         Assertions.assertEquals(Double.NEGATIVE_INFINITY, SphericalCoordinates.normalizeAzimuth(Double.NEGATIVE_INFINITY), EPS);
390         Assertions.assertEquals(Double.POSITIVE_INFINITY, SphericalCoordinates.normalizeAzimuth(Double.POSITIVE_INFINITY), EPS);
391     }
392 
393     @Test
394     void testNormalizePolar() {
395         // act/assert
396         Assertions.assertEquals(0.0, SphericalCoordinates.normalizePolar(0), EPS);
397 
398         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizePolar(Angle.PI_OVER_TWO), EPS);
399         Assertions.assertEquals(Math.PI, SphericalCoordinates.normalizePolar(Math.PI), EPS);
400         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizePolar(Math.PI + Angle.PI_OVER_TWO), EPS);
401         Assertions.assertEquals(0.0, SphericalCoordinates.normalizePolar(Angle.TWO_PI), EPS);
402 
403         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizePolar(-Angle.PI_OVER_TWO), EPS);
404         Assertions.assertEquals(Math.PI, SphericalCoordinates.normalizePolar(-Math.PI), EPS);
405         Assertions.assertEquals(Angle.PI_OVER_TWO, SphericalCoordinates.normalizePolar(-Math.PI - Angle.PI_OVER_TWO), EPS);
406         Assertions.assertEquals(0.0, SphericalCoordinates.normalizePolar(-Angle.TWO_PI), EPS);
407     }
408 
409     @Test
410     void testNormalizePolar_NaNAndInfinite() {
411         // act/assert
412         Assertions.assertEquals(Double.NaN, SphericalCoordinates.normalizePolar(Double.NaN), EPS);
413         Assertions.assertEquals(Double.NEGATIVE_INFINITY, SphericalCoordinates.normalizePolar(Double.NEGATIVE_INFINITY), EPS);
414         Assertions.assertEquals(Double.POSITIVE_INFINITY, SphericalCoordinates.normalizePolar(Double.POSITIVE_INFINITY), EPS);
415     }
416 
417     private void checkSpherical(final SphericalCoordinates c, final double radius, final double azimuth, final double polar) {
418         Assertions.assertEquals(radius, c.getRadius(), EPS);
419         Assertions.assertEquals(azimuth, c.getAzimuth(), EPS);
420         Assertions.assertEquals(polar, c.getPolar(), EPS);
421     }
422 
423     private void checkVector(final Vector3D v, final double x, final double y, final double z) {
424         Assertions.assertEquals(x, v.getX(), EPS);
425         Assertions.assertEquals(y, v.getY(), EPS);
426         Assertions.assertEquals(z, v.getZ(), EPS);
427     }
428 }