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