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.spherical.twod;
18  
19  import java.util.regex.Pattern;
20  
21  import org.apache.commons.geometry.core.GeometryTestUtils;
22  import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
23  import org.apache.commons.geometry.euclidean.threed.Vector3D;
24  import org.apache.commons.geometry.spherical.SphericalTestUtils;
25  import org.apache.commons.geometry.spherical.oned.AngularInterval;
26  import org.apache.commons.geometry.spherical.oned.Point1S;
27  import org.apache.commons.numbers.angle.Angle;
28  import org.apache.commons.numbers.core.Precision;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Test;
31  
32  class GreatCircleTest {
33  
34      private static final double TEST_EPS = 1e-10;
35  
36      private static final Precision.DoubleEquivalence TEST_PRECISION =
37              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
38  
39      private static final Vector3D.Unit X = Vector3D.Unit.PLUS_X;
40      private static final Vector3D.Unit Y = Vector3D.Unit.PLUS_Y;
41      private static final Vector3D.Unit Z = Vector3D.Unit.PLUS_Z;
42  
43      @Test
44      void testFromPole() {
45          // act/assert
46          checkGreatCircle(GreatCircles.fromPole(X, TEST_PRECISION), X, Z);
47          checkGreatCircle(GreatCircles.fromPole(Y, TEST_PRECISION), Y, Z.negate());
48          checkGreatCircle(GreatCircles.fromPole(Z, TEST_PRECISION), Z, Y);
49      }
50  
51      @Test
52      void testFromPoleAndXAxis() {
53          // act/assert
54          checkGreatCircle(GreatCircles.fromPoleAndU(X, Y, TEST_PRECISION), X, Y);
55          checkGreatCircle(GreatCircles.fromPoleAndU(X, Z, TEST_PRECISION), X, Z);
56          checkGreatCircle(GreatCircles.fromPoleAndU(Y, Z, TEST_PRECISION), Y, Z);
57      }
58  
59      @Test
60      void testFromPoints() {
61          // act/assert
62          checkGreatCircle(GreatCircles.fromPoints(
63                      Point2S.of(0, Angle.PI_OVER_TWO),
64                      Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
65                      TEST_PRECISION),
66                  Z, X);
67  
68          checkGreatCircle(GreatCircles.fromPoints(
69                  Point2S.of(0, Angle.PI_OVER_TWO),
70                  Point2S.of(-0.1 * Math.PI, Angle.PI_OVER_TWO),
71                  TEST_PRECISION),
72              Z.negate(), X);
73  
74          checkGreatCircle(GreatCircles.fromPoints(
75                  Point2S.of(0, Angle.PI_OVER_TWO),
76                  Point2S.of(1.5 * Math.PI, Angle.PI_OVER_TWO),
77                  TEST_PRECISION),
78              Z.negate(), X);
79  
80          checkGreatCircle(GreatCircles.fromPoints(
81                  Point2S.of(0, 0),
82                  Point2S.of(0, Angle.PI_OVER_TWO),
83                  TEST_PRECISION),
84              Y, Z);
85      }
86  
87      @Test
88      void testFromPoints_invalidPoints() {
89          // arrange
90          final Point2S p1 = Point2S.of(0, Angle.PI_OVER_TWO);
91          final Point2S p2 = Point2S.of(Math.PI, Angle.PI_OVER_TWO);
92  
93          // act/assert
94          GeometryTestUtils.assertThrowsWithMessage(() -> {
95              GreatCircles.fromPoints(p1, p1, TEST_PRECISION);
96          }, IllegalArgumentException.class, Pattern.compile("^.*points are equal$"));
97  
98          GeometryTestUtils.assertThrowsWithMessage(() -> {
99              GreatCircles.fromPoints(p1, Point2S.of(1e-12, Angle.PI_OVER_TWO), TEST_PRECISION);
100         }, IllegalArgumentException.class, Pattern.compile("^.*points are equal$"));
101 
102         GeometryTestUtils.assertThrowsWithMessage(() -> {
103             GreatCircles.fromPoints(
104                     Point2S.from(Vector3D.Unit.PLUS_X),
105                     Point2S.from(Vector3D.Unit.MINUS_X),
106                     TEST_PRECISION);
107         }, IllegalArgumentException.class, Pattern.compile("^.*points are antipodal$"));
108 
109         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.fromPoints(p1, Point2S.NaN, TEST_PRECISION));
110         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.fromPoints(Point2S.NaN, p2, TEST_PRECISION));
111         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.fromPoints(p1, Point2S.of(Double.POSITIVE_INFINITY, Angle.PI_OVER_TWO), TEST_PRECISION));
112         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.fromPoints(Point2S.of(Double.POSITIVE_INFINITY, Angle.PI_OVER_TWO), p2, TEST_PRECISION));
113     }
114 
115     @Test
116     void testOffset_point() {
117         // --- arrange
118         final GreatCircle circle = GreatCircles.fromPoleAndU(
119                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
120 
121         // --- act/assert
122 
123         // on circle
124         for (double polar = -Angle.PI_OVER_TWO; polar <= Angle.PI_OVER_TWO; polar += 0.1) {
125             Assertions.assertEquals(0, circle.offset(Point2S.of(Angle.PI_OVER_TWO, polar)), TEST_EPS);
126             Assertions.assertEquals(0, circle.offset(Point2S.of(-Angle.PI_OVER_TWO, polar)), TEST_EPS);
127         }
128 
129         // +1/-1
130         Assertions.assertEquals(-1, circle.offset(Point2S.of(Angle.PI_OVER_TWO + 1, Angle.PI_OVER_TWO)), TEST_EPS);
131         Assertions.assertEquals(1, circle.offset(Point2S.of(-Angle.PI_OVER_TWO + 1, Angle.PI_OVER_TWO)), TEST_EPS);
132 
133         // poles
134         Assertions.assertEquals(-Angle.PI_OVER_TWO, circle.offset(Point2S.of(Math.PI, Angle.PI_OVER_TWO)), TEST_EPS);
135         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.offset(Point2S.of(0.0, Angle.PI_OVER_TWO)), TEST_EPS);
136     }
137 
138     @Test
139     void testOffset_vector() {
140         // --- arrange
141         final GreatCircle circle = GreatCircles.fromPoleAndU(
142                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
143 
144         // --- act/assert
145 
146         // on circle
147         Assertions.assertEquals(0, circle.offset(Vector3D.of(0, 1, 0)), TEST_EPS);
148         Assertions.assertEquals(0, circle.offset(Vector3D.of(0, 0, 1)), TEST_EPS);
149         Assertions.assertEquals(0, circle.offset(Vector3D.of(0, -1, 0)), TEST_EPS);
150         Assertions.assertEquals(0, circle.offset(Vector3D.of(0, 0, -1)), TEST_EPS);
151 
152         // +1/-1
153         Assertions.assertEquals(-0.25 * Math.PI, circle.offset(Vector3D.of(-1, 1, 0)), TEST_EPS);
154         Assertions.assertEquals(-0.25 * Math.PI, circle.offset(Vector3D.of(-1, 0, 1)), TEST_EPS);
155         Assertions.assertEquals(-0.25 * Math.PI, circle.offset(Vector3D.of(-1, -1, 0)), TEST_EPS);
156         Assertions.assertEquals(-0.25 * Math.PI, circle.offset(Vector3D.of(-1, 0, -1)), TEST_EPS);
157 
158         Assertions.assertEquals(0.25 * Math.PI, circle.offset(Vector3D.of(1, 1, 0)), TEST_EPS);
159         Assertions.assertEquals(0.25 * Math.PI, circle.offset(Vector3D.of(1, 0, 1)), TEST_EPS);
160         Assertions.assertEquals(0.25 * Math.PI, circle.offset(Vector3D.of(1, -1, 0)), TEST_EPS);
161         Assertions.assertEquals(0.25 * Math.PI, circle.offset(Vector3D.of(1, 0, -1)), TEST_EPS);
162 
163         // poles
164         Assertions.assertEquals(-Angle.PI_OVER_TWO, circle.offset(Vector3D.Unit.MINUS_X), TEST_EPS);
165         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.offset(Vector3D.Unit.PLUS_X), TEST_EPS);
166     }
167 
168     @Test
169     void testAzimuth_point() {
170         // --- arrange
171         final GreatCircle circle = GreatCircles.fromPoleAndU(
172                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
173 
174         // --- act/assert
175 
176         // on circle
177         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(0, 1, 0))), TEST_EPS);
178         Assertions.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(0, 0, 1))), TEST_EPS);
179         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Point2S.from(Vector3D.of(0, -1, 0))), TEST_EPS);
180         Assertions.assertEquals(Math.PI, circle.azimuth(Point2S.from(Vector3D.of(0, 0, -1))), TEST_EPS);
181 
182         // +1/-1
183         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(-1, 1, 0))), TEST_EPS);
184         Assertions.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(-1, 0, 1))), TEST_EPS);
185         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Point2S.from(Vector3D.of(-1, -1, 0))), TEST_EPS);
186         Assertions.assertEquals(Math.PI, circle.azimuth(Point2S.from(Vector3D.of(-1, 0, -1))), TEST_EPS);
187 
188         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Point2S.from(Vector3D.of(1, 1, 0))), TEST_EPS);
189         Assertions.assertEquals(0.0, circle.azimuth(Point2S.from(Vector3D.of(1, 0, 1))), TEST_EPS);
190         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Point2S.from(Vector3D.of(1, -1, 0))), TEST_EPS);
191         Assertions.assertEquals(Math.PI, circle.azimuth(Point2S.from(Vector3D.of(1, 0, -1))), TEST_EPS);
192 
193         // poles
194         Assertions.assertEquals(0, circle.azimuth(Point2S.from(Vector3D.Unit.MINUS_X)), TEST_EPS);
195         Assertions.assertEquals(0, circle.azimuth(Point2S.from(Vector3D.Unit.PLUS_X)), TEST_EPS);
196     }
197 
198     @Test
199     void testAzimuth_vector() {
200         // --- arrange
201         final GreatCircle circle = GreatCircles.fromPoleAndU(
202                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
203 
204         // --- act/assert
205 
206         // on circle
207         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Vector3D.of(0, 1, 0)), TEST_EPS);
208         Assertions.assertEquals(0.0, circle.azimuth(Vector3D.of(0, 0, 1)), TEST_EPS);
209         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Vector3D.of(0, -1, 0)), TEST_EPS);
210         Assertions.assertEquals(Math.PI, circle.azimuth(Vector3D.of(0, 0, -1)), TEST_EPS);
211 
212         // +1/-1
213         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Vector3D.of(-1, 1, 0)), TEST_EPS);
214         Assertions.assertEquals(0.0, circle.azimuth(Vector3D.of(-1, 0, 1)), TEST_EPS);
215         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Vector3D.of(-1, -1, 0)), TEST_EPS);
216         Assertions.assertEquals(Math.PI, circle.azimuth(Vector3D.of(-1, 0, -1)), TEST_EPS);
217 
218         Assertions.assertEquals(Angle.PI_OVER_TWO, circle.azimuth(Vector3D.of(1, 1, 0)), TEST_EPS);
219         Assertions.assertEquals(0.0, circle.azimuth(Vector3D.of(1, 0, 1)), TEST_EPS);
220         Assertions.assertEquals(1.5 * Math.PI, circle.azimuth(Vector3D.of(1, -1, 0)), TEST_EPS);
221         Assertions.assertEquals(Math.PI, circle.azimuth(Vector3D.of(1, 0, -1)), TEST_EPS);
222 
223         // poles
224         Assertions.assertEquals(0, circle.azimuth(Vector3D.Unit.MINUS_X), TEST_EPS);
225         Assertions.assertEquals(0, circle.azimuth(Vector3D.Unit.PLUS_X), TEST_EPS);
226     }
227 
228     @Test
229     void testVectorAt() {
230         // arrange
231         final GreatCircle circle = GreatCircles.fromPoleAndU(
232                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
233 
234         // act/assert
235         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z, circle.vectorAt(0.0), TEST_EPS);
236         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Y, circle.vectorAt(Angle.PI_OVER_TWO), TEST_EPS);
237         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Z, circle.vectorAt(Math.PI), TEST_EPS);
238         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Y, circle.vectorAt(-Angle.PI_OVER_TWO), TEST_EPS);
239         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z, circle.vectorAt(Angle.TWO_PI), TEST_EPS);
240     }
241 
242     @Test
243     void testProject() {
244         // arrange
245         final GreatCircle circle = GreatCircles.fromPoleAndU(
246                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
247 
248         // act/assert
249         SphericalTestUtils.assertPointsEqual(Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
250                 circle.project(Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO)), TEST_EPS);
251         SphericalTestUtils.assertPointsEqual(Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
252                 circle.project(Point2S.of(Angle.PI_OVER_TWO + 1, Angle.PI_OVER_TWO)), TEST_EPS);
253         SphericalTestUtils.assertPointsEqual(Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
254                 circle.project(Point2S.of(Angle.PI_OVER_TWO - 1, Angle.PI_OVER_TWO)), TEST_EPS);
255 
256         SphericalTestUtils.assertPointsEqual(Point2S.of(-Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
257                 circle.project(Point2S.of(-Angle.PI_OVER_TWO, Angle.PI_OVER_TWO)), TEST_EPS);
258         SphericalTestUtils.assertPointsEqual(Point2S.of(-Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
259                 circle.project(Point2S.of(-Angle.PI_OVER_TWO + 1, Angle.PI_OVER_TWO)), TEST_EPS);
260         SphericalTestUtils.assertPointsEqual(Point2S.of(-Angle.PI_OVER_TWO, Angle.PI_OVER_TWO),
261                 circle.project(Point2S.of(-Angle.PI_OVER_TWO - 1, Angle.PI_OVER_TWO)), TEST_EPS);
262     }
263 
264     @Test
265     void testProject_poles() {
266         // arrange
267         final GreatCircle minusXCircle = GreatCircles.fromPoleAndU(
268                 Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
269         final GreatCircle plusZCircle = GreatCircles.fromPoleAndU(
270                 Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
271 
272         // act
273         SphericalTestUtils.assertPointsEqual(Point2S.of(0.0, 0.0),
274                 minusXCircle.project(Point2S.from(Vector3D.Unit.MINUS_X)), TEST_EPS);
275         SphericalTestUtils.assertPointsEqual(Point2S.of(0.0, 0.0),
276                 minusXCircle.project(Point2S.from(Vector3D.Unit.PLUS_X)), TEST_EPS);
277 
278         SphericalTestUtils.assertPointsEqual(Point2S.of(1.5 * Math.PI, Angle.PI_OVER_TWO),
279                 plusZCircle.project(Point2S.from(Vector3D.Unit.PLUS_Z)), TEST_EPS);
280         SphericalTestUtils.assertPointsEqual(Point2S.of(1.5 * Math.PI, Angle.PI_OVER_TWO),
281                 plusZCircle.project(Point2S.from(Vector3D.Unit.MINUS_Z)), TEST_EPS);
282     }
283 
284     @Test
285     void testReverse() {
286         // arrange
287         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
288 
289         // act
290         final GreatCircle reverse = circle.reverse();
291 
292         // assert
293         checkGreatCircle(reverse, Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X);
294     }
295 
296     @Test
297     void testTransform_rotateAroundPole() {
298         // arrange
299         final GreatCircle circle = GreatCircles.fromPoints(
300                 Point2S.of(0, Angle.PI_OVER_TWO),
301                 Point2S.of(1, Angle.PI_OVER_TWO),
302                 TEST_PRECISION);
303 
304         final Transform2S t = Transform2S.createRotation(circle.getPolePoint(), 0.25 * Math.PI);
305 
306         // act
307         final GreatCircle result = circle.transform(t);
308 
309         // assert
310         Assertions.assertNotSame(circle, result);
311         checkGreatCircle(result, Vector3D.Unit.PLUS_Z, Vector3D.Unit.from(1, 1, 0));
312     }
313 
314     @Test
315     void testTransform_rotateAroundNonPole() {
316         // arrange
317         final GreatCircle circle = GreatCircles.fromPoints(
318                 Point2S.of(0, Angle.PI_OVER_TWO),
319                 Point2S.of(1, Angle.PI_OVER_TWO),
320                 TEST_PRECISION);
321 
322         final Transform2S t = Transform2S.createRotation(Point2S.of(0, Angle.PI_OVER_TWO), Angle.PI_OVER_TWO);
323 
324         // act
325         final GreatCircle result = circle.transform(t);
326 
327         // assert
328         Assertions.assertNotSame(circle, result);
329         checkGreatCircle(result, Vector3D.Unit.MINUS_Y, Vector3D.Unit.PLUS_X);
330     }
331 
332     @Test
333     void testTransform_piMinusAzimuth() {
334         // arrange
335         final GreatCircle circle = GreatCircles.fromPoints(
336                 Point2S.of(0, Angle.PI_OVER_TWO),
337                 Point2S.of(1, Angle.PI_OVER_TWO),
338                 TEST_PRECISION);
339 
340         final Transform2S t = Transform2S.createReflection(Point2S.PLUS_J)
341                 .rotate(Point2S.PLUS_K, Math.PI);
342 
343         // act
344         final GreatCircle result = circle.transform(t);
345 
346         // assert
347         Assertions.assertNotSame(circle, result);
348         checkGreatCircle(result, Vector3D.Unit.MINUS_Z, Vector3D.Unit.MINUS_X);
349     }
350 
351     @Test
352     void testSimilarOrientation() {
353         // arrange
354         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
355         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION);
356         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.MINUS_Z, TEST_PRECISION);
357         final GreatCircle d = GreatCircles.fromPole(Vector3D.Unit.from(1, 1, -1), TEST_PRECISION);
358         final GreatCircle e = GreatCircles.fromPole(Vector3D.Unit.from(1, 1, 1), TEST_PRECISION);
359 
360         // act/assert
361         Assertions.assertTrue(a.similarOrientation(a));
362 
363         Assertions.assertFalse(a.similarOrientation(b));
364         Assertions.assertFalse(a.similarOrientation(c));
365         Assertions.assertFalse(a.similarOrientation(d));
366 
367         Assertions.assertTrue(a.similarOrientation(e));
368     }
369 
370     @Test
371     void testSpan() {
372         // arrange
373         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
374 
375         // act
376         final GreatArc span = circle.span();
377 
378         // assert
379         Assertions.assertSame(circle, span.getCircle());
380         Assertions.assertTrue(span.getInterval().isFull());
381 
382         Assertions.assertNull(span.getStartPoint());
383         Assertions.assertNull(span.getEndPoint());
384     }
385 
386     @Test
387     void testArc_points_2s() {
388         // arrange
389         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
390 
391         // act/assert
392         checkArc(circle.arc(Point2S.of(1, Angle.PI_OVER_TWO), Point2S.of(0, 1)),
393                 Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO), Point2S.of(0, 0));
394 
395         Assertions.assertTrue(circle.arc(Point2S.PLUS_I, Point2S.PLUS_I).isFull());
396     }
397 
398     @Test
399     void testArc_points_1s() {
400         // arrange
401         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
402 
403         // act/assert
404         checkArc(circle.arc(Point1S.of(Math.PI), Point1S.of(1.5 * Math.PI)),
405                 Point2S.of(0, Math.PI), Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO));
406 
407         Assertions.assertTrue(circle.arc(Point1S.of(1), Point1S.of(1)).isFull());
408     }
409 
410     @Test
411     void testArc_azimuths() {
412         // arrange
413         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
414 
415         // act/assert
416         checkArc(circle.arc(Math.PI, 1.5 * Math.PI),
417                 Point2S.of(0, Math.PI), Point2S.of(Angle.PI_OVER_TWO, Angle.PI_OVER_TWO));
418 
419         Assertions.assertTrue(circle.arc(1, 1).isFull());
420     }
421 
422     @Test
423     void testArc_interval() {
424         // arrange
425         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
426         final AngularInterval.Convex interval = AngularInterval.Convex.of(1, 2, TEST_PRECISION);
427 
428         // act
429         final GreatArc arc = circle.arc(interval);
430 
431         // assert
432         Assertions.assertSame(circle, arc.getCircle());
433         Assertions.assertSame(interval, arc.getInterval());
434     }
435 
436     @Test
437     void testIntersection_parallel() {
438         // arrange
439         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
440 
441         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, precision);
442         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, precision);
443         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.of(1, 1e-4, 1e-4), precision);
444         final GreatCircle d = GreatCircles.fromPole(Vector3D.Unit.MINUS_X, precision);
445         final GreatCircle e = GreatCircles.fromPole(Vector3D.Unit.of(-1, 1e-4, 1e-4), precision);
446 
447         // act/assert
448         Assertions.assertNull(a.intersection(b));
449         Assertions.assertNull(a.intersection(c));
450         Assertions.assertNull(a.intersection(d));
451         Assertions.assertNull(a.intersection(e));
452     }
453 
454     @Test
455     void testIntersection() {
456         // arrange
457         final GreatCircle a = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION);
458         final GreatCircle b = GreatCircles.fromPole(Vector3D.Unit.PLUS_Y, TEST_PRECISION);
459         final GreatCircle c = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
460 
461         // act/assert
462         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Z,
463                 a.intersection(b).getVector(), TEST_EPS);
464         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_Z,
465                 b.intersection(a).getVector(), TEST_EPS);
466 
467         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_X,
468                 b.intersection(c).getVector(), TEST_EPS);
469         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.MINUS_X,
470                 c.intersection(b).getVector(), TEST_EPS);
471     }
472 
473     @Test
474     void testAngle_withoutReferencePoint() {
475      // arrange
476         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
477         final GreatCircle b = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_I, TEST_PRECISION);
478         final GreatCircle c = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_K, TEST_PRECISION);
479         final GreatCircle d = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION);
480         final GreatCircle e = GreatCircles.fromPoleAndU(
481                 Vector3D.Unit.of(1, 0, 1),
482                 Vector3D.Unit.PLUS_Y,
483                 TEST_PRECISION);
484 
485         final GreatCircle f = GreatCircles.fromPoleAndU(
486                 Vector3D.Unit.of(1, 0, -1),
487                 Vector3D.Unit.PLUS_Y,
488                 TEST_PRECISION);
489 
490         // act/assert
491         Assertions.assertEquals(0, a.angle(a), TEST_EPS);
492         Assertions.assertEquals(Math.PI, a.angle(b), TEST_EPS);
493 
494         Assertions.assertEquals(Angle.PI_OVER_TWO, a.angle(c), TEST_EPS);
495         Assertions.assertEquals(Angle.PI_OVER_TWO, c.angle(a), TEST_EPS);
496 
497         Assertions.assertEquals(Angle.PI_OVER_TWO, a.angle(d), TEST_EPS);
498         Assertions.assertEquals(Angle.PI_OVER_TWO, d.angle(a), TEST_EPS);
499 
500         Assertions.assertEquals(0.25 * Math.PI, a.angle(e), TEST_EPS);
501         Assertions.assertEquals(0.25 * Math.PI, e.angle(a), TEST_EPS);
502 
503         Assertions.assertEquals(0.75 * Math.PI, a.angle(f), TEST_EPS);
504         Assertions.assertEquals(0.75 * Math.PI, f.angle(a), TEST_EPS);
505     }
506 
507     @Test
508     void testAngle_withReferencePoint() {
509         // arrange
510         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
511         final GreatCircle b = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_I, TEST_PRECISION);
512         final GreatCircle c = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_K, TEST_PRECISION);
513         final GreatCircle d = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION);
514         final GreatCircle e = GreatCircles.fromPoleAndU(
515                 Vector3D.Unit.of(1, 0, 1),
516                 Vector3D.Unit.PLUS_Y,
517                 TEST_PRECISION);
518 
519         final GreatCircle f = GreatCircles.fromPoleAndU(
520                 Vector3D.Unit.of(1, 0, -1),
521                 Vector3D.Unit.PLUS_Y,
522                 TEST_PRECISION);
523 
524         // act/assert
525         Assertions.assertEquals(0, a.angle(a, Point2S.PLUS_J), TEST_EPS);
526         Assertions.assertEquals(0, a.angle(a, Point2S.MINUS_J), TEST_EPS);
527 
528         Assertions.assertEquals(-Math.PI, a.angle(b, Point2S.PLUS_J), TEST_EPS);
529         Assertions.assertEquals(-Math.PI, a.angle(b, Point2S.MINUS_J), TEST_EPS);
530 
531         Assertions.assertEquals(Angle.PI_OVER_TWO, a.angle(c, Point2S.PLUS_I), TEST_EPS);
532         Assertions.assertEquals(-Angle.PI_OVER_TWO, a.angle(c, Point2S.MINUS_I), TEST_EPS);
533 
534         Assertions.assertEquals(-Angle.PI_OVER_TWO, c.angle(a, Point2S.PLUS_I), TEST_EPS);
535         Assertions.assertEquals(Angle.PI_OVER_TWO, c.angle(a, Point2S.MINUS_I), TEST_EPS);
536 
537         Assertions.assertEquals(Angle.PI_OVER_TWO, a.angle(d, Point2S.PLUS_J), TEST_EPS);
538         Assertions.assertEquals(-Angle.PI_OVER_TWO, a.angle(d, Point2S.MINUS_J), TEST_EPS);
539 
540         Assertions.assertEquals(-Angle.PI_OVER_TWO, d.angle(a, Point2S.PLUS_J), TEST_EPS);
541         Assertions.assertEquals(Angle.PI_OVER_TWO, d.angle(a, Point2S.MINUS_J), TEST_EPS);
542 
543         Assertions.assertEquals(0.25 * Math.PI, a.angle(e, Point2S.PLUS_J), TEST_EPS);
544         Assertions.assertEquals(-0.25 * Math.PI, a.angle(e, Point2S.MINUS_J), TEST_EPS);
545 
546         Assertions.assertEquals(-0.25 * Math.PI, e.angle(a, Point2S.PLUS_J), TEST_EPS);
547         Assertions.assertEquals(0.25 * Math.PI, e.angle(a, Point2S.MINUS_J), TEST_EPS);
548 
549         Assertions.assertEquals(0.75 * Math.PI, a.angle(f, Point2S.PLUS_J), TEST_EPS);
550         Assertions.assertEquals(-0.75 * Math.PI, a.angle(f, Point2S.MINUS_J), TEST_EPS);
551 
552         Assertions.assertEquals(-0.75 * Math.PI, f.angle(a, Point2S.PLUS_J), TEST_EPS);
553         Assertions.assertEquals(0.75 * Math.PI, f.angle(a, Point2S.MINUS_J), TEST_EPS);
554     }
555 
556     @Test
557     void testAngle_withReferencePoint_pointEquidistanceFromIntersections() {
558         // arrange
559         final GreatCircle a = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
560         final GreatCircle b = GreatCircles.fromPoleAndU(
561                 Vector3D.Unit.of(1, 0, 1),
562                 Vector3D.Unit.PLUS_Y,
563                 TEST_PRECISION);
564 
565         // act/assert
566         Assertions.assertEquals(-0.25 * Math.PI, a.angle(b, Point2S.PLUS_I), TEST_EPS);
567         Assertions.assertEquals(-0.25 * Math.PI, a.angle(b, Point2S.MINUS_I), TEST_EPS);
568     }
569 
570     @Test
571     void testToSubspace() {
572         // arrange
573         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Z, TEST_PRECISION);
574 
575         // act/assert
576         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
577                 circle.toSubspace(Point2S.from(Vector3D.Unit.MINUS_Z)), TEST_EPS);
578 
579         SphericalTestUtils.assertPointsEqual(Point1S.of(0.25 * Math.PI),
580                 circle.toSubspace(Point2S.from(Vector3D.of(-1, -1, -1))), TEST_EPS);
581         SphericalTestUtils.assertPointsEqual(Point1S.of(0.75 * Math.PI),
582                 circle.toSubspace(Point2S.from(Vector3D.of(-1, 1, 1))), TEST_EPS);
583         SphericalTestUtils.assertPointsEqual(Point1S.of(1.25 * Math.PI),
584                 circle.toSubspace(Point2S.from(Vector3D.of(1, -1, 1))), TEST_EPS);
585         SphericalTestUtils.assertPointsEqual(Point1S.of(1.75 * Math.PI),
586                 circle.toSubspace(Point2S.from(Vector3D.of(1, 1, -1))), TEST_EPS);
587 
588         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
589                 circle.toSubspace(Point2S.from(Vector3D.Unit.PLUS_Y)), TEST_EPS);
590         SphericalTestUtils.assertPointsEqual(Point1S.ZERO,
591                 circle.toSubspace(Point2S.from(Vector3D.Unit.MINUS_Y)), TEST_EPS);
592     }
593 
594     @Test
595     void testToSpace() {
596         // arrange
597         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Z, TEST_PRECISION);
598 
599         // act/assert
600         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.Unit.MINUS_Z),
601                 circle.toSpace(Point1S.ZERO), TEST_EPS);
602 
603         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(-1, 0, -1)),
604                 circle.toSpace(Point1S.of(0.25 * Math.PI)), TEST_EPS);
605         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(-1, 0, 1)),
606                 circle.toSpace(Point1S.of(0.75 * Math.PI)), TEST_EPS);
607         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(1, 0, 1)),
608                 circle.toSpace(Point1S.of(1.25 * Math.PI)), TEST_EPS);
609         SphericalTestUtils.assertPointsEqual(Point2S.from(Vector3D.of(1, 0, -1)),
610                 circle.toSpace(Point1S.of(1.75 * Math.PI)), TEST_EPS);
611     }
612 
613     @Test
614     void testEq() {
615         // arrange
616         final double eps = 1e-3;
617         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(eps);
618 
619         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
620 
621         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X, precision);
622         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, precision);
623         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
624 
625         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.of(1e-6, 0, 1), Vector3D.Unit.PLUS_X, precision);
626         final GreatCircle f = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.of(1, 1e-6, 0), precision);
627         final GreatCircle g = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X,
628                 Precision.doubleEquivalenceOfEpsilon(eps));
629 
630         // act/assert
631         Assertions.assertTrue(a.eq(a, precision));
632 
633         Assertions.assertFalse(a.eq(b, precision));
634         Assertions.assertFalse(a.eq(c, precision));
635 
636         Assertions.assertTrue(a.eq(d, precision));
637         Assertions.assertTrue(a.eq(e, precision));
638         Assertions.assertTrue(e.eq(a, precision));
639 
640         Assertions.assertTrue(a.eq(f, precision));
641         Assertions.assertTrue(f.eq(a, precision));
642 
643         Assertions.assertTrue(g.eq(e, precision));
644         Assertions.assertTrue(e.eq(g, precision));
645     }
646 
647     @Test
648     void testHashCode() {
649         // arrange
650         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
651 
652         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
653 
654         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.of(0, 1, 1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
655         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, TEST_PRECISION);
656         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
657 
658         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
659 
660         // act
661         final int hash = a.hashCode();
662 
663         // act/assert
664         Assertions.assertEquals(hash, a.hashCode());
665 
666         Assertions.assertNotEquals(hash, b.hashCode());
667         Assertions.assertNotEquals(hash, c.hashCode());
668         Assertions.assertNotEquals(hash, d.hashCode());
669 
670         Assertions.assertEquals(hash, e.hashCode());
671     }
672 
673     @Test
674     void testEquals() {
675         // arrange
676         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
677 
678         final GreatCircle a = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
679 
680         final GreatCircle b = GreatCircles.fromPoleAndU(Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
681         final GreatCircle c = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.MINUS_X, TEST_PRECISION);
682         final GreatCircle d = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, precision);
683 
684         final GreatCircle e = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
685 
686         // act/assert
687         GeometryTestUtils.assertSimpleEqualsCases(a);
688 
689         Assertions.assertNotEquals(a, b);
690         Assertions.assertNotEquals(a, c);
691         Assertions.assertNotEquals(a, d);
692 
693         Assertions.assertEquals(a, e);
694         Assertions.assertEquals(e, a);
695     }
696 
697     @Test
698     void testToString() {
699         // arrange
700         final GreatCircle circle = GreatCircles.fromPoleAndU(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
701 
702         // act
703         final String str = circle.toString();
704 
705         // assert
706         GeometryTestUtils.assertContains("GreatCircle[", str);
707         GeometryTestUtils.assertContains("pole= (0.0, 0.0, 1.0)", str);
708         GeometryTestUtils.assertContains("u= (1.0, 0.0, 0.0)", str);
709         GeometryTestUtils.assertContains("v= (0.0, 1.0, 0.0)", str);
710     }
711 
712     private static void checkGreatCircle(final GreatCircle circle, final Vector3D pole, final Vector3D u) {
713         SphericalTestUtils.assertVectorsEqual(pole, circle.getPole(), TEST_EPS);
714         SphericalTestUtils.assertVectorsEqual(pole, circle.getW(), TEST_EPS);
715         SphericalTestUtils.assertVectorsEqual(u, circle.getU(), TEST_EPS);
716         SphericalTestUtils.assertVectorsEqual(pole.cross(u), circle.getV(), TEST_EPS);
717 
718         final Point2S plusPolePt = Point2S.from(circle.getPole());
719         final Point2S minusPolePt = Point2S.from(circle.getPole().negate());
720         final Point2S origin = Point2S.from(circle.getU());
721 
722         SphericalTestUtils.assertPointsEqual(plusPolePt, circle.getPolePoint(), TEST_EPS);
723 
724         Assertions.assertFalse(circle.contains(plusPolePt));
725         Assertions.assertFalse(circle.contains(minusPolePt));
726         Assertions.assertTrue(circle.contains(origin));
727 
728         Assertions.assertEquals(HyperplaneLocation.MINUS, circle.classify(plusPolePt));
729         Assertions.assertEquals(HyperplaneLocation.PLUS, circle.classify(minusPolePt));
730         Assertions.assertEquals(HyperplaneLocation.ON, circle.classify(origin));
731     }
732 
733     private static void checkArc(final GreatArc arc, final Point2S start, final Point2S end) {
734         SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS);
735         SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS);
736     }
737 }