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.List;
20  
21  import org.apache.commons.geometry.core.GeometryTestUtils;
22  import org.apache.commons.geometry.core.RegionLocation;
23  import org.apache.commons.geometry.core.partitioning.Split;
24  import org.apache.commons.geometry.core.partitioning.SplitLocation;
25  import org.apache.commons.geometry.euclidean.threed.Vector3D;
26  import org.apache.commons.geometry.spherical.SphericalTestUtils;
27  import org.apache.commons.geometry.spherical.oned.AngularInterval;
28  import org.apache.commons.numbers.angle.Angle;
29  import org.apache.commons.numbers.core.Precision;
30  import org.junit.jupiter.api.Assertions;
31  import org.junit.jupiter.api.Test;
32  
33  class GreatArcTest {
34  
35      private static final double TEST_EPS = 1e-10;
36  
37      private static final Precision.DoubleEquivalence TEST_PRECISION =
38              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
39  
40      @Test
41      void testFromInterval_full() {
42          // act
43          final GreatArc arc = GreatCircles.arcFromInterval(
44                  GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
45                  AngularInterval.full());
46  
47          // assert
48          Assertions.assertTrue(arc.isFull());
49          Assertions.assertFalse(arc.isEmpty());
50          Assertions.assertTrue(arc.isFinite());
51          Assertions.assertFalse(arc.isInfinite());
52  
53          Assertions.assertNull(arc.getStartPoint());
54          Assertions.assertNull(arc.getEndPoint());
55  
56          Assertions.assertEquals(Angle.TWO_PI, arc.getSize(), TEST_EPS);
57          Assertions.assertNull(arc.getCentroid());
58  
59          for (double az = 0; az < Angle.TWO_PI; az += 0.1) {
60              checkClassify(arc, RegionLocation.INSIDE, Point2S.of(az, Angle.PI_OVER_TWO));
61          }
62  
63          checkClassify(arc, RegionLocation.OUTSIDE,
64                  Point2S.PLUS_K, Point2S.of(0, Angle.PI_OVER_TWO + 0.1),
65                  Point2S.MINUS_K, Point2S.of(0, Angle.PI_OVER_TWO - 0.1));
66      }
67  
68      @Test
69      void testFromInterval_partial() {
70          // arrange
71          final GreatArc arc = GreatCircles.arcFromInterval(
72                  GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION),
73                  AngularInterval.Convex.of(Angle.PI_OVER_TWO, 1.5 * Math.PI, TEST_PRECISION));
74  
75          // assert
76          Assertions.assertFalse(arc.isFull());
77          Assertions.assertFalse(arc.isEmpty());
78          Assertions.assertTrue(arc.isFinite());
79          Assertions.assertFalse(arc.isInfinite());
80  
81          checkArc(arc, Point2S.PLUS_K, Point2S.MINUS_K);
82      }
83  
84      @Test
85      void testFromPoints() {
86          // arrange
87          final Point2S start = Point2S.PLUS_I;
88          final Point2S end = Point2S.MINUS_K;
89  
90          // act
91          final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
92  
93          // assert
94          Assertions.assertFalse(arc.isFull());
95          Assertions.assertFalse(arc.isEmpty());
96          Assertions.assertTrue(arc.isFinite());
97          Assertions.assertFalse(arc.isInfinite());
98  
99          SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Y, arc.getCircle().getPole(), TEST_EPS);
100 
101         checkArc(arc, start, end);
102 
103         checkClassify(arc, RegionLocation.INSIDE, Point2S.of(0, 0.75 * Math.PI));
104         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
105         checkClassify(arc, RegionLocation.OUTSIDE,
106                 Point2S.of(0, 0.25 * Math.PI), Point2S.of(Math.PI, 0.75 * Math.PI),
107                 Point2S.of(Math.PI, 0.25 * Math.PI));
108     }
109 
110     @Test
111     void testFromPoints_almostPi() {
112         // arrange
113         final Point2S start = Point2S.PLUS_J;
114         final Point2S end = Point2S.of(1.5 * Math.PI, Angle.PI_OVER_TWO - 1e-5);
115 
116         // act
117         final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
118 
119         // assert
120         Assertions.assertFalse(arc.isFull());
121         Assertions.assertFalse(arc.isEmpty());
122         Assertions.assertTrue(arc.isFinite());
123         Assertions.assertFalse(arc.isInfinite());
124 
125         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_X, arc.getCircle().getPole(), TEST_EPS);
126 
127         checkArc(arc, start, end);
128 
129         checkClassify(arc, RegionLocation.INSIDE, Point2S.PLUS_K);
130         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
131         checkClassify(arc, RegionLocation.OUTSIDE, Point2S.MINUS_K);
132     }
133 
134     @Test
135     void testFromPoints_usesShortestPath() {
136         // act/assert
137         SphericalTestUtils.assertVectorsEqual(
138                 Vector3D.Unit.MINUS_Y,
139                 GreatCircles.arcFromPoints(
140                         Point2S.PLUS_I,
141                         Point2S.of(Math.PI, Angle.PI_OVER_TWO - 1e-5),
142                         TEST_PRECISION).getCircle().getPole(), TEST_EPS);
143 
144         SphericalTestUtils.assertVectorsEqual(
145                 Vector3D.Unit.PLUS_Y,
146                 GreatCircles.arcFromPoints(
147                         Point2S.PLUS_I,
148                         Point2S.of(Math.PI, Angle.PI_OVER_TWO + 1e-5),
149                         TEST_PRECISION).getCircle().getPole(), TEST_EPS);
150     }
151 
152     @Test
153     void testFromPoints_invalidPoints() {
154         // act/assert
155         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.of(1e-12, Angle.PI_OVER_TWO), TEST_PRECISION));
156         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.MINUS_I, TEST_PRECISION));
157     }
158 
159     @Test
160     void testToConvex() {
161         // arrange
162         final GreatArc arc = GreatCircles.arcFromInterval(
163                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
164                 AngularInterval.Convex.of(0.0, Math.PI, TEST_PRECISION));
165 
166         // act
167         final List<GreatArc> result = arc.toConvex();
168 
169         // assert
170         Assertions.assertEquals(1, result.size());
171         Assertions.assertSame(arc, result.get(0));
172     }
173 
174     @Test
175     void testGetMidPoint() {
176         // arrange
177         final GreatArc arc = GreatCircles.arcFromInterval(
178                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
179                 AngularInterval.Convex.of(0.8 * Math.PI, 0.9 * Math.PI, TEST_PRECISION));
180 
181         // act/assert
182         SphericalTestUtils.assertPointsEqual(Point2S.of(0.85 * Math.PI, 0.5 * Math.PI), arc.getMidPoint(), TEST_EPS);
183     }
184 
185     @Test
186     void testGetMidPoint_full() {
187         // arrange
188         final GreatArc arc = GreatCircles.arcFromInterval(
189                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
190                 AngularInterval.full());
191 
192         // act/assert
193         Assertions.assertNull(arc.getMidPoint());
194     }
195 
196     @Test
197     void testReverse_full() {
198         // arrange
199         final GreatArc arc = GreatCircles.arcFromInterval(
200                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
201                 AngularInterval.full());
202 
203         // act
204         final GreatArc result = arc.reverse();
205 
206         // assert
207         checkGreatCircle(result.getCircle(), Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y);
208 
209         Assertions.assertTrue(result.isFull());
210     }
211 
212     @Test
213     void testReverse() {
214         // arrange
215         final GreatArc arc = GreatCircles.arcFromInterval(
216                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
217                 AngularInterval.Convex.of(Angle.PI_OVER_TWO, Math.PI, TEST_PRECISION));
218 
219         // act
220         final GreatArc result = arc.reverse();
221 
222         // assert
223         checkGreatCircle(result.getCircle(), Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y);
224 
225         checkArc(result, Point2S.MINUS_J, Point2S.MINUS_I);
226     }
227 
228     @Test
229     void testTransform() {
230         // arrange
231         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_K, Point2S.MINUS_I, TEST_PRECISION)
232                 .arc(Math.PI, -Angle.PI_OVER_TWO);
233 
234         final Transform2S t = Transform2S.createRotation(Point2S.PLUS_I, Angle.PI_OVER_TWO)
235                 .reflect(Point2S.of(-0.25 * Math.PI,  Angle.PI_OVER_TWO));
236 
237         // act
238         final GreatArc result = arc.transform(t);
239 
240         // assert
241         checkArc(result, Point2S.PLUS_I, Point2S.PLUS_J);
242     }
243 
244     @Test
245     void testSplit_full() {
246         // arrange
247         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
248         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.of(-1, 0, 1), TEST_PRECISION);
249 
250         // act
251         final Split<GreatArc> split = arc.split(splitter);
252 
253         // assert
254         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
255 
256         final GreatArc minus = split.getMinus();
257         Assertions.assertSame(arc.getCircle(), minus.getCircle());
258         checkArc(minus, Point2S.PLUS_J, Point2S.MINUS_J);
259         checkClassify(minus, RegionLocation.OUTSIDE, Point2S.PLUS_I);
260         checkClassify(minus, RegionLocation.INSIDE, Point2S.MINUS_I);
261 
262         final GreatArc plus = split.getPlus();
263         Assertions.assertSame(arc.getCircle(), plus.getCircle());
264         checkArc(plus, Point2S.MINUS_J, Point2S.PLUS_J);
265         checkClassify(plus, RegionLocation.INSIDE, Point2S.PLUS_I);
266         checkClassify(plus, RegionLocation.OUTSIDE, Point2S.MINUS_I);
267     }
268 
269     @Test
270     void testSplit_both() {
271         // arrange
272         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
273                 .arc(Angle.PI_OVER_TWO, Math.PI);
274         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.of(0, 1, 1), TEST_PRECISION);
275 
276         // act
277         final Split<GreatArc> split = arc.split(splitter);
278 
279         // assert
280         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
281 
282         final GreatArc minus = split.getMinus();
283         Assertions.assertSame(arc.getCircle(), minus.getCircle());
284         checkArc(minus, Point2S.of(0, 0), Point2S.of(1.5 * Math.PI, 0.25 * Math.PI));
285 
286         final GreatArc plus = split.getPlus();
287         Assertions.assertSame(arc.getCircle(), plus.getCircle());
288         checkArc(plus, Point2S.of(1.5 * Math.PI, 0.25 * Math.PI), Point2S.MINUS_J);
289     }
290 
291     @Test
292     void testSplit_minus() {
293         // arrange
294         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
295                 .arc(Angle.PI_OVER_TWO, Math.PI);
296         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
297 
298 
299         // act
300         final Split<GreatArc> split = arc.split(splitter);
301 
302         // assert
303         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
304 
305         final GreatArc minus = split.getMinus();
306         Assertions.assertSame(arc, minus);
307 
308         final GreatArc plus = split.getPlus();
309         Assertions.assertNull(plus);
310     }
311 
312     @Test
313     void testSplit_plus() {
314         // arrange
315         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
316                 .arc(Angle.PI_OVER_TWO, Math.PI);
317         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.Unit.from(-1, 0, -1), TEST_PRECISION);
318 
319         // act
320         final Split<GreatArc> split = arc.split(splitter);
321 
322         // assert
323         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
324 
325         final GreatArc minus = split.getMinus();
326         Assertions.assertNull(minus);
327 
328         final GreatArc plus = split.getPlus();
329         Assertions.assertSame(arc, plus);
330     }
331 
332     @Test
333     void testSplit_parallelAndAntiparallel() {
334         // arrange
335         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
336 
337         // act/assert
338         Assertions.assertEquals(SplitLocation.NEITHER,
339                 arc.split(GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION)).getLocation());
340         Assertions.assertEquals(SplitLocation.NEITHER,
341                 arc.split(GreatCircles.fromPole(Vector3D.Unit.MINUS_Z, TEST_PRECISION)).getLocation());
342     }
343 
344     @Test
345     void testToString_full() {
346         // arrange
347         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
348 
349         // act
350         final String str = arc.toString();
351 
352         // assert
353         GeometryTestUtils.assertContains("GreatArc[", str);
354         GeometryTestUtils.assertContains("full= true", str);
355         GeometryTestUtils.assertContains("circle= GreatCircle[", str);
356     }
357 
358     @Test
359     void testToString_notFull() {
360         // arrange
361         final GreatArc arc = GreatCircles.arcFromInterval(
362                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
363                 AngularInterval.Convex.of(1, 2, TEST_PRECISION));
364 
365         // act
366         final String str = arc.toString();
367 
368         // assert
369         GeometryTestUtils.assertContains("GreatArc[", str);
370         GeometryTestUtils.assertContains("start= (", str);
371         GeometryTestUtils.assertContains("end= (", str);
372     }
373 
374     private static void checkClassify(final GreatArc arc, final RegionLocation loc, final Point2S... pts) {
375         for (final Point2S pt : pts) {
376             Assertions.assertEquals(loc, arc.classify(pt), "Unexpected location for point " + pt);
377         }
378     }
379 
380     private static void checkArc(final GreatArc arc, final Point2S start, final Point2S end) {
381         SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS);
382         SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS);
383 
384         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
385 
386         final Point2S mid = arc.getCircle().toSpace(arc.getInterval().getMidPoint());
387 
388         checkClassify(arc, RegionLocation.INSIDE, mid);
389         checkClassify(arc, RegionLocation.OUTSIDE, mid.antipodal());
390 
391         Assertions.assertEquals(start.distance(end), arc.getSize(), TEST_EPS);
392         SphericalTestUtils.assertPointsEq(mid, arc.getCentroid(), TEST_EPS);
393     }
394 
395     private static void checkGreatCircle(final GreatCircle circle, final Vector3D pole, final Vector3D x) {
396         SphericalTestUtils.assertVectorsEqual(pole, circle.getPole(), TEST_EPS);
397         SphericalTestUtils.assertVectorsEqual(x, circle.getU(), TEST_EPS);
398         SphericalTestUtils.assertVectorsEqual(pole.cross(x), circle.getV(), TEST_EPS);
399     }
400 }