1 /*
2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.geometry;
17
18 import com.irurueta.algebra.ArrayUtils;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.algebra.Utils;
21 import com.irurueta.algebra.WrongSizeException;
22 import com.irurueta.geometry.estimators.EuclideanTransformation3DEstimator;
23 import com.irurueta.geometry.estimators.LockedException;
24 import com.irurueta.geometry.estimators.NotReadyException;
25
26 import java.io.Serializable;
27 import java.util.ArrayList;
28
29 /**
30 * This class performs Euclidean transformations on 3D space.
31 * Euclidean transformations include transformations related to rotations and
32 * translations.
33 * Scale cannot be modified on Euclidean transformation.
34 */
35 @SuppressWarnings("DuplicatedCode")
36 public class EuclideanTransformation3D extends Transformation3D implements Serializable {
37
38 /**
39 * Constant indicating number of coordinates required in translation arrays.
40 */
41 public static final int NUM_TRANSLATION_COORDS = 3;
42
43 /**
44 * Constant defining number of homogeneous coordinates in 3D space.
45 */
46 public static final int HOM_COORDS = 4;
47
48 /**
49 * 3D rotation to be performed on geometric objects.
50 */
51 private Rotation3D rotation;
52
53 /**
54 * 3D translation to be performed on geometric objects.
55 * Translation is specified using inhomogeneous coordinates.
56 */
57 private double[] translation;
58
59 /**
60 * Empty constructor.
61 * Creates transformation that has no effect.
62 */
63 public EuclideanTransformation3D() {
64 rotation = Rotation3D.create();
65 translation = new double[NUM_TRANSLATION_COORDS];
66 }
67
68 /**
69 * Creates transformation with provided rotation.
70 *
71 * @param rotation A 2D rotation.
72 * @throws NullPointerException Raised if provided rotation is null.
73 */
74 public EuclideanTransformation3D(final Rotation3D rotation) {
75 if (rotation == null) {
76 throw new NullPointerException();
77 }
78
79 this.rotation = rotation;
80 translation = new double[NUM_TRANSLATION_COORDS];
81 }
82
83 /**
84 * Creates transformation with provided 3D translation.
85 *
86 * @param translation Array indicating 3D translation using in-homogenous
87 * coordinates.
88 * @throws NullPointerException Raised if provided array is null.
89 * @throws IllegalArgumentException Raised if length of array is not equal
90 * to NUM_TRANSLATION_COORDS.
91 */
92 public EuclideanTransformation3D(final double[] translation) {
93 if (translation.length != NUM_TRANSLATION_COORDS) {
94 throw new IllegalArgumentException();
95 }
96
97 rotation = Rotation3D.create();
98 this.translation = translation;
99 }
100
101 /**
102 * Creates transformation with provided 3D rotation and translation.
103 *
104 * @param rotation A 3D rotation.
105 * @param translation Array indicating 3D translation using inhomogeneous
106 * coordinates.
107 * @throws NullPointerException Raised if provided array is null.
108 * @throws IllegalArgumentException Raised if length of array is not equal
109 * to NUM_TRANSLATION_COORDS.
110 */
111 public EuclideanTransformation3D(final Rotation3D rotation, final double[] translation) {
112 if (rotation == null) {
113 throw new NullPointerException();
114 }
115 if (translation.length != NUM_TRANSLATION_COORDS) {
116 throw new IllegalArgumentException();
117 }
118
119 this.rotation = rotation;
120 this.translation = translation;
121 }
122
123 /**
124 * Creates transformation by estimating its internal values using provided 4
125 * corresponding original and transformed points.
126 *
127 * @param inputPoint1 1st input point.
128 * @param inputPoint2 2nd input point.
129 * @param inputPoint3 3rd input point.
130 * @param inputPoint4 4th input point.
131 * @param outputPoint1 1st transformed point corresponding to 1st input
132 * point.
133 * @param outputPoint2 2nd transformed point corresponding to 2nd input
134 * point.
135 * @param outputPoint3 3rd transformed point corresponding to 3rd input
136 * point.
137 * @param outputPoint4 4th transformed point corresponding to 4th input
138 * point.
139 * @throws CoincidentPointsException raised if transformation cannot be
140 * estimated for some reason (point configuration degeneracy, duplicate
141 * points or numerical instabilities).
142 */
143 public EuclideanTransformation3D(
144 final Point3D inputPoint1, final Point3D inputPoint2, final Point3D inputPoint3, final Point3D inputPoint4,
145 final Point3D outputPoint1, final Point3D outputPoint2, final Point3D outputPoint3,
146 final Point3D outputPoint4) throws CoincidentPointsException {
147 internalSetTransformationFromPoints(inputPoint1, inputPoint2, inputPoint3, inputPoint4, outputPoint1,
148 outputPoint2, outputPoint3, outputPoint4);
149 }
150
151 /**
152 * Returns 3D rotation assigned to this transformation.
153 *
154 * @return 3D rotation.
155 */
156 public Rotation3D getRotation() {
157 return rotation;
158 }
159
160 /**
161 * Sets 3D rotation for this transformation.
162 *
163 * @param rotation A 3D rotation.
164 * @throws NullPointerException Raised if provided rotation is null.
165 */
166 public void setRotation(final Rotation3D rotation) {
167 if (rotation == null) {
168 throw new NullPointerException();
169 }
170 this.rotation = rotation;
171 }
172
173 /**
174 * Adds provided rotation to current rotation assigned to this
175 * transformation.
176 *
177 * @param rotation 3D rotation to be added.
178 */
179 public void addRotation(final Rotation3D rotation) {
180 this.rotation.combine(rotation);
181 }
182
183 /**
184 * Returns 3D translation assigned to this transformation as an array
185 * expressed in inhomogeneous coordinates.
186 *
187 * @return 3D translation array.
188 */
189 public double[] getTranslation() {
190 return translation;
191 }
192
193 /**
194 * Sets 3D translation assigned to this transformation as an array expressed
195 * in inhomogeneous coordinates.
196 *
197 * @param translation 3D translation array.
198 * @throws IllegalArgumentException raised if provided array does not have
199 * length equal to NUM_TRANSLATION_COORDS.
200 */
201 public void setTranslation(final double[] translation) {
202 if (translation.length != NUM_TRANSLATION_COORDS) {
203 throw new IllegalArgumentException();
204 }
205
206 this.translation = translation;
207 }
208
209 /**
210 * Adds provided translation to current translation on this transformation.
211 * Provided translation must be expressed as an array of inhomogeneous
212 * coordinates.
213 *
214 * @param translation 3D translation array.
215 * @throws IllegalArgumentException raised if provided array does not have
216 * length equal to NUM_TRANSLATION_COORDS.
217 */
218 public void addTranslation(final double[] translation) {
219 ArrayUtils.sum(this.translation, translation, this.translation);
220 }
221
222 /**
223 * Returns current x coordinate translation assigned to this transformation.
224 *
225 * @return X coordinate translation.
226 */
227 public double getTranslationX() {
228 return translation[0];
229 }
230
231 /**
232 * Sets x coordinate translation to be made by this transformation.
233 *
234 * @param translationX X coordinate translation to be set.
235 */
236 public void setTranslationX(final double translationX) {
237 translation[0] = translationX;
238 }
239
240 /**
241 * Returns current y coordinate translation assigned to this transformation.
242 *
243 * @return Y coordinate translation.
244 */
245 public double getTranslationY() {
246 return translation[1];
247 }
248
249 /**
250 * Sets y coordinate translation to be made by this transformation.
251 *
252 * @param translationY Y coordinate translation to be set.
253 */
254 public void setTranslationY(final double translationY) {
255 translation[1] = translationY;
256 }
257
258 /**
259 * Returns current z coordinate translation assigned to this transformation.
260 *
261 * @return Z coordinate translation.
262 */
263 public double getTranslationZ() {
264 return translation[2];
265 }
266
267 /**
268 * Sets z coordinate translation to be made by this transformation.
269 *
270 * @param translationZ Z coordinate translation to be set.
271 */
272 public void setTranslationZ(final double translationZ) {
273 translation[2] = translationZ;
274 }
275
276 /**
277 * Sets x, y, z coordinates of translation to be made by this
278 * transformation.
279 *
280 * @param translationX translation x coordinate to be set.
281 * @param translationY translation y coordinate to be set.
282 * @param translationZ translation z coordinate to be set.
283 */
284 public void setTranslation(
285 final double translationX, final double translationY, final double translationZ) {
286 translation[0] = translationX;
287 translation[1] = translationY;
288 translation[2] = translationZ;
289 }
290
291 /**
292 * Sets x, y, z coordinates of translation to be made by this
293 * transformation.
294 *
295 * @param translation translation to be set.
296 */
297 public void setTranslation(final Point3D translation) {
298 setTranslation(translation.getInhomX(), translation.getInhomY(), translation.getInhomZ());
299 }
300
301 /**
302 * Gets x, y, z coordinates of translation to be made by this transformation
303 * as a new point.
304 *
305 * @return a new point containing translation coordinates.
306 */
307 public Point3D getTranslationPoint() {
308 final var out = Point3D.create();
309 getTranslationPoint(out);
310 return out;
311 }
312
313 /**
314 * Gets x, y, z coordinates of translation to be made by this transformation
315 * and stores them into provided point.
316 *
317 * @param out point where translation coordinates will be stored.
318 */
319 public void getTranslationPoint(final Point3D out) {
320 out.setInhomogeneousCoordinates(translation[0], translation[1], translation[2]);
321 }
322
323 /**
324 * Adds provided x coordinate to current translation assigned to this
325 * transformation.
326 *
327 * @param translationX X coordinate to be added to current translation.
328 */
329 public void addTranslationX(final double translationX) {
330 translation[0] += translationX;
331 }
332
333 /**
334 * Adds provided y coordinate to current translation assigned to this
335 * transformation.
336 *
337 * @param translationY Y coordinate to be added to current translation.
338 */
339 public void addTranslationY(final double translationY) {
340 translation[1] += translationY;
341 }
342
343 /**
344 * Adds provided z coordinate to current translation assigned to this
345 * transformation.
346 *
347 * @param translationZ Z coordinate to be added to current translation.
348 */
349 public void addTranslationZ(final double translationZ) {
350 translation[2] += translationZ;
351 }
352
353 /**
354 * Adds provided coordinates to current translation assigned to this
355 * transformation.
356 *
357 * @param translationX x coordinate to be added to current translation.
358 * @param translationY y coordinate to be added to current translation.
359 * @param translationZ z coordinate to be added to current translation.
360 */
361 public void addTranslation(
362 final double translationX, final double translationY, final double translationZ) {
363 translation[0] += translationX;
364 translation[1] += translationY;
365 translation[2] += translationZ;
366 }
367
368 /**
369 * Adds provided coordinates to current translation assigned to this
370 * transformation.
371 *
372 * @param translation x, y, z coordinates to be added to current
373 * translation.
374 */
375 public void addTranslation(final Point3D translation) {
376 addTranslation(translation.getInhomX(), translation.getInhomY(), translation.getInhomZ());
377 }
378
379 /**
380 * Represents this transformation as a 4x4 matrix.
381 * A point can be transformed as T * p, where T is the transformation matrix
382 * and p is a point expressed as an homogeneous vector.
383 *
384 * @return This transformation in matrix form.
385 */
386 @Override
387 public Matrix asMatrix() {
388 Matrix m = null;
389 try {
390 m = new Matrix(HOM_COORDS, HOM_COORDS);
391 asMatrix(m);
392 } catch (final WrongSizeException ignore) {
393 // never happens
394 }
395 return m;
396 }
397
398 /**
399 * Represents this transformation as a 4x4 matrix and stores the result in
400 * provided instance.
401 *
402 * @param m instance where transformation matrix will be stored.
403 * @throws IllegalArgumentException raised if provided instance is not a 4x4
404 * matrix.
405 */
406 @Override
407 public void asMatrix(final Matrix m) {
408 if (m.getRows() != HOM_COORDS || m.getColumns() != HOM_COORDS) {
409 throw new IllegalArgumentException();
410 }
411
412 m.initialize(0.0);
413
414 // set rotation
415 m.setSubmatrix(0, 0, Rotation3D.INHOM_COORDS - 1,
416 Rotation3D.INHOM_COORDS - 1, rotation.asInhomogeneousMatrix());
417
418 // set translation
419 m.setSubmatrix(0, HOM_COORDS - 1, translation.length - 1,
420 HOM_COORDS - 1, translation);
421
422 // set last element
423 m.setElementAt(HOM_COORDS - 1, HOM_COORDS - 1, 1.0);
424 }
425
426 /**
427 * Transforms input point using this transformation and stores the result
428 * in provided output points.
429 *
430 * @param inputPoint point to be transformed.
431 * @param outputPoint instance where transformed point data will be stored.
432 */
433 @Override
434 public void transform(final Point3D inputPoint, final Point3D outputPoint) {
435 inputPoint.normalize();
436 rotation.rotate(inputPoint, outputPoint);
437 outputPoint.setInhomogeneousCoordinates(outputPoint.getInhomX() + translation[0],
438 outputPoint.getInhomY() + translation[1], outputPoint.getInhomZ() + translation[2]);
439 }
440
441 /**
442 * Transforms a quadric using this transformation and stores the result into
443 * provided output quadric.
444 *
445 * @param inputQuadric Quadric to be transformed.
446 * @param outputQuadric instance where data of transformed quadric will be
447 * stored.
448 * @throws NonSymmetricMatrixException raised if due to numerical precision
449 * the resulting output quadric matrix is not considered to be symmetric.
450 */
451 @Override
452 public void transform(final Quadric inputQuadric, final Quadric outputQuadric) throws NonSymmetricMatrixException {
453 // point' * quadric * point = 0
454 // point' * T' * transformedQuadric * T * point = 0
455 // where:
456 // - transformedPoint = T * point
457
458 // Hence:
459 // transformedQuadric = T^-1' * quadric * T^-1
460
461 inputQuadric.normalize();
462
463 final var q = inputQuadric.asMatrix();
464 final var invT = inverseAndReturnNew().asMatrix();
465 // normalize transformation matrix invT to increase accuracy
466 var norm = Utils.normF(invT);
467 invT.multiplyByScalar(1.0 / norm);
468
469 final var m = invT.transposeAndReturnNew();
470 try {
471 m.multiply(q);
472 m.multiply(invT);
473 } catch (final WrongSizeException ignore) {
474 // never happens
475 }
476
477 // normalize resulting m matrix to increase accuracy so that it can be
478 // considered symmetric
479 norm = Utils.normF(m);
480 m.multiplyByScalar(1.0 / norm);
481
482 outputQuadric.setParameters(m);
483 }
484
485 /**
486 * Transforms a dual quadric using this transformation and stores the result
487 * into provided output dual quadric.
488 *
489 * @param inputDualQuadric dual quadric to be transformed.
490 * @param outputDualQuadric instance where data of transformed dual quadric
491 * will be stored.
492 * @throws NonSymmetricMatrixException raised if due to numerical precision.
493 * the resulting output dual quadric matrix is not considered to be
494 * symmetric.
495 */
496 @Override
497 public void transform(final DualQuadric inputDualQuadric, final DualQuadric outputDualQuadric)
498 throws NonSymmetricMatrixException {
499 // plane' * dualQuadric * plane = 0
500 // plane' * T^-1 * T * dualQuadric * T' * T^-1'*plane
501
502 // Hence:
503 // transformed plane: T^-1'*plane
504 // transformed dual quadric: T * dualQuadric * T'
505
506 inputDualQuadric.normalize();
507
508 final var dualQ = inputDualQuadric.asMatrix();
509 final var t = asMatrix();
510 // normalize transformation matrix T to increase accuracy
511 var norm = Utils.normF(t);
512 t.multiplyByScalar(1.0 / norm);
513
514 final var transT = t.transposeAndReturnNew();
515 try {
516 t.multiply(dualQ);
517 t.multiply(transT);
518 } catch (final WrongSizeException ignore) {
519 // never happens
520 }
521
522 // normalize resulting m matrix to increase accuracy so that it can be
523 // considered symmetric
524 norm = Utils.normF(t);
525 t.multiplyByScalar(1.0 / norm);
526
527 outputDualQuadric.setParameters(t);
528 }
529
530 /**
531 * Transforms provided input plane using this transformation and stores the
532 * result into provided output plane instance.
533 *
534 * @param inputPlane plane to be transformed.
535 * @param outputPlane instance where data of transformed plane will be
536 * stored.
537 */
538 @Override
539 public void transform(final Plane inputPlane, final Plane outputPlane) {
540 // plane' * point = 0 --> plane' * T^-1 * T * point
541 // (plane' * T^-1)*(T*point) = (T^-1'*plane)'*(T*point)
542 // where:
543 // - transformedPlane = T^-1'*plane
544 // - transformedPoint = T*point
545
546 inputPlane.normalize();
547
548 final var invT = inverseAndReturnNew().asMatrix();
549 final var plane = Matrix.newFromArray(inputPlane.asArray());
550
551 // normalize transformation matrix T to increase accuracy
552 final var norm = Utils.normF(invT);
553 invT.multiplyByScalar(1.0 / norm);
554
555 invT.transpose();
556 try {
557 invT.multiply(plane);
558 } catch (final WrongSizeException ignore) {
559 // never happens
560 }
561
562 outputPlane.setParameters(invT.getBuffer());
563 }
564
565 /**
566 * Transforms a camera using this transformation and stores the result into
567 * provided output camera.
568 *
569 * @param inputCamera camera to be transformed.
570 * @param outputCamera instance where data of transformed camera will be
571 * stored.
572 */
573 @Override
574 public void transform(final PinholeCamera inputCamera, final PinholeCamera outputCamera) {
575 inputCamera.normalize();
576
577 final var invT = inverseAndReturnNew().asMatrix();
578 final var c = inputCamera.getInternalMatrix();
579 try {
580 c.multiply(invT);
581 outputCamera.setInternalMatrix(c);
582 } catch (final WrongSizeException ignore) {
583 // never thrown
584 }
585 }
586
587 /**
588 * Converts this transformation into a metric transformation.
589 *
590 * @return this transformation converted into a metric transformation.
591 */
592 public MetricTransformation3D toMetric() {
593 return new MetricTransformation3D(rotation, translation, MetricTransformation3D.DEFAULT_SCALE);
594 }
595
596 /**
597 * Inverses this transformation.
598 */
599 public void inverse() {
600 inverse(this);
601 }
602
603 /**
604 * Computes the inverse of this transformation and returns the result as a
605 * new transformation instance.
606 *
607 * @return inverse transformation.
608 */
609 public Transformation3D inverseAndReturnNew() {
610 final var result = new EuclideanTransformation3D();
611 inverse(result);
612 return result;
613 }
614
615 /**
616 * Combines this transformation with provided transformation.
617 * The combination is equivalent to multiplying the matrix of this
618 * transformation with the matrix of provided transformation.
619 *
620 * @param transformation Transformation to be combined with.
621 */
622 public void combine(final EuclideanTransformation3D transformation) {
623 combine(transformation, this);
624 }
625
626 /**
627 * Combines this transformation with provided transformation and returns
628 * the result as a new transformation instance.
629 * The combination is equivalent to multiplying the matrix of this
630 * transformation with the matrix pf provided transformation.
631 *
632 * @param transformation Transformation to be combined with.
633 * @return A new transformation resulting of the combination with this
634 * transformation and provided transformation.
635 */
636 public EuclideanTransformation3D combineAndReturnNew(final EuclideanTransformation3D transformation) {
637 final var result = new EuclideanTransformation3D();
638 combine(transformation, result);
639 return result;
640 }
641
642 /**
643 * Estimates this transformation internal parameters by using 4
644 * corresponding original and transformed points.
645 *
646 * @param inputPoint1 1st input point.
647 * @param inputPoint2 2nd input point.
648 * @param inputPoint3 3rd input point.
649 * @param inputPoint4 4th input point.
650 * @param outputPoint1 1st transformed point corresponding to 1st input
651 * point.
652 * @param outputPoint2 2nd transformed point corresponding to 2nd input
653 * point.
654 * @param outputPoint3 3rd transformed point corresponding to 3rd input
655 * point.
656 * @param outputPoint4 4th transformed point corresponding to 4th input
657 * point.
658 * @throws CoincidentPointsException raised if transformation cannot be
659 * estimated for some reason (point configuration degeneracy, duplicate
660 * points or numerical instabilities).
661 */
662 public void setTransformationFromPoints(
663 final Point3D inputPoint1, final Point3D inputPoint2, final Point3D inputPoint3, final Point3D inputPoint4,
664 final Point3D outputPoint1, final Point3D outputPoint2, final Point3D outputPoint3,
665 final Point3D outputPoint4) throws CoincidentPointsException {
666 internalSetTransformationFromPoints(inputPoint1, inputPoint2, inputPoint3, inputPoint4, outputPoint1,
667 outputPoint2, outputPoint3, outputPoint4);
668 }
669
670 /**
671 * Computes the inverse of this transformation and stores the result in
672 * provided instance.
673 *
674 * @param result instance where inverse transformation will be stored.
675 */
676 protected void inverse(final EuclideanTransformation3D result) {
677 // Transformation is as follows: x' = R* x + t
678 // Then inverse transformation is: R'* x' = R' * R * x + R'*t = x + R'*t
679 // --> x = R'*x' - R'*t
680
681 // reverse rotation
682 result.rotation = rotation.inverseRotationAndReturnNew();
683
684 // reverse translation
685 final var t = Matrix.newFromArray(translation, true);
686 t.multiplyByScalar(-1.0);
687 final var invRot = result.rotation.asInhomogeneousMatrix();
688 try {
689 invRot.multiply(t);
690 } catch (final WrongSizeException ignore) {
691 // never happens
692 }
693
694 result.translation = invRot.toArray();
695 }
696
697 /**
698 * Combines this transformation with provided input transformation and
699 * stores the result into provided output transformation.
700 * The combination is equivalent to multiplying the matrix of this
701 * transformation with the matrix of provided input transformation.
702 *
703 * @param inputTransformation transformation to be combined with.
704 * @param outputTransformation transformation where result will be stored.
705 */
706 private void combine(final EuclideanTransformation3D inputTransformation,
707 final EuclideanTransformation3D outputTransformation) {
708 // combination in matrix representation is:
709 // [R1 t1] * [R2 t2] = [R1*R2 + t1*0T R1*t2 + t1*1] = [R1*R2 R1*t2 + t1]
710 // [0T 1 ] [0T 1 ] [0T*R2 + 1*0T 0T*t2 + 1*1 ] [0T 1 ]
711
712 try {
713 // we do translation first, because this.rotation might change later
714 final var r1 = this.rotation.asInhomogeneousMatrix();
715 final var t2 = Matrix.newFromArray(inputTransformation.translation, true);
716 // this is R1 * t2
717 r1.multiply(t2);
718
719 ArrayUtils.sum(r1.toArray(), this.translation, outputTransformation.translation);
720
721 outputTransformation.rotation = this.rotation.combineAndReturnNew(inputTransformation.rotation);
722
723 } catch (final WrongSizeException ignore) {
724 // never happens
725 }
726 }
727
728 /**
729 * Estimates this transformation internal parameters by using 4
730 * corresponding original and transformed points.
731 *
732 * @param inputPoint1 1st input point.
733 * @param inputPoint2 2nd input point.
734 * @param inputPoint3 3rd input point.
735 * @param inputPoint4 4th input point.
736 * @param outputPoint1 1st transformed point corresponding to 1st input
737 * point.
738 * @param outputPoint2 2nd transformed point corresponding to 2nd input
739 * point.
740 * @param outputPoint3 3rd transformed point corresponding to 3rd input
741 * point.
742 * @param outputPoint4 4th transformed point corresponding to 4th input
743 * point.
744 * @throws CoincidentPointsException raised if transformation cannot be
745 * estimated for some reason (point configuration degeneracy, duplicate
746 * points or numerical instabilities).
747 */
748 private void internalSetTransformationFromPoints(
749 final Point3D inputPoint1, final Point3D inputPoint2, final Point3D inputPoint3, final Point3D inputPoint4,
750 final Point3D outputPoint1, final Point3D outputPoint2, final Point3D outputPoint3,
751 final Point3D outputPoint4) throws CoincidentPointsException {
752 final var inputPoints = new ArrayList<Point3D>();
753 inputPoints.add(inputPoint1);
754 inputPoints.add(inputPoint2);
755 inputPoints.add(inputPoint3);
756 inputPoints.add(inputPoint4);
757
758 final var outputPoints = new ArrayList<Point3D>();
759 outputPoints.add(outputPoint1);
760 outputPoints.add(outputPoint2);
761 outputPoints.add(outputPoint3);
762 outputPoints.add(outputPoint4);
763
764 final var estimator = new EuclideanTransformation3DEstimator(inputPoints, outputPoints);
765
766 try {
767 estimator.estimate(this);
768 } catch (final LockedException | NotReadyException ignore) {
769 // never thrown
770 }
771 }
772 }