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