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 }