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  import java.util.List;
    21
    22  import org.apache.commons.geometry.core.RegionLocation;
    23  import org.apache.commons.geometry.core.Transform;
    24  import org.apache.commons.geometry.core.partitioning.Split;
    25  import org.apache.commons.geometry.core.partitioning.SplitLocation;
    26  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    27  import org.apache.commons.geometry.euclidean.threed.line.Lines3D;
    28  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
    29  import org.apache.commons.geometry.euclidean.twod.ConvexArea;
    30  import org.apache.commons.geometry.euclidean.twod.Lines;
    31  import org.apache.commons.geometry.euclidean.twod.Vector2D;
    32  import org.apache.commons.numbers.angle.Angle;
    33  import org.apache.commons.numbers.core.Precision;
    34  import org.junit.jupiter.api.Assertions;
    35  import org.junit.jupiter.api.Test;
    36
    37  class PlaneConvexSubsetTest {
    38
    39      private static final double TEST_EPS = 1e-10;
    40
    41      private static final Precision.DoubleEquivalence TEST_PRECISION =
    42              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    43
    44      @Test
    45      void testToConvex() {
    46          // arrange
    47          final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(
    48                  Arrays.asList(Vector3D.Unit.PLUS_X,  Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_Z), TEST_PRECISION);
    49
    50          // act
    51          final List<PlaneConvexSubset> convex = sp.toConvex();
    52
    53          // assert
    54          Assertions.assertEquals(1, convex.size());
    55          Assertions.assertSame(sp, convex.get(0));
    56      }
    57
    58      @Test
    59      void testReverse() {
    60          // arrange
    61          final Vector3D p1 = Vector3D.of(1, 0, 1);
    62          final Vector3D p2 = Vector3D.of(2, 0, 1);
    63          final Vector3D p3 = Vector3D.of(1, 1, 1);
    64
    65          final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(p1, p2, p3), TEST_PRECISION);
    66
    67          // act
    68          final PlaneConvexSubset reversed = sp.reverse();
    69
    70          // assert
    71          Assertions.assertEquals(sp.getPlane().reverse(), reversed.getPlane());
    72          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z, reversed.getPlane().getNormal(), TEST_EPS);
    73
    74          Assertions.assertEquals(0.5, reversed.getSize(), TEST_EPS);
    75
    76          checkVertices(reversed, p1, p3, p2);
    77
    78          checkPoints(reversed, RegionLocation.INSIDE, Vector3D.of(1.25, 0.25, 1));
    79
    80          checkPoints(reversed, RegionLocation.BOUNDARY, p1, p2, p3);
    81      }
    82
    83      @Test
    84      void testTransform_full() {
    85          // arrange
    86          final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    87          final PlaneConvexSubset sp = Planes.subsetFromConvexArea(plane, ConvexArea.full());
    88
    89          final AffineTransformMatrix3D transform = AffineTransformMatrix3D.identity()
    90                  .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_X, Angle.PI_OVER_TWO))
    91                  .translate(Vector3D.Unit.PLUS_Y);
    92
    93          // act
    94          final PlaneConvexSubset transformed = sp.transform(transform);
    95
    96          // assert
    97          Assertions.assertTrue(transformed.isFull());
    98          Assertions.assertFalse(transformed.isEmpty());
    99
    100         checkPlane(transformed.getPlane(), Vector3D.ZERO, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Z);
    101     }
    102
    103     @Test
    104     void testTransform_halfSpace() {
    105         // arrange
    106         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    107         final PlaneConvexSubset sp = Planes.subsetFromConvexArea(plane,
    108                 ConvexArea.fromBounds(Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(1, 1), TEST_PRECISION)));
    109
    110         final AffineTransformMatrix3D transform = AffineTransformMatrix3D.createRotation(Vector3D.Unit.PLUS_Z,
    111                 QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, Angle.PI_OVER_TWO));
    112
    113         // act
    114         final PlaneConvexSubset transformed = sp.transform(transform);
    115
    116         // assert
    117         Assertions.assertFalse(transformed.isFull());
    118         Assertions.assertFalse(transformed.isEmpty());
    119
    120         checkPlane(transformed.getPlane(), Vector3D.ZERO, Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y);
    121     }
    122
    123     @Test
    124     void testTransform_finite() {
    125         // arrange
    126         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(
    127                 Arrays.asList(Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), Vector3D.of(0, 0, 1)), TEST_PRECISION);
    128
    129         final Transform<Vector3D> transform = AffineTransformMatrix3D.createScale(2)
    130                 .rotate(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, Angle.PI_OVER_TWO));
    131
    132         // act
    133         final PlaneConvexSubset transformed = sp.transform(transform);
    134
    135         // assert
    136         final Vector3D midpt = Vector3D.of(2, 2, -2).multiply(1 / 3.0);
    137         final Vector3D normal = midpt.normalize();
    138         final Vector3D u = Vector3D.of(0, 2, 2).normalize();
    139
    140         checkPlane(transformed.getPlane(), midpt, u, normal.cross(u));
    141
    142         checkVertices(transformed, Vector3D.of(0, 0, -2), Vector3D.of(0, 2, 0), Vector3D.of(2, 0, 0));
    143
    144         checkPoints(transformed, RegionLocation.INSIDE, midpt);
    145     }
    146
    147     @Test
    148     void testTransform_reflection() {
    149         // arrange
    150         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(
    151                 Arrays.asList(Vector3D.of(1, 0, 0), Vector3D.of(0, 1, 0), Vector3D.of(0, 0, 1)), TEST_PRECISION);
    152
    153         final Transform<Vector3D> transform = AffineTransformMatrix3D.createScale(-1, 1, 1);
    154
    155         // act
    156         final PlaneConvexSubset transformed = sp.transform(transform);
    157
    158         // assert
    159         final Vector3D midpt = Vector3D.of(-1, 1, 1).multiply(1 / 3.0);
    160         final Vector3D normal = midpt.negate().normalize();
    161         final Vector3D u = Vector3D.of(1, 1, 0).normalize();
    162
    163         checkPlane(transformed.getPlane(), midpt, u, normal.cross(u));
    164
    165         checkVertices(transformed, Vector3D.of(-1, 0, 0), Vector3D.of(0, 1, 0), Vector3D.of(0, 0, 1));
    166
    167         checkPoints(transformed, RegionLocation.INSIDE, Vector3D.of(-1, 1, 1).multiply(1 / 3.0));
    168     }
    169
    170     @Test
    171     void testSplit_full() {
    172         // arrange
    173         final EmbeddingPlane plane = Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.PLUS_Z, TEST_PRECISION)
    174                 .getEmbedding();
    175         final PlaneConvexSubset sp = Planes.subsetFromConvexArea(plane, ConvexArea.full());
    176
    177         final Plane splitter = Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
    178
    179         // act
    180         final Split<PlaneConvexSubset> split = sp.split(splitter);
    181
    182         // assert
    183         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
    184
    185         final PlaneConvexSubset minus = split.getMinus();
    186         Assertions.assertEquals(1, minus.getEmbedded().getSubspaceRegion().getBoundaries().size());
    187         checkPoints(minus, RegionLocation.BOUNDARY, Vector3D.ZERO, Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Y);
    188         checkPoints(minus, RegionLocation.INSIDE, Vector3D.Unit.MINUS_X);
    189         checkPoints(minus, RegionLocation.OUTSIDE, Vector3D.Unit.PLUS_X);
    190
    191         final PlaneConvexSubset plus = split.getPlus();
    192         Assertions.assertEquals(1, plus.getEmbedded().getSubspaceRegion().getBoundaries().size());
    193         checkPoints(plus, RegionLocation.BOUNDARY, Vector3D.ZERO, Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_Y);
    194         checkPoints(plus, RegionLocation.INSIDE, Vector3D.Unit.PLUS_X);
    195         checkPoints(plus, RegionLocation.OUTSIDE, Vector3D.Unit.MINUS_X);
    196     }
    197
    198     @Test
    199     void testSplit_both() {
    200         // arrange
    201         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    202                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    203                 ), TEST_PRECISION);
    204
    205         final Plane splitter = Planes.fromPointAndNormal(Vector3D.ZERO, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    206
    207         // act
    208         final Split<PlaneConvexSubset> split = sp.split(splitter);
    209
    210         // assert
    211         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
    212
    213         final PlaneConvexSubset minus = split.getMinus();
    214         checkVertices(minus, Vector3D.of(1, 1, 0), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0));
    215
    216         final PlaneConvexSubset plus = split.getPlus();
    217         checkVertices(plus, Vector3D.of(1, 1, 1), Vector3D.of(1, 1, 0), Vector3D.of(0, 2, 0));
    218     }
    219
    220     @Test
    221     void testSplit_plusOnly() {
    222         // arrange
    223         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    224                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    225                 ), TEST_PRECISION);
    226
    227         final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, -3.1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    228
    229         // act
    230         final Split<PlaneConvexSubset> split = sp.split(splitter);
    231
    232         // assert
    233         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
    234
    235         Assertions.assertNull(split.getMinus());
    236
    237         final PlaneConvexSubset plus = split.getPlus();
    238         checkVertices(plus, Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0));
    239     }
    240
    241     @Test
    242     void testSplit_minusOnly() {
    243         // arrange
    244         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    245                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    246                 ), TEST_PRECISION);
    247
    248         final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 0, 1.1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    249
    250         // act
    251         final Split<PlaneConvexSubset> split = sp.split(splitter);
    252
    253         // assert
    254         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
    255
    256         final PlaneConvexSubset minus = split.getMinus();
    257         checkVertices(minus, Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0));
    258
    259         Assertions.assertNull(split.getPlus());
    260     }
    261
    262     @Test
    263     void testSplit_parallelSplitter_on() {
    264         // arrange
    265         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    266                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    267                 ), TEST_PRECISION);
    268
    269         final Plane splitter = sp.getPlane();
    270
    271         // act
    272         final Split<PlaneConvexSubset> split = sp.split(splitter);
    273
    274         // assert
    275         Assertions.assertEquals(SplitLocation.NEITHER, split.getLocation());
    276
    277         Assertions.assertNull(split.getMinus());
    278         Assertions.assertNull(split.getPlus());
    279     }
    280
    281     @Test
    282     void testSplit_parallelSplitter_minus() {
    283         // arrange
    284         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    285                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    286                 ), TEST_PRECISION);
    287
    288         final Plane plane = sp.getPlane();
    289         final Plane splitter = plane.translate(plane.getNormal());
    290
    291         // act
    292         final Split<PlaneConvexSubset> split = sp.split(splitter);
    293
    294         // assert
    295         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
    296
    297         Assertions.assertSame(sp, split.getMinus());
    298         Assertions.assertNull(split.getPlus());
    299     }
    300
    301     @Test
    302     void testSplit_parallelSplitter_plus() {
    303         // arrange
    304         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    305                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    306                 ), TEST_PRECISION);
    307
    308         final Plane plane = sp.getPlane();
    309         final Plane splitter = plane.translate(plane.getNormal().negate());
    310
    311         // act
    312         final Split<PlaneConvexSubset> split = sp.split(splitter);
    313
    314         // assert
    315         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
    316
    317         Assertions.assertNull(split.getMinus());
    318         Assertions.assertSame(sp, split.getPlus());
    319     }
    320
    321     @Test
    322     void testSplit_antiParallelSplitter_on() {
    323         // arrange
    324         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    325                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    326                 ), TEST_PRECISION);
    327
    328         final Plane splitter = sp.getPlane().reverse();
    329
    330         // act
    331         final Split<PlaneConvexSubset> split = sp.split(splitter);
    332
    333         // assert
    334         Assertions.assertEquals(SplitLocation.NEITHER, split.getLocation());
    335
    336         Assertions.assertNull(split.getMinus());
    337         Assertions.assertNull(split.getPlus());
    338     }
    339
    340     @Test
    341     void testSplit_antiParallelSplitter_minus() {
    342         // arrange
    343         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    344                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    345                 ), TEST_PRECISION);
    346
    347         final Plane plane = sp.getPlane().reverse();
    348         final Plane splitter = plane.translate(plane.getNormal());
    349
    350         // act
    351         final Split<PlaneConvexSubset> split = sp.split(splitter);
    352
    353         // assert
    354         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
    355
    356         Assertions.assertSame(sp, split.getMinus());
    357         Assertions.assertNull(split.getPlus());
    358     }
    359
    360     @Test
    361     void testSplit_antiParallelSplitter_plus() {
    362         // arrange
    363         final PlaneConvexSubset ps = Planes.convexPolygonFromVertices(Arrays.asList(
    364                     Vector3D.of(1, 1, 1), Vector3D.of(1, 1, -3), Vector3D.of(0, 2, 0)
    365                 ), TEST_PRECISION);
    366
    367         final Plane plane = ps.getPlane().reverse();
    368         final Plane splitter = plane.translate(plane.getNormal().negate());
    369
    370         // act
    371         final Split<PlaneConvexSubset> split = ps.split(splitter);
    372
    373         // assert
    374         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
    375
    376         Assertions.assertNull(split.getMinus());
    377         Assertions.assertSame(ps, split.getPlus());
    378     }
    379
    380     @Test
    381     void testSplit_usesVertexBasedTypesWhenPossible() {
    382         // arrange
    383         // create an infinite subset
    384         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.ZERO,
    385                 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    386         final PlaneConvexSubset ps = Planes.subsetFromConvexArea(plane, ConvexArea.fromBounds(
    387                     Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION),
    388                     Lines.fromPointAndAngle(Vector2D.of(1, 0), Angle.PI_OVER_TWO, TEST_PRECISION),
    389                     Lines.fromPointAndAngle(Vector2D.of(0, 1), -Angle.PI_OVER_TWO, TEST_PRECISION)
    390                 ));
    391
    392         final Plane splitter = Planes.fromPointAndNormal(Vector3D.of(0, 1, 0), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    393
    394         // act
    395         final Split<PlaneConvexSubset> split = ps.split(splitter);
    396
    397         // assert
    398         Assertions.assertTrue(ps.isInfinite());
    399
    400         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
    401
    402         final PlaneConvexSubset plus = split.getPlus();
    403         Assertions.assertNotNull(plus);
    404         Assertions.assertTrue(plus.isInfinite());
    405         Assertions.assertFalse(plus instanceof ConvexPolygon3D);
    406
    407         final PlaneConvexSubset minus = split.getMinus();
    408         Assertions.assertNotNull(minus);
    409         Assertions.assertFalse(minus.isInfinite());
    410         Assertions.assertTrue(minus instanceof ConvexPolygon3D);
    411     }
    412
    413     @Test
    414     void testIntersection_line() {
    415         // arrange
    416         final PlaneConvexSubset ps = Planes.convexPolygonFromVertices(Arrays.asList(
    417                 Vector3D.of(0, 0, 2), Vector3D.of(1, 0, 2), Vector3D.of(1, 1, 2), Vector3D.of(0, 1, 2)),
    418                 TEST_PRECISION);
    419
    420         // act/assert
    421         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0.5, 0.5, 2),
    422                 ps.intersection(Lines3D.fromPoints(Vector3D.of(0.5, 0.5, 2), Vector3D.ZERO, TEST_PRECISION)), TEST_EPS);
    423
    424         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 2),
    425                 ps.intersection(Lines3D.fromPoints(Vector3D.of(1, 1, 2), Vector3D.of(1, 1, 0), TEST_PRECISION)), TEST_EPS);
    426
    427         Assertions.assertNull(ps.intersection(Lines3D.fromPoints(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION)));
    428         Assertions.assertNull(ps.intersection(Lines3D.fromPoints(Vector3D.of(0, 0, 2), Vector3D.of(1, 1, 2), TEST_PRECISION)));
    429
    430         Assertions.assertNull(ps.intersection(Lines3D.fromPoints(Vector3D.of(4, 4, 2), Vector3D.of(4, 4, 0), TEST_PRECISION)));
    431     }
    432
    433     @Test
    434     void testIntersection_segment() {
    435         // arrange
    436         final PlaneConvexSubset sp = Planes.convexPolygonFromVertices(Arrays.asList(
    437                 Vector3D.of(0, 0, 2), Vector3D.of(1, 0, 2), Vector3D.of(1, 1, 2), Vector3D.of(0, 1, 2)),
    438                 TEST_PRECISION);
    439
    440         // act/assert
    441         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0.5, 0.5, 2),
    442                 sp.intersection(Lines3D.segmentFromPoints(Vector3D.of(0.5, 0.5, 2), Vector3D.ZERO, TEST_PRECISION)), TEST_EPS);
    443
    444         EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, 1, 2),
    445                 sp.intersection(Lines3D.segmentFromPoints(Vector3D.of(1, 1, 2), Vector3D.of(1, 1, 0), TEST_PRECISION)), TEST_EPS);
    446
    447         Assertions.assertNull(sp.intersection(Lines3D.segmentFromPoints(Vector3D.of(0.5, 0.5, 4), Vector3D.of(0.5, 0.5, 3), TEST_PRECISION)));
    448
    449         Assertions.assertNull(sp.intersection(Lines3D.segmentFromPoints(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION)));
    450         Assertions.assertNull(sp.intersection(Lines3D.segmentFromPoints(Vector3D.of(0, 0, 2), Vector3D.of(1, 1, 2), TEST_PRECISION)));
    451
    452         Assertions.assertNull(sp.intersection(Lines3D.segmentFromPoints(Vector3D.of(4, 4, 2), Vector3D.of(4, 4, 0), TEST_PRECISION)));
    453     }
    454
    455     private static void checkPlane(final Plane plane, final Vector3D origin, Vector3D u, Vector3D v) {
    456         u = u.normalize();
    457         v = v.normalize();
    458         final Vector3D w = u.cross(v);
    459
    460         EuclideanTestUtils.assertCoordinatesEqual(origin, plane.getOrigin(), TEST_EPS);
    461         Assertions.assertTrue(plane.contains(origin));
    462
    463         EuclideanTestUtils.assertCoordinatesEqual(w, plane.getNormal(), TEST_EPS);
    464         Assertions.assertEquals(1.0, plane.getNormal().norm(), TEST_EPS);
    465
    466         final double offset = plane.getOriginOffset();
    467         Assertions.assertEquals(Vector3D.ZERO.distance(plane.getOrigin()), Math.abs(offset), TEST_EPS);
    468         EuclideanTestUtils.assertCoordinatesEqual(origin, plane.getNormal().multiply(-offset), TEST_EPS);
    469     }
    470
    471     private static void checkPoints(final PlaneConvexSubset sp, final RegionLocation loc, final Vector3D... pts) {
    472         for (final Vector3D pt : pts) {
    473             Assertions.assertEquals(loc, sp.classify(pt), "Unexpected location for point " + pt);
    474         }
    475     }
    476
    477     private static void checkVertices(final PlaneConvexSubset ps, final Vector3D... pts) {
    478         EuclideanTestUtils.assertVertexLoopSequence(Arrays.asList(pts), ps.getVertices(), TEST_PRECISION);
    479     }
    480 }