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.euclidean.twod;
18  
19  import org.apache.commons.geometry.core.GeometryTestUtils;
20  import org.apache.commons.geometry.core.RegionLocation;
21  import org.apache.commons.geometry.core.partitioning.Split;
22  import org.apache.commons.geometry.core.partitioning.SplitLocation;
23  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
24  import org.apache.commons.geometry.euclidean.oned.Interval;
25  import org.apache.commons.numbers.core.Precision;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.Test;
28  
29  class ReverseRayTest {
30  
31      private static final double TEST_EPS = 1e-10;
32  
33      private static final Precision.DoubleEquivalence TEST_PRECISION =
34              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
35  
36      @Test
37      void testFromPointAndDirection() {
38          // arrange
39          final Vector2D p0 = Vector2D.of(1, 2);
40          final Vector2D p1 = Vector2D.of(2, 2);
41  
42          // act
43          final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(p0, p0.vectorTo(p1), TEST_PRECISION);
44  
45          // assert
46          Assertions.assertFalse(revRay.isFull());
47          Assertions.assertFalse(revRay.isEmpty());
48          Assertions.assertTrue(revRay.isInfinite());
49          Assertions.assertFalse(revRay.isFinite());
50  
51          Assertions.assertNull(revRay.getStartPoint());
52          EuclideanTestUtils.assertCoordinatesEqual(p0, revRay.getEndPoint(), TEST_EPS);
53  
54          GeometryTestUtils.assertNegativeInfinity(revRay.getSubspaceStart());
55          Assertions.assertEquals(1, revRay.getSubspaceEnd(), TEST_EPS);
56  
57          GeometryTestUtils.assertPositiveInfinity(revRay.getSize());
58          Assertions.assertNull(revRay.getCentroid());
59          Assertions.assertNull(revRay.getBounds());
60      }
61  
62      @Test
63      void testFromPointAndDirection_invalidArgs() {
64          // arrange
65          final Vector2D p = Vector2D.of(0, 2);
66          final Vector2D d = Vector2D.of(1e-17, -1e-12);
67  
68          // act/assert
69          GeometryTestUtils.assertThrowsWithMessage(() -> {
70              Lines.reverseRayFromPointAndDirection(p, d, TEST_PRECISION);
71          }, IllegalArgumentException.class, "Line direction cannot be zero");
72      }
73  
74      @Test
75      void testFromPoint() {
76          // arrange
77          final Vector2D p0 = Vector2D.of(1, 1);
78          final Vector2D p1 = Vector2D.of(1, 2);
79          final Vector2D p3 = Vector2D.of(3, 3);
80  
81          final Line line = Lines.fromPoints(p0, p1, TEST_PRECISION);
82  
83          // act
84          final ReverseRay revRay = Lines.reverseRayFromPoint(line, p3);
85  
86          // assert
87          Assertions.assertFalse(revRay.isFull());
88          Assertions.assertFalse(revRay.isEmpty());
89          Assertions.assertTrue(revRay.isInfinite());
90          Assertions.assertFalse(revRay.isFinite());
91  
92          Assertions.assertNull(revRay.getStartPoint());
93          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 3), revRay.getEndPoint(), TEST_EPS);
94  
95          GeometryTestUtils.assertNegativeInfinity(revRay.getSubspaceStart());
96          Assertions.assertEquals(3, revRay.getSubspaceEnd(), TEST_EPS);
97  
98          GeometryTestUtils.assertPositiveInfinity(revRay.getSize());
99          Assertions.assertNull(revRay.getCentroid());
100         Assertions.assertNull(revRay.getBounds());
101     }
102 
103     @Test
104     void testFromPoint_invalidArgs() {
105         // arrange
106         final Vector2D p = Vector2D.of(0, 2);
107         final Vector2D d = Vector2D.of(1, 1);
108         final Line line = Lines.fromPointAndDirection(p, d, TEST_PRECISION);
109 
110         // act/assert
111         GeometryTestUtils.assertThrowsWithMessage(() -> {
112             Lines.reverseRayFromPoint(line, Vector2D.NaN);
113         }, IllegalArgumentException.class, "Invalid reverse ray end point: (NaN, NaN)");
114 
115         GeometryTestUtils.assertThrowsWithMessage(() -> {
116             Lines.reverseRayFromPoint(line, Vector2D.POSITIVE_INFINITY);
117         }, IllegalArgumentException.class, "Invalid reverse ray end point: (Infinity, Infinity)");
118 
119         GeometryTestUtils.assertThrowsWithMessage(() -> {
120             Lines.reverseRayFromPoint(line, Vector2D.NEGATIVE_INFINITY);
121         }, IllegalArgumentException.class, "Invalid reverse ray end point: (-Infinity, -Infinity)");
122     }
123 
124     @Test
125     void testFromLocation() {
126         // arrange
127         final Vector2D p0 = Vector2D.of(1, 1);
128         final Vector2D p1 = Vector2D.of(1, 2);
129 
130         final Line line = Lines.fromPoints(p0, p1, TEST_PRECISION);
131 
132         // act
133         final ReverseRay revRay = Lines.reverseRayFromLocation(line, -2);
134 
135         // assert
136         Assertions.assertFalse(revRay.isFull());
137         Assertions.assertFalse(revRay.isEmpty());
138         Assertions.assertTrue(revRay.isInfinite());
139         Assertions.assertFalse(revRay.isFinite());
140 
141         Assertions.assertNull(revRay.getStartPoint());
142         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, -2), revRay.getEndPoint(), TEST_EPS);
143 
144         GeometryTestUtils.assertNegativeInfinity(revRay.getSubspaceStart());
145         Assertions.assertEquals(-2, revRay.getSubspaceEnd(), TEST_EPS);
146 
147         GeometryTestUtils.assertPositiveInfinity(revRay.getSize());
148         Assertions.assertNull(revRay.getCentroid());
149         Assertions.assertNull(revRay.getBounds());
150     }
151 
152     @Test
153     void testFromLocation_invalidArgs() {
154         // arrange
155         final Vector2D p = Vector2D.of(0, 2);
156         final Vector2D d = Vector2D.of(1, 1);
157         final Line line = Lines.fromPointAndDirection(p, d, TEST_PRECISION);
158 
159         // act/assert
160         GeometryTestUtils.assertThrowsWithMessage(() -> {
161             Lines.reverseRayFromLocation(line, Double.NaN);
162         }, IllegalArgumentException.class, "Invalid reverse ray end location: NaN");
163 
164         GeometryTestUtils.assertThrowsWithMessage(() -> {
165             Lines.reverseRayFromLocation(line, Double.POSITIVE_INFINITY);
166         }, IllegalArgumentException.class, "Invalid reverse ray end location: Infinity");
167 
168         GeometryTestUtils.assertThrowsWithMessage(() -> {
169             Lines.reverseRayFromLocation(line, Double.NEGATIVE_INFINITY);
170         }, IllegalArgumentException.class, "Invalid reverse ray end location: -Infinity");
171     }
172 
173     @Test
174     void testTransform() {
175         // arrange
176         final AffineTransformMatrix2D t = AffineTransformMatrix2D.createRotation(-0.5 * Math.PI)
177                 .translate(Vector2D.Unit.PLUS_X);
178 
179         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(Vector2D.of(1, 0), Vector2D.Unit.PLUS_X, TEST_PRECISION);
180 
181         // act
182         final ReverseRay result = revRay.transform(t);
183 
184         // assert
185         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, -1), result.getEndPoint(), TEST_EPS);
186     }
187 
188     @Test
189     void testTransform_reflection() {
190         // arrange
191         final AffineTransformMatrix2D t = AffineTransformMatrix2D.createRotation(0.5 * Math.PI)
192                 .translate(Vector2D.Unit.PLUS_X)
193                 .scale(1, -1);
194 
195         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(Vector2D.of(2, 3),
196                 Vector2D.Unit.PLUS_X, TEST_PRECISION);
197 
198         // act
199         final ReverseRay result = revRay.transform(t);
200 
201         // assert
202         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, -2), result.getEndPoint(), TEST_EPS);
203     }
204 
205     @Test
206     void testReverse() {
207         // arrange
208         final Vector2D start = Vector2D.of(1, 2);
209 
210         EuclideanTestUtils.permuteSkipZero(-4, 4, 1, (x, y) -> {
211             final Vector2D dir = Vector2D.of(x, y);
212 
213             final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(start, dir, TEST_PRECISION);
214 
215             // act
216             final Ray rev = revRay.reverse();
217 
218             // assert
219             EuclideanTestUtils.assertCoordinatesEqual(revRay.getLine().getOrigin(), rev.getLine().getOrigin(), TEST_EPS);
220             Assertions.assertEquals(-1, revRay.getLine().getDirection().dot(rev.getLine().getDirection()), TEST_EPS);
221 
222             EuclideanTestUtils.assertCoordinatesEqual(revRay.getEndPoint(), rev.getStartPoint(), TEST_EPS);
223         });
224     }
225 
226     @Test
227     void testClosest() {
228         // arrange
229         final Vector2D p1 = Vector2D.of(0, -1);
230         final Vector2D p2 = Vector2D.of(0, 1);
231         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(p2, p1.directionTo(p2), TEST_PRECISION);
232 
233         // act/assert
234         EuclideanTestUtils.assertCoordinatesEqual(p1, revRay.closest(p1), TEST_EPS);
235         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -2), revRay.closest(Vector2D.of(0, -2)), TEST_EPS);
236         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -2), revRay.closest(Vector2D.of(2, -2)), TEST_EPS);
237         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -1), revRay.closest(Vector2D.of(-1, -1)), TEST_EPS);
238 
239         EuclideanTestUtils.assertCoordinatesEqual(p2, revRay.closest(p2), TEST_EPS);
240         EuclideanTestUtils.assertCoordinatesEqual(p2, revRay.closest(Vector2D.of(0, 2)), TEST_EPS);
241         EuclideanTestUtils.assertCoordinatesEqual(p2, revRay.closest(Vector2D.of(-2, 2)), TEST_EPS);
242         EuclideanTestUtils.assertCoordinatesEqual(p2, revRay.closest(Vector2D.of(-1, 1)), TEST_EPS);
243 
244         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, revRay.closest(Vector2D.ZERO), TEST_EPS);
245         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 0.5), revRay.closest(Vector2D.of(1, 0.5)), TEST_EPS);
246         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -0.5), revRay.closest(Vector2D.of(-2, -0.5)), TEST_EPS);
247     }
248 
249     @Test
250     void testClassify() {
251         // arrange
252         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(Vector2D.of(1, 1),
253                 Vector2D.Unit.PLUS_X, TEST_PRECISION);
254 
255         // act/assert
256         EuclideanTestUtils.assertRegionLocation(revRay, RegionLocation.OUTSIDE,
257                 Vector2D.of(2, 2), Vector2D.of(2, 0),
258                 Vector2D.of(2, 1), Vector2D.of(5, 1));
259 
260         EuclideanTestUtils.assertRegionLocation(revRay, RegionLocation.BOUNDARY,
261                 Vector2D.of(1, 1), Vector2D.of(1 + 1e-16, 1));
262 
263         EuclideanTestUtils.assertRegionLocation(revRay, RegionLocation.INSIDE,
264                 Vector2D.of(-2, 1), Vector2D.of(-5, 1 + 1e-16));
265     }
266 
267     @Test
268     void testSplit() {
269         // --- arrange
270         final Vector2D p0 = Vector2D.of(1, 1);
271         final Vector2D p1 = Vector2D.of(-3, 1);
272         final Vector2D high = Vector2D.of(2, 1);
273 
274         final Vector2D delta = Vector2D.of(1e-11, 1e-11);
275 
276         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(p0, Vector2D.Unit.PLUS_X, TEST_PRECISION);
277 
278         // --- act
279 
280         // parallel
281         checkSplit(revRay.split(Lines.fromPointAndAngle(Vector2D.of(2, 2), 0, TEST_PRECISION)),
282                 null, null,
283                 null, p0);
284         checkSplit(revRay.split(Lines.fromPointAndAngle(Vector2D.of(2, 2), Math.PI, TEST_PRECISION)),
285                 null, p0,
286                 null, null);
287 
288         // coincident
289         checkSplit(revRay.split(Lines.fromPointAndAngle(p0.add(delta), 1e-20, TEST_PRECISION)),
290                 null, null,
291                 null, null);
292 
293         // through point on revRay
294         checkSplit(revRay.split(Lines.fromPointAndAngle(p1, 1, TEST_PRECISION)),
295                 null, p1,
296                 p1, p0);
297         checkSplit(revRay.split(Lines.fromPointAndAngle(p1, -1, TEST_PRECISION)),
298                 p1, p0,
299                 null, p1);
300 
301         // through end point
302         checkSplit(revRay.split(Lines.fromPointAndAngle(p0.subtract(delta), 1, TEST_PRECISION)),
303                 null, p0,
304                 null, null);
305         checkSplit(revRay.split(Lines.fromPointAndAngle(p0.add(delta), -1, TEST_PRECISION)),
306                 null, null,
307                 null, p0);
308 
309         // intersection above end point
310         checkSplit(revRay.split(Lines.fromPointAndAngle(high, 1, TEST_PRECISION)),
311                 null, p0,
312                 null, null);
313         checkSplit(revRay.split(Lines.fromPointAndAngle(high, -1, TEST_PRECISION)),
314                 null, null,
315                 null, p0);
316     }
317 
318     @Test
319     void testSplit_smallAngle_pointOnSplitter() {
320         // arrange
321         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-5);
322 
323         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(
324                 Vector2D.of(1, 1e-6), Vector2D.of(-1, 1e-2), precision);
325 
326         final Line splitter = Lines.fromPointAndAngle(Vector2D.ZERO, 0, precision);
327 
328         // act
329         final Split<LineConvexSubset> split = revRay.split(splitter);
330 
331         // assert
332         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
333 
334         Assertions.assertNull(split.getMinus());
335         Assertions.assertSame(revRay, split.getPlus());
336     }
337 
338     @Test
339     void testGetInterval() {
340         // arrange
341         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(Vector2D.of(2, -1), Vector2D.Unit.PLUS_X, TEST_PRECISION);
342 
343         // act
344         final Interval interval = revRay.getInterval();
345 
346         // assert
347         GeometryTestUtils.assertNegativeInfinity(interval.getMin());
348         Assertions.assertEquals(2, interval.getMax(), TEST_EPS);
349 
350         Assertions.assertSame(revRay.getLine().getPrecision(), interval.getMaxBoundary().getPrecision());
351     }
352 
353     @Test
354     void testToString() {
355         // arrange
356         final ReverseRay revRay = Lines.reverseRayFromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
357 
358         // act
359         final String str = revRay.toString();
360 
361         // assert
362         GeometryTestUtils.assertContains("ReverseRay[direction= (1", str);
363         GeometryTestUtils.assertContains(", endPoint= (0", str);
364     }
365 
366     private static void checkSplit(final Split<LineConvexSubset> split, final Vector2D minusStart, final Vector2D minusEnd,
367                                    final Vector2D plusStart, final Vector2D plusEnd) {
368 
369         final LineConvexSubset minus = split.getMinus();
370         if (minusStart == null && minusEnd == null) {
371             Assertions.assertNull(minus);
372         } else {
373             checkPoint(minusStart, minus.getStartPoint());
374             checkPoint(minusEnd, minus.getEndPoint());
375         }
376 
377 
378         final LineConvexSubset plus = split.getPlus();
379         if (plusStart == null && plusEnd == null) {
380             Assertions.assertNull(plus);
381         } else {
382             checkPoint(plusStart, plus.getStartPoint());
383             checkPoint(plusEnd, plus.getEndPoint());
384         }
385     }
386 
387     private static void checkPoint(final Vector2D expected, final Vector2D pt) {
388         if (expected == null) {
389             Assertions.assertNull(pt);
390         } else {
391             EuclideanTestUtils.assertCoordinatesEqual(expected, pt, TEST_EPS);
392         }
393     }
394 }