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