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