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 }