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 }