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