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.euclidean.EuclideanTestUtils;
21  import org.apache.commons.geometry.euclidean.oned.Interval;
22  import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
23  import org.apache.commons.geometry.euclidean.threed.Vector3D;
24  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
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 Ray3DTest {
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 Vector3D pt = Vector3D.of(1, 1, 2);
40  
41          // act
42          final Ray3D ray = Lines3D.rayFromPointAndDirection(pt, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
43  
44          // assert
45          Assertions.assertTrue(ray.isInfinite());
46          Assertions.assertFalse(ray.isFinite());
47  
48          EuclideanTestUtils.assertCoordinatesEqual(pt, ray.getStartPoint(), TEST_EPS);
49          Assertions.assertNull(ray.getEndPoint());
50  
51          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, ray.getDirection(), TEST_EPS);
52  
53          Assertions.assertEquals(2, ray.getSubspaceStart(), TEST_EPS);
54          GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
55  
56          GeometryTestUtils.assertPositiveInfinity(ray.getSize());
57  
58          Assertions.assertNull(ray.getCentroid());
59          Assertions.assertNull(ray.getBounds());
60      }
61  
62      @Test
63      void testFromPointAndDirection_invalidArgs() {
64          // arrange
65          final Vector3D pt = Vector3D.of(0, 2, 4);
66          final Vector3D dir = Vector3D.of(1e-11, 0, 0);
67  
68          // act/assert
69          GeometryTestUtils.assertThrowsWithMessage(() -> {
70              Lines3D.rayFromPointAndDirection(pt, dir, TEST_PRECISION);
71          }, IllegalArgumentException.class, "Line direction cannot be zero");
72      }
73  
74      @Test
75      void testFromPoint() {
76          // arrange
77          final Vector3D pt = Vector3D.of(-2, -1, 2);
78  
79          final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(1, 0, 2), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
80  
81          // act
82          final Ray3D ray = Lines3D.rayFromPoint(line, pt);
83  
84          // assert
85          Assertions.assertTrue(ray.isInfinite());
86          Assertions.assertFalse(ray.isFinite());
87  
88          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, -1, 2), ray.getStartPoint(), TEST_EPS);
89          Assertions.assertNull(ray.getEndPoint());
90  
91          Assertions.assertEquals(-1, ray.getSubspaceStart(), TEST_EPS);
92          GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
93  
94          GeometryTestUtils.assertPositiveInfinity(ray.getSize());
95  
96          Assertions.assertNull(ray.getCentroid());
97          Assertions.assertNull(ray.getBounds());
98      }
99  
100     @Test
101     void testFromPoint_invalidArgs() {
102         // arrange
103         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
104 
105         // act/assert
106         GeometryTestUtils.assertThrowsWithMessage(() -> {
107             Lines3D.rayFromPoint(line, Vector3D.NaN);
108         }, IllegalArgumentException.class, "Invalid ray start location: NaN");
109 
110         GeometryTestUtils.assertThrowsWithMessage(() -> {
111             Lines3D.rayFromPoint(line, Vector3D.NEGATIVE_INFINITY);
112         }, IllegalArgumentException.class, "Invalid ray start location: NaN");
113 
114         GeometryTestUtils.assertThrowsWithMessage(() -> {
115             Lines3D.rayFromPoint(line, Vector3D.POSITIVE_INFINITY);
116         }, IllegalArgumentException.class, "Invalid ray start location: NaN");
117     }
118 
119     @Test
120     void testFromLocation() {
121         // arrange
122         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(-1, 0, 0), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
123 
124         // act
125         final Ray3D ray = Lines3D.rayFromLocation(line, -1);
126 
127         // assert
128         Assertions.assertTrue(ray.isInfinite());
129         Assertions.assertFalse(ray.isFinite());
130 
131         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), ray.getStartPoint(), TEST_EPS);
132         Assertions.assertNull(ray.getEndPoint());
133 
134         Assertions.assertEquals(-1, ray.getSubspaceStart(), TEST_EPS);
135         GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
136 
137         GeometryTestUtils.assertPositiveInfinity(ray.getSize());
138 
139         Assertions.assertNull(ray.getCentroid());
140         Assertions.assertNull(ray.getBounds());
141     }
142 
143     @Test
144     void testTransform() {
145         // arrange
146         final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
147                 .toMatrix()
148                 .translate(Vector3D.Unit.PLUS_Y);
149 
150         final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_X, TEST_PRECISION);
151 
152         // act
153         final Ray3D result = ray.transform(t);
154 
155         // assert
156         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, -1), result.getStartPoint(), TEST_EPS);
157         Assertions.assertNull(result.getEndPoint());
158 
159         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z, result.getDirection(), TEST_EPS);
160     }
161 
162     @Test
163     void testTransform_reflection() {
164         // arrange
165         final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
166                 .toMatrix()
167                 .translate(Vector3D.Unit.PLUS_Y)
168                 .scale(1, 1, -2);
169 
170         final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_X, TEST_PRECISION);
171 
172         // act
173         final Ray3D result = ray.transform(t);
174 
175         // assert
176         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 2), result.getStartPoint(), TEST_EPS);
177         Assertions.assertNull(result.getEndPoint());
178 
179         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, result.getDirection(), TEST_EPS);
180     }
181 
182     @Test
183     void testContains() {
184         // arrange
185         final Vector3D p0 = Vector3D.of(1, 1, 1);
186 
187         final Vector3D delta = Vector3D.of(1e-12, 1e-12, 1e-12);
188 
189         final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
190 
191         // act/assert
192         Assertions.assertFalse(ray.contains(Vector3D.of(2, 2, 2)));
193         Assertions.assertFalse(ray.contains(Vector3D.of(0.9, 1, 1)));
194         Assertions.assertFalse(ray.contains(Vector3D.of(-1, 1, 1)));
195 
196         Assertions.assertTrue(ray.contains(p0));
197         Assertions.assertTrue(ray.contains(p0.subtract(delta)));
198 
199         Assertions.assertTrue(ray.contains(Vector3D.of(1000, 1, 1)));
200     }
201 
202     @Test
203     void testGetInterval() {
204         // arrange
205         final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(2, -1, 3), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
206 
207         // act
208         final Interval interval = ray.getInterval();
209 
210         // assert
211         Assertions.assertEquals(-1, interval.getMin(), TEST_EPS);
212         GeometryTestUtils.assertPositiveInfinity(interval.getMax());
213 
214         Assertions.assertSame(ray.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
215     }
216 
217     @Test
218     void testToString() {
219         // arrange
220         final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
221 
222         // act
223         final String str = ray.toString();
224 
225         // assert
226         GeometryTestUtils.assertContains("Ray3D[startPoint= (0", str);
227         GeometryTestUtils.assertContains(", direction= (1", str);
228     }
229 }