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