Isis 3.0 Programmer Reference
Back | Home
LinearAlgebra.cpp
Go to the documentation of this file.
1 
23 #include "LinearAlgebra.h"
24 
25 // std library
26 #include <cmath>
27 
28 // Qt Library
29 #include <QDebug>
30 #include <QtDebug>
31 #include <QtGlobal>
32 
33 // boost library
34 #include <boost/assign/std/vector.hpp>
35 #include <boost/numeric/ublas/io.hpp>
36 #include <boost/numeric/ublas/matrix.hpp>
37 #include <boost/numeric/ublas/matrix_proxy.hpp>
38 
39 // other Isis library
40 #include "Angle.h"
41 #include "Constants.h"
42 #include "IException.h"
43 #include "IString.h"
44 
45 
46 namespace Isis {
53  }
54 
55 
60 
61 
73  bool LinearAlgebra::isIdentity(const Matrix &matrix) {
74  if (matrix.size1() != matrix.size2()) return false;
75 
76  for (unsigned int row = 0; row < matrix.size1(); row++) {
77  for (unsigned int col = 0; col < matrix.size2(); col++) {
78  if (row == col) {
79  if (!qFuzzyCompare(matrix(row, col), 1.0)) return false;
80  }
81  else {
82  // When using qFuzzy compare against a number that is zero
83  // you must offset the number per Qt documentation
84  if (!qFuzzyCompare(1.0 + matrix(row, col), 1.0)) return false;
85  }
86  }
87  }
88  return true;
89  }
90 
91 
101  bool LinearAlgebra::isOrthogonal(const Matrix &matrix) {
102  if (matrix.size1() != matrix.size2()) return false;
104  if (isIdentity(test)) {
105  // no need to test that transpose is both left and right inverse since the matrix is square
106  return true;
107  }
108  return false;
109  }
110 
111 
124  // derived from naif's isrot routine
126  // rotation matrices must be square
127  if (matrix.size1() != matrix.size2()) return false;
128 
129  /* An exerpt from NAIF's isrot() routine...
130  *
131  * A property of rotation matrices is that their columns form a
132  * right-handed, orthonormal basis in 3-dimensional space. The
133  * converse is true: all 3x3 matrices with this property are
134  * rotation matrices.
135  *
136  * An ordered set of three vectors V1, V2, V3 forms a right-handed,
137  * orthonormal basis if and only if
138  *
139  * 1) || V1 || = || V2 || = || V3 || = 1
140  *
141  * 2) V3 = V1 x V2. Since V1, V2, and V3 are unit vectors,
142  * we also have
143  *
144  * < V3, V1 x V2 > = 1.
145  *
146  * This quantity is the determinant of the matrix whose
147  * colums are V1, V2 and V3.
148  *
149  * When finite precision numbers are used, rotation matrices will
150  * usually fail to satisfy these criteria exactly. We must use
151  * criteria that indicate approximate conformance to the criteria
152  * listed above. We choose
153  *
154  * 1) | || Vi || - 1 | < NTOL, i = 1, 2, 3.
155  * -
156  *
157  * 2) Let
158  *
159  * Vi
160  * Ui = ------ , i = 1, 2, 3.
161  * ||Vi||
162  *
163  * Then we require
164  *
165  * | < U3, U1 x U2 > - 1 | < DTOL;
166  * -
167  *
168  * equivalently, letting U be the matrix whose columns
169  * are U1, U2, and U3, we insist on
170  *
171  * | det(U) - 1 | < DTOL.
172  * _
173  * The columns of M must resemble unit vectors. If the norms are
174  * outside of the allowed range, M is not a rotation matrix.
175  *
176  * Also, the columns of M are required to be pretty nearly orthogonal. The
177  * discrepancy is gauged by taking the determinant of the matrix UNIT,
178  * computed below, whose columns are the unitized columns of M.
179  */
180 
181  // create a matrix whose columns are unit vectors of the columns of the
182  // given matrix
183  Matrix unitMatrix(matrix.size1(), matrix.size2());
184 
185  for (unsigned int i = 0; i < unitMatrix.size1(); i++) {
186  // get the ith column vector from the given matrix
187  Vector columnVector = column(matrix, i);
188  // get the magnitude and if it is not near 1, this is not a rotation matrix
189  double columnMagnitude = magnitude(columnVector);
190  if ( columnMagnitude < 0.9 || columnMagnitude > 1.1 ) {
191  return false;
192  }
193 
194  // put the unitized row into the unitized matrix
195  setColumn(unitMatrix, columnVector / columnMagnitude, i);
196 
197  }
198 
199  try {
200  // get the determinant of the unitized matrix and if it is not near 1,
201  // this is not a rotation matrix
202  double det = determinant(unitMatrix);
203  if ( det >= 0.9 && det <= 1.1 ) {
204  return true;
205  }
206  }
207  catch (IException &e) {
208  // since we can only calculate the determinant for 2x2 or 3x3
209  QString msg = "Unable to determine whether the given matrix is a rotation matrix.";
211  }
212  return false;
213  }
214 
215 
223  bool LinearAlgebra::isZero(const Matrix &matrix) {
224  for (unsigned int i = 0; i < matrix.size1(); i++) {
225  for (unsigned int j = 0; j < matrix.size2(); j++) {
226  // When using qFuzzy compare against a number that is zero
227  // you must offset the number per Qt documentation
228  if (!qFuzzyCompare(matrix(i, j) + 1.0, 1.0)) return false;
229  }
230  }
231 
232  return true;
233  }
234 
235 
243  bool LinearAlgebra::isZero(const Vector &vector) {
244  for (unsigned int i = 0; i < vector.size(); i++) {
245  // When using qFuzzy compare against a number that is zero
246  // you must offset the number per Qt documentation
247  if (!qFuzzyCompare(vector(i) + 1.0, 1.0)) return false;
248  }
249 
250  return true;
251  }
252 
253 
263  return vector.empty();
264  }
265 
266 
274  bool LinearAlgebra::isUnit(const Vector &vector) {
275  if (qFuzzyCompare(dotProduct(vector, vector), 1.0)) {
276  return true;
277  }
278  return false;
279  }
280 
281 
295  try {
296  if (isOrthogonal(matrix)) {
297  return transpose(matrix);
298  }
299 
300  // the determinant method will verify the size of the matrix is 2x2 or 3x3
301  double det = determinant(matrix);
302 
303  if (qFuzzyCompare(det + 1.0, 1.0)) {
304  // the inverse exists <==> the determinant is not 0.0
305  QString msg = "The given matrix is not invertible. The determinant is 0.0.";
307  }
308 
309  // since the determinant is not zero, we can calculate the reciprocal
310  double scale = 1 / det;
311 
312  LinearAlgebra::Matrix inverse = identity(matrix.size1());
313  // find the inverse for 2x2
314  if (matrix.size1() == 2) {
315  inverse(0, 0) = scale * matrix(1, 1);
316  inverse(0, 1) = -scale * matrix(0, 1);
317 
318  inverse(1, 0) = -scale * matrix(1, 0);
319  inverse(1, 1) = scale * matrix(0, 0);
320  return inverse;
321  }
322  // else we have a 3x3
323  inverse(0, 0) = scale * ( matrix(1, 1) * matrix(2, 2) - matrix(2, 1) * matrix(1, 2) );
324  inverse(0, 1) = scale * ( matrix(0, 2) * matrix(2, 1) - matrix(2, 2) * matrix(0, 1) );
325  inverse(0, 2) = scale * ( matrix(0, 1) * matrix(1, 2) - matrix(1, 1) * matrix(0, 2) );
326 
327  inverse(1, 0) = scale * ( matrix(1, 2) * matrix(2, 0) - matrix(2, 2) * matrix(1, 0) );
328  inverse(1, 1) = scale * ( matrix(0, 0) * matrix(2, 2) - matrix(2, 0) * matrix(0, 2) );
329  inverse(1, 2) = scale * ( matrix(0, 2) * matrix(0, 1) - matrix(1, 2) * matrix(0, 0) );
330 
331  inverse(2, 0) = scale * ( matrix(1, 0) * matrix(2, 1) - matrix(2, 0) * matrix(1, 1) );
332  inverse(2, 1) = scale * ( matrix(0, 1) * matrix(2, 0) - matrix(2, 1) * matrix(0, 0) );
333  inverse(2, 2) = scale * ( matrix(0, 0) * matrix(1, 1) - matrix(1, 0) * matrix(0, 1) );
334 
335  return inverse;
336  }
337  catch (IException &e) {
338  QString msg = "Unable to invert the given matrix.";
340  }
341  }
342 
343 
352  // no error testing required so call the boost transpose function
353  return boost::numeric::ublas::trans(matrix);
354  }
355 
356 
367  if (size < 1) {
368  QString msg = "Can not create identity matrix of negative size ["
369  + toString((int) size) + "].";
371  }
372 
373  LinearAlgebra::Matrix m(size, size, 0.0);
374  for (int i = 0; i < size; i++) {
375  m(i, i) = 1.0;
376  }
377 
378  return m;
379  }
380 
381 
391  boost::numeric::ublas::zero_matrix<double> m(rows, columns);
392  return m;
393  }
394 
395 
404  boost::numeric::ublas::zero_vector<double> v(size);
405  return v;
406  }
407 
408 
422  double LinearAlgebra::determinant(const Matrix &matrix) {
423  if ( (matrix.size1() != matrix.size2()) || (matrix.size1() != 2 && matrix.size1() != 3) ) {
424  QString msg = "Unable to calculate the determinant for the given matrix. "
425  "This method only calculates the determinant for 2x2 or 3x3 matrices."
426  "The given matrix is [" + toString((int) matrix.size1()) + "x"
427  + toString((int) matrix.size2()) + "].";
429  }
430 
431  if (matrix.size1() == 2) {
432  return matrix(0, 0) * matrix(1, 1) - matrix(1, 0) * matrix(0, 1);
433  }
434  else {
435 
436  return matrix(0, 0) * (matrix(1, 1)*matrix(2, 2) - matrix(1, 2)*matrix(2, 1))
437  - matrix(0, 1) * (matrix(1, 0)*matrix(2, 2) - matrix(1, 2)*matrix(2, 0))
438  + matrix(0, 2) * (matrix(1, 0)*matrix(2, 1) - matrix(1, 1)*matrix(2, 0));
439  }
440 
441  }
442 
443 
460  // impossible to unitize the zero vector
461  if (LinearAlgebra::isZero(vector)) {
462  QString msg = "Unable to normalize the zero vector.";
464  }
465  return vector / LinearAlgebra::magnitude(vector);
466  }
467 
468 
486  double LinearAlgebra::magnitude(const Vector &vector) {
487  double magnitude;
488  // avoid dividing by maxNorm = 0
489  // no stabilization is needed for the zero vector, magnitude is always 0.0
490  if (LinearAlgebra::isZero(vector)) {
491  magnitude = 0.0;
492  }
493  else {
494  double maxNorm = LinearAlgebra::absoluteMaximum(vector);
495  magnitude = maxNorm * boost::numeric::ublas::norm_2(vector / maxNorm);
496  }
497  return magnitude;
498  }
499 
500 
513  double LinearAlgebra::absoluteMaximum(const Vector &vector) {
514  return boost::numeric::ublas::norm_inf(vector);
515  }
516 
517 
531  LinearAlgebra::Matrix LinearAlgebra::multiply(const Matrix &matrix1, const Matrix &matrix2) {
532  // Check to make sure we can multiply
533  if (matrix1.size2() != matrix2.size1()) {
534  QString msg = "Unable to multiply matrices with mismatched dimensions. "
535  "The left matrix has [" + toString((int) matrix1.size2())
536  + "] columns and the right matrix has [" + toString((int) matrix2.size1())
537  + "] rows.";
538 
540  }
541 
542  // Use boost product routine
543  return boost::numeric::ublas::prod(matrix1, matrix2);
544  }
545 
546 
563  // Check to make sure we can multiply
564  if (matrix.size2() != vector.size()) {
565  QString msg = "Unable to multiply matrix and vector with mismatched dimensions."
566  "The given vector has [" + toString((int) vector.size())
567  + "] components and the given matrix has ["
568  + toString((int) matrix.size2()) + "] columns.";
570  }
571 
572  // Use boost product routine
573  return boost::numeric::ublas::prod(matrix, vector);
574  }
575 
576 
585  LinearAlgebra::Vector LinearAlgebra::multiply(double scalar, const Vector &vector) {
586  // no error checks necessary (use the boost operator* method)
587  return scalar * vector;
588  }
589 
590 
599  LinearAlgebra::Matrix LinearAlgebra::multiply(double scalar, const Matrix &matrix) {
600  // no error checks necessary
601  return scalar * matrix;
602  }
603 
604 
616  LinearAlgebra::Vector LinearAlgebra::add(const Vector &vector1, const Vector &vector2) {
617  // Vectors best be the same size
618  if (vector1.size() != vector2.size()) {
619  QString msg = "Unable to add vectors with mismatched sizes."
620  "Vector1 has [" + toString((int) vector1.size())
621  + "] components and vector2 has ["
622  + toString((int) vector2.size()) + "] components.";
624  }
625 
626  return vector1 + vector2;
627  }
628 
629 
642  LinearAlgebra::Vector LinearAlgebra::subtract(const Vector &vector1, const Vector &vector2) {
643  // Vectors best be the same size
644  if (vector1.size() != vector2.size()) {
645  QString msg = "Unable to subtract vectors with mismatched sizes."
646  "Vector1 has [" + toString((int) vector1.size())
647  + "] components and vector2 has ["
648  + toString((int) vector2.size()) + "] components.";
650  }
651 
652  return vector1 - vector2;
653  }
654 
655 
671  if ((vector1.size() != 3) || (vector2.size() != 3)) {
672  QString msg = "Unable to calculate the cross product on vectors that are not size 3. "
673  "Vector1 has [" + toString((int) vector1.size())
674  + "] components and vector2 has ["
675  + toString((int) vector2.size()) + "] components.";
677  }
678 
679  Vector crossProd(3);
680  crossProd(0) = vector1(1)*vector2(2) - vector1(2)*vector2(1);
681  crossProd(1) = vector1(2)*vector2(0) - vector1(0)*vector2(2);
682  crossProd(2) = vector1(0)*vector2(1) - vector1(1)*vector2(0);
683 
684  return crossProd;
685  }
686 
687 
702  // is this derived from naif's ucrss routine???
704  const Vector &vector2) {
705 
706  double maxVector1 = LinearAlgebra::absoluteMaximum(vector1);
707  double maxVector2 = LinearAlgebra::absoluteMaximum(vector2);
708 
709  LinearAlgebra::Vector normalizedVector1 = vector1;
710  LinearAlgebra::Vector normalizedVector2 = vector2;
711 
712  if (maxVector1 != 0.0) {
713  normalizedVector1 /= maxVector1;
714  }
715 
716  if (maxVector2 != 0.0) {
717  normalizedVector2 /= maxVector2;
718  }
719 
720  LinearAlgebra::Vector vcross = crossProduct(normalizedVector1, normalizedVector2);
721 
722  return normalize(vcross);
723  }
724 
725 
740  if (vector1.size() != vector2.size()) {
741  QString msg = "Unable to compute the outer product for vectors with mismatched sizes."
742  "Vector1 has [" + toString((int) vector1.size())
743  + "] components and vector2 has ["
744  + toString((int) vector2.size()) + "] components.";
746  }
747  return boost::numeric::ublas::outer_prod(vector1, vector2);
748  }
749 
750 
763  double LinearAlgebra::dotProduct(const Vector &vector1, const Vector &vector2) {
764  // no error check needed - this is done by innerProduct
765  return LinearAlgebra::innerProduct(vector1, vector2);
766  }
767 
768 
783  double LinearAlgebra::innerProduct(const Vector &vector1, const Vector &vector2) {
784  if (vector1.size() != vector2.size()) {
785  QString msg = "Unable to compute the dot product for vectors with mismatched sizes."
786  "Vector1 has [" + toString((int) vector1.size())
787  + "] components and vector2 has ["
788  + toString((int) vector2.size()) + "] components.";
790  }
791  return boost::numeric::ublas::inner_prod(vector1, vector2);
792  }
793 
794 
814  // derived from naif's vproj routine
815  LinearAlgebra::Vector LinearAlgebra::project(const Vector &vector1, const Vector &vector2) {
816  if (vector1.size() != vector2.size()) {
817  QString msg = "Unable to project vector1 onto vector2 with mismatched sizes."
818  "Vector1 has [" + toString((int) vector1.size())
819  + "] components and vector2 has ["
820  + toString((int) vector2.size()) + "] components.";
822  }
823 
824  // if vector2 is the zero vector, then the projection of vector1 onto vector2
825  // is also the zero vector
826  if (LinearAlgebra::isZero(vector2)) return vector2;
827 
828  // find the dot product of the two vectors
829  double v1Dotv2 = LinearAlgebra::dotProduct(vector1, vector2);
830 
831  // find the square of the length of vector2 (this is the dot product of the vector2 with itself)
832  double v2Dotv2 = LinearAlgebra::dotProduct(vector2, vector2);
833 
834  return v1Dotv2/v2Dotv2 * vector2;
835  }
836 
837 
853  // derived from naif's vrotv routine
855  Angle angle) {
856  if ((vector.size() != 3) || (axis.size() != 3)) {
857  QString msg = "Unable to rotate vector about the given axis and angle. "
858  "Vectors must be of size 3 to perform rotation. "
859  "The given vector has [" + toString((int) vector.size())
860  + "] components and the given axis has ["
861  + toString((int) axis.size()) + "] components.";
863  }
864 
865  // if the given vector is the zero vector, then the rotation about the vector
866  // is also the zero vector
867  if (isZero(axis)) return vector;
868 
869  // Compute the unit vector that is codirectional with the given axis
870  Vector axisUnitVector = normalize(axis);
871 
872  // Compute the projection of the given vector onto the axis unit vector.
873  Vector projVectorOnAxis = project(vector, axisUnitVector);
874 
875  // Compute the component of the input orthogonal to the AXIS. Call it V1.
876  Vector v1 = vector - projVectorOnAxis;
877 
878  // Rotate V1 by 90 degrees about the AXIS and call the result V2.
879  Vector v2 = LinearAlgebra::crossProduct(axisUnitVector, v1);
880 
881  // Compute cos(angle)*v1 + sin(angle)*v2. This is v1 rotated about
882  // the axis in the plane normal to the axis, call the result rplane
883  double c = cos(angle.radians());
884  double s = sin(angle.radians());
885  Vector rplane = (c*v1) + (s*v2);
886 
887  // Add the rotated component in the normal plane to axis to the
888  // projection of v onto axis to obtain rotation.
889  return (rplane + projVectorOnAxis);
890  }
891 
892 
911  // Derived from NAIF's vperp routine
912  // possible that we may need to restrict size to dimesion 3, but doesn't seem
913  // necessary in the code.
915 
916  // if vector1 is the zero vector, just set p to zero and return.
917  if ( isZero(vector1) ) {
918  return vector1;
919  }
920 
921  // if vector2 is the zero vector, just set p to vector1 and return.
922  if ( isZero(vector2) ) {
923  return vector1;
924  }
925 
926  // normalize (using the max norm) the given vectors and project the first onto the second
927  double max1 = absoluteMaximum(vector1);
928  double max2 = absoluteMaximum(vector2);
929  Vector parallelVector = project(vector1 / max1, vector2 / max2);
930 
931  Vector perpendicularVector = vector1 - parallelVector * max1;
932 
933  return perpendicularVector;
934 
935  }
936 
937 
954  // Derived from NAIF's raxisa routine
956 
957  if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
958  QString msg = "Unable to convert the given matrix to an axis of rotation "
959  "and a rotation angle. A 3x3 matrix is required. The given matrix is ["
960  + toString((int) rotationMatrix.size1()) + "x"
961  + toString((int) rotationMatrix.size2()) + "].";
963  }
964 
965  // if given matrix is not a rotation matrix, we can't proceed.
966  if ( !isRotationMatrix(rotationMatrix) ) {
967  QString msg = "Unable to convert the given matrix to an axis of rotation "
968  "and a rotation angle. The given matrix is not a rotation matrix.";
970  }
971 
972  Angle angle;
973  Vector axis(3);
974 
975  // Construct the quaternion corresponding to the input rotation matrix
976  Vector quaternion = LinearAlgebra::toQuaternion(rotationMatrix);
977 
978  LinearAlgebra::Vector subQuaternion(3);
979  subQuaternion[0] = quaternion[1];
980  subQuaternion[1] = quaternion[2];
981  subQuaternion[2] = quaternion[3];
982 
983 
984  // The quaternion we've just constructed is of the form:
985  //
986  // cos(ANGLE/2) + sin(ANGLE/2) * AXIS
987  //
988  // We take a few precautions to handle the case of an identity
989  // rotation.
990  if (isZero(subQuaternion)) {
991  axis(0) = 0.0;
992  axis(1) = 0.0;
993  axis(2) = 1.0;
994 
995  angle.setRadians(0.0);
996  }
997  else if (qFuzzyCompare(quaternion(0) + 1.0, 1.0)) {
998  axis = subQuaternion;
999  angle.setRadians(Isis::PI);
1000  }
1001  else {
1002  axis = LinearAlgebra::normalize(subQuaternion);
1003  angle.setRadians(2.0 * atan2(magnitude(subQuaternion), quaternion(0)));
1004  }
1005 
1006  return qMakePair(axis, angle);
1007  }
1008 
1009 
1032  // Derived from NAIF's m2eul routine
1034  const QList<int> axes) {
1035 
1036  // check there are 3 axes in the set {1,2,3} with center axis not equal to first or last
1037  if (axes.size() != 3) {
1038  QString msg = "Unable to convert the given matrix to Euler angles. "
1039  "Exactly 3 axis codes are required. The given list has ["
1040  + toString((int) axes.size()) + "] axes.";
1042  }
1043 
1044  QSet<int> validAxes;
1045  validAxes << 1 << 2 << 3;
1046  if (!validAxes.contains(axes[0])
1047  || !validAxes.contains(axes[1])
1048  || !validAxes.contains(axes[2])) {
1049  QString msg = "Unable to convert the given matrix to Euler angles using the given axis codes "
1050  "[" + toString(axes[0]) + ", " + toString(axes[1]) + ", " + toString(axes[2])
1051  + "]. Axis codes must be 1, 2, or 3.";
1053  }
1054 
1055  if (axes[0] == axes[1] || axes[1] == axes[2]) {
1056  QString msg = "Unable to convert the given matrix to Euler angles using the given axis codes "
1057  "[" + toString(axes[0]) + ", " + toString(axes[1]) + ", " + toString(axes[2])
1058  + "]. The middle axis code must differ from its neighbors.";
1060  }
1061 
1062  // check the matrix is 3x3 rotation
1063  if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
1064  QString msg = "Unable to convert the given matrix to Euler angles. A 3x3 matrix is required. "
1065  "The given matrix is ["
1066  + toString((int) rotationMatrix.size1()) + "x"
1067  + toString((int) rotationMatrix.size2()) + "].";
1069  }
1070 
1071  if ( !isRotationMatrix(rotationMatrix) ) {
1072  QString msg = "Unable to convert the given matrix to Euler angles. "
1073  "The given matrix is not a rotation matrix.";
1075  }
1076 
1077  // AXIS3, AXIS2, AXIS1 and R have passed their tests at this
1078  // point. We take the liberty of working with TEMPROT, a
1079  // version of R that has unitized columns.
1080  // DO I = 1, 3
1081  // CALL VHAT ( R(1,I), TEMPROT(1,I) )
1082  // END DO
1083 
1084  LinearAlgebra::Matrix tempRotation(3, 3);
1085  for (int i = 0; i < 3; i++) {
1086  Vector columnVector = column(rotationMatrix, i);
1087  // replace the ith column with a unitized column vector
1088  setColumn(tempRotation, normalize(columnVector), i);
1089  }
1090 
1091  QList<int> nextAxis;
1092  nextAxis << 2 << 3 << 1;
1093  double sign;
1094  // create a matrix of zeros and we will fill in the non zero components below
1095  LinearAlgebra::Matrix change = zeroMatrix(3, 3);
1096 
1097  Angle angle1, angle2, angle3;
1098  if ( axes[0] == axes[2] ) {
1099  if ( axes[1] == nextAxis[axes[0] - 1] ) {
1100  sign = 1.0;
1101  }
1102  else {
1103  sign = -1.0;
1104  }
1105  int c = 6 - axes[0] - axes[1];
1106 
1107  change( axes[0] - 1, 2 ) = 1.0;
1108  change( axes[1] - 1, 0 ) = 1.0;
1109  change( c - 1, 1 ) = sign * 1.0;
1110  LinearAlgebra::Matrix tempMatrix = multiply( tempRotation, change );
1111  tempRotation = multiply( transpose(change), tempMatrix );
1112 
1113  bool degen = ( qFuzzyCompare(tempRotation(0, 2) + 1.0, 1.0)
1114  && qFuzzyCompare(tempRotation(1, 2) + 1.0, 1.0) )
1115  || ( qFuzzyCompare(tempRotation(2, 0) + 1.0, 1.0)
1116  && qFuzzyCompare(tempRotation(2, 1) + 1.0, 1.0) )
1117  || qFuzzyCompare( qAbs(tempRotation(2, 2)), 1.0 );
1118 
1119  if ( degen ) {
1120 
1121  angle3.setRadians( 0.0 );
1122  angle2.setRadians( acos( tempRotation(2, 2) ) );
1123  angle1.setRadians( atan2( tempRotation(0, 1), tempRotation(0, 0) ) );
1124 
1125  }
1126  else {
1127  // the normal case.
1128  angle3.setRadians( atan2( tempRotation(0, 2), tempRotation(1, 2) ) );
1129  angle2.setRadians( acos( tempRotation(2, 2) ) );
1130  angle1.setRadians( atan2( tempRotation(2, 0), -tempRotation(2, 1) ) );
1131  }
1132  }
1133  else {
1134  // The axis order is c-b-a. We're going to find a matrix CHANGE
1135  // such that
1136  //
1137  // T
1138  // CHANGE R CHANGE
1139  if ( axes[1] == nextAxis[axes[0] - 1] ) {
1140  sign = 1.0;
1141  }
1142  else {
1143  sign = -1.0;
1144  }
1145  change( axes[0] - 1, 0 ) = 1.0;
1146  change( axes[1] - 1, 1 ) = 1.0;
1147  change( axes[2] - 1, 2 ) = sign * 1.0;
1148  LinearAlgebra::Matrix tempMatrix = multiply( tempRotation, change );
1149  tempRotation = multiply( transpose(change), tempMatrix );
1150  bool degen = ( qFuzzyCompare(tempRotation(0, 0) + 1.0, 1.0)
1151  && qFuzzyCompare(tempRotation(0, 1) + 1.0, 1.0) )
1152  || ( qFuzzyCompare(tempRotation(1, 2) + 1.0, 1.0)
1153  && qFuzzyCompare(tempRotation(2, 2) + 1.0, 1.0) )
1154  || qFuzzyCompare( qAbs(tempRotation(0, 2)), 1.0 );
1155 
1156  if ( degen ) {
1157 
1158  angle3.setRadians( 0.0 );
1159  angle2.setRadians( asin( -tempRotation(0, 2) ) );
1160  angle1.setRadians( sign * atan2( -tempRotation(1, 0), tempRotation(1, 1) ) );
1161 
1162  }
1163  else {
1164  // the normal case.
1165  angle3.setRadians( atan2( tempRotation(1, 2), tempRotation(2, 2) ) );
1166  angle2.setRadians( asin( -tempRotation(0, 2) ) );
1167  angle1.setRadians( sign * atan2( tempRotation(0, 1), tempRotation(0, 0) ) );
1168  }
1169  }
1170 
1172  eulerAngles << qMakePair(angle3, axes[0])
1173  << qMakePair(angle2, axes[1])
1174  << qMakePair(angle1, axes[2]);
1175  return eulerAngles;
1176  }
1177 
1178 
1192  // Derived from NAIF's m2q routine
1194 
1195  if ((rotationMatrix.size1() != 3) || (rotationMatrix.size2() != 3)) {
1196  QString msg = "Unable to convert the given matrix to a quaternion. "
1197  "A 3x3 matrix is required. The given matrix is ["
1198  + toString((int) rotationMatrix.size1()) + "x"
1199  + toString((int) rotationMatrix.size2()) + "].";
1201  }
1202 
1203  // if given matrix is not a rotation matrix, we can't proceed.
1204  if ( !isRotationMatrix(rotationMatrix) ) {
1205  QString msg = "Unable to convert the given matrix to an axis of rotation "
1206  "and a rotation angle. The given matrix is not a rotation matrix.";
1208  }
1209 
1210  // Let q = c + s1*i + s2*j + s3*k
1211  // where c is the constant and s1, s2, s3 are the coefficients of the imaginary parts
1212  // and |q| = 1, .
1213 
1214  // Denote the following for n,m in {1,2,3}
1215  // cc = c * c
1216  // csn = c * sn
1217  // snm = sn * sm
1218 
1219  // The rotation matrix corresponding to our quaternion is:
1220  //
1221  // | cc+s11-s22-s33 2*s12-2*cs3 2*s13+2*cs2 |
1222  // | 2*s12+2*cs3 cc-s11+s22-s33 2*s23-2*cs1 |
1223  // | 2*s13-2*cs2 2*s23+2*cs1 cc-s11-s22+s33 |
1224  //
1225  //
1226  // since |q| = cc + s11 + s22 + s33 = 1, we can use substitution on the diagonal entries to get
1227  //
1228  // | 1-2*s22-2*s33 2*s12-2*cs3 2*s13+2*cs2 |
1229  // | 2*s12+2*cs3 1-2*s11-2*s33 2*s23-2*cs1 |
1230  // | 2*s13-2*cs2 2*s23+2*cs1 1-2*s11-2*s22 |
1231  //
1232  //
1233  //
1234  // r(1,1) = 1.0 - 2*s22 - 2*s33
1235  // r(2,1) = 2*s12 + 2*cs3
1236  // r(3,1) = 2*s13 - 2*cs2
1237  //
1238  // r(1,2) = 2*s12 - 2*cs3
1239  // r(2,2) = 1.0 - 2*s11 - 2*s33
1240  // r(3,2) = 2*s23 + 2*cs1
1241  //
1242  // r(1,3) = 2*s13 + 2*cs2
1243  // r(2,3) = 2*s23 - 2*cs1
1244  // r(3,3) = 1.0 - 2*s11 - 2*s22
1245 
1246  // Using this matrix, we get the trace by summing the diagonal entries
1247  // trace = (1-2*s22-2*s33) + (1-2*s11-2*s33) + (1-2*s11-2*s22)
1248  // = 3 - 4*(s11 + s22 + s33)
1249 
1250  // Therefore
1251  // 1.0 + trace = 4 - 4*(s11 + s22 + s33)
1252  // = 4*(1 - s11 - s22 - s33) by factoring 4
1253  // = 4*(|q| - s11 - s22 - s33) since |q| = 1
1254  // = 4*cc since |q| = cc + s11 + s22 + s33
1255  //
1256  // Solving for c, we get that
1257  // 4*cc = 1 + trace
1258  // 2*c = +/- sqrt(1 + trace)
1259  // c = +/- 0.5 * sqrt( 1.0 + trace )
1260 
1261  // We also have the following where {n,m,p} = {1,2,3}
1262  // 1.0 + trace - 2.0*r(n,n) = 4.0 - 4.0(snn + smm + spp) - 2(1.0 - 2.0(smm + spp ))
1263  // = 4.0 - 4.0(snn + smm + spp) - 2.0 + 4.0(smm + spp )
1264  // = 2.0 - 4.0*snn
1265  //
1266  // Solving for snn, we get
1267  // 2.0 - 4.0*snn = 1.0 + trace - 2.0*r(n,n)
1268  // -2.0 + 4.0*snn = -1.0 - trace + 2.0*r(n,n)
1269  // 4.0*snn = 1.0 - trace + 2.0*r(n,n) ******** we will use this value *************
1270  // ******** in our code to minimize *************
1271  // ******** computations *************
1272  // 2.0*sn = +/- sqrt(1.0 - trace + 2.0*r(n,n))
1273  // sn = +/- 0.5 * sqrt(1.0 - trace + 2.0*r(n,n)) for any n in {1,2,3}
1274 
1275 
1276  // In addition to these observations, note that all of the product
1277  // pairs can easily be computed by simplifying the expressions
1278  // (r(n,m) - r(m,n)) / 4.0 and (r(n,m) + r(m,n)) / 4.0
1279  //
1280  // For example,
1281  // (r(3,2) - r(2,3)) / 4.0 = ( (2*s23 + 2*cs1) - (2*s23 - 2*cs1) ) / 4.0
1282  // = ( 4*cs1 ) / 4.0
1283  // = cs1
1284  //
1285  // So we have the following
1286  // cs1 = (r(3,2) - r(2,3))/4.0
1287  // cs2 = (r(1,3) - r(3,1))/4.0
1288  // cs3 = (r(2,1) - r(1,2))/4.0
1289  // s12 = (r(2,1) + r(1,2))/4.0
1290  // s13 = (r(3,1) + r(1,3))/4.0
1291  // s23 = (r(2,3) + r(3,2))/4.0
1292 
1293  //but taking sums or differences of numbers that are nearly equal
1294  //or nearly opposite results in a loss of precision. as a result
1295  //we should take some care in which terms to select when computing
1296  //c, s1, s2, s3. however, by simply starting with one of the
1297  //large quantities cc, s11, s22, or s33 we can make sure that we
1298  //use the best of the 6 quantities above when computing the
1299  //remaining components of the quaternion.
1300 
1301  double trace = rotationMatrix(0, 0) + rotationMatrix(1, 1) + rotationMatrix(2, 2);
1302  double mtrace = 1.0 - trace;
1303 
1304  // cc4 = 4 * c * c where c is the constant term (first) of the quaternion
1305  double cc4 = 1.0 + trace;
1306  // snn4 = 4 * sn * sn where sn is the coefficient for the n+1 term of the quaternion vector
1307  double s114 = mtrace + 2.0*rotationMatrix(0, 0);
1308  double s224 = mtrace + 2.0*rotationMatrix(1, 1);
1309  double s334 = mtrace + 2.0*rotationMatrix(2, 2);
1310 
1311  // Note that cc4 + s114 + s224 + s334 = 4|q| = 4
1312  // Thus, at least one of the 4 terms is greater than 1.
1313  double normalizingFactor;
1314  LinearAlgebra::Vector quaternion(4);
1315  if ( cc4 >= 1.0 ) { // true if the trace is non-negative
1316 
1317  quaternion[0] = sqrt( cc4 * 0.25 ); // note that q0 = c = sqrt(4*c*c*0.25)
1318  normalizingFactor = 1.0 / ( quaternion[0] * 4.0 );
1319 
1320  // multiply the difference of the entries symmetric about the diagonal by the factor 1/4c
1321  // to get s1, s2, and s3
1322  // (see calculations for cs1, cs2, cs3 in comments above)
1323  quaternion[1] = ( rotationMatrix(2, 1) - rotationMatrix(1, 2) ) * normalizingFactor;
1324  quaternion[2] = ( rotationMatrix(0, 2) - rotationMatrix(2, 0) ) * normalizingFactor;
1325  quaternion[3] = ( rotationMatrix(1, 0) - rotationMatrix(0, 1) ) * normalizingFactor;
1326 
1327  }
1328  else if ( s114 >= 1.0 ) {
1329 
1330  quaternion[1] = sqrt( s114 * 0.25 ); // note that q1 = s1 = sqrt(4*s1*s1*0.25)
1331  normalizingFactor = 1.0 /( quaternion[1] * 4.0 );
1332 
1333  quaternion[0] = ( rotationMatrix(2, 1) - rotationMatrix(1, 2) ) * normalizingFactor;
1334  quaternion[2] = ( rotationMatrix(0, 1) + rotationMatrix(1, 0) ) * normalizingFactor;
1335  quaternion[3] = ( rotationMatrix(0, 2) + rotationMatrix(2, 0) ) * normalizingFactor;
1336 
1337  }
1338  else if ( s224 >= 1.0 ) {
1339 
1340  quaternion[2] = sqrt( s224 * 0.25 );
1341  normalizingFactor = 1.0 /( quaternion[2] * 4.0 );
1342 
1343  quaternion[0] = ( rotationMatrix(0, 2) - rotationMatrix(2, 0) ) * normalizingFactor;
1344  quaternion[1] = ( rotationMatrix(0, 1) + rotationMatrix(1, 0) ) * normalizingFactor;
1345  quaternion[3] = ( rotationMatrix(1, 2) + rotationMatrix(2, 1) ) * normalizingFactor;
1346  }
1347  else { // s334 >= 1.0
1348 
1349  quaternion[3] = sqrt( s334 * 0.25 );
1350  normalizingFactor = 1.0 /( quaternion[3] * 4.0 );
1351 
1352  quaternion[0] = ( rotationMatrix(1, 0) - rotationMatrix(0, 1) ) * normalizingFactor;
1353  quaternion[1] = ( rotationMatrix(0, 2) + rotationMatrix(2, 0) ) * normalizingFactor;
1354  quaternion[2] = ( rotationMatrix(1, 2) + rotationMatrix(2, 1) ) * normalizingFactor;
1355 
1356  }
1357 
1358  // if the magnitude of this quaternion is not one, we polish it up a bit.
1359  if ( !isUnit(quaternion) ) {
1360  quaternion = normalize(quaternion);
1361  }
1362 
1363  // M2Q always returns a quaternion with scalar part greater than or equal to zero.
1364  if ( quaternion[0] < 0.0 ) {
1365  quaternion *= -1.0;
1366  }
1367 
1368  return quaternion;
1369  }
1370 
1371 
1386  // Derived from NAIF's axisar routine
1388  if (axis.size() != 3) {
1389  QString msg = "Unable to convert the given vector and angle to a rotation matrix. "
1390  "The given vector with size [" + toString((int) axis.size())
1391  + "] is not a 3D axis vector.";
1393  }
1394 
1395  // intialize the matrix with the 3x3 identity.
1396  LinearAlgebra::Matrix rotationMatrix = identity(3);
1397 
1398  // The matrix we want rotates every vector by angle about axis.
1399  // In particular, it does so to our basis vectors. The columns
1400  // of the output matrix are the images of the basis vectors
1401  // under this rotation.
1402  for (unsigned int i = 0; i < axis.size(); i++) {
1403  // rotate the ith column of the matrix (the ith standard basis vector)
1404  // about the given axis using the given angle
1405  Vector stdBasisVector = column(rotationMatrix, i);
1406 
1407  // replace the ith column with the rotated vector
1408  setColumn(rotationMatrix, rotate(stdBasisVector, axis, angle), i);
1409  }
1410 
1411  return rotationMatrix;
1412  }
1413 
1414 
1425  return LinearAlgebra::toMatrix(axisAngle.first, axisAngle.second);
1426  }
1427 
1428 
1443  // Derived from NAIF's eul2m routine
1445  const EulerAngle &angle2,
1446  const EulerAngle &angle1) {
1447  QSet<int> validAxes;
1448  validAxes << 1 << 2 << 3;
1449  if (!validAxes.contains(angle3.second)
1450  || !validAxes.contains(angle2.second)
1451  || !validAxes.contains(angle1.second)) {
1452  QString msg = "Unable to convert the given Euler angles to a matrix using the given axis "
1453  "codes [" + toString(angle3.second) + ", " + toString(angle2.second) + ", "
1454  + toString(angle1.second) + "]. Axis codes must be 1, 2, or 3.";
1456  }
1457 
1458  LinearAlgebra::Matrix m(3, 3);
1459  // implement eul2m
1460  // Calculate the 3x3 rotation matrix generated by a rotation
1461  // of a specified angle about a specified axis. This rotation
1462  // is thought of as rotating the coordinate system.
1463  //
1464  //ROTATE(angle1, axis1);
1465  double sinAngle = sin(angle1.first.radians());
1466  double cosAngle = cos(angle1.first.radians());
1467  // get the index offset based on the axis number corresponding to this angle
1468  double indexOffset = ((angle1.second % 3) + 3) % 3;
1469  QList<int> indexSet;
1470  indexSet << 2 << 0 << 1 << 2 << 0;
1471  int index1 = indexSet[indexOffset + 0];
1472  int index2 = indexSet[indexOffset + 1];
1473  int index3 = indexSet[indexOffset + 2];
1474  m(index1, index1) = 1.0; m(index1, index2) = 0.0; m(index1, index3) = 0.0;
1475  m(index2, index1) = 0.0; m(index2, index2) = cosAngle; m(index2, index3) = sinAngle;
1476  m(index3, index1) = 0.0; m(index3, index2) = -sinAngle; m(index3, index3) = cosAngle;
1477 
1478  //
1479  // C ROTMAT applies a rotation of ANGLE radians about axis IAXIS to a
1480  // C matrix. This rotation is thought of as rotating the coordinate
1481  // C system.
1482  // C
1483  //CALL ROTMAT ( R, ANGLE2, AXIS2, R1 )
1484  sinAngle = sin(angle2.first.radians());
1485  cosAngle = cos(angle2.first.radians());
1486  indexOffset = ((angle2.second % 3) + 3) % 3;
1487  index1 = indexSet[indexOffset + 0];
1488  index2 = indexSet[indexOffset + 1];
1489  index3 = indexSet[indexOffset + 2];
1490 
1491  LinearAlgebra::Matrix tempMatrix(3, 3);
1492  for (int i = 0; i < 3; i++) {
1493  tempMatrix(index1, i) = m(index1, i);
1494  tempMatrix(index2, i) = cosAngle*m(index2, i) + sinAngle*m(index3, i);
1495  tempMatrix(index3, i) = -sinAngle*m(index2, i) + cosAngle*m(index3, i);
1496  }
1497 
1498  //CALL ROTMAT ( tempMatrix, ANGLE3, AXIS3, m )
1499  sinAngle = sin(angle3.first.radians());
1500  cosAngle = cos(angle3.first.radians());
1501  indexOffset = ((angle3.second % 3) + 3) % 3;
1502  index1 = indexSet[indexOffset + 0];
1503  index2 = indexSet[indexOffset + 1];
1504  index3 = indexSet[indexOffset + 2];
1505  for (int i = 0; i < 3; i++) {
1506  m(index1, i) = tempMatrix(index1, i);
1507  m(index2, i) = cosAngle*tempMatrix(index2, i) + sinAngle*tempMatrix(index3, i);
1508  m(index3, i) = -sinAngle*tempMatrix(index2, i) + cosAngle*tempMatrix(index3, i);
1509  }
1510 
1511  return m;
1512  }
1513 
1514 
1528 
1529  if (eulerAngles.size() != 3) {
1530  QString msg = "Unable to convert the given Euler angles to a matrix. "
1531  "Exactly 3 Euler angles are required. The given list has ["
1532  + toString((int) eulerAngles.size()) + "] angles.";
1534  }
1535  return LinearAlgebra::toMatrix(eulerAngles[0], eulerAngles[1], eulerAngles[2]);
1536  }
1537 
1538 
1539 
1553  // Derived from NAIF's q2m routine
1555  if (quaternion.size() != 4) {
1556  QString msg = "Unable to convert the given vector to a rotation matrix. "
1557  "The given vector with [" + toString((int) quaternion.size())
1558  + "] components is not a quaternion.";
1560  }
1561 
1562  Vector q = quaternion;
1563  if ( !isUnit(quaternion) && !isZero(quaternion) ) {
1564  q = normalize(quaternion);
1565  }
1566 
1567  LinearAlgebra::Matrix matrix(3, 3);
1568  // For efficiency, we avoid duplicating calculations where possible.
1569  double q0q1 = q[0] * q[1];
1570  double q0q2 = q[0] * q[2];
1571  double q0q3 = q[0] * q[3];
1572 
1573  double q1q1 = q[1] * q[1];
1574  double q1q2 = q[1] * q[2];
1575  double q1q3 = q[1] * q[3];
1576 
1577  double q2q2 = q[2] * q[2];
1578  double q2q3 = q[2] * q[3];
1579 
1580  double q3q3 = q[3] * q[3];
1581 
1582  matrix(0, 0) = 1.0 - 2.0 * ( q2q2 + q3q3 );
1583  matrix(0, 1) = 2.0 * ( q1q2 - q0q3 );
1584  matrix(0, 2) = 2.0 * ( q1q3 + q0q2 );
1585 
1586  matrix(1, 0) = 2.0 * ( q1q2 + q0q3 );
1587  matrix(1, 1) = 1.0 - 2.0 * ( q1q1 + q3q3 );
1588  matrix(1, 2) = 2.0 * ( q2q3 - q0q1 );
1589 
1590  matrix(2, 0) = 2.0 * ( q1q3 - q0q2 );
1591  matrix(2, 1) = 2.0 * ( q2q3 + q0q1 );
1592  matrix(2, 2) = 1.0 - 2.0 * ( q1q1 + q2q2 );
1593 
1594  return matrix;
1595  }
1596 
1597 
1608  void LinearAlgebra::setRow(Matrix &matrix, const Vector &vector, int rowIndex) {
1609  if ( (rowIndex+1 > (int) matrix.size1()) ) {
1610  QString msg = "Unable to set the matrix row to the given vector. Row index "
1611  + toString(rowIndex) + " is out of bounds. The given matrix only has "
1612  + toString((int) matrix.size1()) + " rows." ;
1614  }
1615  boost::numeric::ublas::row(matrix, rowIndex) = vector;
1616  }
1617 
1618 
1629  void LinearAlgebra::setColumn(Matrix &matrix, const Vector &vector, int columnIndex) {
1630  if ( (columnIndex+1 > (int) matrix.size2()) ) {
1631  QString msg = "Unable to set the matrix column to the given vector. Column index "
1632  + toString(columnIndex) + " is out of bounds. The given matrix only has "
1633  + toString((int) matrix.size1()) + " columns." ;
1635  }
1636  boost::numeric::ublas::column(matrix, columnIndex) = vector;
1637  }
1638 
1639 
1652  LinearAlgebra::Vector LinearAlgebra::row(const Matrix &matrix, int rowIndex) {
1653  if ( (rowIndex+1 > (int) matrix.size1()) ) {
1654  QString msg = "Unable to get the matrix row to the given vector. Row index "
1655  + toString(rowIndex) + " is out of bounds. The given matrix only has "
1656  + toString((int) matrix.size1()) + " rows." ;
1658  }
1659  return boost::numeric::ublas::row(matrix, rowIndex);
1660  }
1661 
1662 
1675  LinearAlgebra::Vector LinearAlgebra::column(const Matrix &matrix, int columnIndex) {
1676  if ( (columnIndex+1 > (int) matrix.size2()) ) {
1677  QString msg = "Unable to get the matrix column to the given vector. Column index "
1678  + toString(columnIndex) + " is out of bounds. The given matrix only has "
1679  + toString((int) matrix.size1()) + " columns." ;
1681  }
1682  return boost::numeric::ublas::column(matrix, columnIndex);
1683  }
1684 
1685 
1696  LinearAlgebra::Vector LinearAlgebra::vector(double v0, double v1, double v2) {
1697  Vector v(3);
1698  v[0] = v0;
1699  v[1] = v1;
1700  v[2] = v2;
1701  return v;
1702  }
1703 
1704 
1716  LinearAlgebra::Vector LinearAlgebra::vector(double v0, double v1, double v2, double v3) {
1717  Vector v(4);
1718  v[0] = v0;
1719  v[1] = v1;
1720  v[2] = v2;
1721  v[3] = v3;
1722  return v;
1723  }
1724 
1725 
1734  void LinearAlgebra::setVec3(Vector *v, double v0, double v1, double v2) {
1735  (*v)[0] = v0;
1736  (*v)[1] = v1;
1737  (*v)[2] = v2;
1738  }
1739 
1740 
1750  void LinearAlgebra::setVec4(Vector *v, double v0, double v1, double v2, double v3) {
1751  (*v)[0] = v0;
1752  (*v)[1] = v1;
1753  (*v)[2] = v2;
1754  (*v)[3] = v3;
1755  }
1756 
1757 
1771  LinearAlgebra::Vector LinearAlgebra::subVector(const Vector &v, int startIndex, int size) {
1772  LinearAlgebra::Vector sub(size);
1773  for (int i = 0; i < size; i++) {
1774  sub[i] = v[i + startIndex];
1775  }
1776  return sub;
1777  }
1778 
1779 
1792  QDebug operator<<(QDebug dbg, const LinearAlgebra::Vector &vector) {
1793  QDebugStateSaver saver(dbg);
1794  dbg.noquote() << toString(vector);
1795  return dbg;
1796  }
1797 
1798 
1810  QDebug operator<<(QDebug dbg, const LinearAlgebra::Matrix &matrix) {
1811  QDebugStateSaver saver(dbg);
1812  for (unsigned int i = 0; i < matrix.size1(); i++) {
1813  dbg.noquote() << " ";
1814  for (unsigned int j = 0; j < matrix.size2(); j++) {
1815  dbg.noquote() << toString(matrix(i, j), 15) << " ";
1816  }
1817  dbg.noquote() << endl;
1818  }
1819  return dbg;
1820  }
1821 
1822 
1834  QString toString(const LinearAlgebra::Vector &vector, int precision) {
1835  QString result = "( ";
1836  for (unsigned int i = 0; i < vector.size(); i++) {
1837  result += toString(vector(i), precision);
1838  if (i != vector.size() - 1) result += ", ";
1839  }
1840  result += " )";
1841  return result;
1842  }
1843 }
static Vector project(const Vector &vector1, const Vector &vector2)
Compute the vector projection of vector1 onto vector2.
static Vector vector(double v0, double v1, double v2)
Constructs a 3 dimensional vector with the given component values.
Definition: Process.h:32
static double innerProduct(const Vector &vector1, const Vector &vector2)
Computes the inner product of the given vectors.
static Matrix transpose(const Matrix &matrix)
Returns the transpose of the given matrix.
static Vector subtract(const Vector &vector1, const Vector &vector2)
Subtracts the right vector from the left vector.
void setRadians(double radians)
Set the angle in units of Radians.
Definition: Angle.h:252
double radians() const
Convert an angle to a double.
Definition: Angle.h:239
static bool isRotationMatrix(const Matrix &matrix)
Determines whether the given matrix is a rotation matrix.
static bool isIdentity(const Matrix &matrix)
Determines whether the given matrix is the identity.
static Vector column(const Matrix &matrix, int columnIndex)
Returns a vector whose components match those of the given matrix column.
static Matrix multiply(const Matrix &matrix1, const Matrix &matrix2)
Returns the product of two matrices.
static double magnitude(const Vector &vector)
Computes the magnitude (i.e., the length) of the given vector using the Euclidean norm (L2 norm)...
const double PI(3.14159265358979323846)
The mathematical constant PI.
static Vector toQuaternion(const Matrix &rotationMatrix)
Converts a rotation&#39;s representation from a matrix to a unit quaternion.
static Vector rotate(const Vector &vector, const Vector &axis, Angle angle)
Rotates a vector about an axis vector given a specified angle.
static Vector subVector(const Vector &v, int start, int size)
Constructs a vector of given size using the given vector and starting index.
LinearAlgebra()
Default constructor for a LinearAlgebra object.
static void setColumn(Matrix &matrix, const Vector &vector, int columnIndex)
Sets the column of the given matrix to the values of the given vector.
boost::numeric::ublas::matrix< double > Matrix
Definition for an Isis::LinearAlgebra::Matrix of doubles.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
static Vector normalize(const Vector &vector)
Returns a unit vector that is codirectional with the given vector by dividing each component of the v...
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:154
static Matrix zeroMatrix(int rows, int columns)
Returns a matrix with given dimensions that is filled with zeroes.
static double dotProduct(const Vector &vector1, const Vector &vector2)
Computes the dot product of the given vectors.
static Matrix outerProduct(const Vector &vector1, const Vector &vector2)
Computes the outer product of the given vectors.
static Matrix toMatrix(const AxisAngle &axisAngle)
Converts a rotation&#39;s representation from an axis of rotation and its corresponding rotation angle to...
static Vector add(const Vector &vector1, const Vector &vector2)
Adds the two given vectors.
boost::numeric::ublas::vector< double > Vector
Definition for an Isis::LinearAlgebra::Vector of doubles.
static QList< EulerAngle > toEulerAngles(const Matrix &rotationMatrix, const QList< int > axes)
Converts a rotation&#39;s representation from a matrix to a set of Euler angles with corresponding axes...
static bool isEmpty(const Vector &vector)
Determines whether the given vector is empty (i.e.
static Vector row(const Matrix &matrix, int rowIndex)
Returns a vector whose components match those of the given matrix row.
static Matrix inverse(const Matrix &matrix)
Returns the inverse of a 2x2 or 3x3 matrix.
static AxisAngle toAxisAngle(const Matrix &rotationMatrix)
Converts a rotation&#39;s representation from a matrix to a axis of rotation and its corresponding rotati...
static bool isZero(const Matrix &matrix)
Determines whether the given matrix is filled with zereos.
static double determinant(const Matrix &matrix)
Returns the determinant of the given 3x3 matrix.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
static Matrix identity(int size)
Returns the identity matrix of size NxN.
static Vector zeroVector(int size)
Returns a vector of given length that is filled with zeroes.
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:126
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 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 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 bool isUnit(const Vector &vector)
Determines whether the given vector is a unit vector.
Defines an angle and provides unit conversions.
Definition: Angle.h:58
Isis exception class.
Definition: IException.h:99
static bool isOrthogonal(const Matrix &matrix)
Determines whether the given matrix is orthogonal by verifying that the matrix and its tranpose are i...
static Vector crossProduct(const Vector &vector1, const Vector &vector2)
Returns the cross product of two vectors.
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.
Definition: Hillshade.cpp:308
~LinearAlgebra()
Destructor for a LinearAlgebra object.
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 double absoluteMaximum(const Vector &vector)
Returns the maximum norm (L-infinity norm) for the given vector.
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...

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:22:19