1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.geometry;
17
18 import com.irurueta.algebra.Matrix;
19 import com.irurueta.algebra.WrongSizeException;
20
21
22
23
24 @SuppressWarnings("DuplicatedCode")
25 public class RotationUtils {
26
27
28
29
30 public static final int N_ANGULAR_RATES = 3;
31
32
33
34
35 public static final int SKEW_MATRIX_SIZE = 4;
36
37
38
39
40 private RotationUtils() {
41 }
42
43
44
45
46
47
48
49
50
51
52
53 public static void w2omega(final double w1, final double w2, final double w3, final Matrix result) {
54 if (result.getRows() != SKEW_MATRIX_SIZE || result.getColumns() != SKEW_MATRIX_SIZE) {
55 throw new IllegalArgumentException("result must be 4x4");
56 }
57
58 result.setElementAt(0, 0, 0.0);
59 result.setElementAt(1, 0, w1);
60 result.setElementAt(2, 0, w2);
61 result.setElementAt(3, 0, w3);
62
63 result.setElementAt(0, 1, -w1);
64 result.setElementAt(1, 1, 0.0);
65 result.setElementAt(2, 1, -w3);
66 result.setElementAt(3, 1, w2);
67
68 result.setElementAt(0, 2, -w2);
69 result.setElementAt(1, 2, w3);
70 result.setElementAt(2, 2, 0.0);
71 result.setElementAt(3, 2, -w1);
72
73 result.setElementAt(0, 3, -w3);
74 result.setElementAt(1, 3, -w2);
75 result.setElementAt(2, 3, w1);
76 result.setElementAt(3, 3, 0.0);
77 }
78
79
80
81
82
83
84
85
86
87
88 public static void w2omega(final double[] w, final Matrix result) {
89 if (w.length != N_ANGULAR_RATES) {
90 throw new IllegalArgumentException("w must have length 3");
91 }
92 w2omega(w[0], w[1], w[2], result);
93 }
94
95
96
97
98
99
100
101
102
103
104 public static Matrix w2omega(final double w1, final double w2, final double w3) {
105 Matrix result = null;
106 try {
107 result = new Matrix(SKEW_MATRIX_SIZE, SKEW_MATRIX_SIZE);
108 w2omega(w1, w2, w3, result);
109 } catch (final WrongSizeException ignore) {
110
111 }
112
113 return result;
114 }
115
116
117
118
119
120
121
122
123 public static Matrix w2omega(final double[] w) {
124 if (w.length != N_ANGULAR_RATES) {
125 throw new IllegalArgumentException("w must have length 3");
126 }
127 return w2omega(w[0], w[1], w[2]);
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 public static void quaternionToPiMatrix(final Quaternion quaternion, final Matrix result) {
142 if (result.getRows() != Quaternion.N_PARAMS || result.getColumns() != Quaternion.N_ANGLES) {
143 throw new IllegalArgumentException("result matrix must be 4x3");
144 }
145
146 final var a = quaternion.getA();
147 final var b = quaternion.getB();
148 final var c = quaternion.getC();
149 final var d = quaternion.getD();
150
151 result.setElementAt(0, 0, -b);
152 result.setElementAt(1, 0, a);
153 result.setElementAt(2, 0, d);
154 result.setElementAt(3, 0, -c);
155
156 result.setElementAt(0, 1, -c);
157 result.setElementAt(1, 1, -d);
158 result.setElementAt(2, 1, a);
159 result.setElementAt(3, 1, b);
160
161 result.setElementAt(0, 2, -d);
162 result.setElementAt(1, 2, c);
163 result.setElementAt(2, 2, -b);
164 result.setElementAt(3, 2, a);
165 }
166
167
168
169
170
171
172
173
174
175
176
177 public static Matrix quaternionToPiMatrix(final Quaternion quaternion) {
178 Matrix m = null;
179 try {
180 m = new Matrix(Quaternion.N_PARAMS, Quaternion.N_ANGLES);
181 quaternionToPiMatrix(quaternion, m);
182 } catch (final WrongSizeException ignore) {
183
184 }
185 return m;
186 }
187
188
189
190
191
192
193
194
195
196
197
198 public static void quaternionToConjugatedPiMatrix(final Quaternion quaternion, final Matrix result) {
199 quaternionToPiMatrix(quaternion.conjugateAndReturnNew(), result);
200 }
201
202
203
204
205
206
207
208
209
210
211 public static Matrix quaternionToConjugatedPiMatrix(final Quaternion quaternion) {
212 Matrix m = null;
213 try {
214 m = new Matrix(Quaternion.N_PARAMS, Quaternion.N_ANGLES);
215 quaternionToConjugatedPiMatrix(quaternion, m);
216 } catch (final WrongSizeException ignore) {
217
218 }
219 return m;
220 }
221
222
223
224
225
226
227
228
229
230 public static void piMatrixToConjugatedPiMatrix(final Matrix pi, final Matrix result) {
231 if (pi.getRows() != Quaternion.N_PARAMS || pi.getColumns() != Quaternion.N_ANGLES) {
232 throw new IllegalArgumentException("pi must be 4x3");
233 }
234 if (result.getRows() != Quaternion.N_PARAMS || result.getColumns() != Quaternion.N_ANGLES) {
235 throw new IllegalArgumentException("result must be 4x3");
236 }
237
238 result.copyFrom(pi);
239
240 result.setElementAtIndex(0, -pi.getElementAtIndex(0));
241 result.setElementAtIndex(2, -pi.getElementAtIndex(2));
242 result.setElementAtIndex(3, -pi.getElementAtIndex(3));
243 result.setElementAtIndex(4, -pi.getElementAtIndex(4));
244 result.setElementAtIndex(5, -pi.getElementAtIndex(5));
245 result.setElementAtIndex(7, -pi.getElementAtIndex(7));
246 result.setElementAtIndex(8, -pi.getElementAtIndex(8));
247 result.setElementAtIndex(9, -pi.getElementAtIndex(9));
248 result.setElementAtIndex(10, -pi.getElementAtIndex(10));
249 }
250
251
252
253
254
255
256
257
258
259 public static Matrix piMatrixToConjugatedPiMatrix(final Matrix pi) {
260 Matrix m = null;
261 try {
262 m = new Matrix(Quaternion.N_PARAMS, Quaternion.N_ANGLES);
263 piMatrixToConjugatedPiMatrix(pi, m);
264 } catch (final WrongSizeException ignore) {
265
266 }
267 return m;
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 public static void rotationMatrixTimesVector(
285 final Quaternion q, final double[] point, final double[] result, final Matrix jacobianQ,
286 final Matrix jacobianP) {
287 if (point.length != Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH) {
288 throw new IllegalArgumentException("point must have length 3");
289 }
290 if (result.length != Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH) {
291 throw new IllegalArgumentException("result must have length 3");
292 }
293 if (jacobianQ != null && (jacobianQ.getRows() != Quaternion.N_ANGLES
294 || jacobianQ.getColumns() != Quaternion.N_PARAMS)) {
295 throw new IllegalArgumentException("jacobian wrt of quaternion must be 3x4");
296 }
297 if (jacobianP != null && (jacobianP.getRows() != MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS
298 || jacobianP.getColumns() != MatrixRotation3D.ROTATION3D_INHOM_MATRIX_COLS)) {
299 throw new IllegalArgumentException("jacobian wrt of point must be 3x3");
300 }
301
302 try {
303 final var r = new Matrix(MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS,
304 MatrixRotation3D.ROTATION3D_INHOM_MATRIX_COLS);
305 q.toMatrixRotation(r);
306
307 if (jacobianP != null) {
308 jacobianP.copyFrom(r);
309 }
310
311 final var p = Matrix.newFromArray(point, true);
312 r.multiply(p);
313
314
315 r.getSubmatrixAsArray(0, 0, r.getRows() - 1,
316 r.getColumns() - 1, result);
317
318 if (jacobianQ != null) {
319 final var a = q.getA();
320 final var b = q.getB();
321 final var c = q.getC();
322 final var d = q.getD();
323
324 final var x = point[0];
325 final var y = point[1];
326 final var z = point[2];
327
328 final var axdycz = 2.0 * (a * x - d * y + c * z);
329 final var bxcydz = 2.0 * (b * x + c * y + d * z);
330 final var cxbyaz = 2.0 * (c * x - b * y - a * z);
331 final var dxaybz = 2.0 * (d * x + a * y - b * z);
332
333 jacobianQ.setElementAt(0, 0, axdycz);
334 jacobianQ.setElementAt(1, 0, dxaybz);
335 jacobianQ.setElementAt(2, 0, -cxbyaz);
336
337 jacobianQ.setElementAt(0, 1, bxcydz);
338 jacobianQ.setElementAt(1, 1, cxbyaz);
339 jacobianQ.setElementAt(2, 1, dxaybz);
340
341 jacobianQ.setElementAt(0, 2, -cxbyaz);
342 jacobianQ.setElementAt(1, 2, bxcydz);
343 jacobianQ.setElementAt(2, 2, -axdycz);
344
345 jacobianQ.setElementAt(0, 3, -dxaybz);
346 jacobianQ.setElementAt(1, 3, axdycz);
347 jacobianQ.setElementAt(2, 3, bxcydz);
348 }
349 } catch (final WrongSizeException ignore) {
350
351 }
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369 public static double[] rotationMatrixTimesVector(
370 final Quaternion q, final double[] point, final Matrix jacobianQ, final Matrix jacobianP) {
371 final var result = new double[Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH];
372 rotationMatrixTimesVector(q, point, result, jacobianQ, jacobianP);
373 return result;
374 }
375
376
377
378
379
380
381
382
383
384
385
386
387 public static void rotationMatrixTimesVector(final Quaternion q, final double[] point, final double[] result) {
388 rotationMatrixTimesVector(q, point, result, null, null);
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403 public static double[] rotationMatrixTimesVector(final Quaternion q, final double[] point) {
404 return rotationMatrixTimesVector(q, point, null, null);
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public static void rotationMatrixTimesVector(
421 final Quaternion q, final Point3D point, final Point3D result, final Matrix jacobianQ,
422 final Matrix jacobianP) {
423 final var coords = new double[]{point.getInhomX(), point.getInhomY(), point.getInhomZ()};
424 final var rp = rotationMatrixTimesVector(q, coords, jacobianQ, jacobianP);
425 result.setInhomogeneousCoordinates(rp[0], rp[1], rp[2]);
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441 public static Point3D rotationMatrixTimesVector(
442 final Quaternion q, final Point3D point, final Matrix jacobianQ, final Matrix jacobianP) {
443 final var result = Point3D.create();
444 rotationMatrixTimesVector(q, point, result, jacobianQ, jacobianP);
445 return result;
446 }
447
448
449
450
451
452
453
454
455
456
457 public static void rotationMatrixTimesVector(final Quaternion q, final Point3D point, final Point3D result) {
458 rotationMatrixTimesVector(q, point, result, null, null);
459 }
460
461
462
463
464
465
466
467
468
469
470 public static Point3D rotationMatrixTimesVector(final Quaternion q, final Point3D point) {
471 return rotationMatrixTimesVector(q, point, null, null);
472 }
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488 public static void transposedRotationMatrixTimesVector(
489 final Quaternion q, final double[] point, final double[] result, final Matrix jacobianQ,
490 final Matrix jacobianP) {
491 if (point.length != Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH) {
492 throw new IllegalArgumentException("point must have length 3");
493 }
494 if (result.length != Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH) {
495 throw new IllegalArgumentException("result must have length 3");
496 }
497 if (jacobianQ != null && (jacobianQ.getRows() != Quaternion.N_ANGLES
498 || jacobianQ.getColumns() != Quaternion.N_PARAMS)) {
499 throw new IllegalArgumentException("jacobian wrt of quaternion must be 3x4");
500 }
501 if (jacobianP != null && (jacobianP.getRows() != MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS
502 || jacobianP.getColumns() != MatrixRotation3D.ROTATION3D_INHOM_MATRIX_COLS)) {
503 throw new IllegalArgumentException("jacobian wrt of point must be 3x3");
504 }
505
506 try {
507 final var rt = new Matrix(MatrixRotation3D.ROTATION3D_INHOM_MATRIX_ROWS,
508 MatrixRotation3D.ROTATION3D_INHOM_MATRIX_COLS);
509 q.toMatrixRotation(rt);
510
511
512 rt.transpose();
513
514 if (jacobianP != null) {
515 jacobianP.copyFrom(rt);
516 }
517
518 final var p = Matrix.newFromArray(point, true);
519 rt.multiply(p);
520
521
522 rt.getSubmatrixAsArray(0, 0, rt.getRows() - 1,
523 rt.getColumns() - 1, result);
524
525 if (jacobianQ != null) {
526 final var a = q.getA();
527 final var b = q.getB();
528 final var c = q.getC();
529 final var d = q.getD();
530
531 final var x = p.getElementAtIndex(0);
532 final var y = p.getElementAtIndex(1);
533 final var z = p.getElementAtIndex(2);
534
535 final var axdycz = 2.0 * (a * x + d * y - c * z);
536 final var bxcydz = 2.0 * (b * x + c * y + d * z);
537 final var cxbyaz = 2.0 * (c * x - b * y + a * z);
538 final var dxaybz = 2.0 * (d * x - a * y - b * z);
539
540 jacobianQ.setElementAt(0, 0, axdycz);
541 jacobianQ.setElementAt(1, 0, -dxaybz);
542 jacobianQ.setElementAt(2, 0, cxbyaz);
543
544 jacobianQ.setElementAt(0, 1, bxcydz);
545 jacobianQ.setElementAt(1, 1, cxbyaz);
546 jacobianQ.setElementAt(2, 1, dxaybz);
547
548 jacobianQ.setElementAt(0, 2, -cxbyaz);
549 jacobianQ.setElementAt(1, 2, bxcydz);
550 jacobianQ.setElementAt(2, 2, axdycz);
551
552 jacobianQ.setElementAt(0, 3, -dxaybz);
553 jacobianQ.setElementAt(1, 3, -axdycz);
554 jacobianQ.setElementAt(2, 3, bxcydz);
555 }
556 } catch (final WrongSizeException ignore) {
557
558 }
559 }
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575 public static double[] transposedRotationMatrixTimesVector(
576 final Quaternion q, final double[] point, final Matrix jacobianQ, final Matrix jacobianP) {
577 final var result = new double[Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH];
578 transposedRotationMatrixTimesVector(q, point, result, jacobianQ, jacobianP);
579 return result;
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593 public static void transposedRotationMatrixTimesVector(
594 final Quaternion q, final double[] point, final double[] result) {
595 transposedRotationMatrixTimesVector(q, point, result, null, null);
596 }
597
598
599
600
601
602
603
604
605
606
607
608
609
610 public static double[] transposedRotationMatrixTimesVector(final Quaternion q, final double[] point) {
611 return transposedRotationMatrixTimesVector(q, point, null, null);
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627 public static void transposedRotationMatrixTimesVector(
628 final Quaternion q, final Point3D point, final Point3D result, final Matrix jacobianQ,
629 final Matrix jacobianP) {
630 final var coords = new double[]{point.getInhomX(), point.getInhomY(), point.getInhomZ()};
631 final var rp = transposedRotationMatrixTimesVector(q, coords, jacobianQ, jacobianP);
632 result.setInhomogeneousCoordinates(rp[0], rp[1], rp[2]);
633 }
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648 public static Point3D transposedRotationMatrixTimesVector(
649 final Quaternion q, final Point3D point, final Matrix jacobianQ, final Matrix jacobianP) {
650 final var result = Point3D.create();
651 transposedRotationMatrixTimesVector(q, point, result, jacobianQ, jacobianP);
652 return result;
653 }
654
655
656
657
658
659
660
661
662
663 public static void transposedRotationMatrixTimesVector(
664 final Quaternion q, final Point3D point, final Point3D result) {
665 transposedRotationMatrixTimesVector(q, point, result, null, null);
666 }
667
668
669
670
671
672
673
674
675
676 public static Point3D transposedRotationMatrixTimesVector(final Quaternion q, final Point3D point) {
677 return transposedRotationMatrixTimesVector(q, point, null, null);
678 }
679
680
681
682
683
684
685 public static void cameraBodyToCameraSensorRotation(final MatrixRotation3D result) {
686 Quaternion.eulerToMatrixRotation(-Math.PI / 2.0, 0, -Math.PI / 2.0, result);
687 }
688
689
690
691
692
693
694 public static MatrixRotation3D cameraBodyToCameraSensorRotation() {
695 final var result = new MatrixRotation3D();
696 cameraBodyToCameraSensorRotation(result);
697 return result;
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715 public static void quaternionToEulerGaussian(
716 final Quaternion q, final Matrix quaternionCovariance, final double[] angles,
717 final Matrix anglesCovariance) {
718 if (quaternionCovariance.getRows() != Quaternion.N_PARAMS
719 || quaternionCovariance.getColumns() != Quaternion.N_PARAMS) {
720 throw new IllegalArgumentException("quaternion covariance must be 4x4");
721 }
722
723 try {
724 final var eq = new Matrix(Quaternion.N_ANGLES, Quaternion.N_PARAMS);
725 q.toEulerAngles(angles, eq);
726
727
728 anglesCovariance.copyFrom(eq);
729 anglesCovariance.multiply(quaternionCovariance);
730 anglesCovariance.multiply(eq.transposeAndReturnNew());
731 } catch (final WrongSizeException ignore) {
732
733 }
734 }
735
736
737
738
739
740
741
742
743
744
745 public static void angularRatesToSkew(final double[] angularRates, final Matrix result) {
746 if (angularRates.length != Quaternion.N_ANGLES) {
747 throw new IllegalArgumentException("angular rates must have length 3");
748 }
749 if (result.getRows() != Quaternion.N_PARAMS || result.getColumns() != Quaternion.N_PARAMS) {
750 throw new IllegalArgumentException("result matrix must be 4x4");
751 }
752
753 result.setElementAt(0, 0, 0.0);
754 result.setElementAt(1, 0, angularRates[0]);
755 result.setElementAt(2, 0, angularRates[1]);
756 result.setElementAt(3, 0, angularRates[2]);
757
758 result.setElementAt(0, 1, -angularRates[0]);
759 result.setElementAt(1, 1, 0.0);
760 result.setElementAt(2, 1, -angularRates[2]);
761 result.setElementAt(3, 1, angularRates[1]);
762
763 result.setElementAt(0, 2, -angularRates[1]);
764 result.setElementAt(1, 2, angularRates[2]);
765 result.setElementAt(2, 2, 0.0);
766 result.setElementAt(3, 2, -angularRates[0]);
767
768 result.setElementAt(0, 3, -angularRates[2]);
769 result.setElementAt(1, 3, -angularRates[1]);
770 result.setElementAt(2, 3, angularRates[0]);
771 result.setElementAt(3, 3, 0.0);
772 }
773
774
775
776
777
778
779
780
781
782
783 public static Matrix angularRatesToSkew(final double[] angularRates) {
784 Matrix result = null;
785 try {
786 result = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
787 angularRatesToSkew(angularRates, result);
788 } catch (final WrongSizeException ignore) {
789
790 }
791 return result;
792 }
793
794 }