1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
43 final GreatArc arc = GreatCircles.arcFromInterval(
44 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
45 AngularInterval.full());
46
47
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
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
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
87 final Point2S start = Point2S.PLUS_I;
88 final Point2S end = Point2S.MINUS_K;
89
90
91 final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
92
93
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
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
117 final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
118
119
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
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
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
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
167 final List<GreatArc> result = arc.toConvex();
168
169
170 Assertions.assertEquals(1, result.size());
171 Assertions.assertSame(arc, result.get(0));
172 }
173
174 @Test
175 void testGetMidPoint() {
176
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
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
188 final GreatArc arc = GreatCircles.arcFromInterval(
189 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
190 AngularInterval.full());
191
192
193 Assertions.assertNull(arc.getMidPoint());
194 }
195
196 @Test
197 void testReverse_full() {
198
199 final GreatArc arc = GreatCircles.arcFromInterval(
200 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
201 AngularInterval.full());
202
203
204 final GreatArc result = arc.reverse();
205
206
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
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
220 final GreatArc result = arc.reverse();
221
222
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
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
238 final GreatArc result = arc.transform(t);
239
240
241 checkArc(result, Point2S.PLUS_I, Point2S.PLUS_J);
242 }
243
244 @Test
245 void testSplit_full() {
246
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
251 final Split<GreatArc> split = arc.split(splitter);
252
253
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
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
277 final Split<GreatArc> split = arc.split(splitter);
278
279
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
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
300 final Split<GreatArc> split = arc.split(splitter);
301
302
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
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
320 final Split<GreatArc> split = arc.split(splitter);
321
322
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
335 final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
336
337
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
347 final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
348
349
350 final String str = arc.toString();
351
352
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
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
366 final String str = arc.toString();
367
368
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 }