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