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;
18  
19  import java.util.Arrays;
20  
21  import org.apache.commons.geometry.euclidean.threed.line.Lines3D;
22  import org.apache.commons.geometry.euclidean.threed.shape.Parallelepiped;
23  import org.apache.commons.numbers.core.Precision;
24  import org.junit.jupiter.api.Test;
25  
26  class BoundarySourceLinecaster3DTest {
27  
28      private static final double TEST_EPS = 1e-10;
29  
30      private static final Precision.DoubleEquivalence TEST_PRECISION =
31              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
32  
33      private static final BoundarySource3D UNIT_CUBE = Parallelepiped.builder(TEST_PRECISION)
34              .setPosition(Vector3D.of(0.5, 0.5, 0.5))
35              .build();
36  
37      @Test
38      void testLinecast_line_simple() {
39          // arrange
40          final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
41  
42          // act/assert
43  
44          // no intersections
45          LinecastChecker3D.with(linecaster)
46              .expectNothing()
47              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0, 4, 4), Vector3D.Unit.MINUS_X, TEST_PRECISION));
48  
49          // through center; two directions
50          LinecastChecker3D.with(linecaster)
51              .expect(Vector3D.of(0, 0.5, 0.5), Vector3D.Unit.MINUS_X)
52              .and(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
53              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0.5, 0.5, 0.5), Vector3D.Unit.PLUS_X, TEST_PRECISION));
54  
55          LinecastChecker3D.with(linecaster)
56              .expect(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
57              .and(Vector3D.of(0, 0.5, 0.5), Vector3D.Unit.MINUS_X)
58              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0.5, 0.5, 0.5), Vector3D.Unit.MINUS_X, TEST_PRECISION));
59      }
60  
61      @Test
62      void testLinecast_line_alongFace() {
63          // arrange
64          final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
65  
66          // act/assert
67          LinecastChecker3D.with(linecaster)
68              .expect(Vector3D.ZERO, Vector3D.Unit.MINUS_Y)
69              .and(Vector3D.ZERO, Vector3D.Unit.MINUS_Z)
70              .and(Vector3D.of(0, 1, 1), Vector3D.Unit.PLUS_Z)
71              .and(Vector3D.of(0, 1, 1), Vector3D.Unit.PLUS_Y)
72              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(0, 1, 1), TEST_PRECISION));
73      }
74  
75      @Test
76      void testLinecast_line_corners() {
77          // arrange
78          final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
79  
80          // act/assert
81  
82          // through single corner vertex
83          LinecastChecker3D.with(linecaster)
84              .expect(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Z)
85              .and(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Y)
86              .and(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_X)
87              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(1, 1, 1), Vector3D.of(1, -1, -1), TEST_PRECISION));
88  
89          // through two corner vertices
90          LinecastChecker3D.with(linecaster)
91              .expect(Vector3D.ZERO, Vector3D.Unit.MINUS_X)
92              .and(Vector3D.ZERO, Vector3D.Unit.MINUS_Y)
93              .and(Vector3D.ZERO, Vector3D.Unit.MINUS_Z)
94              .and(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Z)
95              .and(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Y)
96              .and(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_X)
97              .whenGiven(Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.of(1, 1, 1), TEST_PRECISION));
98      }
99  
100     @Test
101     void testLinecast_line_removesDuplicatePoints() {
102         // arrange
103         final BoundarySource3D src = BoundarySource3D.of(
104                     Planes.convexPolygonFromVertices(Arrays.asList(Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y), TEST_PRECISION),
105                     Planes.convexPolygonFromVertices(Arrays.asList(Vector3D.ZERO, Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X), TEST_PRECISION)
106                 );
107         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(src);
108 
109         // act/assert
110         LinecastChecker3D.with(linecaster)
111             .expect(Vector3D.of(0, 0.5, 0), Vector3D.Unit.PLUS_Z)
112             .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(-1, 0.5, 1), Vector3D.of(1, 0, -1), TEST_PRECISION));
113     }
114 
115     @Test
116     void testLinecast_segment_simple() {
117         // arrange
118         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
119 
120         // act/assert
121 
122         // no intersections; underlying line does not intersect
123         LinecastChecker3D.with(linecaster)
124             .expectNothing()
125             .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0, 4, 4), Vector3D.Unit.MINUS_X, TEST_PRECISION)
126                     .segment(-10, 10));
127 
128         // no intersections; underlying line does intersect
129         LinecastChecker3D.with(linecaster)
130             .expectNothing()
131             .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0.5, 0.5, 0.5), Vector3D.Unit.PLUS_X, TEST_PRECISION)
132                     .segment(2, 10));
133 
134         // no boundaries excluded; two directions
135         LinecastChecker3D.with(linecaster)
136             .expect(Vector3D.of(0, 0.5, 0.5), Vector3D.Unit.MINUS_X)
137             .and(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
138             .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0.5, 0.5, 0.5), Vector3D.Unit.PLUS_X, TEST_PRECISION)
139                     .segment(-10, 10));
140 
141         LinecastChecker3D.with(linecaster)
142             .expect(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
143             .and(Vector3D.of(0, 0.5, 0.5), Vector3D.Unit.MINUS_X)
144             .whenGiven(Lines3D.fromPointAndDirection(Vector3D.of(0.5, 0.5, 0.5), Vector3D.Unit.MINUS_X, TEST_PRECISION)
145                     .segment(-10, 10));
146     }
147 
148     @Test
149     void testLinecast_segment_boundaryExcluded() {
150         // arrange
151         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
152 
153         // act/assert
154         final Vector3D center = Vector3D.of(0.5, 0.5, 0.5);
155         LinecastChecker3D.with(linecaster)
156             .expect(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
157             .whenGiven(Lines3D.fromPointAndDirection(center, Vector3D.Unit.PLUS_X, TEST_PRECISION)
158                     .rayFrom(center));
159 
160         LinecastChecker3D.with(linecaster)
161             .expect(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
162             .whenGiven(Lines3D.fromPointAndDirection(center, Vector3D.Unit.MINUS_X, TEST_PRECISION)
163                     .reverseRayTo(center));
164     }
165 
166     @Test
167     void testLinecast_segment_startEndPointsOnBoundaries() {
168         // arrange
169         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
170 
171         // act/assert
172         LinecastChecker3D.with(linecaster)
173             .expect(Vector3D.of(1, 0.5, 0.5), Vector3D.Unit.PLUS_X)
174             .and(Vector3D.of(0, 0.5, 0.5), Vector3D.Unit.MINUS_X)
175             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(1, 0.5, 0.5), Vector3D.of(0, 0.5, 0.5), TEST_PRECISION));
176     }
177 
178     @Test
179     void testLinecast_segment_alongFace() {
180         // arrange
181         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
182 
183         // act/assert
184 
185         // includes two intersecting boundaries
186         LinecastChecker3D.with(linecaster)
187             .expect(Vector3D.of(0, 1, 0), Vector3D.Unit.MINUS_X)
188             .and(Vector3D.of(1, 1, 0), Vector3D.Unit.PLUS_X)
189             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(-1, 1, 0), Vector3D.of(2, 1, 0), TEST_PRECISION));
190 
191         // one intersecting boundary
192         LinecastChecker3D.with(linecaster)
193             .expect(Vector3D.of(1, 1, 0), Vector3D.Unit.PLUS_X)
194             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(0.25, 1, 0), Vector3D.of(2, 1, 0), TEST_PRECISION));
195 
196         // no intersecting boundary
197         LinecastChecker3D.with(linecaster)
198             .expectNothing()
199             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(0.25, 1, 0), Vector3D.of(0.75, 1, 0), TEST_PRECISION));
200     }
201 
202     @Test
203     void testLinecast_segment_corners() {
204         // arrange
205         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(UNIT_CUBE);
206 
207         final Vector3D corner = Vector3D.of(1, 1, 1);
208 
209         // act/assert
210 
211         // through corner
212         LinecastChecker3D.with(linecaster)
213             .expect(corner, Vector3D.Unit.PLUS_Z)
214             .and(corner, Vector3D.Unit.PLUS_Y)
215             .and(corner, Vector3D.Unit.PLUS_X)
216             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(0.5, 0.5, 0.5), Vector3D.of(2, 2, 2), TEST_PRECISION));
217 
218         // starts on corner
219         LinecastChecker3D.with(linecaster)
220             .expect(corner, Vector3D.Unit.PLUS_Z)
221             .and(corner, Vector3D.Unit.PLUS_Y)
222             .and(corner, Vector3D.Unit.PLUS_X)
223             .whenGiven(Lines3D.segmentFromPoints(corner, Vector3D.of(2, 0, 2), TEST_PRECISION));
224 
225         // ends on corner
226         LinecastChecker3D.with(linecaster)
227             .expect(corner, Vector3D.Unit.PLUS_Z)
228             .and(corner, Vector3D.Unit.PLUS_Y)
229             .and(corner, Vector3D.Unit.PLUS_X)
230             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(0, 2, 2), corner, TEST_PRECISION));
231     }
232 
233     @Test
234     void testLinecast_segment_removesDuplicatePoints() {
235         // arrange
236         final BoundarySource3D src = BoundarySource3D.of(
237                     Planes.convexPolygonFromVertices(Arrays.asList(Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y), TEST_PRECISION),
238                     Planes.convexPolygonFromVertices(Arrays.asList(Vector3D.ZERO, Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X), TEST_PRECISION)
239                 );
240         final BoundarySourceLinecaster3D linecaster = new BoundarySourceLinecaster3D(src);
241 
242         // act/assert
243         LinecastChecker3D.with(linecaster)
244             .expect(Vector3D.of(0, 0.5, 0), Vector3D.Unit.PLUS_Z)
245             .whenGiven(Lines3D.segmentFromPoints(Vector3D.of(-1, 0.5, 1), Vector3D.of(1, 0.5, -1), TEST_PRECISION));
246     }
247 }