Isis 3 Programmer Reference
LinearAlgebra.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "LinearAlgebra.h"
8
9// std library
10#include <cmath>
11
12// Qt Library
13#include <QDebug>
14#include <QtDebug>
15#include <QtGlobal>
16
17// boost library
18#include <boost/assign/std/vector.hpp>
19#include <boost/numeric/ublas/io.hpp>
20#include <boost/numeric/ublas/matrix.hpp>
21#include <boost/numeric/ublas/matrix_proxy.hpp>
22
23// Armadillo
24#include <armadillo>
25
26// other Isis library
27#include "Angle.h"
28#include "Constants.h"
29#include "IException.h"
30#include "IString.h"
31
32
33namespace Isis {
41
42
48
49
61 bool LinearAlgebra::isIdentity(const Matrix &matrix) {
62 if (matrix.size1() != matrix.size2()) return false;
63
64 for (unsigned int row = 0; row < matrix.size1(); row++) {
65 for (unsigned int col = 0; col < matrix.size2(); col++) {
66 if (row == col) {
67 if (!qFuzzyCompare(matrix(row, col), 1.0)) return false;
68 }
69 else {
70 // When using qFuzzy compare against a number that is zero
71 // you must offset the number per Qt documentation
72 if (!qFuzzyCompare(1.0 + matrix(row, col), 1.0)) return false;
73 }
74 }
75 }
76 return true;
77 }
78
79
89 bool LinearAlgebra::isOrthogonal(const Matrix &matrix) {
90 if (matrix.size1() != matrix.size2()) return false;
92 if (isIdentity(test)) {
93 // no need to test that transpose is both left and right inverse since the matrix is square
94 return true;
95 }
96 return false;
97 }
98
99
112 // derived from naif's isrot routine
114 // rotation matrices must be square
115 if (matrix.size1() != matrix.size2()) return false;
116
117 /* An exerpt from NAIF's isrot() routine...
118 *
119 * A property of rotation matrices is that their columns form a
120 * right-handed, orthonormal basis in 3-dimensional space. The
121 * converse is true: all 3x3 matrices with this property are
122 * rotation matrices.
123 *
124 * An ordered set of three vectors V1, V2, V3 forms a right-handed,
125 * orthonormal basis if and only if
126 *
127 * 1) || V1 || = || V2 || = || V3 || = 1
128 *
129 * 2) V3 = V1 x V2. Since V1, V2, and V3 are unit vectors,
130 * we also have
131 *
132 * < V3, V1 x V2 > = 1.
133 *
134 * This quantity is the determinant of the matrix whose
135 * colums are V1, V2 and V3.
136 *
137 * When finite precision numbers are used, rotation matrices will
138 * usually fail to satisfy these criteria exactly. We must use
139 * criteria that indicate approximate conformance to the criteria
140 * listed above. We choose
141 *
142 * 1) | || Vi || - 1 | < NTOL, i = 1, 2, 3.
143 * -
144 *
145 * 2) Let
146 *
147 * Vi
148 * Ui = ------ , i = 1, 2, 3.
149 * ||Vi||
150 *
151 * Then we require
152 *
153 * | < U3, U1 x U2 > - 1 | < DTOL;
154 * -
155 *
156 * equivalently, letting U be the matrix whose columns
157 * are U1, U2, and U3, we insist on
158 *
159 * | det(U) - 1 | < DTOL.
160 * _
161 * The columns of M must resemble unit vectors. If the norms are
162 * outside of the allowed range, M is not a rotation matrix.
163 *
164 * Also, the columns of M are required to be pretty nearly orthogonal. The
165 * discrepancy is gauged by taking the determinant of the matrix UNIT,
166 * computed below, whose columns are the unitized columns of M.
167 */
168
169 // create a matrix whose columns are unit vectors of the columns of the
170 // given matrix
171 Matrix unitMatrix(matrix.size1(), matrix.size2());
172
173 for (unsigned int i = 0; i < unitMatrix.size1(); i++) {
174 // get the ith column vector from the given matrix
175 Vector columnVector = column(matrix, i);
176 // get the magnitude and if it is not near 1, this is not a rotation matrix
177 double columnMagnitude = magnitude(columnVector);
178 if ( columnMagnitude < 0.9 || columnMagnitude > 1.1 ) {
179 return false;
180 }
181
182 // put the unitized row into the unitized matrix
183 setColumn(unitMatrix, columnVector / columnMagnitude, i);
184
185 }
186
187 try {
188 // get the determinant of the unitized matrix and if it is not near 1,
189 // this is not a rotation matrix
190 double det = determinant(unitMatrix);
191 if ( det >= 0.9 && det <= 1.1 ) {
192 return true;
193 }
194 }
195 catch (IException &e) {
196 // since we can only calculate the determinant for 2x2 or 3x3
197 QString msg = "Unable to determine whether the given matrix is a rotation matrix.";
198 throw IException(e, IException::Programmer, msg, _FILEINFO_);
199 }
200 return false;
201 }
202
203
211 bool LinearAlgebra::isZero(const Matrix &matrix) {
212 for (unsigned int i = 0; i < matrix.size1(); i++) {
213 for (unsigned int j = 0; j < matrix.size2(); j++) {
214 // When using qFuzzy compare against a number that is zero
215 // you must offset the number per Qt documentation
216 if (!qFuzzyCompare(matrix(i, j) + 1.0, 1.0)) return false;
217 }
218 }
219
220 return true;
221 }
222
223
231 bool LinearAlgebra::isZero(const Vector &vector) {
232 for (unsigned int i = 0; i < vector.size(); i++) {
233 // When using qFuzzy compare against a number that is zero
234 // you must offset the number per Qt documentation
235 if (!qFuzzyCompare(vector(i) + 1.0, 1.0)) return false;
236 }
237
238 return true;
239 }
240
241
251 return vector.empty();
252 }
253
254
262 bool LinearAlgebra::isUnit(const Vector &vector) {
263 if (qFuzzyCompare(dotProduct(vector, vector), 1.0)) {
264 return true;
265 }
266 return false;
267 }
268
269
283 try {
284 if (isOrthogonal(matrix)) {
285 return transpose(matrix);
286 }
287
288 // the determinant method will verify the size of the matrix is 2x2 or 3x3
289 double det = determinant(matrix);
290
291 if (qFuzzyCompare(det + 1.0, 1.0)) {
292 // the inverse exists <==> the determinant is not 0.0
293 QString msg = "The given matrix is not invertible. The determinant is 0.0.";
294 throw IException(IException::Programmer, msg, _FILEINFO_);
295 }
296
297 // since the determinant is not zero, we can calculate the reciprocal
298 double scale = 1 / det;
299
300 LinearAlgebra::Matrix inverse = identity(matrix.size1());
301 // find the inverse for 2x2
302 if (matrix.size1() == 2) {
303 inverse(0, 0) = scale * matrix(1, 1);
304 inverse(0, 1) = -scale * matrix(0, 1);
305
306 inverse(1, 0) = -scale * matrix(1, 0);
307 inverse(1, 1) = scale * matrix(0, 0);
308 return inverse;
309 }
310 // else we have a 3x3
311 inverse(0, 0) = scale * ( matrix(1, 1) * matrix(2, 2) - matrix(2, 1) * matrix(1, 2) );
312 inverse(0, 1) = scale * ( matrix(0, 2) * matrix(2, 1) - matrix(2, 2) * matrix(0, 1) );
313 inverse(0, 2) = scale * ( matrix(0, 1) * matrix(1, 2) - matrix(1, 1) * matrix(0, 2) );
314
315 inverse(1, 0) = scale * ( matrix(1, 2) * matrix(2, 0) - matrix(2, 2) * matrix(1, 0) );
316 inverse(1, 1) = scale * ( matrix(0, 0) * matrix(2, 2) - matrix(2, 0) * matrix(0, 2) );
317 inverse(1, 2) = scale * ( matrix(0, 2) * matrix(0, 1) - matrix(1, 2) * matrix(0, 0) );
318
319 inverse(2, 0) = scale * ( matrix(1, 0) * matrix(2, 1) - matrix(2, 0) * matrix(1, 1) );
320 inverse(2, 1) = scale * ( matrix(0, 1) * matrix(2, 0) - matrix(2, 1) * matrix(0, 0) );
321 inverse(2, 2) = scale * ( matrix(0, 0) * matrix(1, 1) - matrix(1, 0) * matrix(0, 1) );
322
323 return inverse;
324 }
325 catch (IException &e) {
326 QString msg = "Unable to invert the given matrix.";
327 throw IException(e, IException::Programmer, msg, _FILEINFO_);
328 }
329 }
330
331
340 // Copy values into Armadillo matrix
341 arma::mat arMat(matrix.size1(), matrix.size2());
342 for (size_t i = 0; i < matrix.size1(); i++) {
343 for (size_t j = 0; j < matrix.size2(); j++) {
344 arMat(i, j) = matrix(i, j);
345 }
346 }
347
348 arma::mat invArMat = arma::pinv(arMat);
349
350 // Copy values back to Boost matrix
351 LinearAlgebra::Matrix inverse(invArMat.n_rows, invArMat.n_cols);
352 for (size_t i = 0; i < invArMat.n_rows; i++) {
353 for (size_t j = 0; j < invArMat.n_cols; j++) {
354 inverse(i, j) = invArMat(i, j);
355 }
356 }
357
358 return inverse;
359 }
360
361
370 // no error testing required so call the boost transpose function
371 return boost::numeric::ublas::trans(matrix);
372 }
373
374
385 if (size < 1) {
386 QString msg = "Can not create identity matrix of negative size ["
387 + toString((int) size) + "].";
388 throw IException(IException::Programmer, msg, _FILEINFO_);
389 }
390
391 LinearAlgebra::Matrix m(size, size, 0.0);
392 for (int i = 0; i < size; i++) {
393 m(i, i) = 1.0;
394 }
395
396 return m;
397 }
398
399
409 boost::numeric::ublas::zero_matrix<double> m(rows, columns);
410 return m;
411 }
412
413
422 boost::numeric::ublas::zero_vector<double> v(size);
423 return v;
424 }
425
426
440 double LinearAlgebra::determinant(const Matrix &matrix) {
441 if ( (matrix.size1() != matrix.size2()) || (matrix.size1() != 2 && matrix.size1() != 3) ) {
442 QString msg = "Unable to calculate the determinant for the given matrix. "
443 "This method only calculates the determinant for 2x2 or 3x3 matrices."
444 "The given matrix is [" + toString((int) matrix.size1()) + "x"
445 + toString((int) matrix.size2()) + "].";
446 throw IException(IException::Programmer, msg, _FILEINFO_);
447 }
448
449 if (matrix.size1() == 2) {
450 return matrix(0, 0) * matrix(1, 1) - matrix(1, 0) * matrix(0, 1);
451 }
452 else {
453
454 return matrix(0, 0) * (matrix(1, 1)*matrix(2, 2) - matrix(1, 2)*matrix(2, 1))
455 - matrix(0, 1) * (matrix(1, 0)*matrix(2, 2) - matrix(1, 2)*matrix(2, 0))
456 + matrix(0, 2) * (matrix(1, 0)*matrix(2, 1) - matrix(1, 1)*matrix(2, 0));
457 }
458
459 }
460
461
478 // impossible to unitize the zero vector
479 if (LinearAlgebra::isZero(vector)) {
480 QString msg = "Unable to normalize the zero vector.";
481 throw IException(IException::Unknown, msg, _FILEINFO_);
482 }
483 return vector / LinearAlgebra::magnitude(vector);
484 }
485
486
504 double LinearAlgebra::magnitude(const Vector &vector) {
505 double magnitude;
506 // avoid dividing by maxNorm = 0
507 // no stabilization is needed for the zero vector, magnitude is always 0.0
508 if (LinearAlgebra::isZero(vector)) {
509 magnitude = 0.0;
510 }
511 else {
512 double maxNorm = LinearAlgebra::absoluteMaximum(vector);
513 magnitude = maxNorm * boost::numeric::ublas::norm_2(vector / maxNorm);
514 }
515 return magnitude;
516 }
517
518
532 return boost::numeric::ublas::norm_inf(vector);
533 }
534
535
550 // Check to make sure we can multiply
551 if (matrix1.size2() != matrix2.size1()) {
552 QString msg = "Unable to multiply matrices with mismatched dimensions. "
553 "The left matrix has [" + toString((int) matrix1.size2())
554 + "] columns and the right matrix has [" + toString((int) matrix2.size1())
555 + "] rows.";
556
557 throw IException(IException::Programmer, msg, _FILEINFO_);
558 }
559
560 // Use boost product routine
561 return boost::numeric::ublas::prod(matrix1, matrix2);
562 }
563
564
581 // Check to make sure we can multiply
582 if (matrix.size2() != vector.size()) {
583 QString msg = "Unable to multiply matrix and vector with mismatched dimensions."
584 "The given vector has [" + toString((int) vector.size())
585 + "] components and the given matrix has ["
586 + toString((int) matrix.size2()) + "] columns.";
587 throw IException(IException::Programmer, msg, _FILEINFO_);
588 }
589
590 // Use boost product routine
591 return boost::numeric::ublas::prod(matrix, vector);
592 }
593
594
604 // no error checks necessary (use the boost operator* method)
605 return scalar * vector;
606 }
607
608
618 // no error checks necessary
619 return scalar * matrix;
620 }
621
622
634 LinearAlgebra::Vector LinearAlgebra::add(const Vector &vector1, const Vector &vector2) {
635 // Vectors best be the same size
636 if (vector1.size() != vector2.size()) {
637 QString msg = "Unable to add vectors with mismatched sizes."
638 "Vector1 has [" + toString((int) vector1.size())
639 + "] components and vector2 has ["
640 + toString((int) vector2.size()) + "] components.";
641 throw IException(IException::Programmer, msg, _FILEINFO_);
642 }
643
644 return vector1 + vector2;
645 }
646
647
661 // Vectors best be the same size
662 if (vector1.size() != vector2.size()) {
663 QString msg = "Unable to subtract vectors with mismatched sizes."
664 "Vector1 has [" + toString((int) vector1.size())
665 + "] components and vector2 has ["
666 + toString((int) vector2.size()) + "] components.";
667 throw IException(IException::Programmer, msg, _FILEINFO_);
668 }
669
670 return vector1 - vector2;
671 }
672
673
689 if ((vector1.size() != 3) || (vector2.size() != 3)) {
690 QString msg = "Unable to calculate the cross product on vectors that are not size 3. "
691 "Vector1 has [" + toString((int) vector1.size())
692 + "] components and vector2 has ["
693 + toString((int) vector2.size()) + "] components.";
694 throw IException(IException::Programmer, msg, _FILEINFO_);
695 }
696
697 Vector crossProd(3);
698 crossProd(0) = vector1(1)*vector2(2) - vector1(2)*vector2(1);
699 crossProd(1) = vector1(2)*vector2(0) - vector1(0)*vector2(2);
700 crossProd(2) = vector1(0)*vector2(1) - vector1(1)*vector2(0);
701
702 return crossProd;
703 }
704
705
720 // is this derived from naif's ucrss routine???
722 const Vector &vector2) {
723
724 double maxVector1 = LinearAlgebra::absoluteMaximum(vector1);
725 double maxVector2 = LinearAlgebra::absoluteMaximum(vector2);
726
727 LinearAlgebra::Vector normalizedVector1 = vector1;
728 LinearAlgebra::Vector normalizedVector2 = vector2;
729
730 if (maxVector1 != 0.0) {
731 normalizedVector1 /= maxVector1;
732 }
733
734 if (maxVector2 != 0.0) {
735 normalizedVector2 /= maxVector2;
736 }
737
738 LinearAlgebra::Vector vcross = crossProduct(normalizedVector1, normalizedVector2);
739
740 return normalize(vcross);
741 }
742
743
758 if (vector1.size() != vector2.size()) {
759 QString msg = "Unable to compute the outer product for vectors with mismatched sizes."
760 "Vector1 has [" + toString((int) vector1.size())
761 + "] components and vector2 has ["
762 + toString((int) vector2.size()) + "] components.";
763 throw IException(IException::Programmer, msg, _FILEINFO_);
764 }
765 return boost::numeric::ublas::outer_prod(vector1, vector2);
766 }
767
768
781 double LinearAlgebra::dotProduct(const Vector &vector1, const Vector &vector2) {
782 // no error check needed - this is done by innerProduct
783 return LinearAlgebra::innerProduct(vector1, vector2);
784 }
785
786
801 double LinearAlgebra::innerProduct(const Vector &vector1, const Vector &vector2) {
802 if (vector1.size() != vector2.size()) {
803 QString msg = "Unable to compute the dot product for vectors with mismatched sizes."
804 "Vector1 has [" + toString((int) vector1.size())
805 + "] components and vector2 has ["
806 + toString((int) vector2.size()) + "] components.";
807 throw IException(IException::Programmer, msg, _FILEINFO_);
808 }
809 return boost::numeric::ublas::inner_prod(vector1, vector2);
810 }
811
812
832 // derived from naif's vproj routine
834 if (vector1.size() != vector2.size()) {
835 QString msg = "Unable to project vector1 onto vector2 with mismatched sizes."
836 "Vector1 has [" + toString((int) vector1.size())
837 + "] components and vector2 has ["
838 + toString((int) vector2.size()) + "] components.";
839 throw IException(IException::Programmer, msg, _FILEINFO_);
840 }
841
842 // if vector2 is the zero vector, then the projection of vector1 onto vector2
843 // is also the zero vector
844 if (LinearAlgebra::isZero(vector2)) return vector2;
845
846 // find the dot product of the two vectors
847 double v1Dotv2 = LinearAlgebra::dotProduct(vector1, vector2);
848
849 // find the square of the length of vector2 (this is the dot product of the vector2 with itself)
850 double v2Dotv2 = LinearAlgebra::dotProduct(vector2, vector2);
851
852 return v1Dotv2/v2Dotv2 * vector2;
853 }
854
855
871 // derived from naif's vrotv routine
873 Angle angle) {
874 if ((vector.size() != 3) || (axis.size() != 3)) {
875 QString msg = "Unable to rotate vector about the given axis and angle. "
876 "Vectors must be of size 3 to perform rotation. "
877 "The given vector has [" + toString((int) vector.size())
878 + "] components and the given axis has ["
879 + toString((int) axis.size()) + "] components.";
880 throw IException(IException::Programmer, msg, _FILEINFO_);
881 }
882
883 // if the given vector is the zero vector, then the rotation about the vector
884 // is also the zero vector
885 if (isZero(axis)) return vector;
886
887 // Compute the unit vector that is codirectional with the given axis
888 Vector axisUnitVector = normalize(axis);
889
890 // Compute the projection of the given vector onto the axis unit vector.
891 Vector projVectorOnAxis = project(vector, axisUnitVector);
892
893 // Compute the component of the input orthogonal to the AXIS. Call it V1.
894 Vector v1 = vector - projVectorOnAxis;
895
896 // Rotate V1 by 90 degrees about the AXIS and call the result V2.
897 Vector v2 = LinearAlgebra::crossProduct(axisUnitVector, v1);
898
899 // Compute cos(angle)*v1 + sin(angle)*v2. This is v1 rotated about
900 // the axis in the plane normal to the axis, call the result rplane
901 double c = cos(angle.radians());
902 double s = sin(angle.radians());
903 Vector rplane = (c*v1) + (s*v2);
904
905 // Add the rotated component in the normal plane to axis to the
906 // projection of v onto axis to obtain rotation.
907 return (rplane + projVectorOnAxis);
908 }
909
910
929 // Derived from NAIF's vperp routine
930 // possible that we may need to restrict size to dimesion 3, but doesn't seem
931 // necessary in the code.
933
934 // if vector1 is the zero vector, just set p to zero and return.
935 if ( isZero(vector1) ) {
936 return vector1;
937 }
938
939 // if vector2 is the zero vector, just set p to vector1 and return.
940 if ( isZero(vector2) ) {
941 return vector1;
942 }
943
944 // normalize (using the max norm) the given vectors and project the first onto the second
945 double max1 = absoluteMaximum(vector1);
946 double max2 = absoluteMaximum(vector2);
947 Vector parallelVector = project(vector1 / max1, vector2 / max2);
948
949 Vector perpendicularVector = vector1 - parallelVector * max1;
950
951 return perpendicularVector;
952
953 }
954
955
972 // Derived from NAIF's raxisa routine
974
975 if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
976 QString msg = "Unable to convert the given matrix to an axis of rotation "
977 "and a rotation angle. A 3x3 matrix is required. The given matrix is ["
978 + toString((int) rotationMatrix.size1()) + "x"
979 + toString((int) rotationMatrix.size2()) + "].";
980 throw IException(IException::Programmer, msg, _FILEINFO_);
981 }
982
983 // if given matrix is not a rotation matrix, we can't proceed.
984 if ( !isRotationMatrix(rotationMatrix) ) {
985 QString msg = "Unable to convert the given matrix to an axis of rotation "
986 "and a rotation angle. The given matrix is not a rotation matrix.";
987 throw IException(IException::Programmer, msg, _FILEINFO_);
988 }
989
990 Angle angle;
991 Vector axis(3);
992
993 // Construct the quaternion corresponding to the input rotation matrix
994 Vector quaternion = LinearAlgebra::toQuaternion(rotationMatrix);
995
997 subQuaternion[0] = quaternion[1];
998 subQuaternion[1] = quaternion[2];
999 subQuaternion[2] = quaternion[3];
1000
1001
1002 // The quaternion we've just constructed is of the form:
1003 //
1004 // cos(ANGLE/2) + sin(ANGLE/2) * AXIS
1005 //
1006 // We take a few precautions to handle the case of an identity
1007 // rotation.
1008 if (isZero(subQuaternion)) {
1009 axis(0) = 0.0;
1010 axis(1) = 0.0;
1011 axis(2) = 1.0;
1012
1013 angle.setRadians(0.0);
1014 }
1015 else if (qFuzzyCompare(quaternion(0) + 1.0, 1.0)) {
1016 axis = subQuaternion;
1017 angle.setRadians(Isis::PI);
1018 }
1019 else {
1020 axis = LinearAlgebra::normalize(subQuaternion);
1021 angle.setRadians(2.0 * atan2(magnitude(subQuaternion), quaternion(0)));
1022 }
1023
1024 return qMakePair(axis, angle);
1025 }
1026
1027
1050 // Derived from NAIF's m2eul routine
1051 QList<LinearAlgebra::EulerAngle> LinearAlgebra::toEulerAngles(const Matrix &rotationMatrix,
1052 const QList<int> axes) {
1053
1054 // check there are 3 axes in the set {1,2,3} with center axis not equal to first or last
1055 if (axes.size() != 3) {
1056 QString msg = "Unable to convert the given matrix to Euler angles. "
1057 "Exactly 3 axis codes are required. The given list has ["
1058 + toString((int) axes.size()) + "] axes.";
1059 throw IException(IException::Programmer, msg, _FILEINFO_);
1060 }
1061
1062 QSet<int> validAxes;
1063 validAxes << 1 << 2 << 3;
1064 if (!validAxes.contains(axes[0])
1065 || !validAxes.contains(axes[1])
1066 || !validAxes.contains(axes[2])) {
1067 QString msg = "Unable to convert the given matrix to Euler angles using the given axis codes "
1068 "[" + toString(axes[0]) + ", " + toString(axes[1]) + ", " + toString(axes[2])
1069 + "]. Axis codes must be 1, 2, or 3.";
1070 throw IException(IException::Programmer, msg, _FILEINFO_);
1071 }
1072
1073 if (axes[0] == axes[1] || axes[1] == axes[2]) {
1074 QString msg = "Unable to convert the given matrix to Euler angles using the given axis codes "
1075 "[" + toString(axes[0]) + ", " + toString(axes[1]) + ", " + toString(axes[2])
1076 + "]. The middle axis code must differ from its neighbors.";
1077 throw IException(IException::Programmer, msg, _FILEINFO_);
1078 }
1079
1080 // check the matrix is 3x3 rotation
1081 if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
1082 QString msg = "Unable to convert the given matrix to Euler angles. A 3x3 matrix is required. "
1083 "The given matrix is ["
1084 + toString((int) rotationMatrix.size1()) + "x"
1085 + toString((int) rotationMatrix.size2()) + "].";
1086 throw IException(IException::Programmer, msg, _FILEINFO_);
1087 }
1088
1089 if ( !isRotationMatrix(rotationMatrix) ) {
1090 QString msg = "Unable to convert the given matrix to Euler angles. "
1091 "The given matrix is not a rotation matrix.";
1092 throw IException(IException::Programmer, msg, _FILEINFO_);
1093 }
1094
1095 // AXIS3, AXIS2, AXIS1 and R have passed their tests at this
1096 // point. We take the liberty of working with TEMPROT, a
1097 // version of R that has unitized columns.
1098 // DO I = 1, 3
1099 // CALL VHAT ( R(1,I), TEMPROT(1,I) )
1100 // END DO
1101
1103 for (int i = 0; i < 3; i++) {
1104 Vector columnVector = column(rotationMatrix, i);
1105 // replace the ith column with a unitized column vector
1106 setColumn(tempRotation, normalize(columnVector), i);
1107 }
1108
1109 QList<int> nextAxis;
1110 nextAxis << 2 << 3 << 1;
1111 double sign;
1112 // create a matrix of zeros and we will fill in the non zero components below
1113 LinearAlgebra::Matrix change = zeroMatrix(3, 3);
1114
1116 if ( axes[0] == axes[2] ) {
1117 if ( axes[1] == nextAxis[axes[0] - 1] ) {
1118 sign = 1.0;
1119 }
1120 else {
1121 sign = -1.0;
1122 }
1123 int c = 6 - axes[0] - axes[1];
1124
1125 change( axes[0] - 1, 2 ) = 1.0;
1126 change( axes[1] - 1, 0 ) = 1.0;
1127 change( c - 1, 1 ) = sign * 1.0;
1128 LinearAlgebra::Matrix tempMatrix = multiply( tempRotation, change );
1129 tempRotation = multiply( transpose(change), tempMatrix );
1130
1131 bool degen = ( qFuzzyCompare(tempRotation(0, 2) + 1.0, 1.0)
1132 && qFuzzyCompare(tempRotation(1, 2) + 1.0, 1.0) )
1133 || ( qFuzzyCompare(tempRotation(2, 0) + 1.0, 1.0)
1134 && qFuzzyCompare(tempRotation(2, 1) + 1.0, 1.0) )
1135 || qFuzzyCompare( qAbs(tempRotation(2, 2)), 1.0 );
1136
1137 if ( degen ) {
1138
1139 angle3.setRadians( 0.0 );
1140 angle2.setRadians( acos( tempRotation(2, 2) ) );
1141 angle1.setRadians( atan2( tempRotation(0, 1), tempRotation(0, 0) ) );
1142
1143 }
1144 else {
1145 // the normal case.
1146 angle3.setRadians( atan2( tempRotation(0, 2), tempRotation(1, 2) ) );
1147 angle2.setRadians( acos( tempRotation(2, 2) ) );
1148 angle1.setRadians( atan2( tempRotation(2, 0), -tempRotation(2, 1) ) );
1149 }
1150 }
1151 else {
1152 // The axis order is c-b-a. We're going to find a matrix CHANGE
1153 // such that
1154 //
1155 // T
1156 // CHANGE R CHANGE
1157 if ( axes[1] == nextAxis[axes[0] - 1] ) {
1158 sign = 1.0;
1159 }
1160 else {
1161 sign = -1.0;
1162 }
1163 change( axes[0] - 1, 0 ) = 1.0;
1164 change( axes[1] - 1, 1 ) = 1.0;
1165 change( axes[2] - 1, 2 ) = sign * 1.0;
1166 LinearAlgebra::Matrix tempMatrix = multiply( tempRotation, change );
1167 tempRotation = multiply( transpose(change), tempMatrix );
1168 bool degen = ( qFuzzyCompare(tempRotation(0, 0) + 1.0, 1.0)
1169 && qFuzzyCompare(tempRotation(0, 1) + 1.0, 1.0) )
1170 || ( qFuzzyCompare(tempRotation(1, 2) + 1.0, 1.0)
1171 && qFuzzyCompare(tempRotation(2, 2) + 1.0, 1.0) )
1172 || qFuzzyCompare( qAbs(tempRotation(0, 2)), 1.0 );
1173
1174 if ( degen ) {
1175
1176 angle3.setRadians( 0.0 );
1177 angle2.setRadians( asin( -tempRotation(0, 2) ) );
1178 angle1.setRadians( sign * atan2( -tempRotation(1, 0), tempRotation(1, 1) ) );
1179
1180 }
1181 else {
1182 // the normal case.
1183 angle3.setRadians( atan2( tempRotation(1, 2), tempRotation(2, 2) ) );
1184 angle2.setRadians( asin( -tempRotation(0, 2) ) );
1185 angle1.setRadians( sign * atan2( tempRotation(0, 1), tempRotation(0, 0) ) );
1186 }
1187 }
1188
1189 QList<LinearAlgebra::EulerAngle> eulerAngles;
1190 eulerAngles << qMakePair(angle3, axes[0])
1191 << qMakePair(angle2, axes[1])
1192 << qMakePair(angle1, axes[2]);
1193 return eulerAngles;
1194 }
1195
1196
1210 // Derived from NAIF's m2q routine
1212
1213 if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
1214 QString msg = "Unable to convert the given matrix to a quaternion. "
1215 "A 3x3 matrix is required. The given matrix is ["
1216 + toString((int) rotationMatrix.size1()) + "x"
1217 + toString((int) rotationMatrix.size2()) + "].";
1218 throw IException(IException::Programmer, msg, _FILEINFO_);
1219 }
1220
1221 // if given matrix is not a rotation matrix, we can't proceed.
1222 if ( !isRotationMatrix(rotationMatrix) ) {
1223 QString msg = "Unable to convert the given matrix to an axis of rotation "
1224 "and a rotation angle. The given matrix is not a rotation matrix.";
1225 throw IException(IException::Programmer, msg, _FILEINFO_);
1226 }
1227
1228 // Let q = c + s1*i + s2*j + s3*k
1229 // where c is the constant and s1, s2, s3 are the coefficients of the imaginary parts
1230 // and |q| = 1, .
1231
1232 // Denote the following for n,m in {1,2,3}
1233 // cc = c * c
1234 // csn = c * sn
1235 // snm = sn * sm
1236
1237 // The rotation matrix corresponding to our quaternion is:
1238 //
1239 // | cc+s11-s22-s33 2*s12-2*cs3 2*s13+2*cs2 |
1240 // | 2*s12+2*cs3 cc-s11+s22-s33 2*s23-2*cs1 |
1241 // | 2*s13-2*cs2 2*s23+2*cs1 cc-s11-s22+s33 |
1242 //
1243 //
1244 // since |q| = cc + s11 + s22 + s33 = 1, we can use substitution on the diagonal entries to get
1245 //
1246 // | 1-2*s22-2*s33 2*s12-2*cs3 2*s13+2*cs2 |
1247 // | 2*s12+2*cs3 1-2*s11-2*s33 2*s23-2*cs1 |
1248 // | 2*s13-2*cs2 2*s23+2*cs1 1-2*s11-2*s22 |
1249 //
1250 //
1251 //
1252 // r(1,1) = 1.0 - 2*s22 - 2*s33
1253 // r(2,1) = 2*s12 + 2*cs3
1254 // r(3,1) = 2*s13 - 2*cs2
1255 //
1256 // r(1,2) = 2*s12 - 2*cs3
1257 // r(2,2) = 1.0 - 2*s11 - 2*s33
1258 // r(3,2) = 2*s23 + 2*cs1
1259 //
1260 // r(1,3) = 2*s13 + 2*cs2
1261 // r(2,3) = 2*s23 - 2*cs1
1262 // r(3,3) = 1.0 - 2*s11 - 2*s22
1263
1264 // Using this matrix, we get the trace by summing the diagonal entries
1265 // trace = (1-2*s22-2*s33) + (1-2*s11-2*s33) + (1-2*s11-2*s22)
1266 // = 3 - 4*(s11 + s22 + s33)
1267
1268 // Therefore
1269 // 1.0 + trace = 4 - 4*(s11 + s22 + s33)
1270 // = 4*(1 - s11 - s22 - s33) by factoring 4
1271 // = 4*(|q| - s11 - s22 - s33) since |q| = 1
1272 // = 4*cc since |q| = cc + s11 + s22 + s33
1273 //
1274 // Solving for c, we get that
1275 // 4*cc = 1 + trace
1276 // 2*c = +/- sqrt(1 + trace)
1277 // c = +/- 0.5 * sqrt( 1.0 + trace )
1278
1279 // We also have the following where {n,m,p} = {1,2,3}
1280 // 1.0 + trace - 2.0*r(n,n) = 4.0 - 4.0(snn + smm + spp) - 2(1.0 - 2.0(smm + spp ))
1281 // = 4.0 - 4.0(snn + smm + spp) - 2.0 + 4.0(smm + spp )
1282 // = 2.0 - 4.0*snn
1283 //
1284 // Solving for snn, we get
1285 // 2.0 - 4.0*snn = 1.0 + trace - 2.0*r(n,n)
1286 // -2.0 + 4.0*snn = -1.0 - trace + 2.0*r(n,n)
1287 // 4.0*snn = 1.0 - trace + 2.0*r(n,n) ******** we will use this value *************
1288 // ******** in our code to minimize *************
1289 // ******** computations *************
1290 // 2.0*sn = +/- sqrt(1.0 - trace + 2.0*r(n,n))
1291 // sn = +/- 0.5 * sqrt(1.0 - trace + 2.0*r(n,n)) for any n in {1,2,3}
1292
1293
1294 // In addition to these observations, note that all of the product
1295 // pairs can easily be computed by simplifying the expressions
1296 // (r(n,m) - r(m,n)) / 4.0 and (r(n,m) + r(m,n)) / 4.0
1297 //
1298 // For example,
1299 // (r(3,2) - r(2,3)) / 4.0 = ( (2*s23 + 2*cs1) - (2*s23 - 2*cs1) ) / 4.0
1300 // = ( 4*cs1 ) / 4.0
1301 // = cs1
1302 //
1303 // So we have the following
1304 // cs1 = (r(3,2) - r(2,3))/4.0
1305 // cs2 = (r(1,3) - r(3,1))/4.0
1306 // cs3 = (r(2,1) - r(1,2))/4.0
1307 // s12 = (r(2,1) + r(1,2))/4.0
1308 // s13 = (r(3,1) + r(1,3))/4.0
1309 // s23 = (r(2,3) + r(3,2))/4.0
1310
1311 //but taking sums or differences of numbers that are nearly equal
1312 //or nearly opposite results in a loss of precision. as a result
1313 //we should take some care in which terms to select when computing
1314 //c, s1, s2, s3. however, by simply starting with one of the
1315 //large quantities cc, s11, s22, or s33 we can make sure that we
1316 //use the best of the 6 quantities above when computing the
1317 //remaining components of the quaternion.
1318
1319 double trace = rotationMatrix(0, 0) + rotationMatrix(1, 1) + rotationMatrix(2, 2);
1320 double mtrace = 1.0 - trace;
1321
1322 // cc4 = 4 * c * c where c is the constant term (first) of the quaternion
1323 double cc4 = 1.0 + trace;
1324 // snn4 = 4 * sn * sn where sn is the coefficient for the n+1 term of the quaternion vector
1325 double s114 = mtrace + 2.0*rotationMatrix(0, 0);
1326 double s224 = mtrace + 2.0*rotationMatrix(1, 1);
1327 double s334 = mtrace + 2.0*rotationMatrix(2, 2);
1328
1329 // Note that cc4 + s114 + s224 + s334 = 4|q| = 4
1330 // Thus, at least one of the 4 terms is greater than 1.
1331 double normalizingFactor;
1333 if ( cc4 >= 1.0 ) { // true if the trace is non-negative
1334
1335 quaternion[0] = sqrt( cc4 * 0.25 ); // note that q0 = c = sqrt(4*c*c*0.25)
1336 normalizingFactor = 1.0 / ( quaternion[0] * 4.0 );
1337
1338 // multiply the difference of the entries symmetric about the diagonal by the factor 1/4c
1339 // to get s1, s2, and s3
1340 // (see calculations for cs1, cs2, cs3 in comments above)
1341 quaternion[1] = ( rotationMatrix(2, 1) - rotationMatrix(1, 2) ) * normalizingFactor;
1342 quaternion[2] = ( rotationMatrix(0, 2) - rotationMatrix(2, 0) ) * normalizingFactor;
1343 quaternion[3] = ( rotationMatrix(1, 0) - rotationMatrix(0, 1) ) * normalizingFactor;
1344
1345 }
1346 else if ( s114 >= 1.0 ) {
1347
1348 quaternion[1] = sqrt( s114 * 0.25 ); // note that q1 = s1 = sqrt(4*s1*s1*0.25)
1349 normalizingFactor = 1.0 /( quaternion[1] * 4.0 );
1350
1351 quaternion[0] = ( rotationMatrix(2, 1) - rotationMatrix(1, 2) ) * normalizingFactor;
1352 quaternion[2] = ( rotationMatrix(0, 1) + rotationMatrix(1, 0) ) * normalizingFactor;
1353 quaternion[3] = ( rotationMatrix(0, 2) + rotationMatrix(2, 0) ) * normalizingFactor;
1354
1355 }
1356 else if ( s224 >= 1.0 ) {
1357
1358 quaternion[2] = sqrt( s224 * 0.25 );
1359 normalizingFactor = 1.0 /( quaternion[2] * 4.0 );
1360
1361 quaternion[0] = ( rotationMatrix(0, 2) - rotationMatrix(2, 0) ) * normalizingFactor;
1362 quaternion[1] = ( rotationMatrix(0, 1) + rotationMatrix(1, 0) ) * normalizingFactor;
1363 quaternion[3] = ( rotationMatrix(1, 2) + rotationMatrix(2, 1) ) * normalizingFactor;
1364 }
1365 else { // s334 >= 1.0
1366
1367 quaternion[3] = sqrt( s334 * 0.25 );
1368 normalizingFactor = 1.0 /( quaternion[3] * 4.0 );
1369
1370 quaternion[0] = ( rotationMatrix(1, 0) - rotationMatrix(0, 1) ) * normalizingFactor;
1371 quaternion[1] = ( rotationMatrix(0, 2) + rotationMatrix(2, 0) ) * normalizingFactor;
1372 quaternion[2] = ( rotationMatrix(1, 2) + rotationMatrix(2, 1) ) * normalizingFactor;
1373
1374 }
1375
1376 // if the magnitude of this quaternion is not one, we polish it up a bit.
1377 if ( !isUnit(quaternion) ) {
1378 quaternion = normalize(quaternion);
1379 }
1380
1381 // M2Q always returns a quaternion with scalar part greater than or equal to zero.
1382 if ( quaternion[0] < 0.0 ) {
1383 quaternion *= -1.0;
1384 }
1385
1386 return quaternion;
1387 }
1388
1389
1404 // Derived from NAIF's axisar routine
1406 if (axis.size() != 3) {
1407 QString msg = "Unable to convert the given vector and angle to a rotation matrix. "
1408 "The given vector with size [" + toString((int) axis.size())
1409 + "] is not a 3D axis vector.";
1410 throw IException(IException::Programmer, msg, _FILEINFO_);
1411 }
1412
1413 // intialize the matrix with the 3x3 identity.
1414 LinearAlgebra::Matrix rotationMatrix = identity(3);
1415
1416 // The matrix we want rotates every vector by angle about axis.
1417 // In particular, it does so to our basis vectors. The columns
1418 // of the output matrix are the images of the basis vectors
1419 // under this rotation.
1420 for (unsigned int i = 0; i < axis.size(); i++) {
1421 // rotate the ith column of the matrix (the ith standard basis vector)
1422 // about the given axis using the given angle
1423 Vector stdBasisVector = column(rotationMatrix, i);
1424
1425 // replace the ith column with the rotated vector
1426 setColumn(rotationMatrix, rotate(stdBasisVector, axis, angle), i);
1427 }
1428
1429 return rotationMatrix;
1430 }
1431
1432
1443 return LinearAlgebra::toMatrix(axisAngle.first, axisAngle.second);
1444 }
1445
1446
1461 // Derived from NAIF's eul2m routine
1463 const EulerAngle &angle2,
1464 const EulerAngle &angle1) {
1465 QSet<int> validAxes;
1466 validAxes << 1 << 2 << 3;
1467 if (!validAxes.contains(angle3.second)
1468 || !validAxes.contains(angle2.second)
1469 || !validAxes.contains(angle1.second)) {
1470 QString msg = "Unable to convert the given Euler angles to a matrix using the given axis "
1471 "codes [" + toString(angle3.second) + ", " + toString(angle2.second) + ", "
1472 + toString(angle1.second) + "]. Axis codes must be 1, 2, or 3.";
1473 throw IException(IException::Programmer, msg, _FILEINFO_);
1474 }
1475
1477 // implement eul2m
1478 // Calculate the 3x3 rotation matrix generated by a rotation
1479 // of a specified angle about a specified axis. This rotation
1480 // is thought of as rotating the coordinate system.
1481 //
1482 //ROTATE(angle1, axis1);
1483 double sinAngle = sin(angle1.first.radians());
1484 double cosAngle = cos(angle1.first.radians());
1485 // get the index offset based on the axis number corresponding to this angle
1486 double indexOffset = ((angle1.second % 3) + 3) % 3;
1487 QList<int> indexSet;
1488 indexSet << 2 << 0 << 1 << 2 << 0;
1489 int index1 = indexSet[indexOffset + 0];
1490 int index2 = indexSet[indexOffset + 1];
1491 int index3 = indexSet[indexOffset + 2];
1492 m(index1, index1) = 1.0; m(index1, index2) = 0.0; m(index1, index3) = 0.0;
1493 m(index2, index1) = 0.0; m(index2, index2) = cosAngle; m(index2, index3) = sinAngle;
1494 m(index3, index1) = 0.0; m(index3, index2) = -sinAngle; m(index3, index3) = cosAngle;
1495
1496 //
1497 // C ROTMAT applies a rotation of ANGLE radians about axis IAXIS to a
1498 // C matrix. This rotation is thought of as rotating the coordinate
1499 // C system.
1500 // C
1501 //CALL ROTMAT ( R, ANGLE2, AXIS2, R1 )
1502 sinAngle = sin(angle2.first.radians());
1503 cosAngle = cos(angle2.first.radians());
1504 indexOffset = ((angle2.second % 3) + 3) % 3;
1505 index1 = indexSet[indexOffset + 0];
1506 index2 = indexSet[indexOffset + 1];
1507 index3 = indexSet[indexOffset + 2];
1508
1510 for (int i = 0; i < 3; i++) {
1511 tempMatrix(index1, i) = m(index1, i);
1512 tempMatrix(index2, i) = cosAngle*m(index2, i) + sinAngle*m(index3, i);
1513 tempMatrix(index3, i) = -sinAngle*m(index2, i) + cosAngle*m(index3, i);
1514 }
1515
1516 //CALL ROTMAT ( tempMatrix, ANGLE3, AXIS3, m )
1517 sinAngle = sin(angle3.first.radians());
1518 cosAngle = cos(angle3.first.radians());
1519 indexOffset = ((angle3.second % 3) + 3) % 3;
1520 index1 = indexSet[indexOffset + 0];
1521 index2 = indexSet[indexOffset + 1];
1522 index3 = indexSet[indexOffset + 2];
1523 for (int i = 0; i < 3; i++) {
1524 m(index1, i) = tempMatrix(index1, i);
1525 m(index2, i) = cosAngle*tempMatrix(index2, i) + sinAngle*tempMatrix(index3, i);
1526 m(index3, i) = -sinAngle*tempMatrix(index2, i) + cosAngle*tempMatrix(index3, i);
1527 }
1528
1529 return m;
1530 }
1531
1532
1545 LinearAlgebra::Matrix LinearAlgebra::toMatrix(const QList<EulerAngle> &eulerAngles) {
1546
1547 if (eulerAngles.size() != 3) {
1548 QString msg = "Unable to convert the given Euler angles to a matrix. "
1549 "Exactly 3 Euler angles are required. The given list has ["
1550 + toString((int) eulerAngles.size()) + "] angles.";
1551 throw IException(IException::Programmer, msg, _FILEINFO_);
1552 }
1553 return LinearAlgebra::toMatrix(eulerAngles[0], eulerAngles[1], eulerAngles[2]);
1554 }
1555
1556
1557
1571 // Derived from NAIF's q2m routine
1573 if (quaternion.size() != 4) {
1574 QString msg = "Unable to convert the given vector to a rotation matrix. "
1575 "The given vector with [" + toString((int) quaternion.size())
1576 + "] components is not a quaternion.";
1577 throw IException(IException::Programmer, msg, _FILEINFO_);
1578 }
1579
1580 Vector q = quaternion;
1581 if ( !isUnit(quaternion) && !isZero(quaternion) ) {
1582 q = normalize(quaternion);
1583 }
1584
1585 LinearAlgebra::Matrix matrix(3, 3);
1586 // For efficiency, we avoid duplicating calculations where possible.
1587 double q0q1 = q[0] * q[1];
1588 double q0q2 = q[0] * q[2];
1589 double q0q3 = q[0] * q[3];
1590
1591 double q1q1 = q[1] * q[1];
1592 double q1q2 = q[1] * q[2];
1593 double q1q3 = q[1] * q[3];
1594
1595 double q2q2 = q[2] * q[2];
1596 double q2q3 = q[2] * q[3];
1597
1598 double q3q3 = q[3] * q[3];
1599
1600 matrix(0, 0) = 1.0 - 2.0 * ( q2q2 + q3q3 );
1601 matrix(0, 1) = 2.0 * ( q1q2 - q0q3 );
1602 matrix(0, 2) = 2.0 * ( q1q3 + q0q2 );
1603
1604 matrix(1, 0) = 2.0 * ( q1q2 + q0q3 );
1605 matrix(1, 1) = 1.0 - 2.0 * ( q1q1 + q3q3 );
1606 matrix(1, 2) = 2.0 * ( q2q3 - q0q1 );
1607
1608 matrix(2, 0) = 2.0 * ( q1q3 - q0q2 );
1609 matrix(2, 1) = 2.0 * ( q2q3 + q0q1 );
1610 matrix(2, 2) = 1.0 - 2.0 * ( q1q1 + q2q2 );
1611
1612 return matrix;
1613 }
1614
1615
1626 void LinearAlgebra::setRow(Matrix &matrix, const Vector &vector, int rowIndex) {
1627 if ( (rowIndex+1 > (int) matrix.size1()) ) {
1628 QString msg = "Unable to set the matrix row to the given vector. Row index "
1629 + toString(rowIndex) + " is out of bounds. The given matrix only has "
1630 + toString((int) matrix.size1()) + " rows." ;
1631 throw IException(IException::Programmer, msg, _FILEINFO_);
1632 }
1633 boost::numeric::ublas::row(matrix, rowIndex) = vector;
1634 }
1635
1636
1647 void LinearAlgebra::setColumn(Matrix &matrix, const Vector &vector, int columnIndex) {
1648 if ( (columnIndex+1 > (int) matrix.size2()) ) {
1649 QString msg = "Unable to set the matrix column to the given vector. Column index "
1650 + toString(columnIndex) + " is out of bounds. The given matrix only has "
1651 + toString((int) matrix.size1()) + " columns." ;
1652 throw IException(IException::Programmer, msg, _FILEINFO_);
1653 }
1654 boost::numeric::ublas::column(matrix, columnIndex) = vector;
1655 }
1656
1657
1670 LinearAlgebra::Vector LinearAlgebra::row(const Matrix &matrix, int rowIndex) {
1671 if ( (rowIndex+1 > (int) matrix.size1()) ) {
1672 QString msg = "Unable to get the matrix row to the given vector. Row index "
1673 + toString(rowIndex) + " is out of bounds. The given matrix only has "
1674 + toString((int) matrix.size1()) + " rows." ;
1675 throw IException(IException::Programmer, msg, _FILEINFO_);
1676 }
1677 return boost::numeric::ublas::row(matrix, rowIndex);
1678 }
1679
1680
1693 LinearAlgebra::Vector LinearAlgebra::column(const Matrix &matrix, int columnIndex) {
1694 if ( (columnIndex+1 > (int) matrix.size2()) ) {
1695 QString msg = "Unable to get the matrix column to the given vector. Column index "
1696 + toString(columnIndex) + " is out of bounds. The given matrix only has "
1697 + toString((int) matrix.size1()) + " columns." ;
1698 throw IException(IException::Programmer, msg, _FILEINFO_);
1699 }
1700 return boost::numeric::ublas::column(matrix, columnIndex);
1701 }
1702
1703
1714 LinearAlgebra::Vector LinearAlgebra::vector(double v0, double v1, double v2) {
1715 Vector v(3);
1716 v[0] = v0;
1717 v[1] = v1;
1718 v[2] = v2;
1719 return v;
1720 }
1721
1722
1734 LinearAlgebra::Vector LinearAlgebra::vector(double v0, double v1, double v2, double v3) {
1735 Vector v(4);
1736 v[0] = v0;
1737 v[1] = v1;
1738 v[2] = v2;
1739 v[3] = v3;
1740 return v;
1741 }
1742
1743
1752 void LinearAlgebra::setVec3(Vector *v, double v0, double v1, double v2) {
1753 (*v)[0] = v0;
1754 (*v)[1] = v1;
1755 (*v)[2] = v2;
1756 }
1757
1758
1768 void LinearAlgebra::setVec4(Vector *v, double v0, double v1, double v2, double v3) {
1769 (*v)[0] = v0;
1770 (*v)[1] = v1;
1771 (*v)[2] = v2;
1772 (*v)[3] = v3;
1773 }
1774
1775
1789 LinearAlgebra::Vector LinearAlgebra::subVector(const Vector &v, int startIndex, int size) {
1791 for (int i = 0; i < size; i++) {
1792 sub[i] = v[i + startIndex];
1793 }
1794 return sub;
1795 }
1796
1797
1810 QDebug operator<<(QDebug dbg, const LinearAlgebra::Vector &vector) {
1811 QDebugStateSaver saver(dbg);
1812 dbg.noquote() << toString(vector);
1813 return dbg;
1814 }
1815
1816
1828 QDebug operator<<(QDebug dbg, const LinearAlgebra::Matrix &matrix) {
1829 QDebugStateSaver saver(dbg);
1830 for (unsigned int i = 0; i < matrix.size1(); i++) {
1831 dbg.noquote() << " ";
1832 for (unsigned int j = 0; j < matrix.size2(); j++) {
1833 dbg.noquote() << toString(matrix(i, j), 15) << " ";
1834 }
1835 dbg.noquote() << endl;
1836 }
1837 return dbg;
1838 }
1839
1840
1852 QString toString(const LinearAlgebra::Vector &vector, int precision) {
1853 QString result = "( ";
1854 for (unsigned int i = 0; i < vector.size(); i++) {
1855 result += toString(vector(i), precision);
1856 if (i != vector.size() - 1) result += ", ";
1857 }
1858 result += " )";
1859 return result;
1860 }
1861}
Defines an angle and provides unit conversions.
Definition Angle.h:45
double radians() const
Convert an angle to a double.
Definition Angle.h:226
void setRadians(double radians)
Set the angle in units of Radians.
Definition Angle.h:239
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
static AxisAngle toAxisAngle(const Matrix &rotationMatrix)
Converts a rotation's representation from a matrix to a axis of rotation and its corresponding rotati...
static Matrix identity(int size)
Returns the identity matrix of size NxN.
~LinearAlgebra()
Destructor for a LinearAlgebra object.
static Vector subtract(const Vector &vector1, const Vector &vector2)
Subtracts the right vector from the left vector.
static Vector crossProduct(const Vector &vector1, const Vector &vector2)
Returns the cross product of two vectors.
static Vector zeroVector(int size)
Returns a vector of given length that is filled with zeroes.
static Vector vector(double v0, double v1, double v2)
Constructs a 3 dimensional vector with the given component values.
LinearAlgebra()
Default constructor for a LinearAlgebra object.
static Vector add(const Vector &vector1, const Vector &vector2)
Adds the two given vectors.
static Vector row(const Matrix &matrix, int rowIndex)
Returns a vector whose components match those of the given matrix row.
static Vector subVector(const Vector &v, int start, int size)
Constructs a vector of given size using the given vector and starting index.
static Matrix toMatrix(const AxisAngle &axisAngle)
Converts a rotation's representation from an axis of rotation and its corresponding rotation angle to...
static bool isUnit(const Vector &vector)
Determines whether the given vector is a unit vector.
static Vector column(const Matrix &matrix, int columnIndex)
Returns a vector whose components match those of the given matrix column.
static bool isEmpty(const Vector &vector)
Determines whether the given vector is empty (i.e.
static Vector project(const Vector &vector1, const Vector &vector2)
Compute the vector projection of vector1 onto vector2.
static void setRow(Matrix &matrix, const Vector &vector, int rowIndex)
Sets the row of the given matrix to the values of the given vector.
static void setColumn(Matrix &matrix, const Vector &vector, int columnIndex)
Sets the column of the given matrix to the values of the given vector.
static void setVec4(Vector *v, double v0, double v1, double v2, double v3)
Fills the first four elements of the given vector with the given values.
static Matrix multiply(const Matrix &matrix1, const Matrix &matrix2)
Returns the product of two matrices.
static double innerProduct(const Vector &vector1, const Vector &vector2)
Computes the inner product of the given vectors.
static Matrix zeroMatrix(int rows, int columns)
Returns a matrix with given dimensions that is filled with zeroes.
static Vector normalizedCrossProduct(const Vector &vector1, const Vector &vector2)
Divides each vector by its corresponding absolute maximum, computes the cross product of the new vect...
static Vector rotate(const Vector &vector, const Vector &axis, Angle angle)
Rotates a vector about an axis vector given a specified angle.
static double magnitude(const Vector &vector)
Computes the magnitude (i.e., the length) of the given vector using the Euclidean norm (L2 norm).
static Vector perpendicular(const Vector &vector1, const Vector &vector2)
Finds the unique vector P such that A = V + P, V is parallel to B and P is perpendicular to B,...
static Matrix pseudoinverse(const Matrix &matrix)
Returns the pseudoinverse of a matrix.
static double determinant(const Matrix &matrix)
Returns the determinant of the given 3x3 matrix.
static Matrix inverse(const Matrix &matrix)
Returns the inverse of a 2x2 or 3x3 matrix.
boost::numeric::ublas::vector< double > Vector
Definition for an Isis::LinearAlgebra::Vector of doubles.
static bool isIdentity(const Matrix &matrix)
Determines whether the given matrix is the identity.
static void setVec3(Vector *v, double v0, double v1, double v2)
Fills the first three elements of the given vector with the given values.
static bool isZero(const Matrix &matrix)
Determines whether the given matrix is filled with zereos.
static Matrix outerProduct(const Vector &vector1, const Vector &vector2)
Computes the outer product of the given vectors.
boost::numeric::ublas::matrix< double > Matrix
Definition for an Isis::LinearAlgebra::Matrix of doubles.
static bool isOrthogonal(const Matrix &matrix)
Determines whether the given matrix is orthogonal by verifying that the matrix and its tranpose are i...
static double absoluteMaximum(const Vector &vector)
Returns the maximum norm (L-infinity norm) for the given vector.
static Vector toQuaternion(const Matrix &rotationMatrix)
Converts a rotation's representation from a matrix to a unit quaternion.
static Matrix transpose(const Matrix &matrix)
Returns the transpose of the given matrix.
static double dotProduct(const Vector &vector1, const Vector &vector2)
Computes the dot product of the given vectors.
static QList< EulerAngle > toEulerAngles(const Matrix &rotationMatrix, const QList< int > axes)
Converts a rotation's representation from a matrix to a set of Euler angles with corresponding axes.
static Vector normalize(const Vector &vector)
Returns a unit vector that is codirectional with the given vector by dividing each component of the v...
static bool isRotationMatrix(const Matrix &matrix)
Determines whether the given matrix is a rotation matrix.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.
const double PI
The mathematical constant PI.
Definition Constants.h:40
This is free and unencumbered software released into the public domain.