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.Transform;
21  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
22  import org.apache.commons.geometry.euclidean.oned.Interval;
23  import org.apache.commons.geometry.euclidean.oned.Vector1D;
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.apache.commons.numbers.angle.Angle;
28  import org.apache.commons.numbers.core.Precision;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Test;
31  
32  class LineConvexSubset3DTest {
33  
34      private static final double TEST_EPS = 1e-10;
35  
36      private static final Precision.DoubleEquivalence TEST_PRECISION =
37              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
38  
39      @Test
40      void testFromInterval_intervalArg_finite() {
41          // arrange
42          final Precision.DoubleEquivalence intervalPrecision = Precision.doubleEquivalenceOfEpsilon(1e-2);
43          final Interval interval = Interval.of(-1, 2, intervalPrecision);
44  
45          final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
46  
47          // act
48          final Segment3D segment = (Segment3D) Lines3D.subsetFromInterval(line, interval);
49  
50          // assert
51          final double side = 1.0 / Math.sqrt(3);
52          checkFiniteSegment(segment, Vector3D.of(-side, -side, -side), Vector3D.of(2 * side, 2 * side, 2 * side));
53      }
54  
55      @Test
56      void testFromInterval_intervalArg_full() {
57          // arrange
58          final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
59  
60          // act
61          final LineConvexSubset3D span = Lines3D.subsetFromInterval(line, Interval.full());
62  
63          // assert
64          Assertions.assertTrue(span.isInfinite());
65          Assertions.assertFalse(span.isFinite());
66  
67          GeometryTestUtils.assertNegativeInfinity(span.getSubspaceStart());
68          GeometryTestUtils.assertPositiveInfinity(span.getSubspaceEnd());
69  
70          Assertions.assertNull(span.getStartPoint());
71          Assertions.assertNull(span.getEndPoint());
72  
73          Assertions.assertSame(Interval.full(), span.getInterval());
74      }
75  
76      @Test
77      void testFromInterval_intervalArg_positiveHalfSpace() {
78          // arrange
79          final Precision.DoubleEquivalence intervalPrecision = Precision.doubleEquivalenceOfEpsilon(1e-2);
80          final Interval interval = Interval.min(-1, intervalPrecision);
81  
82          final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
83  
84          // act
85          final Ray3D ray = (Ray3D) Lines3D.subsetFromInterval(line, interval);
86  
87          // assert
88          Assertions.assertTrue(ray.isInfinite());
89          Assertions.assertFalse(ray.isFinite());
90  
91          Assertions.assertEquals(-1.0, ray.getSubspaceStart(), TEST_EPS);
92          GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
93  
94          final double side = 1.0 / Math.sqrt(3);
95  
96          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-side, -side, -side), ray.getStartPoint(), TEST_EPS);
97          Assertions.assertNull(ray.getEndPoint());
98  
99          checkInterval(interval, ray.getInterval());
100     }
101 
102     @Test
103     void testFromInterval_intervalArg_negativeHalfSpace() {
104         // arrange
105         final Precision.DoubleEquivalence intervalPrecision = Precision.doubleEquivalenceOfEpsilon(1e-2);
106         final Interval interval = Interval.max(2, intervalPrecision);
107 
108         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
109 
110         // act
111         final ReverseRay3D halfLine = (ReverseRay3D) Lines3D.subsetFromInterval(line, interval);
112 
113         // assert
114         GeometryTestUtils.assertNegativeInfinity(halfLine.getSubspaceStart());
115         Assertions.assertEquals(2, halfLine.getSubspaceEnd(), TEST_EPS);
116 
117         final double side = 1.0 / Math.sqrt(3);
118 
119         Assertions.assertNull(halfLine.getStartPoint());
120         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * side, 2 * side, 2 * side), halfLine.getEndPoint(), TEST_EPS);
121 
122         checkInterval(interval, halfLine.getInterval());
123     }
124 
125     @Test
126     void testFromInterval_doubleArgs_finite() {
127         // arrange
128         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
129 
130         // act
131         final Segment3D segment = (Segment3D) Lines3D.subsetFromInterval(line, -1, 2);
132 
133         // assert
134         final double side = 1.0 / Math.sqrt(3);
135         checkFiniteSegment(segment, Vector3D.of(-side, -side, -side), Vector3D.of(2 * side, 2 * side, 2 * side));
136     }
137 
138     @Test
139     void testFromInterval_doubleArgs_full() {
140         // arrange
141         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
142 
143         // act
144         final LineConvexSubset3D span = Lines3D.subsetFromInterval(line, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
145 
146         // assert
147         GeometryTestUtils.assertNegativeInfinity(span.getSubspaceStart());
148         GeometryTestUtils.assertPositiveInfinity(span.getSubspaceEnd());
149 
150         Assertions.assertNull(span.getStartPoint());
151         Assertions.assertNull(span.getEndPoint());
152     }
153 
154     @Test
155     void testFromInterval_doubleArgs_positiveHalfSpace() {
156         // arrange
157         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
158 
159         // act
160         final Ray3D ray = (Ray3D) Lines3D.subsetFromInterval(line, -1, Double.POSITIVE_INFINITY);
161 
162         // assert
163         Assertions.assertEquals(-1.0, ray.getSubspaceStart(), TEST_EPS);
164         GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
165 
166         final double side = 1.0 / Math.sqrt(3);
167 
168         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-side, -side, -side), ray.getStartPoint(), TEST_EPS);
169         Assertions.assertNull(ray.getEndPoint());
170     }
171 
172     @Test
173     void testFromInterval_doubleArgs_negativeHalfSpace() {
174         // arrange
175         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
176 
177         // act
178         final ReverseRay3D halfLine = (ReverseRay3D) Lines3D.subsetFromInterval(line, 2, Double.NEGATIVE_INFINITY);
179 
180         // assert
181         GeometryTestUtils.assertNegativeInfinity(halfLine.getSubspaceStart());
182         Assertions.assertEquals(2, halfLine.getSubspaceEnd(), TEST_EPS);
183 
184         final double side = 1.0 / Math.sqrt(3);
185 
186         Assertions.assertNull(halfLine.getStartPoint());
187         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * side, 2 * side, 2 * side), halfLine.getEndPoint(), TEST_EPS);
188     }
189 
190     @Test
191     void testFromInterval_doubleArgs_invalidArgs() {
192         // arrange
193         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
194 
195         // act/assert
196         GeometryTestUtils.assertThrowsWithMessage(() -> {
197             Lines3D.subsetFromInterval(line, Double.NaN, 0);
198         }, IllegalArgumentException.class, "Invalid line convex subset interval: NaN, 0.0");
199 
200         GeometryTestUtils.assertThrowsWithMessage(() -> {
201             Lines3D.subsetFromInterval(line, 0, Double.NaN);
202         }, IllegalArgumentException.class, "Invalid line convex subset interval: 0.0, NaN");
203 
204         GeometryTestUtils.assertThrowsWithMessage(() -> {
205             Lines3D.subsetFromInterval(line, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
206         }, IllegalArgumentException.class, "Invalid line convex subset interval: Infinity, Infinity");
207 
208         GeometryTestUtils.assertThrowsWithMessage(() -> {
209             Lines3D.subsetFromInterval(line, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
210         }, IllegalArgumentException.class, "Invalid line convex subset interval: -Infinity, -Infinity");
211     }
212 
213     @Test
214     void testFromInterval_vectorArgs() {
215         // arrange
216         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
217 
218         // act
219         final Segment3D segment = (Segment3D) Lines3D.subsetFromInterval(line, Vector1D.of(-1), Vector1D.of(2));
220 
221         // assert
222         final double side = 1.0 / Math.sqrt(3);
223         checkFiniteSegment(segment, Vector3D.of(-side, -side, -side), Vector3D.of(2 * side, 2 * side, 2 * side));
224     }
225 
226     @Test
227     void testSpaceSubspaceConversion() {
228         // arrange
229         final Segment3D segment = Lines3D.segmentFromPoints(Vector3D.ZERO, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
230 
231         // act/assert
232         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(3), segment.toSubspace(Vector3D.of(1, 3, 5)), TEST_EPS);
233         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 3, 0), segment.toSpace(Vector1D.of(3)), TEST_EPS);
234     }
235 
236     @Test
237     void testGetSubspaceRegion() {
238         // arrange
239         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION);
240         final Interval interval = Interval.full();
241 
242         final LineConvexSubset3D subset = Lines3D.subsetFromInterval(line, interval);
243 
244         // act/assert
245         Assertions.assertSame(interval, subset.getInterval());
246         Assertions.assertSame(interval, subset.getSubspaceRegion());
247     }
248 
249     @Test
250     void testTransform_infinite() {
251         // arrange
252         final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.of(0, 1, -1), TEST_PRECISION);
253         final LineConvexSubset3D subset = Lines3D.subsetFromInterval(line,
254                 Interval.min(line.toSubspace(Vector3D.of(1, 0, 0)).getX(), TEST_PRECISION));
255 
256         final Transform<Vector3D> transform = AffineTransformMatrix3D.identity()
257                 .scale(2, 1, 1)
258                 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, Angle.PI_OVER_TWO));
259 
260         // act
261         final LineConvexSubset3D transformed = subset.transform(transform);
262 
263         // assert
264         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, -2), transformed.getStartPoint(), TEST_EPS);
265         Assertions.assertNull(transformed.getEndPoint());
266         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 1, 0).normalize(), transformed.getLine().getDirection(), TEST_EPS);
267     }
268 
269     private static void checkInterval(final Interval expected, final Interval actual) {
270         Assertions.assertEquals(expected.getMin(), actual.getMin(), TEST_EPS);
271         Assertions.assertEquals(expected.getMax(), actual.getMax(), TEST_EPS);
272     }
273 
274     private static void checkFiniteSegment(final LineConvexSubset3D subset, final Vector3D start, final Vector3D end) {
275         checkFiniteSegment(subset, start, end, TEST_PRECISION);
276     }
277 
278     private static void checkFiniteSegment(final LineConvexSubset3D subset, final Vector3D start, final Vector3D end, final Precision.DoubleEquivalence precision) {
279         Assertions.assertFalse(subset.isInfinite());
280         Assertions.assertTrue(subset.isFinite());
281 
282         EuclideanTestUtils.assertCoordinatesEqual(start, subset.getStartPoint(), TEST_EPS);
283         EuclideanTestUtils.assertCoordinatesEqual(end, subset.getEndPoint(), TEST_EPS);
284 
285         final Line3D line = subset.getLine();
286 
287         Assertions.assertEquals(line.toSubspace(subset.getStartPoint()).getX(), subset.getSubspaceStart(), TEST_EPS);
288         Assertions.assertEquals(line.toSubspace(subset.getEndPoint()).getX(), subset.getSubspaceEnd(), TEST_EPS);
289 
290         Assertions.assertSame(precision, line.getPrecision());
291     }
292 }