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 org.apache.commons.geometry.core.GeometryTestUtils;
    20  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    21  import org.apache.commons.geometry.euclidean.threed.EmbeddingPlane.SubspaceTransform;
    22  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
    23  import org.apache.commons.geometry.euclidean.twod.AffineTransformMatrix2D;
    24  import org.apache.commons.geometry.euclidean.twod.Vector2D;
    25  import org.apache.commons.numbers.angle.Angle;
    26  import org.apache.commons.numbers.core.Precision;
    27  import org.junit.jupiter.api.Assertions;
    28  import org.junit.jupiter.api.Test;
    29
    30  class EmbeddingPlaneTest {
    31
    32      private static final double TEST_EPS = 1e-10;
    33
    34      private static final Precision.DoubleEquivalence TEST_PRECISION =
    35              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    36
    37      @Test
    38      void testFromPointAndPlaneVectors() {
    39          // arrange
    40          final Vector3D pt = Vector3D.of(1, 2, 3);
    41
    42          // act/assert
    43          checkPlane(Planes.fromPointAndPlaneVectors(pt, Vector3D.of(2, 0, 0), Vector3D.of(3, 0.1, 0),  TEST_PRECISION),
    44                  Vector3D.of(0, 0, 3), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y);
    45
    46          checkPlane(Planes.fromPointAndPlaneVectors(pt, Vector3D.of(2, 0, 0), Vector3D.of(3, -0.1, 0),  TEST_PRECISION),
    47                  Vector3D.of(0, 0, 3), Vector3D.Unit.PLUS_X, Vector3D.Unit.MINUS_Y);
    48
    49          checkPlane(Planes.fromPointAndPlaneVectors(pt, Vector3D.of(0, 0.1, 0), Vector3D.of(0, -3, 1),  TEST_PRECISION),
    50                  Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_Z);
    51      }
    52
    53      @Test
    54      void testFromPointAndPlaneVectors_illegalArguments() {
    55          // arrange
    56          final Vector3D pt = Vector3D.of(1, 2, 3);
    57
    58          // act/assert
    59
    60          // identical vectors
    61          Assertions.assertThrows(IllegalArgumentException.class, () -> Planes.fromPointAndPlaneVectors(pt, Vector3D.of(0, 0, 1), Vector3D.of(0, 0, 1), TEST_PRECISION));
    62          // zero vector
    63          Assertions.assertThrows(IllegalArgumentException.class, () -> Planes.fromPointAndPlaneVectors(pt, Vector3D.of(0, 0, 1), Vector3D.ZERO, TEST_PRECISION));
    64          // collinear vectors
    65          Assertions.assertThrows(IllegalArgumentException.class, () -> Planes.fromPointAndPlaneVectors(pt, Vector3D.of(0, 0, 1), Vector3D.of(0, 0, 2), TEST_PRECISION));
    66          // collinear vectors - reversed
    67          Assertions.assertThrows(IllegalArgumentException.class, () -> Planes.fromPointAndPlaneVectors(pt, Vector3D.of(0, 0, 1), Vector3D.of(0, 0, -2), TEST_PRECISION));
    68      }
    69
    70      @Test
    71      void testGetEmbedding() {
    72          // arrange
    73          final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.ZERO,
    74                  Vector3D.Unit.PLUS_X, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
    75
    76          // act/assert
    77          Assertions.assertSame(plane, plane.getEmbedding());
    78      }
    79
    80      @Test
    81      void testPointAt() {
    82          // arrange
    83          final Vector3D pt = Vector3D.of(0, 0, 1);
    84          final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt,
    85                  Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X, TEST_PRECISION);
    86
    87          // act/assert
    88          EuclideanTestUtils.assertCoordinatesEqual(pt, plane.pointAt(Vector2D.ZERO, 0), TEST_EPS);
    89          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.ZERO, plane.pointAt(Vector2D.ZERO, -1), TEST_EPS);
    90          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, -1), plane.pointAt(Vector2D.ZERO, -2), TEST_EPS);
    91          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 0, 2), plane.pointAt(Vector2D.ZERO, 1), TEST_EPS);
    92
    93          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 2, 1), plane.pointAt(Vector2D.of(2, 1), 0), TEST_EPS);
    94          EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(4, -3, 6), plane.pointAt(Vector2D.of(-3, -4), 5), TEST_EPS);
    95      }
    96
    97      @Test
    98      void testReverse() {
    99          // arrange
    100         final Vector3D pt = Vector3D.of(0, 0, 1);
    101         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt,
    102                 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    103
    104         // act
    105         final EmbeddingPlane reversed = plane.reverse();
    106
    107         // assert
    108         checkPlane(reversed, pt, Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_X);
    109
    110         Assertions.assertTrue(reversed.contains(Vector3D.of(1, 1, 1)));
    111         Assertions.assertTrue(reversed.contains(Vector3D.of(-1, -1, 1)));
    112         Assertions.assertFalse(reversed.contains(Vector3D.ZERO));
    113
    114         Assertions.assertEquals(1.0, reversed.offset(Vector3D.ZERO), TEST_EPS);
    115     }
    116
    117     @Test
    118     void testTransform_rotationAroundPoint() {
    119         // arrange
    120         final Vector3D pt = Vector3D.of(0, 0, 1);
    121         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt, Vector3D.Unit.PLUS_Y, Vector3D.Unit.MINUS_X, TEST_PRECISION);
    122
    123         final AffineTransformMatrix3D mat = AffineTransformMatrix3D.createRotation(pt,
    124                 QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, Angle.PI_OVER_TWO));
    125
    126         // act
    127         final EmbeddingPlane result = plane.transform(mat);
    128
    129         // assert
    130         checkPlane(result, Vector3D.ZERO, Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_Z);
    131     }
    132
    133     @Test
    134     void testTransform_asymmetricScaling() {
    135         // arrange
    136         final Vector3D pt = Vector3D.of(0, 1, 0);
    137         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt, Vector3D.Unit.MINUS_Z, Vector3D.of(-1, 1, 0), TEST_PRECISION);
    138
    139         final AffineTransformMatrix3D mat = AffineTransformMatrix3D.createScale(2, 1, 1);
    140
    141         // act
    142         final EmbeddingPlane result = plane.transform(mat);
    143
    144         // assert
    145         final Vector3D expectedU = Vector3D.Unit.MINUS_Z;
    146         final Vector3D expectedV = Vector3D.Unit.of(-2, 1, 0);
    147         final Vector3D expectedNormal = Vector3D.Unit.of(1, 2, 0);
    148
    149         final Vector3D transformedPt = mat.apply(plane.getOrigin());
    150         final Vector3D expectedOrigin = transformedPt.project(expectedNormal);
    151
    152         checkPlane(result, expectedOrigin, expectedU, expectedV);
    153
    154         Assertions.assertTrue(result.contains(transformedPt));
    155         Assertions.assertFalse(plane.contains(transformedPt));
    156     }
    157
    158     @Test
    159     void testTransform_negateOneComponent() {
    160         // arrange
    161         final Vector3D pt = Vector3D.of(0, 0, 1);
    162         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    163
    164         final AffineTransformMatrix3D transform = AffineTransformMatrix3D.from(v -> Vector3D.of(-v.getX(), v.getY(), v.getZ()));
    165
    166         // act
    167         final EmbeddingPlane result = plane.transform(transform);
    168
    169         // assert
    170         checkPlane(result, Vector3D.of(0, 0, 1), Vector3D.Unit.MINUS_X, Vector3D.Unit.PLUS_Y);
    171     }
    172
    173     @Test
    174     void testTransform_negateTwoComponents() {
    175         // arrange
    176         final Vector3D pt = Vector3D.of(0, 0, 1);
    177         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt, Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    178
    179         final AffineTransformMatrix3D transform = AffineTransformMatrix3D.from(v -> Vector3D.of(-v.getX(), -v.getY(), v.getZ()));
    180
    181         // act
    182         final EmbeddingPlane result = plane.transform(transform);
    183
    184         // assert
    185         checkPlane(result, Vector3D.of(0, 0, 1), Vector3D.Unit.MINUS_X, Vector3D.Unit.MINUS_Y);
    186     }
    187
    188     @Test
    189     void testTransform_negateAllComponents() {
    190         // arrange
    191         final Vector3D pt = Vector3D.of(0, 0, 1);
    192         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt,
    193                 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    194
    195         final AffineTransformMatrix3D transform = AffineTransformMatrix3D.from(Vector3D::negate);
    196
    197         // act
    198         final EmbeddingPlane result = plane.transform(transform);
    199
    200         // assert
    201         checkPlane(result, Vector3D.of(0, 0, -1), Vector3D.Unit.MINUS_X, Vector3D.Unit.MINUS_Y);
    202     }
    203
    204     @Test
    205     void testTransform_consistency() {
    206         // arrange
    207         final Vector3D pt = Vector3D.of(1, 2, 3);
    208         final Vector3D normal = Vector3D.Unit.from(1, 1, 1);
    209         final Vector3D u = normal.orthogonal(Vector3D.Unit.PLUS_X);
    210         final Vector3D v = normal.cross(u).normalize();
    211
    212         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(pt, u, v, TEST_PRECISION);
    213
    214         final Vector3D p1 = plane.project(Vector3D.of(4, 5, 6));
    215         final Vector3D p2 = plane.project(Vector3D.of(-7, -8, -9));
    216         final Vector3D p3 = plane.project(Vector3D.of(10, -11, 12));
    217
    218         final Vector3D notOnPlane1 = plane.getOrigin().add(plane.getNormal());
    219         final Vector3D notOnPlane2 = plane.getOrigin().subtract(plane.getNormal());
    220
    221         EuclideanTestUtils.permuteSkipZero(-4, 4, 1, (a, b, c) -> {
    222             final AffineTransformMatrix3D t = AffineTransformMatrix3D.identity()
    223                     .rotate(Vector3D.of(-1, 2, 3),
    224                             QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_X, 0.3 * a))
    225                     .scale(Math.max(a, 1), Math.max(b, 1), Math.max(c, 1))
    226                     .translate(c, b, a);
    227
    228             // act
    229             final EmbeddingPlane result = plane.transform(t);
    230
    231             // assert
    232             Vector3D expectedNormal = t.normalTransform().apply(plane.getNormal()).normalize();
    233             if (!t.preservesOrientation()) {
    234                 expectedNormal = expectedNormal.negate();
    235             }
    236
    237             EuclideanTestUtils.assertCoordinatesEqual(expectedNormal, result.getNormal(), TEST_EPS);
    238
    239             Assertions.assertTrue(result.contains(t.apply(p1)));
    240             Assertions.assertTrue(result.contains(t.apply(p2)));
    241             Assertions.assertTrue(result.contains(t.apply(p3)));
    242
    243             Assertions.assertFalse(result.contains(t.apply(notOnPlane1)));
    244             Assertions.assertFalse(result.contains(t.apply(notOnPlane2)));
    245         });
    246     }
    247
    248     @Test
    249     void testRotate() {
    250         // arrange
    251         final Vector3D p1 = Vector3D.of(1.2, 3.4, -5.8);
    252         final Vector3D p2 = Vector3D.of(3.4, -5.8, 1.2);
    253         final Vector3D p3 = Vector3D.of(-2.0, 4.3, 0.7);
    254         EmbeddingPlane plane  = Planes.fromPoints(p1, p2, p3, TEST_PRECISION).getEmbedding();
    255         final Vector3D oldNormal = plane.getNormal();
    256
    257         // act/assert
    258         plane = plane.rotate(p2, QuaternionRotation.fromAxisAngle(p2.subtract(p1), 1.7));
    259         Assertions.assertTrue(plane.contains(p1));
    260         Assertions.assertTrue(plane.contains(p2));
    261         Assertions.assertFalse(plane.contains(p3));
    262
    263         plane = plane.rotate(p2, QuaternionRotation.fromAxisAngle(oldNormal, 0.1));
    264         Assertions.assertFalse(plane.contains(p1));
    265         Assertions.assertTrue(plane.contains(p2));
    266         Assertions.assertFalse(plane.contains(p3));
    267
    268         plane = plane.rotate(p1, QuaternionRotation.fromAxisAngle(oldNormal, 0.1));
    269         Assertions.assertFalse(plane.contains(p1));
    270         Assertions.assertFalse(plane.contains(p2));
    271         Assertions.assertFalse(plane.contains(p3));
    272     }
    273
    274     @Test
    275     void testTranslate() {
    276         // arrange
    277         final Vector3D p1 = Vector3D.of(1.2, 3.4, -5.8);
    278         final Vector3D p2 = Vector3D.of(3.4, -5.8, 1.2);
    279         final Vector3D p3 = Vector3D.of(-2.0, 4.3, 0.7);
    280         EmbeddingPlane plane  = Planes.fromPoints(p1, p2, p3, TEST_PRECISION).getEmbedding();
    281
    282         // act/assert
    283         plane = plane.translate(Vector3D.Sum.create()
    284                 .addScaled(2.0, plane.getU())
    285                 .addScaled(-1.5, plane.getV()).get());
    286         Assertions.assertTrue(plane.contains(p1));
    287         Assertions.assertTrue(plane.contains(p2));
    288         Assertions.assertTrue(plane.contains(p3));
    289
    290         plane = plane.translate(plane.getNormal().multiply(-1.2));
    291         Assertions.assertFalse(plane.contains(p1));
    292         Assertions.assertFalse(plane.contains(p2));
    293         Assertions.assertFalse(plane.contains(p3));
    294
    295         plane = plane.translate(plane.getNormal().multiply(+1.2));
    296         Assertions.assertTrue(plane.contains(p1));
    297         Assertions.assertTrue(plane.contains(p2));
    298         Assertions.assertTrue(plane.contains(p3));
    299     }
    300
    301     @Test
    302     void testSubspaceTransform() {
    303         // arrange
    304         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(0, 0, 1),
    305                 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    306
    307         // act/assert
    308         checkSubspaceTransform(plane.subspaceTransform(AffineTransformMatrix3D.createScale(2, 3, 4)),
    309                 Vector3D.of(0, 0, 4), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y,
    310                 Vector3D.of(0, 0, 4), Vector3D.of(2, 0, 4), Vector3D.of(0, 3, 4));
    311
    312         checkSubspaceTransform(plane.subspaceTransform(AffineTransformMatrix3D.createTranslation(2, 3, 4)),
    313                 Vector3D.of(0, 0, 5), Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y,
    314                 Vector3D.of(2, 3, 5), Vector3D.of(3, 3, 5), Vector3D.of(2, 4, 5));
    315
    316         checkSubspaceTransform(plane.subspaceTransform(QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, Angle.PI_OVER_TWO)),
    317                 Vector3D.of(1, 0, 0), Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y,
    318                 Vector3D.of(1, 0, 0), Vector3D.of(1, 0, -1), Vector3D.of(1, 1, 0));
    319     }
    320
    321     private void checkSubspaceTransform(final SubspaceTransform st,
    322                                         final Vector3D origin, final Vector3D u, final Vector3D v,
    323                                         final Vector3D tOrigin, final Vector3D tU, final Vector3D tV) {
    324
    325         final EmbeddingPlane plane = st.getPlane();
    326         final AffineTransformMatrix2D transform = st.getTransform();
    327
    328         checkPlane(plane, origin, u, v);
    329
    330         EuclideanTestUtils.assertCoordinatesEqual(tOrigin, plane.toSpace(transform.apply(Vector2D.ZERO)), TEST_EPS);
    331         EuclideanTestUtils.assertCoordinatesEqual(tU, plane.toSpace(transform.apply(Vector2D.Unit.PLUS_X)), TEST_EPS);
    332         EuclideanTestUtils.assertCoordinatesEqual(tV, plane.toSpace(transform.apply(Vector2D.Unit.PLUS_Y)), TEST_EPS);
    333     }
    334
    335     @Test
    336     void testSubspaceTransform_transformsPointsCorrectly() {
    337         // arrange
    338         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 2, 3),
    339                 Vector3D.of(-1, -1, 1), Vector3D.of(-1, 1, 1), TEST_PRECISION);
    340
    341         EuclideanTestUtils.permuteSkipZero(-2, 2, 0.5, (a, b, c) -> {
    342             // create a somewhat complicate transform to try to hit all of the edge cases
    343             final AffineTransformMatrix3D transform = AffineTransformMatrix3D.createTranslation(Vector3D.of(a, b, c))
    344                     .rotate(QuaternionRotation.fromAxisAngle(Vector3D.of(b, c, a), Math.PI * c))
    345                     .scale(0.1, 4, 8);
    346
    347             // act
    348             final SubspaceTransform st = plane.subspaceTransform(transform);
    349
    350             // assert
    351             EuclideanTestUtils.permute(-5, 5, 1, (x, y) -> {
    352                 final Vector2D subPt = Vector2D.of(x, y);
    353                 final Vector3D expected = transform.apply(plane.toSpace(subPt));
    354                 final Vector3D actual = st.getPlane().toSpace(
    355                         st.getTransform().apply(subPt));
    356
    357                 EuclideanTestUtils.assertCoordinatesEqual(expected, actual, TEST_EPS);
    358             });
    359         });
    360     }
    361
    362     @Test
    363     void testEq_stdAndEmbedding() {
    364         // arrange
    365         final Plane stdPlane = Planes.fromPointAndNormal(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    366         final EmbeddingPlane embeddingPlane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 1, 1),
    367                 Vector3D.of(1, 1, 0), Vector3D.of(-1, 1, 0), TEST_PRECISION);
    368
    369         final EmbeddingPlane nonEqEmbeddingPlane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 1, 1),
    370                 Vector3D.of(1, 1, 1), Vector3D.of(-1, 1, 1), TEST_PRECISION);
    371
    372         // act/assert
    373         Assertions.assertTrue(stdPlane.eq(embeddingPlane, TEST_PRECISION));
    374         Assertions.assertTrue(embeddingPlane.eq(stdPlane, TEST_PRECISION));
    375
    376         Assertions.assertFalse(stdPlane.eq(nonEqEmbeddingPlane, TEST_PRECISION));
    377         Assertions.assertFalse(nonEqEmbeddingPlane.eq(stdPlane, TEST_PRECISION));
    378     }
    379
    380     @Test
    381     void testSimilarOrientation_stdAndEmbedding() {
    382         // arrange
    383         final Plane stdPlane = Planes.fromPointAndNormal(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    384         final EmbeddingPlane embeddingPlane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 1, 1),
    385                 Vector3D.of(1, 1, 1), Vector3D.of(-1, 1, 1), TEST_PRECISION);
    386
    387         final EmbeddingPlane nonSimilarEmbeddingPlane = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 1, 1),
    388                 Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_X, TEST_PRECISION);
    389
    390         // act/assert
    391         Assertions.assertTrue(stdPlane.similarOrientation(embeddingPlane));
    392         Assertions.assertTrue(embeddingPlane.similarOrientation(stdPlane));
    393
    394         Assertions.assertFalse(stdPlane.similarOrientation(nonSimilarEmbeddingPlane));
    395         Assertions.assertFalse(nonSimilarEmbeddingPlane.similarOrientation(stdPlane));
    396     }
    397
    398     @Test
    399     void testHashCode() {
    400         // arrange
    401         final Vector3D pt = Vector3D.of(1, 2, 3);
    402         final Vector3D u = Vector3D.Unit.PLUS_X;
    403         final Vector3D v = Vector3D.Unit.PLUS_Y;
    404
    405         final EmbeddingPlane a = Planes.fromPointAndPlaneVectors(pt, u, v, TEST_PRECISION);
    406         final EmbeddingPlane b = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 2, 4), u, v, TEST_PRECISION);
    407         final EmbeddingPlane c = Planes.fromPointAndPlaneVectors(pt, Vector3D.of(1, 1, 0), v, TEST_PRECISION);
    408         final EmbeddingPlane d = Planes.fromPointAndPlaneVectors(pt, u, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
    409         final EmbeddingPlane e = Planes.fromPointAndPlaneVectors(pt, u, v, Precision.doubleEquivalenceOfEpsilon(1e-8));
    410         final EmbeddingPlane f = Planes.fromPointAndPlaneVectors(pt, u, v, TEST_PRECISION);
    411
    412         // act/assert
    413         final int hash = a.hashCode();
    414
    415         Assertions.assertEquals(hash, a.hashCode());
    416
    417         Assertions.assertNotEquals(hash, b.hashCode());
    418         Assertions.assertNotEquals(hash, c.hashCode());
    419         Assertions.assertNotEquals(hash, d.hashCode());
    420         Assertions.assertNotEquals(hash, e.hashCode());
    421
    422         Assertions.assertEquals(hash, f.hashCode());
    423     }
    424
    425     @Test
    426     void testEquals() {
    427         // arrange
    428         final Vector3D pt = Vector3D.of(1, 2, 3);
    429         final Vector3D u = Vector3D.Unit.PLUS_X;
    430         final Vector3D v = Vector3D.Unit.PLUS_Y;
    431
    432         final EmbeddingPlane a = Planes.fromPointAndPlaneVectors(pt, u, v, TEST_PRECISION);
    433         final EmbeddingPlane b = Planes.fromPointAndPlaneVectors(Vector3D.of(1, 2, 4), u, v, TEST_PRECISION);
    434         final EmbeddingPlane c = Planes.fromPointAndPlaneVectors(pt, Vector3D.Unit.MINUS_X, v, TEST_PRECISION);
    435         final EmbeddingPlane d = Planes.fromPointAndPlaneVectors(pt, u, Vector3D.Unit.MINUS_Y, TEST_PRECISION);
    436         final EmbeddingPlane e = Planes.fromPointAndPlaneVectors(pt, u, v, Precision.doubleEquivalenceOfEpsilon(1e-8));
    437         final EmbeddingPlane f = Planes.fromPointAndPlaneVectors(pt, u, v, TEST_PRECISION);
    438
    439         final Plane stdPlane = Planes.fromPointAndNormal(pt, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    440
    441         // act/assert
    442         GeometryTestUtils.assertSimpleEqualsCases(a);
    443
    444         Assertions.assertNotEquals(a, b);
    445         Assertions.assertNotEquals(a, c);
    446         Assertions.assertNotEquals(a, d);
    447         Assertions.assertNotEquals(a, e);
    448
    449         Assertions.assertEquals(a, f);
    450         Assertions.assertEquals(f, a);
    451
    452         Assertions.assertNotEquals(a, stdPlane);
    453     }
    454
    455     @Test
    456     void testToString() {
    457         // arrange
    458         final EmbeddingPlane plane = Planes.fromPointAndPlaneVectors(Vector3D.ZERO,
    459                 Vector3D.Unit.PLUS_X, Vector3D.Unit.PLUS_Y, TEST_PRECISION);
    460
    461         // act
    462         final String str = plane.toString();
    463
    464         // assert
    465         Assertions.assertTrue(str.startsWith("EmbeddingPlane["));
    466         Assertions.assertTrue(str.matches(".*origin= \\(0(\\.0)?, 0(\\.0)?\\, 0(\\.0)?\\).*"));
    467         Assertions.assertTrue(str.matches(".*u= \\(1(\\.0)?, 0(\\.0)?\\, 0(\\.0)?\\).*"));
    468         Assertions.assertTrue(str.matches(".*v= \\(0(\\.0)?, 1(\\.0)?\\, 0(\\.0)?\\).*"));
    469         Assertions.assertTrue(str.matches(".*w= \\(0(\\.0)?, 0(\\.0)?\\, 1(\\.0)?\\).*"));
    470     }
    471
    472     private static void checkPlane(final EmbeddingPlane plane, final Vector3D origin, Vector3D u, Vector3D v) {
    473         u = u.normalize();
    474         v = v.normalize();
    475         final Vector3D w = u.cross(v);
    476
    477         EuclideanTestUtils.assertCoordinatesEqual(origin, plane.getOrigin(), TEST_EPS);
    478         Assertions.assertTrue(plane.contains(origin));
    479
    480         EuclideanTestUtils.assertCoordinatesEqual(u, plane.getU(), TEST_EPS);
    481         Assertions.assertEquals(1.0, plane.getU().norm(), TEST_EPS);
    482
    483         EuclideanTestUtils.assertCoordinatesEqual(v, plane.getV(), TEST_EPS);
    484         Assertions.assertEquals(1.0, plane.getV().norm(), TEST_EPS);
    485
    486         EuclideanTestUtils.assertCoordinatesEqual(w, plane.getW(), TEST_EPS);
    487         Assertions.assertEquals(1.0, plane.getW().norm(), TEST_EPS);
    488
    489         EuclideanTestUtils.assertCoordinatesEqual(w, plane.getNormal(), TEST_EPS);
    490         Assertions.assertEquals(1.0, plane.getNormal().norm(), TEST_EPS);
    491
    492         final double offset = plane.getOriginOffset();
    493         Assertions.assertEquals(Vector3D.ZERO.distance(plane.getOrigin()), Math.abs(offset), TEST_EPS);
    494         EuclideanTestUtils.assertCoordinatesEqual(origin, plane.getNormal().multiply(-offset), TEST_EPS);
    495     }
    496 }