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.threed.line;
    18
    19  import org.apache.commons.geometry.core.GeometryTestUtils;
    20  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
    21  import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
    22  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    23  import org.apache.commons.geometry.euclidean.oned.Interval;
    24  import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
    25  import org.apache.commons.geometry.euclidean.threed.Bounds3D;
    26  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    27  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
    28  import org.junit.Assert;
    29  import org.junit.Test;
    30
    31  public class Segment3DTest {
    32
    33      private static final double TEST_EPS = 1e-10;
    34
    35      private static final DoublePrecisionContext TEST_PRECISION =
    36              new EpsilonDoublePrecisionContext(TEST_EPS);
    37
    38      @Test
    39      public void testFromPoints() {
    40          // arrange
    41          final Vector3D p1 = Vector3D.of(1, 1, 2);
    42          final Vector3D p2 = Vector3D.of(1, 3, 2);
    43
    44          // act
    45          final Segment3D seg = Lines3D.segmentFromPoints(p1, p2, TEST_PRECISION);
    46
    47          // assert
    48          Assert.assertFalse(seg.isInfinite());
    49          Assert.assertTrue(seg.isFinite());
    50
    51          EuclideanTestUtils.assertCoordinatesEqual(p1, seg.getStartPoint(), TEST_EPS);
    52          EuclideanTestUtils.assertCoordinatesEqual(p2, seg.getEndPoint(), TEST_EPS);
    53
    54          Assert.assertEquals(1, seg.getSubspaceStart(), TEST_EPS);
    55          Assert.assertEquals(3, seg.getSubspaceEnd(), TEST_EPS);
    56
    57          Assert.assertEquals(2, seg.getSize(), TEST_EPS);
    58
    59          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 2), seg.getCentroid(), TEST_EPS);
    60          final Bounds3D bounds = seg.getBounds();
    61          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 2), bounds.getMin(), TEST_EPS);
    62          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 3, 2), bounds.getMax(), TEST_EPS);
    63      }
    64
    65      @Test
    66      public void testFromPoints_invalidArgs() {
    67          // arrange
    68          final Vector3D p1 = Vector3D.of(0, 2, 4);
    69          final Vector3D p2 = Vector3D.of(1e-17, 2, 4);
    70
    71          // act/assert
    72          GeometryTestUtils.assertThrows(() -> {
    73              Lines3D.segmentFromPoints(p1, p1, TEST_PRECISION);
    74          }, IllegalArgumentException.class, "Line direction cannot be zero");
    75
    76          GeometryTestUtils.assertThrows(() -> {
    77              Lines3D.segmentFromPoints(p1, p2, TEST_PRECISION);
    78          }, IllegalArgumentException.class, "Line direction cannot be zero");
    79      }
    80
    81      @Test
    82      public void testFromPoints_givenLine() {
    83          // arrange
    84          final Vector3D p1 = Vector3D.of(-1, -1, 2);
    85          final Vector3D p2 = Vector3D.of(3, 3, 3);
    86
    87          final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(1, 0, 2), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    88
    89          // act
    90          final Segment3D seg = Lines3D.segmentFromPoints(line, p2, p1); // reverse location order
    91
    92          // assert
    93          Assert.assertFalse(seg.isInfinite());
    94          Assert.assertTrue(seg.isFinite());
    95
    96          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, -1, 2), seg.getStartPoint(), TEST_EPS);
    97          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 3, 2), seg.getEndPoint(), TEST_EPS);
    98
    99          Assert.assertEquals(-1, seg.getSubspaceStart(), TEST_EPS);
    100         Assert.assertEquals(3, seg.getSubspaceEnd(), TEST_EPS);
    101
    102         Assert.assertEquals(4, seg.getSize(), TEST_EPS);
    103
    104         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 2), seg.getCentroid(), TEST_EPS);
    105         final Bounds3D bounds = seg.getBounds();
    106         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, -1, 2), bounds.getMin(), TEST_EPS);
    107         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 3, 2), bounds.getMax(), TEST_EPS);
    108     }
    109
    110     @Test
    111     public void testFromPoints_givenLine_singlePoint() {
    112         // arrange
    113         final Vector3D p1 = Vector3D.of(-1, 2, 0);
    114
    115         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    116
    117         // act
    118         final Segment3D seg = Lines3D.segmentFromPoints(line, p1, p1);
    119
    120         // assert
    121         Assert.assertFalse(seg.isInfinite());
    122         Assert.assertTrue(seg.isFinite());
    123
    124         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 0), seg.getStartPoint(), TEST_EPS);
    125         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 0), seg.getEndPoint(), TEST_EPS);
    126
    127         Assert.assertEquals(2, seg.getSubspaceStart(), TEST_EPS);
    128         Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
    129
    130         Assert.assertEquals(0, seg.getSize(), TEST_EPS);
    131
    132         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 0), seg.getCentroid(), TEST_EPS);
    133         final Bounds3D bounds = seg.getBounds();
    134         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 0), bounds.getMin(), TEST_EPS);
    135         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, 0), bounds.getMax(), TEST_EPS);
    136     }
    137
    138     @Test
    139     public void testFromPoints_givenLine_invalidArgs() {
    140         // arrange
    141         final Vector3D p0 = Vector3D.of(1, 0, 0);
    142         final Vector3D p1 = Vector3D.of(2, 0, 0);
    143
    144         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
    145
    146         // act/assert
    147         GeometryTestUtils.assertThrows(() -> {
    148             Lines3D.segmentFromPoints(line, Vector3D.NaN, p1);
    149         }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
    150
    151         GeometryTestUtils.assertThrows(() -> {
    152             Lines3D.segmentFromPoints(line, p0, Vector3D.NaN);
    153         }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
    154
    155         GeometryTestUtils.assertThrows(() -> {
    156             Lines3D.segmentFromPoints(line, Vector3D.NEGATIVE_INFINITY, p1);
    157         }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
    158
    159         GeometryTestUtils.assertThrows(() -> {
    160             Lines3D.segmentFromPoints(line, p0, Vector3D.POSITIVE_INFINITY);
    161         }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
    162     }
    163
    164     @Test
    165     public void testFromLocations() {
    166         // arrange
    167         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(-1, 0, 0), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    168
    169         // act
    170         final Segment3D seg = Lines3D.segmentFromLocations(line, -1, 2);
    171
    172         // assert
    173         Assert.assertFalse(seg.isInfinite());
    174         Assert.assertTrue(seg.isFinite());
    175
    176         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), seg.getStartPoint(), TEST_EPS);
    177         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 2), seg.getEndPoint(), TEST_EPS);
    178
    179         Assert.assertEquals(-1, seg.getSubspaceStart(), TEST_EPS);
    180         Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
    181
    182         Assert.assertEquals(3, seg.getSize(), TEST_EPS);
    183
    184         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 0.5), seg.getCentroid(), TEST_EPS);
    185         final Bounds3D bounds = seg.getBounds();
    186         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), bounds.getMin(), TEST_EPS);
    187         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 2), bounds.getMax(), TEST_EPS);
    188     }
    189
    190     @Test
    191     public void testFromLocations_reversedLocationOrder() {
    192         // arrange
    193         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(-1, 0, 1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    194
    195         // act
    196         final Segment3D seg = Lines3D.segmentFromLocations(line, 2, -1);
    197
    198         // assert
    199         Assert.assertFalse(seg.isInfinite());
    200         Assert.assertTrue(seg.isFinite());
    201
    202         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), seg.getStartPoint(), TEST_EPS);
    203         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 2), seg.getEndPoint(), TEST_EPS);
    204
    205         Assert.assertEquals(-1, seg.getSubspaceStart(), TEST_EPS);
    206         Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
    207
    208         Assert.assertEquals(3, seg.getSize(), TEST_EPS);
    209
    210         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 0.5), seg.getCentroid(), TEST_EPS);
    211         final Bounds3D bounds = seg.getBounds();
    212         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), bounds.getMin(), TEST_EPS);
    213         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 2), bounds.getMax(), TEST_EPS);
    214     }
    215
    216     @Test
    217     public void testFromLocations_singlePoint() {
    218         // arrange
    219         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(-1, 0, 0), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    220
    221         // act
    222         final Segment3D seg = Lines3D.segmentFromLocations(line, 1, 1);
    223
    224         // assert
    225         Assert.assertFalse(seg.isInfinite());
    226         Assert.assertTrue(seg.isFinite());
    227
    228         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 1), seg.getStartPoint(), TEST_EPS);
    229         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 1), seg.getEndPoint(), TEST_EPS);
    230
    231         Assert.assertEquals(1, seg.getSubspaceStart(), TEST_EPS);
    232         Assert.assertEquals(1, seg.getSubspaceEnd(), TEST_EPS);
    233
    234         Assert.assertEquals(0, seg.getSize(), TEST_EPS);
    235
    236         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 1), seg.getCentroid(), TEST_EPS);
    237         final Bounds3D bounds = seg.getBounds();
    238         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 1), bounds.getMin(), TEST_EPS);
    239         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, 1), bounds.getMax(), TEST_EPS);
    240     }
    241
    242     @Test
    243     public void testFromLocations_invalidArgs() {
    244         // arrange
    245         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.MINUS_Z, TEST_PRECISION);
    246
    247         // act/assert
    248         GeometryTestUtils.assertThrows(() -> {
    249             Lines3D.segmentFromLocations(line, Double.NaN, 2);
    250         }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
    251
    252         GeometryTestUtils.assertThrows(() -> {
    253             Lines3D.segmentFromLocations(line, 1, Double.NaN);
    254         }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
    255
    256         GeometryTestUtils.assertThrows(() -> {
    257             Lines3D.segmentFromLocations(line, Double.NEGATIVE_INFINITY, 2);
    258         }, IllegalArgumentException.class, "Invalid line segment locations: -Infinity, 2.0");
    259
    260         GeometryTestUtils.assertThrows(() -> {
    261             Lines3D.segmentFromLocations(line, 1, Double.POSITIVE_INFINITY);
    262         }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, Infinity");
    263     }
    264
    265     @Test
    266     public void testTransform() {
    267         // arrange
    268         final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
    269                 .toMatrix()
    270                 .translate(Vector3D.Unit.PLUS_Y);
    271
    272         final Segment3D seg = Lines3D.segmentFromPoints(Vector3D.of(1, 0, 0), Vector3D.of(2, 0, 0), TEST_PRECISION);
    273
    274         // act
    275         final Segment3D result = seg.transform(t);
    276
    277         // assert
    278         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, -1), result.getStartPoint(), TEST_EPS);
    279         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, -2), result.getEndPoint(), TEST_EPS);
    280     }
    281
    282     @Test
    283     public void testTransform_reflection() {
    284         // arrange
    285         final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
    286                 .toMatrix()
    287                 .translate(Vector3D.Unit.PLUS_Y)
    288                 .scale(1, 1, -2);
    289
    290         final Segment3D seg = Lines3D.segmentFromPoints(Vector3D.of(1, 0, 0), Vector3D.of(2, 0, 0), TEST_PRECISION);
    291
    292         // act
    293         final Segment3D result = seg.transform(t);
    294
    295         // assert
    296         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 2), result.getStartPoint(), TEST_EPS);
    297         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 4), result.getEndPoint(), TEST_EPS);
    298     }
    299
    300     @Test
    301     public void testContains() {
    302         // arrange
    303         final Vector3D p0 = Vector3D.of(1, 1, 1);
    304         final Vector3D p1 = Vector3D.of(3, 1, 1);
    305
    306         final Vector3D delta = Vector3D.of(1e-12, 1e-12, 1e-12);
    307
    308         final Segment3D seg = Lines3D.segmentFromPoints(Vector3D.of(1, 1, 1), Vector3D.of(3, 1, 1), TEST_PRECISION);
    309
    310         // act/assert
    311         Assert.assertFalse(seg.contains(Vector3D.of(2, 2, 2)));
    312         Assert.assertFalse(seg.contains(Vector3D.of(0.9, 1, 1)));
    313         Assert.assertFalse(seg.contains(Vector3D.of(3.1, 1, 1)));
    314
    315         Assert.assertTrue(seg.contains(p0));
    316         Assert.assertTrue(seg.contains(p1));
    317
    318         Assert.assertTrue(seg.contains(p0.subtract(delta)));
    319         Assert.assertTrue(seg.contains(p1.add(delta)));
    320
    321         Assert.assertTrue(seg.contains(p0.lerp(p1, 0.5)));
    322     }
    323
    324     @Test
    325     public void testGetInterval() {
    326         // arrange
    327         final Segment3D seg = Lines3D.segmentFromPoints(Vector3D.of(2, -1, 3), Vector3D.of(2, 2, 3), TEST_PRECISION);
    328
    329         // act
    330         final Interval interval = seg.getInterval();
    331
    332         // assert
    333         Assert.assertEquals(-1, interval.getMin(), TEST_EPS);
    334         Assert.assertEquals(2, interval.getMax(), TEST_EPS);
    335
    336         Assert.assertSame(seg.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
    337     }
    338
    339     @Test
    340     public void testGetInterval_singlePoint() {
    341         // arrange
    342         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
    343         final Segment3D seg = Lines3D.segmentFromLocations(line, 1, 1);
    344
    345         // act
    346         final Interval interval = seg.getInterval();
    347
    348         // assert
    349         Assert.assertEquals(1, interval.getMin(), TEST_EPS);
    350         Assert.assertEquals(1, interval.getMax(), TEST_EPS);
    351         Assert.assertEquals(0, interval.getSize(), TEST_EPS);
    352
    353         Assert.assertSame(seg.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
    354     }
    355
    356     @Test
    357     public void testToString() {
    358         // arrange
    359         final Segment3D seg = Lines3D.segmentFromPoints(Vector3D.ZERO, Vector3D.of(1, 0, 0), TEST_PRECISION);
    360
    361         // act
    362         final String str = seg.toString();
    363
    364         // assert
    365         GeometryTestUtils.assertContains("Segment3D[startPoint= (0", str);
    366         GeometryTestUtils.assertContains(", endPoint= (1", str);
    367     }
    368 }