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.RegionLocation;
23 import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
24 import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
25 import org.apache.commons.geometry.core.partitioning.Split;
26 import org.apache.commons.geometry.spherical.SphericalTestUtils;
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 CutAngleTest {
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 @Test
40 void testFromAzimuthAndDirection() {
41
42 checkCutAngle(CutAngles.fromAzimuthAndDirection(0.0, true, TEST_PRECISION),
43 0.0, true);
44 checkCutAngle(CutAngles.fromAzimuthAndDirection(Math.PI, true, TEST_PRECISION),
45 Math.PI, true);
46 checkCutAngle(CutAngles.fromAzimuthAndDirection(-Angle.PI_OVER_TWO, true, TEST_PRECISION),
47 -Angle.PI_OVER_TWO, true);
48
49 checkCutAngle(CutAngles.fromAzimuthAndDirection(0.0, false, TEST_PRECISION),
50 0.0, false);
51 checkCutAngle(CutAngles.fromAzimuthAndDirection(Math.PI, false, TEST_PRECISION),
52 Math.PI, false);
53 checkCutAngle(CutAngles.fromAzimuthAndDirection(-Angle.PI_OVER_TWO, false, TEST_PRECISION),
54 -Angle.PI_OVER_TWO, false);
55 }
56
57 @Test
58 void testFromPointAndDirection() {
59
60 final Point1S pt = Point1S.of(-Angle.PI_OVER_TWO);
61
62
63 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION),
64 0.0, true);
65 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION),
66 Math.PI, true);
67 checkCutAngle(CutAngles.fromPointAndDirection(pt, true, TEST_PRECISION),
68 -Angle.PI_OVER_TWO, true);
69
70 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION),
71 0.0, false);
72 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.PI, false, TEST_PRECISION),
73 Math.PI, false);
74 checkCutAngle(CutAngles.fromPointAndDirection(pt, false, TEST_PRECISION),
75 -Angle.PI_OVER_TWO, false);
76 }
77
78 @Test
79 void testCreatePositiveFacing() {
80
81 checkCutAngle(CutAngles.createPositiveFacing(Point1S.ZERO, TEST_PRECISION),
82 0.0, true);
83 checkCutAngle(CutAngles.createPositiveFacing(Point1S.PI, TEST_PRECISION),
84 Math.PI, true);
85 checkCutAngle(CutAngles.createPositiveFacing(-Angle.PI_OVER_TWO, TEST_PRECISION),
86 -Angle.PI_OVER_TWO, true);
87 }
88
89 @Test
90 void testCreateNegativeFacing() {
91
92 checkCutAngle(CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION),
93 0.0, false);
94 checkCutAngle(CutAngles.createNegativeFacing(Point1S.PI, TEST_PRECISION),
95 Math.PI, false);
96 checkCutAngle(CutAngles.createNegativeFacing(-Angle.PI_OVER_TWO, TEST_PRECISION),
97 -Angle.PI_OVER_TWO, false);
98 }
99
100 @Test
101 void testOffset() {
102
103 final CutAngle zeroPos = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
104 final CutAngle zeroNeg = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
105 final CutAngle negPiPos = CutAngles.createPositiveFacing(-Math.PI, TEST_PRECISION);
106
107 final CutAngle piNeg = CutAngles.createNegativeFacing(Math.PI, TEST_PRECISION);
108 final CutAngle twoAndAHalfPiPos = CutAngles.createPositiveFacing(2.5 * Math.PI, TEST_PRECISION);
109
110
111 checkOffset(zeroPos, 0, 0);
112 checkOffset(zeroPos, Angle.TWO_PI, 0);
113 checkOffset(zeroPos, 2.5 * Math.PI, Angle.PI_OVER_TWO);
114 checkOffset(zeroPos, Math.PI, Math.PI);
115 checkOffset(zeroPos, 3.5 * Math.PI, 1.5 * Math.PI);
116
117 checkOffset(zeroNeg, 0, 0);
118 checkOffset(zeroNeg, Angle.TWO_PI, 0);
119 checkOffset(zeroNeg, 2.5 * Math.PI, -Angle.PI_OVER_TWO);
120 checkOffset(zeroNeg, Math.PI, -Math.PI);
121 checkOffset(zeroNeg, 3.5 * Math.PI, -1.5 * Math.PI);
122
123 checkOffset(negPiPos, 0, -Math.PI);
124 checkOffset(negPiPos, Angle.TWO_PI, -Math.PI);
125 checkOffset(negPiPos, 2.5 * Math.PI, -Angle.PI_OVER_TWO);
126 checkOffset(negPiPos, Math.PI, 0);
127 checkOffset(negPiPos, 3.5 * Math.PI, Angle.PI_OVER_TWO);
128
129 checkOffset(piNeg, 0, Math.PI);
130 checkOffset(piNeg, Angle.TWO_PI, Math.PI);
131 checkOffset(piNeg, 2.5 * Math.PI, Angle.PI_OVER_TWO);
132 checkOffset(piNeg, Math.PI, 0);
133 checkOffset(piNeg, 3.5 * Math.PI, -Angle.PI_OVER_TWO);
134
135 checkOffset(twoAndAHalfPiPos, 0, -Angle.PI_OVER_TWO);
136 checkOffset(twoAndAHalfPiPos, Angle.TWO_PI, -Angle.PI_OVER_TWO);
137 checkOffset(twoAndAHalfPiPos, 2.5 * Math.PI, 0);
138 checkOffset(twoAndAHalfPiPos, Math.PI, Angle.PI_OVER_TWO);
139 checkOffset(twoAndAHalfPiPos, 3.5 * Math.PI, Math.PI);
140 }
141
142 @Test
143 void testClassify() {
144
145 final CutAngle zeroPos = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
146 final CutAngle zeroNeg = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
147 final CutAngle negPiPos = CutAngles.createPositiveFacing(-Math.PI, TEST_PRECISION);
148
149
150 checkClassify(zeroPos, HyperplaneLocation.ON,
151 0, 1e-16, -1e-16,
152 Angle.TWO_PI - 1e-11, Angle.TWO_PI + 1e-11);
153 checkClassify(zeroPos, HyperplaneLocation.PLUS,
154 0.5, 2.5 * Math.PI,
155 -0.5, -Angle.PI_OVER_TWO);
156
157 checkClassify(zeroNeg, HyperplaneLocation.ON,
158 0, 1e-16, -1e-16,
159 Angle.TWO_PI - 1e-11, Angle.TWO_PI + 1e-11);
160 checkClassify(zeroNeg, HyperplaneLocation.MINUS,
161 0.5, 2.5 * Math.PI,
162 -0.5, -Angle.PI_OVER_TWO);
163
164 checkClassify(negPiPos, HyperplaneLocation.ON, Math.PI, Math.PI + 1e-11);
165 checkClassify(negPiPos, HyperplaneLocation.MINUS, 0.5, 2.5 * Math.PI,
166 0, 1e-11, Angle.TWO_PI, Angle.TWO_PI - 1e-11);
167 checkClassify(negPiPos, HyperplaneLocation.PLUS, -0.5, -Angle.PI_OVER_TWO);
168 }
169
170 @Test
171 void testContains() {
172
173 final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
174
175
176 Assertions.assertFalse(pt.contains(Point1S.ZERO));
177 Assertions.assertFalse(pt.contains(Point1S.of(Angle.TWO_PI)));
178
179 Assertions.assertFalse(pt.contains(Point1S.of(Math.PI)));
180 Assertions.assertFalse(pt.contains(Point1S.of(0.25 * Math.PI)));
181 Assertions.assertFalse(pt.contains(Point1S.of(-0.25 * Math.PI)));
182
183 Assertions.assertTrue(pt.contains(Point1S.of(Angle.PI_OVER_TWO)));
184 Assertions.assertTrue(pt.contains(Point1S.of(Angle.PI_OVER_TWO + 1e-11)));
185 Assertions.assertTrue(pt.contains(Point1S.of(2.5 * Math.PI)));
186 Assertions.assertTrue(pt.contains(Point1S.of(-3.5 * Math.PI)));
187 }
188
189 @Test
190 void testReverse() {
191
192 final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
193
194
195 final CutAngle result = pt.reverse();
196
197
198 checkCutAngle(result, Angle.PI_OVER_TWO, true);
199 Assertions.assertSame(TEST_PRECISION, result.getPrecision());
200
201 checkCutAngle(result.reverse(), Angle.PI_OVER_TWO, false);
202 }
203
204 @Test
205 void testProject() {
206
207 final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
208
209
210 for (double az = -Angle.TWO_PI; az <= Angle.TWO_PI; az += 0.2) {
211 Assertions.assertEquals(Angle.PI_OVER_TWO, pt.project(Point1S.of(az)).getAzimuth(), TEST_EPS);
212 }
213 }
214
215 @Test
216 void testSimilarOrientation() {
217
218 final CutAngle a = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
219 final CutAngle b = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
220 final CutAngle c = CutAngles.createPositiveFacing(-Angle.PI_OVER_TWO, TEST_PRECISION);
221
222
223 Assertions.assertTrue(a.similarOrientation(a));
224 Assertions.assertFalse(a.similarOrientation(b));
225 Assertions.assertTrue(a.similarOrientation(c));
226 }
227
228 @Test
229 void testTransform_rotate() {
230
231 final Transform1S transform = Transform1S.createRotation(Angle.PI_OVER_TWO);
232
233
234 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION).transform(transform),
235 Angle.PI_OVER_TWO, true);
236 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION).transform(transform),
237 Angle.PI_OVER_TWO, false);
238
239 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(1.5 * Math.PI), true, TEST_PRECISION).transform(transform),
240 Angle.TWO_PI, true);
241 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(-Angle.PI_OVER_TWO), false, TEST_PRECISION).transform(transform),
242 0.0, false);
243 }
244
245 @Test
246 void testTransform_negate() {
247
248 final Transform1S transform = Transform1S.createNegation();
249
250
251 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION).transform(transform),
252 0.0, false);
253 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION).transform(transform),
254 0.0, true);
255
256 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(1.5 * Math.PI), true, TEST_PRECISION).transform(transform),
257 -1.5 * Math.PI, false);
258 checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(-Angle.PI_OVER_TWO), false, TEST_PRECISION).transform(transform),
259 Angle.PI_OVER_TWO, true);
260 }
261
262 @Test
263 void testSpan() {
264
265 final CutAngle pt = CutAngles.fromPointAndDirection(Point1S.of(1.0), false, TEST_PRECISION);
266
267
268 final HyperplaneConvexSubset<Point1S> result = pt.span();
269
270
271 Assertions.assertSame(pt, result.getHyperplane());
272 }
273
274 @Test
275 void testEq() {
276
277 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
278
279 final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
280
281 final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, precision);
282 final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, precision);
283 final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
284
285 final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
286 final CutAngle f = CutAngles.fromPointAndDirection(Point1S.of(Angle.TWO_PI), true, precision);
287 final CutAngle g = CutAngles.fromPointAndDirection(Point1S.of(1e-4), true, precision);
288 final CutAngle h = CutAngles.fromPointAndDirection(Point1S.of(-1e-4), true, precision);
289
290
291 Assertions.assertTrue(a.eq(a, precision));
292
293 Assertions.assertFalse(a.eq(b, precision));
294 Assertions.assertFalse(a.eq(c, precision));
295
296 Assertions.assertTrue(a.eq(d, precision));
297 Assertions.assertTrue(a.eq(e, precision));
298 Assertions.assertTrue(a.eq(f, precision));
299 Assertions.assertTrue(a.eq(g, precision));
300 Assertions.assertTrue(a.eq(h, precision));
301 }
302
303 @Test
304 void testHashCode() {
305
306 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
307
308 final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
309 final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION);
310 final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION);
311 final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
312 final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
313
314 final int hash = a.hashCode();
315
316
317 Assertions.assertEquals(hash, a.hashCode());
318
319 Assertions.assertNotEquals(hash, b.hashCode());
320 Assertions.assertNotEquals(hash, c.hashCode());
321 Assertions.assertNotEquals(hash, d.hashCode());
322
323 Assertions.assertEquals(hash, e.hashCode());
324 }
325
326 @Test
327 void testEquals() {
328
329 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
330
331 final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
332 final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION);
333 final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION);
334 final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
335 final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
336
337
338 GeometryTestUtils.assertSimpleEqualsCases(a);
339
340 Assertions.assertNotEquals(a, b);
341 Assertions.assertNotEquals(a, c);
342 Assertions.assertNotEquals(a, d);
343
344 Assertions.assertEquals(a, e);
345 }
346
347 @Test
348 void testToString() {
349
350 final CutAngle pt = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
351
352
353 final String str = pt.toString();
354
355
356 Assertions.assertTrue(str.startsWith("CutAngle["));
357 Assertions.assertTrue(str.contains("point= ") && str.contains("positiveFacing= "));
358 }
359
360 @Test
361 void testSubset_split() {
362
363 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
364
365 final CutAngle pt = CutAngles.createPositiveFacing(-1.5, precision);
366 final HyperplaneConvexSubset<Point1S> sub = pt.span();
367
368
369 checkSplit(sub, CutAngles.createPositiveFacing(1.0, precision), false, true);
370 checkSplit(sub, CutAngles.createPositiveFacing(-1.5 + 1e-2, precision), true, false);
371
372 checkSplit(sub, CutAngles.createNegativeFacing(1.0, precision), true, false);
373 checkSplit(sub, CutAngles.createNegativeFacing(-1.5 + 1e-2, precision), false, true);
374
375 checkSplit(sub, CutAngles.createNegativeFacing(-1.5, precision), false, false);
376 checkSplit(sub, CutAngles.createNegativeFacing(-1.5 + 1e-4, precision), false, false);
377 checkSplit(sub, CutAngles.createNegativeFacing(-1.5 - 1e-4, precision), false, false);
378 }
379
380 private void checkSplit(final HyperplaneConvexSubset<Point1S> sub, final CutAngle splitter, final boolean minus, final boolean plus) {
381 final Split<? extends HyperplaneConvexSubset<Point1S>> split = sub.split(splitter);
382
383 Assertions.assertSame(minus ? sub : null, split.getMinus());
384 Assertions.assertSame(plus ? sub : null, split.getPlus());
385 }
386
387 @Test
388 void testSubset_simpleMethods() {
389
390 final CutAngle pt = CutAngles.createPositiveFacing(1, TEST_PRECISION);
391 final HyperplaneConvexSubset<Point1S> sub = pt.span();
392
393
394 Assertions.assertSame(pt, sub.getHyperplane());
395 Assertions.assertFalse(sub.isFull());
396 Assertions.assertFalse(sub.isEmpty());
397 Assertions.assertFalse(sub.isInfinite());
398 Assertions.assertTrue(sub.isFinite());
399 Assertions.assertEquals(0.0, sub.getSize(), TEST_EPS);
400 SphericalTestUtils.assertPointsEqual(Point1S.of(1), sub.getCentroid(), TEST_EPS);
401
402 final List<? extends HyperplaneConvexSubset<Point1S>> list = sub.toConvex();
403 Assertions.assertEquals(1, list.size());
404 Assertions.assertSame(sub, list.get(0));
405 }
406
407 @Test
408 void testSubset_classify() {
409
410 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
411 final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
412 final HyperplaneConvexSubset<Point1S> sub = pt.span();
413
414
415 Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(0.95)));
416 Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(1)));
417 Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(1.05)));
418
419 Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(1.11)));
420 Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(0.89)));
421
422 Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(-3)));
423 Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(10)));
424 }
425
426 @Test
427 void testSubset_contains() {
428
429 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
430 final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
431 final HyperplaneConvexSubset<Point1S> sub = pt.span();
432
433
434 Assertions.assertTrue(sub.contains(Point1S.of(0.95)));
435 Assertions.assertTrue(sub.contains(Point1S.of(1)));
436 Assertions.assertTrue(sub.contains(Point1S.of(1.05)));
437
438 Assertions.assertFalse(sub.contains(Point1S.of(1.11)));
439 Assertions.assertFalse(sub.contains(Point1S.of(0.89)));
440
441 Assertions.assertFalse(sub.contains(Point1S.of(-3)));
442 Assertions.assertFalse(sub.contains(Point1S.of(10)));
443 }
444
445 @Test
446 void testSubset_closestContained() {
447
448 final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
449 final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
450 final HyperplaneConvexSubset<Point1S> sub = pt.span();
451
452 final Point1S expected = Point1S.of(1);
453
454
455 Assertions.assertEquals(expected, sub.closest(Point1S.ZERO));
456 Assertions.assertEquals(expected, sub.closest(Point1S.of(Angle.PI_OVER_TWO)));
457 Assertions.assertEquals(expected, sub.closest(Point1S.PI));
458 Assertions.assertEquals(expected, sub.closest(Point1S.of(-Angle.PI_OVER_TWO)));
459 Assertions.assertEquals(expected, sub.closest(Point1S.of(Angle.TWO_PI)));
460 }
461
462 @Test
463 void testSubset_transform() {
464
465 final CutAngle pt = CutAngles.fromPointAndDirection(Point1S.of(Angle.PI_OVER_TWO), true, TEST_PRECISION);
466
467 final Transform1S transform = Transform1S.createNegation().rotate(Math.PI);
468
469
470 final HyperplaneConvexSubset<Point1S> result = pt.span().transform(transform);
471
472
473 checkCutAngle((CutAngle) result.getHyperplane(), Angle.PI_OVER_TWO, false);
474 }
475
476 @Test
477 void testSubset_reverse() {
478
479 final CutAngle pt = CutAngles.createPositiveFacing(2.0, TEST_PRECISION);
480 final HyperplaneConvexSubset<Point1S> sub = pt.span();
481
482
483 final HyperplaneConvexSubset<Point1S> result = sub.reverse();
484
485
486 Assertions.assertEquals(2.0, ((CutAngle) result.getHyperplane()).getAzimuth(), TEST_EPS);
487 Assertions.assertFalse(((CutAngle) result.getHyperplane()).isPositiveFacing());
488
489 Assertions.assertEquals(sub.getHyperplane(), result.reverse().getHyperplane());
490 }
491
492 @Test
493 void testSubset_toString() {
494
495 final CutAngle pt = CutAngles.createPositiveFacing(2, TEST_PRECISION);
496 final HyperplaneConvexSubset<Point1S> sub = pt.span();
497
498
499 final String str = sub.toString();
500
501
502 Assertions.assertTrue(str.contains("CutAngleConvexSubset["));
503 Assertions.assertTrue(str.contains("point= "));
504 Assertions.assertTrue(str.contains("positiveFacing= "));
505 }
506
507 private static void checkCutAngle(final CutAngle angle, final double az, final boolean positiveFacing) {
508 checkCutAngle(angle, az, positiveFacing, TEST_PRECISION);
509 }
510
511 private static void checkCutAngle(final CutAngle angle, final double az, final boolean positiveFacing, final Precision.DoubleEquivalence precision) {
512 Assertions.assertEquals(az, angle.getAzimuth(), TEST_EPS);
513 Assertions.assertEquals(Angle.Rad.WITHIN_0_AND_2PI.applyAsDouble(az), angle.getNormalizedAzimuth(), TEST_EPS);
514 Assertions.assertEquals(az, angle.getPoint().getAzimuth(), TEST_EPS);
515 Assertions.assertEquals(positiveFacing, angle.isPositiveFacing());
516
517 Assertions.assertSame(precision, angle.getPrecision());
518 }
519
520 private static void checkOffset(final CutAngle pt, final double az, final double offset) {
521 Assertions.assertEquals(offset, pt.offset(Point1S.of(az)), TEST_EPS);
522 }
523
524 private static void checkClassify(final CutAngle pt, final HyperplaneLocation loc, final double... azimuths) {
525 for (final double az : azimuths) {
526 Assertions.assertEquals(loc, pt.classify(Point1S.of(az)), "Unexpected location for azimuth " + az);
527 }
528 }
529 }