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