1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.threed.shape;
18
19 import java.util.Arrays;
20 import java.util.Comparator;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.TreeSet;
24
25 import org.apache.commons.geometry.core.GeometryTestUtils;
26 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
27 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
28 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
29 import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
30 import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset;
31 import org.apache.commons.geometry.euclidean.threed.RegionBSPTree3D;
32 import org.apache.commons.geometry.euclidean.threed.Vector3D;
33 import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
34 import org.apache.commons.numbers.angle.PlaneAngleRadians;
35 import org.junit.Assert;
36 import org.junit.Test;
37
38 public class ParallelepipedTest {
39
40 private static final double TEST_EPS = 1e-10;
41
42 private static final DoublePrecisionContext TEST_PRECISION =
43 new EpsilonDoublePrecisionContext(TEST_EPS);
44
45 private static final Comparator<Vector3D> VERTEX_COMPARATOR = (a, b) -> {
46 int cmp = TEST_PRECISION.compare(a.getX(), b.getX());
47 if (cmp == 0) {
48 cmp = TEST_PRECISION.compare(a.getY(), b.getY());
49 if (cmp == 0) {
50 cmp = TEST_PRECISION.compare(a.getZ(), b.getZ());
51 }
52 }
53 return cmp;
54 };
55
56 @Test
57 public void testUnitCube() {
58
59 final Parallelepiped p = Parallelepiped.unitCube(TEST_PRECISION);
60
61
62 Assert.assertEquals(1, p.getSize(), TEST_EPS);
63 Assert.assertEquals(6, p.getBoundarySize(), TEST_EPS);
64 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
65
66 final List<PlaneConvexSubset> boundaries = p.getBoundaries();
67 Assert.assertEquals(6, boundaries.size());
68
69 assertVertices(p,
70 Vector3D.of(-0.5, -0.5, -0.5),
71 Vector3D.of(0.5, -0.5, -0.5),
72 Vector3D.of(0.5, 0.5, -0.5),
73 Vector3D.of(-0.5, 0.5, -0.5),
74
75 Vector3D.of(-0.5, -0.5, 0.5),
76 Vector3D.of(0.5, -0.5, 0.5),
77 Vector3D.of(0.5, 0.5, 0.5),
78 Vector3D.of(-0.5, 0.5, 0.5)
79 );
80 }
81
82 @Test
83 public void testFromTransformedUnitCube() {
84
85 final AffineTransformMatrix3D t = AffineTransformMatrix3D.createTranslation(Vector3D.of(1, 0, 2))
86 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, Math.PI * 0.25))
87 .scale(Vector3D.of(2, 1, 1));
88
89
90 final Parallelepiped p = Parallelepiped.fromTransformedUnitCube(t, TEST_PRECISION);
91
92
93 final double sqrt2 = Math.sqrt(2);
94 final double invSqrt2 = 1 / sqrt2;
95
96 Assert.assertEquals(2, p.getSize(), TEST_EPS);
97 Assert.assertEquals(4 + (4 * Math.sqrt(2.5)), p.getBoundarySize(), TEST_EPS);
98 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * invSqrt2, invSqrt2, 2),
99 p.getCentroid(), TEST_EPS);
100
101 assertVertices(p,
102 Vector3D.of(0, invSqrt2, 1.5),
103 Vector3D.of(2 * invSqrt2, 0, 1.5),
104 Vector3D.of(2 * sqrt2, invSqrt2, 1.5),
105 Vector3D.of(2 * invSqrt2, sqrt2, 1.5),
106
107 Vector3D.of(0, invSqrt2, 2.5),
108 Vector3D.of(2 * invSqrt2, 0, 2.5),
109 Vector3D.of(2 * sqrt2, invSqrt2, 2.5),
110 Vector3D.of(2 * invSqrt2, sqrt2, 2.5)
111 );
112 }
113
114 @Test
115 public void testFromTransformedUnitCube_transformDoesNotPreserveOrientation() {
116
117 final AffineTransformMatrix3D t = AffineTransformMatrix3D.createTranslation(Vector3D.of(1, 0, 2))
118 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, Math.PI * 0.25))
119 .scale(Vector3D.of(2, 1, -1));
120
121
122 final Parallelepiped p = Parallelepiped.fromTransformedUnitCube(t, TEST_PRECISION);
123
124
125 final double sqrt2 = Math.sqrt(2);
126 final double invSqrt2 = 1 / sqrt2;
127
128 Assert.assertEquals(2, p.getSize(), TEST_EPS);
129 Assert.assertEquals(4 + (4 * Math.sqrt(2.5)), p.getBoundarySize(), TEST_EPS);
130 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2 * invSqrt2, invSqrt2, -2),
131 p.getCentroid(), TEST_EPS);
132
133 assertVertices(p,
134 Vector3D.of(0, invSqrt2, -1.5),
135 Vector3D.of(2 * invSqrt2, 0, -1.5),
136 Vector3D.of(2 * sqrt2, invSqrt2, -1.5),
137 Vector3D.of(2 * invSqrt2, sqrt2, -1.5),
138
139 Vector3D.of(0, invSqrt2, -2.5),
140 Vector3D.of(2 * invSqrt2, 0, -2.5),
141 Vector3D.of(2 * sqrt2, invSqrt2, -2.5),
142 Vector3D.of(2 * invSqrt2, sqrt2, -2.5)
143 );
144 }
145
146 @Test
147 public void testFromTransformedUnitCube_zeroSizeRegion() {
148
149 GeometryTestUtils.assertThrows(() -> {
150 Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1e-16, 1, 1)),
151 TEST_PRECISION);
152 }, IllegalArgumentException.class);
153
154 GeometryTestUtils.assertThrows(() -> {
155 Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1, 1e-16, 1)),
156 TEST_PRECISION);
157 }, IllegalArgumentException.class);
158
159 GeometryTestUtils.assertThrows(() -> {
160 Parallelepiped.fromTransformedUnitCube(AffineTransformMatrix3D.createScale(Vector3D.of(1, 1, 1e-16)),
161 TEST_PRECISION);
162 }, IllegalArgumentException.class);
163 }
164
165 @Test
166 public void testAxisAligned_minFirst() {
167
168 final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 5, 6), TEST_PRECISION);
169
170
171 final List<PlaneConvexSubset> boundaries = p.getBoundaries();
172 Assert.assertEquals(6, boundaries.size());
173
174 assertVertices(p,
175 Vector3D.of(1, 2, 3),
176 Vector3D.of(4, 2, 3),
177 Vector3D.of(4, 5, 3),
178 Vector3D.of(1, 5, 3),
179
180 Vector3D.of(1, 2, 6),
181 Vector3D.of(4, 2, 6),
182 Vector3D.of(4, 5, 6),
183 Vector3D.of(1, 5, 6)
184 );
185 }
186
187 @Test
188 public void testAxisAligned_maxFirst() {
189
190 final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(4, 5, 6), Vector3D.of(1, 2, 3), TEST_PRECISION);
191
192
193 final List<PlaneConvexSubset> boundaries = p.getBoundaries();
194 Assert.assertEquals(6, boundaries.size());
195
196 assertVertices(p,
197 Vector3D.of(1, 2, 3),
198 Vector3D.of(4, 2, 3),
199 Vector3D.of(4, 5, 3),
200 Vector3D.of(1, 5, 3),
201
202 Vector3D.of(1, 2, 6),
203 Vector3D.of(4, 2, 6),
204 Vector3D.of(4, 5, 6),
205 Vector3D.of(1, 5, 6)
206 );
207 }
208
209 @Test
210 public void testAxisAligned_illegalArgs() {
211
212 GeometryTestUtils.assertThrows(() -> {
213 Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(1, 5, 6), TEST_PRECISION);
214 }, IllegalArgumentException.class);
215
216 GeometryTestUtils.assertThrows(() -> {
217 Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 2, 6), TEST_PRECISION);
218 }, IllegalArgumentException.class);
219
220 GeometryTestUtils.assertThrows(() -> {
221 Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(1, 5, 3), TEST_PRECISION);
222 }, IllegalArgumentException.class);
223 }
224
225 @Test
226 public void testBuilder_defaultValues() {
227
228 final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
229
230
231 final Parallelepiped p = builder.build();
232
233
234 Assert.assertEquals(1, p.getSize(), TEST_EPS);
235 Assert.assertEquals(6, p.getBoundarySize(), TEST_EPS);
236 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
237
238 final List<PlaneConvexSubset> boundaries = p.getBoundaries();
239 Assert.assertEquals(6, boundaries.size());
240
241 assertVertices(p,
242 Vector3D.of(-0.5, -0.5, -0.5),
243 Vector3D.of(0.5, -0.5, -0.5),
244 Vector3D.of(0.5, 0.5, -0.5),
245 Vector3D.of(-0.5, 0.5, -0.5),
246
247 Vector3D.of(-0.5, -0.5, 0.5),
248 Vector3D.of(0.5, -0.5, 0.5),
249 Vector3D.of(0.5, 0.5, 0.5),
250 Vector3D.of(-0.5, 0.5, 0.5)
251 );
252 }
253
254 @Test
255 public void testBuilder_withRotation() {
256
257 final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
258
259
260 final Parallelepiped p = builder
261 .setScale(1, 2, 3)
262 .setRotation(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Z, PlaneAngleRadians.PI_OVER_TWO))
263 .setPosition(Vector3D.of(1, 2, -1))
264 .build();
265
266
267 Assert.assertEquals(6, p.getSize(), TEST_EPS);
268 Assert.assertEquals(22, p.getBoundarySize(), TEST_EPS);
269 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 2, -1), p.getCentroid(), TEST_EPS);
270
271 assertVertices(p,
272 Vector3D.of(0, 1.5, 0.5),
273 Vector3D.of(2, 1.5, 0.5),
274 Vector3D.of(2, 2.5, 0.5),
275 Vector3D.of(0, 2.5, 0.5),
276
277 Vector3D.of(0, 1.5, -2.5),
278 Vector3D.of(2, 1.5, -2.5),
279 Vector3D.of(2, 2.5, -2.5),
280 Vector3D.of(0, 2.5, -2.5)
281 );
282 }
283
284 @Test
285 public void testBuilder_withUniformScale() {
286
287 final Parallelepiped.Builder builder = Parallelepiped.builder(TEST_PRECISION);
288
289
290 final Parallelepiped p = builder
291 .setScale(0.5)
292 .build();
293
294
295 Assert.assertEquals(0.125, p.getSize(), TEST_EPS);
296 Assert.assertEquals(1.5, p.getBoundarySize(), TEST_EPS);
297 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, p.getCentroid(), TEST_EPS);
298
299 assertVertices(p,
300 Vector3D.of(-0.25, -0.25, -0.25),
301 Vector3D.of(0.25, -0.25, -0.25),
302 Vector3D.of(0.25, 0.25, -0.25),
303 Vector3D.of(-0.25, 0.25, -0.25),
304
305 Vector3D.of(-0.25, -0.25, 0.25),
306 Vector3D.of(0.25, -0.25, 0.25),
307 Vector3D.of(0.25, 0.25, 0.25),
308 Vector3D.of(-0.25, 0.25, 0.25)
309 );
310 }
311
312 @Test
313 public void testToTree() {
314
315 final Parallelepiped p = Parallelepiped.axisAligned(Vector3D.of(1, 2, 3), Vector3D.of(4, 5, 6), TEST_PRECISION);
316
317
318 final RegionBSPTree3D tree = p.toTree();
319
320
321 Assert.assertEquals(27, tree.getSize(), TEST_EPS);
322 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2.5, 3.5, 4.5), tree.getCentroid(), TEST_EPS);
323 }
324
325 private static void assertVertices(final Parallelepiped p, final Vector3D... vertices) {
326 final Set<Vector3D> expectedVertices = new TreeSet<>(VERTEX_COMPARATOR);
327 expectedVertices.addAll(Arrays.asList(vertices));
328
329 final Set<Vector3D> actualVertices = new TreeSet<>(VERTEX_COMPARATOR);
330 for (final PlaneConvexSubset boundary : p.getBoundaries()) {
331 actualVertices.addAll(boundary.getVertices());
332 }
333
334 Assert.assertEquals(expectedVertices.size(), actualVertices.size());
335 for (final Vector3D expected : expectedVertices) {
336 Assert.assertTrue("Expected vertices to contain " + expected, actualVertices.contains(expected));
337 }
338 }
339 }