1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.threed;
18
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.regex.Pattern;
22
23 import org.apache.commons.geometry.core.GeometryTestUtils;
24 import org.apache.commons.geometry.core.RegionLocation;
25 import org.apache.commons.geometry.core.Transform;
26 import org.apache.commons.geometry.core.partitioning.Split;
27 import org.apache.commons.geometry.core.partitioning.SplitLocation;
28 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
29 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
30 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
31 import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
32 import org.apache.commons.geometry.euclidean.twod.ConvexArea;
33 import org.apache.commons.geometry.euclidean.twod.Lines;
34 import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
35 import org.apache.commons.geometry.euclidean.twod.Vector2D;
36 import org.apache.commons.geometry.euclidean.twod.shape.Parallelogram;
37 import org.apache.commons.numbers.angle.PlaneAngleRadians;
38 import org.junit.Assert;
39 import org.junit.Test;
40
41 public class EmbeddedTreePlaneSubsetTest {
42
43 private static final double TEST_EPS = 1e-10;
44
45 private static final DoublePrecisionContext TEST_PRECISION =
46 new EpsilonDoublePrecisionContext(TEST_EPS);
47
48 private static final EmbeddingPlane XY_PLANE = Planes.fromPointAndPlaneVectors(Vector3D.ZERO,
49 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
50
51 @Test
52 public void testCtor_plane() {
53
54 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE);
55
56
57 Assert.assertFalse(ps.isFull());
58 Assert.assertTrue(ps.isEmpty());
59
60 Assert.assertEquals(0, ps.getSize(), TEST_EPS);
61 }
62
63 @Test
64 public void testCtor_plane_booleanFalse() {
65
66 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
67
68
69 Assert.assertFalse(ps.isFull());
70 Assert.assertTrue(ps.isEmpty());
71
72 Assert.assertEquals(0, ps.getSize(), TEST_EPS);
73 }
74
75 @Test
76 public void testCtor_plane_booleanTrue() {
77
78 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, true);
79
80
81 Assert.assertTrue(ps.isFull());
82 Assert.assertFalse(ps.isEmpty());
83
84 GeometryTestUtils.assertPositiveInfinity(ps.getSize());
85 }
86
87 @Test
88 public void testSpaceConversion() {
89
90 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 0, 0),
91 Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
92
93 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, true);
94
95
96 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), ps.toSubspace(Vector3D.of(-5, 1, 2)), TEST_EPS);
97 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, -2, 4), ps.toSpace(Vector2D.of(-2, 4)), TEST_EPS);
98 }
99
100 @Test
101 public void testToConvex_full() {
102
103 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, true);
104
105
106 final List<PlaneConvexSubset> convex = ps.toConvex();
107
108
109 Assert.assertEquals(1, convex.size());
110 Assert.assertTrue(convex.get(0).isFull());
111 }
112
113 @Test
114 public void testToConvex_empty() {
115
116 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
117
118
119 final List<PlaneConvexSubset> convex = ps.toConvex();
120
121
122 Assert.assertEquals(0, convex.size());
123 }
124
125 @Test
126 public void testToConvex_nonConvexRegion() {
127
128 final ConvexArea a = ConvexArea.convexPolygonFromVertices(Arrays.asList(
129 Vector2D.of(0, 0), Vector2D.of(1, 0),
130 Vector2D.of(1, 1), Vector2D.of(0, 1)
131 ), TEST_PRECISION);
132 final ConvexArea b = ConvexArea.convexPolygonFromVertices(Arrays.asList(
133 Vector2D.of(1, 0), Vector2D.of(2, 0),
134 Vector2D.of(2, 1), Vector2D.of(1, 1)
135 ), TEST_PRECISION);
136
137 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
138 ps.add(Planes.subsetFromConvexArea(XY_PLANE, a));
139 ps.add(Planes.subsetFromConvexArea(XY_PLANE, b));
140
141
142 final List<PlaneConvexSubset> convex = ps.toConvex();
143
144
145 Assert.assertEquals(2, convex.size());
146 Assert.assertEquals(1, convex.get(0).getSize(), TEST_EPS);
147 Assert.assertEquals(1, convex.get(1).getSize(), TEST_EPS);
148 }
149
150 @Test
151 public void testToTriangles_empty() {
152
153 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
154
155
156 final List<Triangle3D> tris = ps.toTriangles();
157
158
159 Assert.assertEquals(0, tris.size());
160 }
161
162 @Test
163 public void testToTriangles_infinite() {
164
165 final Pattern pattern = Pattern.compile("^Cannot convert infinite plane subset to triangles: .*");
166
167
168 GeometryTestUtils.assertThrows(() -> {
169 new EmbeddedTreePlaneSubset(XY_PLANE, true).toTriangles();
170 }, IllegalStateException.class, pattern);
171
172 GeometryTestUtils.assertThrows(() -> {
173 final EmbeddedTreePlaneSubset halfSpace = new EmbeddedTreePlaneSubset(XY_PLANE, false);
174 halfSpace.getSubspaceRegion().getRoot()
175 .insertCut(Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION));
176
177 halfSpace.toTriangles();
178 }, IllegalStateException.class, pattern);
179
180 GeometryTestUtils.assertThrows(() -> {
181 final RegionBSPTree2D tree = RegionBSPTree2D.empty();
182 tree.insert(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION));
183 tree.insert(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(0, 1), TEST_PRECISION));
184
185 final EmbeddedTreePlaneSubset halfSpaceWithVertices = new EmbeddedTreePlaneSubset(XY_PLANE, tree);
186
187 halfSpaceWithVertices.toTriangles();
188 }, IllegalStateException.class, pattern);
189 }
190
191 @Test
192 public void testToTriangles_finite() {
193
194 final Vector3D p1 = Vector3D.ZERO;
195 final Vector3D p2 = Vector3D.of(1, 0, 0);
196 final Vector3D p3 = Vector3D.of(2, 1, 0);
197 final Vector3D p4 = Vector3D.of(1.5, 1, 0);
198
199 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE);
200 ps.add(Planes.convexPolygonFromVertices(Arrays.asList(
201 p1, p2, p3, p4
202 ), TEST_PRECISION));
203
204
205 final List<Triangle3D> tris = ps.toTriangles();
206
207
208 Assert.assertEquals(2, tris.size());
209
210 EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(p4, p1, p2),
211 tris.get(0).getVertices(), TEST_PRECISION);
212 EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(p4, p2, p3),
213 tris.get(1).getVertices(), TEST_PRECISION);
214 }
215
216 @Test
217 public void testToTriangles_finite_disjoint() {
218
219 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE);
220 ps.add(Planes.convexPolygonFromVertices(Arrays.asList(
221 Vector3D.ZERO, Vector3D.of(1, 0, 0),
222 Vector3D.of(2, 1, 0), Vector3D.of(1.5, 1, 0)
223 ), TEST_PRECISION));
224
225 ps.add(Planes.convexPolygonFromVertices(Arrays.asList(
226 Vector3D.of(-1, -1, 0), Vector3D.of(0, -1, 0), Vector3D.of(-1, 0, 0)
227 ), TEST_PRECISION));
228
229
230 final List<Triangle3D> tris = ps.toTriangles();
231
232
233 Assert.assertEquals(3, tris.size());
234 }
235
236 @Test
237 public void testGetBounds_noBounds() {
238
239 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, 1),
240 Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X, TEST_PRECISION);
241
242 final EmbeddedTreePlaneSubset full = new EmbeddedTreePlaneSubset(plane, true);
243 final EmbeddedTreePlaneSubset empty = new EmbeddedTreePlaneSubset(plane, false);
244
245 final EmbeddedTreePlaneSubset halfPlane = new EmbeddedTreePlaneSubset(plane, false);
246 halfPlane.getSubspaceRegion().getRoot().insertCut(Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION));
247
248
249 Assert.assertNull(full.getBounds());
250 Assert.assertNull(empty.getBounds());
251 Assert.assertNull(halfPlane.getBounds());
252 }
253
254 @Test
255 public void testGetBounds_hasBounds() {
256
257 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, 1),
258 Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X, TEST_PRECISION);
259
260 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, false);
261 ps.getSubspaceRegion().add(ConvexArea.convexPolygonFromVertices(Arrays.asList(
262 Vector2D.of(1, 1), Vector2D.of(2, 1), Vector2D.of(1, 2)
263 ), TEST_PRECISION));
264
265
266 final Bounds3D bounds = ps.getBounds();
267
268
269 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-2, 1, 1), bounds.getMin(), TEST_EPS);
270 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 2, 1), bounds.getMax(), TEST_EPS);
271 }
272
273 @Test
274 public void testSplit_empty() {
275
276 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
277
278 final Plane splitter = Planes.fromNormal(Vector3D.Unit.PLUS_X, TEST_PRECISION);
279
280
281 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
282
283
284 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
285
286 Assert.assertNull(split.getMinus());
287 Assert.assertNull(split.getPlus());
288 }
289
290 @Test
291 public void testSplit_halfSpace() {
292
293 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
294 ps.getSubspaceRegion().getRoot().cut(
295 Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION));
296
297 final Plane splitter = Planes.fromNormal(Vector3D.Unit.PLUS_X, TEST_PRECISION);
298
299
300 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
301
302
303 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
304
305 final EmbeddedTreePlaneSubset minus = split.getMinus();
306 checkPoints(minus, RegionLocation.INSIDE, Vector3D.of(-1, 1, 0));
307 checkPoints(minus, RegionLocation.OUTSIDE, Vector3D.of(1, 1, 0), Vector3D.of(0, -1, 0));
308
309 final EmbeddedTreePlaneSubset plus = split.getPlus();
310 checkPoints(plus, RegionLocation.OUTSIDE, Vector3D.of(-1, 1, 0), Vector3D.of(0, -1, 0));
311 checkPoints(plus, RegionLocation.INSIDE, Vector3D.of(1, 1, 0));
312 }
313
314 @Test
315 public void testSplit_both() {
316
317 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
318 ps.getSubspaceRegion().union(
319 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
320
321 final Plane splitter = Planes.fromNormal(Vector3D.Unit.PLUS_X, TEST_PRECISION);
322
323
324 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
325
326
327 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
328
329 final EmbeddedTreePlaneSubset minus = split.getMinus();
330 checkPoints(minus, RegionLocation.INSIDE, Vector3D.of(-0.5, 0, 0));
331 checkPoints(minus, RegionLocation.OUTSIDE,
332 Vector3D.of(0.5, 0, 0), Vector3D.of(1.5, 0, 0),
333 Vector3D.of(0, 1.5, 0), Vector3D.of(0, -1.5, 0));
334
335 final EmbeddedTreePlaneSubset plus = split.getPlus();
336 checkPoints(plus, RegionLocation.INSIDE, Vector3D.of(0.5, 0, 0));
337 checkPoints(plus, RegionLocation.OUTSIDE,
338 Vector3D.of(-0.5, 0, 0), Vector3D.of(1.5, 0, 0),
339 Vector3D.of(0, 1.5, 0), Vector3D.of(0, -1.5, 0));
340 }
341
342 @Test
343 public void testSplit_intersects_plusOnly() {
344
345 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
346 ps.getSubspaceRegion().union(
347 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
348
349 final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, 1), Vector3D.of(0.1, 0, 1), TEST_PRECISION);
350
351
352 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
353
354
355 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
356
357 Assert.assertSame(ps, split.getMinus());
358 Assert.assertNull(split.getPlus());
359 }
360
361 @Test
362 public void testSplit_intersects_minusOnly() {
363
364 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
365 ps.getSubspaceRegion().union(
366 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
367
368 final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, 1), Vector3D.of(0.1, 0, -1), TEST_PRECISION);
369
370
371 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
372
373
374 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
375
376 Assert.assertNull(split.getMinus());
377 Assert.assertSame(ps, split.getPlus());
378 }
379
380 @Test
381 public void testSplit_parallel_plusOnly() {
382
383 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
384 ps.getSubspaceRegion().union(
385 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
386
387 final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
388
389
390 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
391
392
393 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
394
395 Assert.assertSame(ps, split.getMinus());
396 Assert.assertNull(split.getPlus());
397 }
398
399 @Test
400 public void testSplit_parallel_minusOnly() {
401
402 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
403 ps.getSubspaceRegion().union(
404 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
405
406 final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, 1), Vector3D.Unit.MINUS_Z, TEST_PRECISION);
407
408
409 final Split<EmbeddedTreePlaneSubset> split = ps.split(splitter);
410
411
412 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
413
414 Assert.assertNull(split.getMinus());
415 Assert.assertSame(ps, split.getPlus());
416 }
417
418 @Test
419 public void testSplit_coincident() {
420
421 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
422 ps.getSubspaceRegion().union(
423 Parallelogram.axisAligned(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION).toTree());
424
425
426 final Split<EmbeddedTreePlaneSubset> split = ps.split(ps.getPlane());
427
428
429 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
430
431 Assert.assertNull(split.getMinus());
432 Assert.assertNull(split.getPlus());
433 }
434
435 @Test
436 public void testTransform_empty() {
437
438 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
439
440 final AffineTransformMatrix3D transform = AffineTransformMatrix3D.createTranslation(Vector3D.Unit.PLUS_Z);
441
442
443 final EmbeddedTreePlaneSubset result = ps.transform(transform);
444
445
446 Assert.assertNotSame(ps, result);
447
448 final Plane resultPlane = result.getPlane();
449 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1), resultPlane.getOrigin(), TEST_EPS);
450 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, resultPlane.getNormal(), TEST_EPS);
451
452 Assert.assertFalse(result.isFull());
453 Assert.assertTrue(result.isEmpty());
454 }
455
456 @Test
457 public void testTransform_full() {
458
459 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, true);
460
461 final AffineTransformMatrix3D transform = AffineTransformMatrix3D.createTranslation(Vector3D.Unit.PLUS_Z);
462
463
464 final EmbeddedTreePlaneSubset result = ps.transform(transform);
465
466
467 Assert.assertNotSame(ps, result);
468
469 final Plane resultPlane = result.getPlane();
470 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1), resultPlane.getOrigin(), TEST_EPS);
471 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, resultPlane.getNormal(), TEST_EPS);
472
473 Assert.assertTrue(result.isFull());
474 Assert.assertFalse(result.isEmpty());
475 }
476
477 @Test
478 public void testTransform() {
479
480 final ConvexArea area = ConvexArea.convexPolygonFromVertices(
481 Arrays.asList(Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.Unit.PLUS_Y), TEST_PRECISION);
482 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
483
484 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, area.toTree());
485
486 final Transform<Vector3D> transform = AffineTransformMatrix3D.identity()
487 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, PlaneAngleRadians.PI_OVER_TWO))
488 .translate(Vector3D.of(1, 0, 0));
489
490
491 final EmbeddedTreePlaneSubset result = ps.transform(transform);
492
493
494 Assert.assertNotSame(ps, result);
495
496 final Plane resultPlane = result.getPlane();
497 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(2, 0, 0), resultPlane.getOrigin(), TEST_EPS);
498 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_X, resultPlane.getNormal(), TEST_EPS);
499
500 checkPoints(result, RegionLocation.INSIDE, Vector3D.of(2, 0.25, -0.25));
501 checkPoints(result, RegionLocation.OUTSIDE, Vector3D.of(1, 0.25, -0.25), Vector3D.of(3, 0.25, -0.25));
502
503 checkPoints(result, RegionLocation.BOUNDARY,
504 Vector3D.of(2, 0, 0), Vector3D.of(2, 0, -1), Vector3D.of(2, 1, 0));
505 }
506
507 @Test
508 public void testTransform_reflection() {
509
510 final ConvexArea area = ConvexArea.convexPolygonFromVertices(
511 Arrays.asList(Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.Unit.PLUS_Y), TEST_PRECISION);
512 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
513
514 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, area.toTree());
515
516 final Transform<Vector3D> transform = AffineTransformMatrix3D.createScale(-1, 1, 1);
517
518
519 final EmbeddedTreePlaneSubset result = ps.transform(transform);
520
521
522 Assert.assertNotSame(ps, result);
523
524 final Plane resultPlane = result.getPlane();
525 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1), resultPlane.getOrigin(), TEST_EPS);
526 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z, resultPlane.getNormal(), TEST_EPS);
527
528 checkPoints(result, RegionLocation.INSIDE, Vector3D.of(-0.25, 0.25, 1));
529 checkPoints(result, RegionLocation.OUTSIDE, Vector3D.of(0.25, 0.25, 0), Vector3D.of(0.25, 0.25, 2));
530
531 checkPoints(result, RegionLocation.BOUNDARY,
532 Vector3D.of(-1, 0, 1), Vector3D.of(0, 1, 1), Vector3D.of(0, 0, 1));
533 }
534
535 @Test
536 public void testAddMethods() {
537
538 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(
539 Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
540 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, false);
541
542
543 ps.add(Planes.subsetFromConvexArea(plane, ConvexArea.convexPolygonFromVertices(Arrays.asList(
544 Vector2D.ZERO, Vector2D.of(1, 0), Vector2D.of(0, 1)
545 ), TEST_PRECISION)));
546
547 final RegionBSPTree2D tree = RegionBSPTree2D.empty();
548 tree.add(ConvexArea.convexPolygonFromVertices(Arrays.asList(
549 Vector2D.of(1, 0), Vector2D.of(1, 1), Vector2D.of(0, 1)
550 ), TEST_PRECISION));
551 ps.add(new EmbeddedTreePlaneSubset(plane, tree));
552
553
554 Assert.assertFalse(ps.isFull());
555 Assert.assertFalse(ps.isEmpty());
556 Assert.assertTrue(ps.isFinite());
557 Assert.assertFalse(ps.isInfinite());
558
559 Assert.assertEquals(1, ps.getSize(), TEST_EPS);
560
561 checkPoints(ps, RegionLocation.INSIDE, Vector3D.of(0.5, 0.5, 1));
562 checkPoints(ps, RegionLocation.BOUNDARY,
563 Vector3D.of(0, 0, 1), Vector3D.of(1, 0, 1),
564 Vector3D.of(1, 1, 1), Vector3D.of(0, 1, 1));
565 checkPoints(ps, RegionLocation.OUTSIDE,
566 Vector3D.of(0.5, 0.5, 0), Vector3D.of(0.5, 0.5, 2),
567 Vector3D.of(-0.5, 0.5, 1), Vector3D.of(0.5, -0.5, 1),
568 Vector3D.of(1.5, 0.5, 1), Vector3D.of(0.5, 1.5, 1));
569 }
570
571 @Test
572 public void testAddMethods_rotatesEquivalentPlanesWithDifferentUAndV() {
573
574 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(
575 Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
576
577 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, false);
578
579 final EmbeddingPlane otherPlane1 = Planes.fromPointAndPlaneVectors(
580 Vector3D.of(0, 0, 1), Vector3D.of(1e-12, 1, 0), Vector3D.Unit.MINUS_X, TEST_PRECISION);
581
582 final EmbeddingPlane otherPlane2 = Planes.fromPointAndPlaneVectors(
583 Vector3D.of(0, 0, 1), Vector3D.of(0, -1, 1e-12), Vector3D.Unit.PLUS_X, TEST_PRECISION);
584
585 final ConvexArea area = ConvexArea.convexPolygonFromVertices(Arrays.asList(
586 Vector2D.of(0, -1), Vector2D.of(1, -1), Vector2D.of(1, 1), Vector2D.of(0, 1)
587 ), TEST_PRECISION);
588
589
590 ps.add(Planes.subsetFromConvexArea(plane, area));
591 ps.add(new EmbeddedTreePlaneSubset(otherPlane1, area.toTree()));
592 ps.add(Planes.subsetFromConvexArea(otherPlane2, area));
593
594
595 Assert.assertEquals(4, ps.getSize(), TEST_EPS);
596 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1), ps.getCentroid(), TEST_EPS);
597
598 final Bounds3D bounds = ps.getBounds();
599 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, -1, 1), bounds.getMin(), TEST_EPS);
600 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 1), bounds.getMax(), TEST_EPS);
601 }
602
603 @Test
604 public void testAddMethods_rotatesEquivalentPlanesWithDifferentUAndV_singleConvexArea() {
605
606 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(
607 Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
608
609 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, false);
610
611 final EmbeddingPlane otherPlane1 = Planes.fromPointAndPlaneVectors(
612 Vector3D.of(0, 0, 1), Vector3D.of(1e-12, 1, 0), Vector3D.Unit.MINUS_X, TEST_PRECISION);
613
614 final ConvexArea area = ConvexArea.convexPolygonFromVertices(Arrays.asList(
615 Vector2D.ZERO, Vector2D.of(1, 0), Vector2D.of(1, 2), Vector2D.of(0, 2)
616 ), TEST_PRECISION);
617
618
619 ps.add(Planes.subsetFromConvexArea(otherPlane1, area));
620
621
622 Assert.assertEquals(2, ps.getSize(), TEST_EPS);
623 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0.5, 1), ps.getCentroid(), TEST_EPS);
624
625 final Bounds3D bounds = ps.getBounds();
626 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-2, 0, 1), bounds.getMin(), TEST_EPS);
627 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 1), bounds.getMax(), TEST_EPS);
628 }
629
630 @Test
631 public void testAddMethods_rotatesEquivalentPlanesWithDifferentUAndV_singleTree() {
632
633 final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(
634 Vector3D.of(0, 0, 1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
635
636 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(plane, false);
637
638 final EmbeddingPlane otherPlane1 = Planes.fromPointAndPlaneVectors(
639 Vector3D.of(0, 0, 1), Vector3D.Unit.MINUS_X, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
640
641 final ConvexArea area = ConvexArea.convexPolygonFromVertices(Arrays.asList(
642 Vector2D.ZERO, Vector2D.of(1, 0), Vector2D.of(1, 2), Vector2D.of(0, 2)
643 ), TEST_PRECISION);
644
645
646 ps.add(new EmbeddedTreePlaneSubset(otherPlane1, area.toTree()));
647
648
649 Assert.assertEquals(2, ps.getSize(), TEST_EPS);
650 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-0.5, -1, 1), ps.getCentroid(), TEST_EPS);
651
652 final Bounds3D bounds = ps.getBounds();
653 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, -2, 1), bounds.getMin(), TEST_EPS);
654 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 1), bounds.getMax(), TEST_EPS);
655 }
656
657 @Test
658 public void testAddMethods_validatesPlane() {
659
660 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(XY_PLANE, false);
661
662
663 GeometryTestUtils.assertThrows(() -> {
664 ps.add(Planes.subsetFromConvexArea(
665 Planes.fromPointAndPlaneVectors(Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.MINUS_Z, TEST_PRECISION),
666 ConvexArea.full()));
667 }, IllegalArgumentException.class);
668
669 GeometryTestUtils.assertThrows(() -> {
670 ps.add(new EmbeddedTreePlaneSubset(
671 Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, -1), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION),
672 false));
673 }, IllegalArgumentException.class);
674 }
675
676 @Test
677 public void testToString() {
678
679 final EmbeddedTreePlaneSubset ps = new EmbeddedTreePlaneSubset(
680 Planes.fromNormal(Vector3D.Unit.PLUS_Z, TEST_PRECISION).getEmbedding());
681
682
683 final String str = ps.toString();
684
685
686 GeometryTestUtils.assertContains("EmbeddedTreePlaneSubset[plane= EmbeddingPlane[", str);
687 GeometryTestUtils.assertContains("subspaceRegion= RegionBSPTree2D[", str);
688 }
689
690 private static void checkPoints(final EmbeddedTreePlaneSubset ps, final RegionLocation loc, final Vector3D... pts) {
691 for (final Vector3D pt : pts) {
692 Assert.assertEquals("Unexpected location for point " + pt, loc, ps.classify(pt));
693 }
694 }
695 }