1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.spherical.oned;
18
19 import java.util.List;
20
21 import org.apache.commons.geometry.core.GeometryTestUtils;
22 import org.apache.commons.geometry.core.Region;
23 import org.apache.commons.geometry.core.RegionLocation;
24 import org.apache.commons.geometry.core.partitioning.Split;
25 import org.apache.commons.geometry.core.partitioning.SplitLocation;
26 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
27 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
28 import org.apache.commons.numbers.angle.PlaneAngleRadians;
29 import org.junit.Assert;
30 import org.junit.Test;
31
32 public class AngularIntervalTest {
33
34 private static final double TEST_EPS = 1e-10;
35
36 private static final DoublePrecisionContext TEST_PRECISION =
37 new EpsilonDoublePrecisionContext(TEST_EPS);
38
39 @Test
40 public void testOf_doubles() {
41
42 checkInterval(AngularInterval.of(0, 1, TEST_PRECISION), 0, 1);
43 checkInterval(AngularInterval.of(1, 0, TEST_PRECISION), 1, PlaneAngleRadians.TWO_PI);
44 checkInterval(AngularInterval.of(-2, -1.5, TEST_PRECISION), -2, -1.5);
45 checkInterval(AngularInterval.of(-2, -2.5, TEST_PRECISION), -2, PlaneAngleRadians.TWO_PI - 2.5);
46
47 checkFull(AngularInterval.of(1, 1, TEST_PRECISION));
48 checkFull(AngularInterval.of(0, 1e-11, TEST_PRECISION));
49 checkFull(AngularInterval.of(0, -1e-11, TEST_PRECISION));
50 checkFull(AngularInterval.of(0, PlaneAngleRadians.TWO_PI, TEST_PRECISION));
51 }
52
53 @Test
54 public void testOf_doubles_invalidArgs() {
55
56 GeometryTestUtils.assertThrows(() -> {
57 AngularInterval.of(Double.NEGATIVE_INFINITY, 0, TEST_PRECISION);
58 }, IllegalArgumentException.class);
59
60 GeometryTestUtils.assertThrows(() -> {
61 AngularInterval.of(0, Double.POSITIVE_INFINITY, TEST_PRECISION);
62 }, IllegalArgumentException.class);
63
64 GeometryTestUtils.assertThrows(() -> {
65 AngularInterval.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, TEST_PRECISION);
66 }, IllegalArgumentException.class);
67
68 GeometryTestUtils.assertThrows(() -> {
69 AngularInterval.of(Double.NaN, 0, TEST_PRECISION);
70 }, IllegalArgumentException.class);
71
72 GeometryTestUtils.assertThrows(() -> {
73 AngularInterval.of(0, Double.NaN, TEST_PRECISION);
74 }, IllegalArgumentException.class);
75
76 GeometryTestUtils.assertThrows(() -> {
77 AngularInterval.of(Double.NaN, Double.NaN, TEST_PRECISION);
78 }, IllegalArgumentException.class);
79 }
80
81 @Test
82 public void testOf_points() {
83
84 checkInterval(AngularInterval.of(Point1S.of(0), Point1S.of(1), TEST_PRECISION), 0, 1);
85 checkInterval(AngularInterval.of(Point1S.of(1), Point1S.of(0), TEST_PRECISION), 1, PlaneAngleRadians.TWO_PI);
86 checkInterval(AngularInterval.of(Point1S.of(-2), Point1S.of(-1.5), TEST_PRECISION), -2, -1.5);
87 checkInterval(AngularInterval.of(Point1S.of(-2), Point1S.of(-2.5), TEST_PRECISION), -2, PlaneAngleRadians.TWO_PI - 2.5);
88
89 checkFull(AngularInterval.of(Point1S.of(1), Point1S.of(1), TEST_PRECISION));
90 checkFull(AngularInterval.of(Point1S.of(0), Point1S.of(1e-11), TEST_PRECISION));
91 checkFull(AngularInterval.of(Point1S.of(0), Point1S.of(-1e-11), TEST_PRECISION));
92 }
93
94 @Test
95 public void testOf_points_invalidArgs() {
96
97 GeometryTestUtils.assertThrows(() -> {
98 AngularInterval.of(Point1S.of(Double.NEGATIVE_INFINITY), Point1S.ZERO, TEST_PRECISION);
99 }, IllegalArgumentException.class);
100
101 GeometryTestUtils.assertThrows(() -> {
102 AngularInterval.of(Point1S.ZERO, Point1S.of(Double.POSITIVE_INFINITY), TEST_PRECISION);
103 }, IllegalArgumentException.class);
104
105 GeometryTestUtils.assertThrows(() -> {
106 AngularInterval.of(Point1S.of(Double.POSITIVE_INFINITY), Point1S.of(Double.NEGATIVE_INFINITY), TEST_PRECISION);
107 }, IllegalArgumentException.class);
108
109 GeometryTestUtils.assertThrows(() -> {
110 AngularInterval.of(Point1S.NaN, Point1S.ZERO, TEST_PRECISION);
111 }, IllegalArgumentException.class);
112
113 GeometryTestUtils.assertThrows(() -> {
114 AngularInterval.of(Point1S.ZERO, Point1S.NaN, TEST_PRECISION);
115 }, IllegalArgumentException.class);
116
117 GeometryTestUtils.assertThrows(() -> {
118 AngularInterval.of(Point1S.NaN, Point1S.NaN, TEST_PRECISION);
119 }, IllegalArgumentException.class);
120 }
121
122 @Test
123 public void testOf_orientedPoints() {
124
125 final DoublePrecisionContext precisionA = new EpsilonDoublePrecisionContext(1e-3);
126 final DoublePrecisionContext precisionB = new EpsilonDoublePrecisionContext(1e-2);
127
128 final CutAngle zeroPos = CutAngles.createPositiveFacing(Point1S.ZERO, precisionA);
129 final CutAngle zeroNeg = CutAngles.createNegativeFacing(Point1S.ZERO, precisionA);
130
131 final CutAngle piPos = CutAngles.createPositiveFacing(Point1S.PI, precisionA);
132 final CutAngle piNeg = CutAngles.createNegativeFacing(Point1S.PI, precisionA);
133
134 final CutAngle almostPiPos = CutAngles.createPositiveFacing(Point1S.of(PlaneAngleRadians.PI + 5e-3), precisionB);
135
136
137 checkInterval(AngularInterval.of(zeroNeg, piPos), 0, PlaneAngleRadians.PI);
138 checkInterval(AngularInterval.of(zeroPos, piNeg), PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI);
139
140 checkFull(AngularInterval.of(zeroPos, zeroNeg));
141 checkFull(AngularInterval.of(zeroPos, piPos));
142 checkFull(AngularInterval.of(piNeg, zeroNeg));
143
144 checkFull(AngularInterval.of(almostPiPos, piNeg));
145 checkFull(AngularInterval.of(piNeg, almostPiPos));
146 }
147
148 @Test
149 public void testOf_orientedPoints_invalidArgs() {
150
151 final CutAngle pt = CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION);
152 final CutAngle nan = CutAngles.createPositiveFacing(Point1S.NaN, TEST_PRECISION);
153
154
155 GeometryTestUtils.assertThrows(() -> {
156 AngularInterval.of(pt, nan);
157 }, IllegalArgumentException.class);
158
159 GeometryTestUtils.assertThrows(() -> {
160 AngularInterval.of(nan, pt);
161 }, IllegalArgumentException.class);
162
163 GeometryTestUtils.assertThrows(() -> {
164 AngularInterval.of(nan, nan);
165 }, IllegalArgumentException.class);
166 }
167
168 @Test
169 public void testFull() {
170
171 final AngularInterval.Convex interval = AngularInterval.full();
172
173
174 checkFull(interval);
175 }
176
177 @Test
178 public void testClassify_full() {
179
180 final AngularInterval interval = AngularInterval.full();
181
182
183 for (double a = -2 * PlaneAngleRadians.PI; a >= 4 * PlaneAngleRadians.PI; a += 0.5) {
184 checkClassify(interval, RegionLocation.INSIDE, Point1S.of(a));
185 }
186 }
187
188 @Test
189 public void testClassify_almostFull() {
190
191 final AngularInterval interval = AngularInterval.of(1 + 2e-10, 1, TEST_PRECISION);
192
193
194 checkClassify(interval, RegionLocation.BOUNDARY,
195 Point1S.of(1 + 2e-10), Point1S.of(1 + 6e-11), Point1S.of(1));
196
197 checkClassify(interval, RegionLocation.INSIDE, Point1S.of(1 + 6e-11 + PlaneAngleRadians.PI));
198
199 for (double a = 1 + 1e-9; a >= 1 - 1e-9 + PlaneAngleRadians.TWO_PI; a += 0.5) {
200 checkClassify(interval, RegionLocation.INSIDE, Point1S.of(a));
201 }
202 }
203
204 @Test
205 public void testClassify_sizeableGap() {
206
207 final AngularInterval interval = AngularInterval.of(0.25, -0.25, TEST_PRECISION);
208
209
210 checkClassify(interval, RegionLocation.OUTSIDE,
211 Point1S.ZERO, Point1S.of(-0.2), Point1S.of(0.2));
212 checkClassify(interval, RegionLocation.BOUNDARY,
213 Point1S.of(-0.25), Point1S.of(0.2499999999999));
214 checkClassify(interval, RegionLocation.INSIDE,
215 Point1S.of(1), Point1S.PI, Point1S.of(-1));
216 }
217
218 @Test
219 public void testClassify_halfPi() {
220
221 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
222
223
224 checkClassify(interval, RegionLocation.OUTSIDE,
225 Point1S.ZERO, Point1S.of(PlaneAngleRadians.PI_OVER_TWO - 0.1), Point1S.of(-PlaneAngleRadians.PI_OVER_TWO + 0.1));
226 checkClassify(interval, RegionLocation.BOUNDARY,
227 Point1S.of(PlaneAngleRadians.PI_OVER_TWO), Point1S.of(1.5 * PlaneAngleRadians.PI));
228 checkClassify(interval, RegionLocation.INSIDE,
229 Point1S.PI, Point1S.of(PlaneAngleRadians.PI_OVER_TWO + 0.1), Point1S.of(-PlaneAngleRadians.PI_OVER_TWO - 0.1));
230 }
231
232 @Test
233 public void testClassify_almostEmpty() {
234
235 final AngularInterval interval = AngularInterval.of(1, 1 + 2e-10, TEST_PRECISION);
236
237
238 checkClassify(interval, RegionLocation.BOUNDARY,
239 Point1S.of(1 + 2e-10), Point1S.of(1 + 6e-11), Point1S.of(1));
240
241 checkClassify(interval, RegionLocation.OUTSIDE, Point1S.of(1 + 6e-11 + PlaneAngleRadians.PI));
242
243 for (double a = 1 + 1e-9; a >= 1 - 1e-9 + PlaneAngleRadians.TWO_PI; a += 0.5) {
244 checkClassify(interval, RegionLocation.OUTSIDE, Point1S.of(a));
245 }
246 }
247
248 @Test
249 public void testProject_full() {
250
251 final AngularInterval interval = AngularInterval.full();
252
253
254 Assert.assertNull(interval.project(Point1S.ZERO));
255 Assert.assertNull(interval.project(Point1S.PI));
256 }
257
258 @Test
259 public void testProject() {
260
261 final AngularInterval interval = AngularInterval.of(1, 2, TEST_PRECISION);
262
263
264 Assert.assertEquals(1, interval.project(Point1S.ZERO).getAzimuth(), TEST_EPS);
265 Assert.assertEquals(1, interval.project(Point1S.of(1)).getAzimuth(), TEST_EPS);
266 Assert.assertEquals(1, interval.project(Point1S.of(1.5)).getAzimuth(), TEST_EPS);
267
268 Assert.assertEquals(2, interval.project(Point1S.of(2)).getAzimuth(), TEST_EPS);
269 Assert.assertEquals(2, interval.project(Point1S.PI).getAzimuth(), TEST_EPS);
270 Assert.assertEquals(2, interval.project(Point1S.of(1.4 + PlaneAngleRadians.PI)).getAzimuth(), TEST_EPS);
271
272 Assert.assertEquals(1, interval.project(Point1S.of(1.5 + PlaneAngleRadians.PI)).getAzimuth(), TEST_EPS);
273 Assert.assertEquals(1, interval.project(Point1S.of(1.6 + PlaneAngleRadians.PI)).getAzimuth(), TEST_EPS);
274 }
275
276 @Test
277 public void testTransform_full() {
278
279 final AngularInterval interval = AngularInterval.full();
280
281 final Transform1S rotate = Transform1S.createRotation(PlaneAngleRadians.PI_OVER_TWO);
282 final Transform1S invert = Transform1S.createNegation().rotate(PlaneAngleRadians.PI_OVER_TWO);
283
284
285 checkFull(interval.transform(rotate));
286 checkFull(interval.transform(invert));
287 }
288
289 @Test
290 public void testTransform() {
291
292 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI, TEST_PRECISION);
293
294 final Transform1S rotate = Transform1S.createRotation(PlaneAngleRadians.PI_OVER_TWO);
295 final Transform1S invert = Transform1S.createNegation().rotate(PlaneAngleRadians.PI_OVER_TWO);
296
297
298 checkInterval(interval.transform(rotate), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
299 checkInterval(interval.transform(invert), -0.5 * PlaneAngleRadians.PI, 0.0);
300 }
301
302 @Test
303 public void testWrapsZero() {
304
305 Assert.assertFalse(AngularInterval.full().wrapsZero());
306 Assert.assertFalse(AngularInterval.of(0, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION).wrapsZero());
307 Assert.assertFalse(AngularInterval.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI, TEST_PRECISION).wrapsZero());
308 Assert.assertFalse(AngularInterval.of(PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI, TEST_PRECISION).wrapsZero());
309 Assert.assertFalse(AngularInterval.of(1.5 * PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI - 1e-5, TEST_PRECISION).wrapsZero());
310
311 Assert.assertTrue(AngularInterval.of(1.5 * PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI, TEST_PRECISION).wrapsZero());
312 Assert.assertTrue(AngularInterval.of(1.5 * PlaneAngleRadians.PI, 2.5 * PlaneAngleRadians.PI, TEST_PRECISION).wrapsZero());
313 Assert.assertTrue(AngularInterval.of(-2.5 * PlaneAngleRadians.PI, -1.5 * PlaneAngleRadians.PI, TEST_PRECISION).wrapsZero());
314 }
315
316 @Test
317 public void testToTree_full() {
318
319 final AngularInterval interval = AngularInterval.full();
320
321
322 final RegionBSPTree1S tree = interval.toTree();
323
324
325 Assert.assertTrue(tree.isFull());
326 Assert.assertFalse(tree.isEmpty());
327
328 checkClassify(tree, RegionLocation.INSIDE,
329 Point1S.ZERO, Point1S.of(PlaneAngleRadians.PI_OVER_TWO),
330 Point1S.PI, Point1S.of(-PlaneAngleRadians.PI_OVER_TWO));
331 }
332
333 @Test
334 public void testToTree_intervalEqualToPi() {
335
336 final AngularInterval interval = AngularInterval.of(0.0, PlaneAngleRadians.PI, TEST_PRECISION);
337
338
339 final RegionBSPTree1S tree = interval.toTree();
340
341
342 Assert.assertFalse(tree.isFull());
343 Assert.assertFalse(tree.isEmpty());
344
345 checkClassify(tree, RegionLocation.BOUNDARY,
346 Point1S.ZERO, Point1S.PI);
347
348 checkClassify(tree, RegionLocation.INSIDE,
349 Point1S.of(1e-4), Point1S.of(0.25 * PlaneAngleRadians.PI),
350 Point1S.of(-1.25 * PlaneAngleRadians.PI), Point1S.of(PlaneAngleRadians.PI - 1e-4));
351
352 checkClassify(tree, RegionLocation.OUTSIDE,
353 Point1S.of(-1e-4), Point1S.of(-0.25 * PlaneAngleRadians.PI),
354 Point1S.of(1.25 * PlaneAngleRadians.PI), Point1S.of(-PlaneAngleRadians.PI + 1e-4));
355 }
356
357 @Test
358 public void testToTree_intervalLessThanPi() {
359
360 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI, TEST_PRECISION);
361
362
363 final RegionBSPTree1S tree = interval.toTree();
364
365
366 Assert.assertFalse(tree.isFull());
367 Assert.assertFalse(tree.isEmpty());
368
369 checkClassify(tree, RegionLocation.BOUNDARY,
370 Point1S.of(PlaneAngleRadians.PI_OVER_TWO), Point1S.PI);
371
372 checkClassify(tree, RegionLocation.INSIDE,
373 Point1S.of(0.51 * PlaneAngleRadians.PI), Point1S.of(0.75 * PlaneAngleRadians.PI),
374 Point1S.of(0.99 * PlaneAngleRadians.PI));
375
376 checkClassify(tree, RegionLocation.OUTSIDE,
377 Point1S.ZERO, Point1S.of(0.25 * PlaneAngleRadians.PI),
378 Point1S.of(1.25 * PlaneAngleRadians.PI), Point1S.of(1.75 * PlaneAngleRadians.PI));
379 }
380
381 @Test
382 public void testToTree_intervalGreaterThanPi() {
383
384 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
385
386
387 final RegionBSPTree1S tree = interval.toTree();
388
389
390 Assert.assertFalse(tree.isFull());
391 Assert.assertFalse(tree.isEmpty());
392
393 checkClassify(tree, RegionLocation.BOUNDARY,
394 Point1S.of(PlaneAngleRadians.PI_OVER_TWO), Point1S.PI);
395
396 checkClassify(tree, RegionLocation.INSIDE,
397 Point1S.ZERO, Point1S.of(0.25 * PlaneAngleRadians.PI),
398 Point1S.of(1.25 * PlaneAngleRadians.PI), Point1S.of(1.75 * PlaneAngleRadians.PI));
399
400 checkClassify(tree, RegionLocation.OUTSIDE,
401 Point1S.of(0.51 * PlaneAngleRadians.PI), Point1S.of(0.75 * PlaneAngleRadians.PI),
402 Point1S.of(0.99 * PlaneAngleRadians.PI));
403 }
404
405 @Test
406 public void testToConvex_lessThanPi() {
407
408 final AngularInterval interval = AngularInterval.of(0, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
409
410
411 final List<AngularInterval.Convex> result = interval.toConvex();
412
413
414 Assert.assertEquals(1, result.size());
415 checkInterval(interval, 0, PlaneAngleRadians.PI_OVER_TWO);
416 }
417
418 @Test
419 public void testToConvex_equalToPi() {
420
421 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI, TEST_PRECISION);
422
423
424 final List<AngularInterval.Convex> result = interval.toConvex();
425
426
427 Assert.assertEquals(1, result.size());
428 checkInterval(interval, PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI);
429 }
430
431 @Test
432 public void testToConvex_overPi() {
433
434 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
435
436
437 final List<AngularInterval.Convex> result = interval.toConvex();
438
439
440 Assert.assertEquals(2, result.size());
441 checkInterval(result.get(0), PlaneAngleRadians.PI, 1.75 * PlaneAngleRadians.PI);
442 checkInterval(result.get(1), 1.75 * PlaneAngleRadians.PI, 2.5 * PlaneAngleRadians.PI);
443 }
444
445 @Test
446 public void testToConvex_overPi_splitAtZero() {
447
448 final AngularInterval interval = AngularInterval.of(1.25 * PlaneAngleRadians.PI, 2.75 * PlaneAngleRadians.PI, TEST_PRECISION);
449
450
451 final List<AngularInterval.Convex> result = interval.toConvex();
452
453
454 Assert.assertEquals(2, result.size());
455 checkInterval(result.get(0), 1.25 * PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI);
456 checkInterval(result.get(1), PlaneAngleRadians.TWO_PI, 2.75 * PlaneAngleRadians.PI);
457 }
458
459 @Test
460 public void testSplit_full() {
461
462 final AngularInterval interval = AngularInterval.full();
463 final CutAngle pt = CutAngles.createNegativeFacing(PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
464
465
466 final Split<RegionBSPTree1S> split = interval.split(pt);
467
468
469 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
470
471 final RegionBSPTree1S minus = split.getMinus();
472 checkClassify(minus, RegionLocation.BOUNDARY, Point1S.of(PlaneAngleRadians.PI_OVER_TWO));
473 checkClassify(minus, RegionLocation.INSIDE,
474 Point1S.PI, Point1S.of(-PlaneAngleRadians.PI_OVER_TWO), Point1S.of(-0.25 * PlaneAngleRadians.PI));
475 checkClassify(minus, RegionLocation.OUTSIDE,
476 Point1S.ZERO, Point1S.of(0.25 * PlaneAngleRadians.PI));
477
478 final RegionBSPTree1S plus = split.getPlus();
479 checkClassify(plus, RegionLocation.BOUNDARY, Point1S.of(PlaneAngleRadians.PI_OVER_TWO));
480 checkClassify(plus, RegionLocation.INSIDE,
481 Point1S.ZERO, Point1S.of(0.25 * PlaneAngleRadians.PI));
482 checkClassify(plus, RegionLocation.OUTSIDE,
483 Point1S.PI, Point1S.of(-PlaneAngleRadians.PI_OVER_TWO), Point1S.of(-0.25 * PlaneAngleRadians.PI));
484 }
485
486 @Test
487 public void testSplit_interval_both() {
488
489 final AngularInterval interval = AngularInterval.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI, TEST_PRECISION);
490 final CutAngle cut = CutAngles.createNegativeFacing(0.75 * PlaneAngleRadians.PI, TEST_PRECISION);
491
492
493 final Split<RegionBSPTree1S> split = interval.split(cut);
494
495
496 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
497
498 final RegionBSPTree1S minus = split.getMinus();
499 checkClassify(minus, RegionLocation.BOUNDARY, Point1S.of(PlaneAngleRadians.PI), cut.getPoint());
500 checkClassify(minus, RegionLocation.INSIDE, Point1S.of(0.8 * PlaneAngleRadians.PI));
501 checkClassify(minus, RegionLocation.OUTSIDE,
502 Point1S.ZERO, Point1S.of(PlaneAngleRadians.TWO_PI), Point1S.of(-PlaneAngleRadians.PI_OVER_TWO),
503 Point1S.of(0.7 * PlaneAngleRadians.PI));
504
505 final RegionBSPTree1S plus = split.getPlus();
506 checkClassify(plus, RegionLocation.BOUNDARY, Point1S.of(PlaneAngleRadians.PI_OVER_TWO), cut.getPoint());
507 checkClassify(plus, RegionLocation.INSIDE, Point1S.of(0.6 * PlaneAngleRadians.PI));
508 checkClassify(plus, RegionLocation.OUTSIDE,
509 Point1S.ZERO, Point1S.of(PlaneAngleRadians.TWO_PI), Point1S.of(-PlaneAngleRadians.PI_OVER_TWO),
510 Point1S.of(0.8 * PlaneAngleRadians.PI));
511 }
512
513 @Test
514 public void testToString() {
515
516 final AngularInterval interval = AngularInterval.of(1, 2, TEST_PRECISION);
517
518
519 final String str = interval.toString();
520
521
522 Assert.assertTrue(str.contains("AngularInterval"));
523 Assert.assertTrue(str.contains("min= 1.0"));
524 Assert.assertTrue(str.contains("max= 2.0"));
525 }
526
527 @Test
528 public void testConvex_of_doubles() {
529
530 checkInterval(AngularInterval.Convex.of(0, 1, TEST_PRECISION), 0, 1);
531 checkInterval(AngularInterval.Convex.of(0, PlaneAngleRadians.PI, TEST_PRECISION), 0, PlaneAngleRadians.PI);
532 checkInterval(AngularInterval.Convex.of(PlaneAngleRadians.PI + 2, 1, TEST_PRECISION), PlaneAngleRadians.PI + 2, PlaneAngleRadians.TWO_PI + 1);
533 checkInterval(AngularInterval.Convex.of(-2, -1.5, TEST_PRECISION), -2, -1.5);
534
535 checkFull(AngularInterval.Convex.of(1, 1, TEST_PRECISION));
536 checkFull(AngularInterval.Convex.of(0, 1e-11, TEST_PRECISION));
537 checkFull(AngularInterval.Convex.of(0, -1e-11, TEST_PRECISION));
538 checkFull(AngularInterval.Convex.of(0, PlaneAngleRadians.TWO_PI, TEST_PRECISION));
539 }
540
541 @Test
542 public void testConvex_of_doubles_invalidArgs() {
543
544 GeometryTestUtils.assertThrows(() -> {
545 AngularInterval.Convex.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION);
546 }, IllegalArgumentException.class);
547
548 GeometryTestUtils.assertThrows(() -> {
549 AngularInterval.Convex.of(0, PlaneAngleRadians.PI + 1e-1, TEST_PRECISION);
550 }, IllegalArgumentException.class);
551
552 GeometryTestUtils.assertThrows(() -> {
553 AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO + 1, TEST_PRECISION);
554 }, IllegalArgumentException.class);
555
556 GeometryTestUtils.assertThrows(() -> {
557 AngularInterval.Convex.of(0, -0.5, TEST_PRECISION);
558 }, IllegalArgumentException.class);
559 }
560
561 @Test
562 public void testConvex_of_points() {
563
564 checkInterval(AngularInterval.Convex.of(Point1S.of(0), Point1S.of(1), TEST_PRECISION), 0, 1);
565 checkInterval(AngularInterval.Convex.of(Point1S.of(0), Point1S.of(PlaneAngleRadians.PI), TEST_PRECISION),
566 0, PlaneAngleRadians.PI);
567 checkInterval(AngularInterval.Convex.of(Point1S.of(PlaneAngleRadians.PI + 2), Point1S.of(1), TEST_PRECISION),
568 PlaneAngleRadians.PI + 2, PlaneAngleRadians.TWO_PI + 1);
569 checkInterval(AngularInterval.Convex.of(Point1S.of(-2), Point1S.of(-1.5), TEST_PRECISION), -2, -1.5);
570
571 checkFull(AngularInterval.Convex.of(Point1S.of(1), Point1S.of(1), TEST_PRECISION));
572 checkFull(AngularInterval.Convex.of(Point1S.of(0), Point1S.of(1e-11), TEST_PRECISION));
573 checkFull(AngularInterval.Convex.of(Point1S.of(0), Point1S.of(-1e-11), TEST_PRECISION));
574 checkFull(AngularInterval.Convex.of(Point1S.of(0), Point1S.of(PlaneAngleRadians.TWO_PI), TEST_PRECISION));
575 }
576
577 @Test
578 public void testConvex_of_points_invalidArgs() {
579
580 GeometryTestUtils.assertThrows(() -> {
581 AngularInterval.Convex.of(Point1S.of(Double.NEGATIVE_INFINITY),
582 Point1S.of(Double.POSITIVE_INFINITY), TEST_PRECISION);
583 }, IllegalArgumentException.class);
584
585 GeometryTestUtils.assertThrows(() -> {
586 AngularInterval.Convex.of(Point1S.of(0), Point1S.of(PlaneAngleRadians.PI + 1e-1), TEST_PRECISION);
587 }, IllegalArgumentException.class);
588
589 GeometryTestUtils.assertThrows(() -> {
590 AngularInterval.Convex.of(Point1S.of(PlaneAngleRadians.PI_OVER_TWO),
591 Point1S.of(-PlaneAngleRadians.PI_OVER_TWO + 1), TEST_PRECISION);
592 }, IllegalArgumentException.class);
593
594 GeometryTestUtils.assertThrows(() -> {
595 AngularInterval.Convex.of(Point1S.of(0), Point1S.of(-0.5), TEST_PRECISION);
596 }, IllegalArgumentException.class);
597 }
598
599 @Test
600 public void testConvex_of_cutAngles() {
601
602 final DoublePrecisionContext precisionA = new EpsilonDoublePrecisionContext(1e-3);
603 final DoublePrecisionContext precisionB = new EpsilonDoublePrecisionContext(1e-2);
604
605 final CutAngle zeroPos = CutAngles.createPositiveFacing(Point1S.ZERO, precisionA);
606 final CutAngle zeroNeg = CutAngles.createNegativeFacing(Point1S.ZERO, precisionA);
607
608 final CutAngle piPos = CutAngles.createPositiveFacing(Point1S.PI, precisionA);
609 final CutAngle piNeg = CutAngles.createNegativeFacing(Point1S.PI, precisionA);
610
611 final CutAngle almostPiPos = CutAngles.createPositiveFacing(Point1S.of(PlaneAngleRadians.PI + 5e-3), precisionB);
612
613
614 checkInterval(AngularInterval.Convex.of(zeroNeg, piPos), 0, PlaneAngleRadians.PI);
615 checkInterval(AngularInterval.Convex.of(zeroPos, piNeg), PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI);
616
617 checkFull(AngularInterval.Convex.of(zeroPos, zeroNeg));
618 checkFull(AngularInterval.Convex.of(zeroPos, piPos));
619 checkFull(AngularInterval.Convex.of(piNeg, zeroNeg));
620
621 checkFull(AngularInterval.Convex.of(almostPiPos, piNeg));
622 checkFull(AngularInterval.Convex.of(piNeg, almostPiPos));
623 }
624
625 @Test
626 public void testConvex_of_cutAngles_invalidArgs() {
627
628 final CutAngle pt = CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION);
629 final CutAngle nan = CutAngles.createPositiveFacing(Point1S.NaN, TEST_PRECISION);
630
631
632 GeometryTestUtils.assertThrows(() -> {
633 AngularInterval.Convex.of(pt, nan);
634 }, IllegalArgumentException.class);
635
636 GeometryTestUtils.assertThrows(() -> {
637 AngularInterval.Convex.of(nan, pt);
638 }, IllegalArgumentException.class);
639
640 GeometryTestUtils.assertThrows(() -> {
641 AngularInterval.Convex.of(nan, nan);
642 }, IllegalArgumentException.class);
643
644 GeometryTestUtils.assertThrows(() -> {
645 AngularInterval.Convex.of(
646 CutAngles.createNegativeFacing(1, TEST_PRECISION),
647 CutAngles.createPositiveFacing(0.5, TEST_PRECISION));
648 }, IllegalArgumentException.class);
649 }
650
651 @Test
652 public void testConvex_toConvex() {
653
654 final AngularInterval.Convex full = AngularInterval.full();
655 final AngularInterval.Convex interval = AngularInterval.Convex.of(0, 1, TEST_PRECISION);
656
657 List<AngularInterval.Convex> result;
658
659
660 result = full.toConvex();
661 Assert.assertEquals(1, result.size());
662 Assert.assertSame(full, result.get(0));
663
664 result = interval.toConvex();
665 Assert.assertEquals(1, result.size());
666 Assert.assertSame(interval, result.get(0));
667 }
668
669 @Test
670 public void testSplitDiameter_full() {
671
672 final AngularInterval.Convex full = AngularInterval.full();
673 final CutAngle splitter = CutAngles.createPositiveFacing(Point1S.of(PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION);
674
675
676 final Split<AngularInterval.Convex> split = full.splitDiameter(splitter);
677
678
679 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
680
681 checkInterval(split.getMinus(), 1.5 * PlaneAngleRadians.PI, 2.5 * PlaneAngleRadians.PI);
682 checkInterval(split.getPlus(), 0.5 * PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
683 }
684
685 @Test
686 public void testSplitDiameter_full_splitOnZero() {
687
688 final AngularInterval.Convex full = AngularInterval.full();
689 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION);
690
691
692 final Split<AngularInterval.Convex> split = full.splitDiameter(splitter);
693
694
695 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
696
697 checkInterval(split.getMinus(), 0, PlaneAngleRadians.PI);
698 checkInterval(split.getPlus(), PlaneAngleRadians.PI, PlaneAngleRadians.TWO_PI);
699 }
700
701 @Test
702 public void testSplitDiameter_minus() {
703
704 final AngularInterval.Convex interval = AngularInterval.Convex.of(0.1, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
705 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION);
706
707
708 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
709
710
711 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
712
713 Assert.assertSame(interval, split.getMinus());
714 Assert.assertNull(split.getPlus());
715 }
716
717 @Test
718 public void testSplitDiameter_plus() {
719
720 final AngularInterval.Convex interval = AngularInterval.Convex.of(-0.4 * PlaneAngleRadians.PI, 0.4 * PlaneAngleRadians.PI, TEST_PRECISION);
721 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.of(PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION);
722
723
724 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
725
726
727 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
728
729 Assert.assertNull(split.getMinus());
730 Assert.assertSame(interval, split.getPlus());
731 }
732
733 @Test
734 public void testSplitDiameter_both_negativeFacingSplitter() {
735
736 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
737 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.of(PlaneAngleRadians.PI), TEST_PRECISION);
738
739
740 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
741
742
743 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
744
745 checkInterval(split.getMinus(), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
746 checkInterval(split.getPlus(), PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI);
747 }
748
749 @Test
750 public void testSplitDiameter_both_positiveFacingSplitter() {
751
752 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
753 final CutAngle splitter = CutAngles.createPositiveFacing(Point1S.of(PlaneAngleRadians.PI), TEST_PRECISION);
754
755
756 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
757
758
759 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
760
761 checkInterval(split.getMinus(), PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI);
762 checkInterval(split.getPlus(), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
763 }
764
765 @Test
766 public void testSplitDiameter_both_antipodal_negativeFacingSplitter() {
767
768 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
769 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION);
770
771
772 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
773
774
775 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
776
777 checkInterval(split.getMinus(), PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI);
778 checkInterval(split.getPlus(), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
779 }
780
781 @Test
782 public void testSplitDiameter_both_antipodal_positiveFacingSplitter() {
783
784 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
785 final CutAngle splitter = CutAngles.createPositiveFacing(Point1S.ZERO, TEST_PRECISION);
786
787
788 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
789
790
791 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
792
793 checkInterval(split.getMinus(), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
794 checkInterval(split.getPlus(), PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI);
795 }
796
797 @Test
798 public void testSplitDiameter_splitOnBoundary_negativeFacing() {
799
800 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
801 final CutAngle splitter = CutAngles.createNegativeFacing(Point1S.of(PlaneAngleRadians.PI_OVER_TWO), TEST_PRECISION);
802
803
804 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
805
806
807 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
808
809 Assert.assertSame(interval, split.getMinus());
810 Assert.assertNull(split.getPlus());
811 }
812
813 @Test
814 public void testSplitDiameter_splitOnBoundary_positiveFacing() {
815
816 final AngularInterval.Convex interval = AngularInterval.Convex.of(0, PlaneAngleRadians.PI, TEST_PRECISION);
817 final CutAngle splitter = CutAngles.createPositiveFacing(Point1S.of(PlaneAngleRadians.PI), TEST_PRECISION);
818
819
820 final Split<AngularInterval.Convex> split = interval.splitDiameter(splitter);
821
822
823 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
824
825 Assert.assertSame(interval, split.getMinus());
826 Assert.assertNull(split.getPlus());
827 }
828
829 @Test
830 public void testConvex_transform() {
831
832 final AngularInterval.Convex interval = AngularInterval.Convex.of(PlaneAngleRadians.PI_OVER_TWO, PlaneAngleRadians.PI, TEST_PRECISION);
833
834 final Transform1S rotate = Transform1S.createRotation(PlaneAngleRadians.PI_OVER_TWO);
835 final Transform1S invert = Transform1S.createNegation().rotate(PlaneAngleRadians.PI_OVER_TWO);
836
837
838 checkInterval(interval.transform(rotate), PlaneAngleRadians.PI, 1.5 * PlaneAngleRadians.PI);
839 checkInterval(interval.transform(invert), -0.5 * PlaneAngleRadians.PI, 0.0);
840 }
841
842 private static void checkFull(final AngularInterval interval) {
843 Assert.assertTrue(interval.isFull());
844 Assert.assertFalse(interval.isEmpty());
845
846 Assert.assertNull(interval.getMinBoundary());
847 Assert.assertEquals(0, interval.getMin(), TEST_EPS);
848 Assert.assertNull(interval.getMaxBoundary());
849 Assert.assertEquals(PlaneAngleRadians.TWO_PI, interval.getMax(), TEST_EPS);
850
851 Assert.assertNull(interval.getCentroid());
852 Assert.assertNull(interval.getMidPoint());
853
854 Assert.assertEquals(PlaneAngleRadians.TWO_PI, interval.getSize(), TEST_EPS);
855 Assert.assertEquals(0, interval.getBoundarySize(), TEST_EPS);
856
857 checkClassify(interval, RegionLocation.INSIDE, Point1S.ZERO, Point1S.of(PlaneAngleRadians.PI));
858 }
859
860 private static void checkInterval(final AngularInterval interval, final double min, final double max) {
861
862 Assert.assertFalse(interval.isFull());
863 Assert.assertFalse(interval.isEmpty());
864
865 final CutAngle minBoundary = interval.getMinBoundary();
866 Assert.assertEquals(min, minBoundary.getAzimuth(), TEST_EPS);
867 Assert.assertFalse(minBoundary.isPositiveFacing());
868
869 final CutAngle maxBoundary = interval.getMaxBoundary();
870 Assert.assertEquals(max, maxBoundary.getAzimuth(), TEST_EPS);
871 Assert.assertTrue(maxBoundary.isPositiveFacing());
872
873 Assert.assertEquals(min, interval.getMin(), TEST_EPS);
874 Assert.assertEquals(max, interval.getMax(), TEST_EPS);
875
876 Assert.assertEquals(0.5 * (max + min), interval.getMidPoint().getAzimuth(), TEST_EPS);
877 Assert.assertSame(interval.getMidPoint(), interval.getCentroid());
878
879 Assert.assertEquals(0, interval.getBoundarySize(), TEST_EPS);
880 Assert.assertEquals(max - min, interval.getSize(), TEST_EPS);
881
882 checkClassify(interval, RegionLocation.INSIDE, interval.getMidPoint());
883 checkClassify(interval, RegionLocation.BOUNDARY,
884 interval.getMinBoundary().getPoint(), interval.getMaxBoundary().getPoint());
885 checkClassify(interval, RegionLocation.OUTSIDE, Point1S.of(interval.getMidPoint().getAzimuth() + PlaneAngleRadians.PI));
886 }
887
888 private static void checkClassify(final Region<Point1S> region, final RegionLocation loc, final Point1S... pts) {
889 for (final Point1S pt : pts) {
890 Assert.assertEquals("Unexpected location for point " + pt, loc, region.classify(pt));
891 }
892 }
893 }