1 // Copyright 2003 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Converted to D:  madric@gmail.com (Vijay Nayar)
16 
17 module s2.util.math.matrix3x3;
18 
19 import s2.util.math.mathutil;
20 import s2.util.math.s2const;
21 import s2.util.math.vector;
22 import mathutil = s2.util.math.mathutil;
23 import conv = std.conv;
24 import math = std.math;
25 import traits = std.traits;
26 
27 /**
28  * A simple class to handle 3x3 matrices
29  * The aim of this class is to be able to manipulate 3x3 matrices
30  * and 3D vectors as naturally as possible and make calculations
31  * readable.
32  * For that reason, the operators +, -, * are overloaded.
33  * (Reading a = a + b*2 - c is much easier to read than
34  * a = Sub(Add(a, Mul(b,2)),c)   )
35  *
36  * Please be careful about overflows when using those matrices wth integer types
37  * The calculations are carried with ElemT. eg : if you are using ubyte as the
38  * base type, all values will be modulo 256.
39  * This feature is necessary to use the class in a more general framework with
40  * ElemT != plain old data type.
41  */
42 struct Matrix3x3(ElemT)
43 if (traits.isNumeric!ElemT) {
44 private:
45   ElemT[3][3] _m;
46 
47 public:
48   alias ThisT = Matrix3x3!ElemT;
49   alias VectorT = Vector!(ElemT, 3);
50 
51   static if (traits.isIntegral!ElemT) {
52     alias FloatT = double;
53   } else {
54     alias FloatT = ElemT;
55   }
56 
57   this(ThisT v) {
58     _m = v._m;
59   }
60 
61   /// Constructor explicitly setting the values of all the coefficient of the matrix.
62   this(
63       in ElemT m00, in ElemT m01, in ElemT m02,
64       in ElemT m10, in ElemT m11, in ElemT m12,
65       in ElemT m20, in ElemT m21, in ElemT m22) {
66     _m[0][0] = m00;
67     _m[0][1] = m01;
68     _m[0][2] = m02;
69 
70     _m[1][0] = m10;
71     _m[1][1] = m11;
72     _m[1][2] = m12;
73 
74     _m[2][0] = m20;
75     _m[2][1] = m21;
76     _m[2][2] = m22;
77   }
78 
79   /// Casting constructor
80   MatrixT opCast(MatrixT : Matrix3x3!ElemT2, ElemT2)() const {
81     return MatrixT(
82         conv.to!ElemT2(_m[0][0]),
83         conv.to!ElemT2(_m[0][1]),
84         conv.to!ElemT2(_m[0][2]),
85         conv.to!ElemT2(_m[1][0]),
86         conv.to!ElemT2(_m[1][1]),
87         conv.to!ElemT2(_m[1][2]),
88         conv.to!ElemT2(_m[2][0]),
89         conv.to!ElemT2(_m[2][1]),
90         conv.to!ElemT2(_m[2][2]));
91   }
92 
93   /// Change the value of all the coefficients of the matrix.
94   ThisT set(
95       in ElemT m00, in ElemT m01, in ElemT m02,
96       in ElemT m10, in ElemT m11, in ElemT m12,
97       in ElemT m20, in ElemT m21, in ElemT m22) {
98     _m[0][0] = m00;
99     _m[0][1] = m01;
100     _m[0][2] = m02;
101 
102     _m[1][0] = m10;
103     _m[1][1] = m11;
104     _m[1][2] = m12;
105 
106     _m[2][0] = m20;
107     _m[2][1] = m21;
108     _m[2][2] = m22;
109 
110     return this;
111   }
112 
113   /// Support Matrix operators +=, -=.
114   ThisT opOpAssign(string op)(in ThisT mb)
115   if (op == "+" || op == "-") {
116     static foreach (x; 0..3) {
117       static foreach (y; 0..3) {
118         mixin("_m[x][y] " ~ op ~ "= mb._m[x][y];");
119       }
120     }
121     return this;
122   }
123 
124   // Matrix multiplication
125   ThisT opOpAssign(string op)(in ThisT mb)
126   if (op == "*") {
127     this = this * mb;
128     return this;
129   }
130 
131   // Element-wise assignment operators.
132   ThisT opOpAssign(string op)(in ElemT k) {
133     static foreach (x; 0..3) {
134       static foreach (y; 0..3) {
135          mixin("_m[x][y] " ~ op ~ "= k;");
136       }
137     }
138     return this;
139   }
140 
141   // Support Matrix operators +, -
142   ThisT opBinary(string op)(in ThisT mb) const
143   if (op == "+" || op == "-") {
144     ThisT v = ThisT(this);
145     return mixin("v " ~ op ~ "= mb");
146   }
147 
148   // Matrix multiplication.
149   ThisT opBinary(string op)(in ThisT mb) const
150   if (op == "*") {
151     ThisT v = ThisT(
152         _m[0][0] * mb._m[0][0] + _m[0][1] * mb._m[1][0] + _m[0][2] * mb._m[2][0],
153         _m[0][0] * mb._m[0][1] + _m[0][1] * mb._m[1][1] + _m[0][2] * mb._m[2][1],
154         _m[0][0] * mb._m[0][2] + _m[0][1] * mb._m[1][2] + _m[0][2] * mb._m[2][2],
155 
156         _m[1][0] * mb._m[0][0] + _m[1][1] * mb._m[1][0] + _m[1][2] * mb._m[2][0],
157         _m[1][0] * mb._m[0][1] + _m[1][1] * mb._m[1][1] + _m[1][2] * mb._m[2][1],
158         _m[1][0] * mb._m[0][2] + _m[1][1] * mb._m[1][2] + _m[1][2] * mb._m[2][2],
159 
160         _m[2][0] * mb._m[0][0] + _m[2][1] * mb._m[1][0] + _m[2][2] * mb._m[2][0],
161         _m[2][0] * mb._m[0][1] + _m[2][1] * mb._m[1][1] + _m[2][2] * mb._m[2][1],
162         _m[2][0] * mb._m[0][2] + _m[2][1] * mb._m[1][2] + _m[2][2] * mb._m[2][2]);
163 
164     return v;
165   }
166 
167   // Change the sign of all the coefficients in the matrix
168   ThisT opUnary(string op)() const
169   if (op == "-") {
170     return ThisT(
171         -_m[0][0], -_m[0][1], -_m[0][2],
172         -_m[1][0], -_m[1][1], -_m[1][2],
173         -_m[2][0], -_m[2][1], -_m[2][2]);
174   }
175 
176   // Matrix scalar binary operators.
177   ThisT opBinary(string op)(in ElemT k) const {
178     ThisT v = ThisT(this);
179     return mixin("v " ~ op ~ "= k");
180   }
181 
182   ThisT opBinaryRight(string op)(in ElemT k) const {
183     ThisT v = ThisT();
184     static foreach (x; 0..3) {
185       static foreach (y; 0..3) {
186         mixin("v._m[x][y] = k " ~ op ~ " _m[x][y];");
187       }
188     }
189     return v;
190   }
191 
192   // Multiplication of a matrix by a vector
193   VectorT opBinary(string op)(in VectorT v) const
194   if (op == "*") {
195     return VectorT(
196       _m[0][0] * v[0] + _m[0][1] * v[1] + _m[0][2] * v[2],
197       _m[1][0] * v[0] + _m[1][1] * v[1] + _m[1][2] * v[2],
198       _m[2][0] * v[0] + _m[2][1] * v[1] + _m[2][2] * v[2]);
199   }
200 
201   // Return the determinant of the matrix
202   ElemT det() const {
203     return _m[0][0] * _m[1][1] * _m[2][2]
204          + _m[0][1] * _m[1][2] * _m[2][0]
205          + _m[0][2] * _m[1][0] * _m[2][1]
206          - _m[2][0] * _m[1][1] * _m[0][2]
207          - _m[2][1] * _m[1][2] * _m[0][0]
208          - _m[2][2] * _m[1][0] * _m[0][1];
209   }
210 
211   // Return the trace of the matrix
212   ElemT trace() const {
213     return _m[0][0] + _m[1][1] + _m[2][2];
214   }
215 
216   // Return a pointer to the data array for interface with other libraries
217   // like opencv
218   @property
219   ref ElemT[3][3] data() {
220     return _m;
221   }
222 
223   // Return matrix element (i,j) with 0<=i<=2 0<=j<=2
224   ref inout(ElemT) opIndex(in size_t i, in size_t j) inout
225   in {
226     assert(i >= 0 && i < 3);
227     assert(j >= 0 && j < 3);
228   } do {
229     return _m[i][j];
230   }
231 
232   void opIndexAssign(ElemT value, in size_t i, in size_t j)
233   in {
234     assert(i >= 0 && i < 3);
235     assert(j >= 0 && j < 3);
236   } do {
237     _m[i][j] = value;
238   }
239 
240   // Return matrix element (i/3,i%3) with 0<=i<=8 (access concatenated rows).
241   ElemT opIndex(in size_t i)
242   in {
243     assert(i >= 0 && i < 9);
244   } do {
245     return _m[i/3][i%3];
246   }
247 
248   ElemT opIndexAssign(ElemT value, in size_t i)
249   in {
250     assert(i >= 0 && i < 9);
251   } do {
252     return _m[i/3][i%3] = value;
253   }
254 
255   // Return the transposed matrix
256   ThisT transpose() const {
257     return ThisT(
258         _m[0][0], _m[1][0], _m[2][0],
259         _m[0][1], _m[1][1], _m[2][1],
260         _m[0][2], _m[1][2], _m[2][2]);
261   }
262 
263   // Return the transposed of the matrix of the cofactors
264   // (Useful for inversion for example)
265   ThisT cofactorMatrixTransposed() const {
266     return ThisT(
267       _m[1][1] * _m[2][2] - _m[2][1] * _m[1][2],
268       _m[2][1] * _m[0][2] - _m[0][1] * _m[2][2],
269       _m[0][1] * _m[1][2] - _m[1][1] * _m[0][2],
270 
271       _m[1][2] * _m[2][0] - _m[2][2] * _m[1][0],
272       _m[2][2] * _m[0][0] - _m[0][2] * _m[2][0],
273       _m[0][2] * _m[1][0] - _m[1][2] * _m[0][0],
274 
275       _m[1][0] * _m[2][1] - _m[2][0] * _m[1][1],
276       _m[2][0] * _m[0][1] - _m[0][0] * _m[2][1],
277       _m[0][0] * _m[1][1] - _m[1][0] * _m[0][1]);
278   }
279 
280   // Matrix inversion
281   ThisT inverse() const {
282     ElemT det = det();
283     assert(det != cast(ElemT) 0, "Can't inverse. Determinant = 0.");
284     return (cast(ElemT) 1 / det) * cofactorMatrixTransposed();
285   }
286 
287   // Return the vector 3D at row i
288   VectorT row(in int i) const
289   in {
290     assert(i >= 0 && i < 3);
291   } do {
292     return VectorT(_m[i][0], _m[i][1], _m[i][2]);
293   }
294 
295   // Return the vector 3D at col i
296   VectorT col(in int i) const
297   in {
298     assert(i >= 0 && i < 3);
299   } do {
300     return VectorT(_m[0][i], _m[1][i], _m[2][i]);
301   }
302 
303   // Create a matrix from 3 row vectors
304   static ThisT fromRows(in VectorT v1, in VectorT v2, in VectorT v3) {
305     ThisT temp;
306     temp.set(
307         v1[0], v1[1], v1[2],
308         v2[0], v2[1], v2[2],
309         v3[0], v3[1], v3[2]);
310     return temp;
311   }
312 
313   // Create a matrix from 3 column vectors
314   static ThisT fromCols(in VectorT v1, in VectorT v2, in VectorT v3) {
315     ThisT temp;
316     temp.set(
317         v1[0], v2[0], v3[0],
318         v1[1], v2[1], v3[1],
319         v1[2], v2[2], v3[2]);
320     return temp;
321   }
322 
323   // Set the vector in row i to be v1
324   void setRow(int i, in VectorT v1)
325   in {
326     assert(i >= 0 && i < 3);
327   } do {
328     _m[i][0] = v1[0];
329     _m[i][1] = v1[1];
330     _m[i][2] = v1[2];
331   }
332 
333   // Set the vector in column i to be v1
334   void setCol(int i, in VectorT v1)
335   in {
336     assert(i >= 0 && i < 3);
337   } do {
338     _m[0][i] = v1[0];
339     _m[1][i] = v1[1];
340     _m[2][i] = v1[2];
341   }
342 
343   // Return a matrix M close to the original but verifying MtM = I
344   // (useful to compensate for errors in a rotation matrix)
345   static if (traits.isFloatingPoint!ElemT) {
346     ThisT orthogonalize() const {
347       VectorT r1, r2, r3;
348       r1 = row(0).normalize();
349       r2 = (row(2).crossProd(r1)).normalize();
350       r3 = (r1.crossProd(r2)).normalize();
351       return fromRows(r1, r2, r3);
352     }
353   }
354 
355   // Return the identity matrix
356   static ThisT identity() {
357     ThisT temp;
358     temp.set(conv.to!ElemT(1), conv.to!ElemT(0), conv.to!ElemT(0),
359              conv.to!ElemT(0), conv.to!ElemT(1), conv.to!ElemT(0),
360              conv.to!ElemT(0), conv.to!ElemT(0), conv.to!ElemT(1));
361     return temp;
362   }
363 
364   // Return a matrix full of zeros
365   static ThisT zero() {
366     return ThisT();
367   }
368 
369   // Return a diagonal matrix with the coefficients in v
370   static ThisT diagonal(in VectorT v) {
371     return ThisT(
372         v[0], conv.to!ElemT(0), conv.to!ElemT(0),
373         conv.to!ElemT(0), v[1], conv.to!ElemT(0),
374         conv.to!ElemT(0), conv.to!ElemT(0), v[2]);
375   }
376 
377   // Return the matrix vvT
378   static ThisT sym3(in VectorT v) {
379     return ThisT(
380       v[0]*v[0], v[0]*v[1], v[0]*v[2],
381       v[1]*v[0], v[1]*v[1], v[1]*v[2],
382       v[2]*v[0], v[2]*v[1], v[2]*v[2]);
383   }
384 
385   // Return a matrix M such that:
386   // for each u,  M * u = v.CrossProd(u)
387   static ThisT antiSym3(in VectorT v) {
388     return ThisT(
389         conv.to!ElemT(0),    -v[2],     v[1],
390         v[2],     conv.to!ElemT(0),    -v[0],
391         -v[1],       v[0],   conv.to!ElemT(0));
392   }
393 
394   // Returns matrix that rotates |rot| radians around axis rot.
395   static if (traits.isFloatingPoint!ElemT) {
396     static ThisT rodrigues(in VectorT rot) {
397       ThisT R;
398       ElemT theta = rot.norm();
399       VectorT w = VectorT(rot).normalize();
400       ThisT Wv = ThisT.antiSym3(w);
401       ThisT I = ThisT.identity();
402       ThisT A = ThisT.sym3(w);
403       R = (1 - math.cos(theta)) * A + math.sin(theta) * Wv + math.cos(theta) * I;
404       return R;
405     }
406   }
407 
408   // Returns v.Transpose() * (*this) * u
409   ElemT mulBothSides(in VectorT v, in VectorT u) const {
410     return (this * u).dotProd(v);
411   }
412 
413   // Use the 3x3 matrix as a projective transform for 2d points
414   Vector2!ElemT project(in Vector!(ElemT, 2) v) const {
415     VectorT temp = Matrix3x3!ElemT(this) * VectorT(v[0], v[1], 1);
416     return Vector2!ElemT(temp[0] / temp[2], temp[1] / temp[2]);
417   }
418 
419   // Return the Frobenius norm of the matrix: sqrt(sum(aij^2))
420   ElemT frobeniusNorm() const {
421     ElemT sum = conv.to!ElemT(0);
422     for (int i = 0; i < 3; i++) {
423       for (int j = 0; j < 3; j++) {
424         sum += _m[i][j] * _m[i][j];
425       }
426     }
427     return conv.to!ElemT(math.sqrt(conv.to!FloatT(sum)));
428   }
429 
430   // Finds the eigen values of the matrix. Return the number of real eigenvalues
431   // found
432   int eigenValues(out VectorT eig_val) const {
433     real r1, r2, r3;  // NOLINT
434     // characteristic polynomial is
435     // x^3 + (a11*a22+a22*a33+a33*a11)*x^2 - trace(A)*x - det(A)
436     ElemT a = -trace();
437     ElemT b = _m[0][0]*_m[1][1] + _m[1][1]*_m[2][2] + _m[2][2]*_m[0][0]
438             - _m[1][0]*_m[0][1] - _m[2][1]*_m[1][2] - _m[0][2]*_m[2][0];
439     ElemT c = -det();
440     bool res = mathutil.realRootsForCubic(a, b, c, r1, r2, r3);
441     eig_val[0] = conv.to!ElemT(r1);
442     if (res) {
443       eig_val[1] = conv.to!ElemT(r2);
444       eig_val[2] = conv.to!ElemT(r3);
445       return 3;
446     }
447     return 1;
448   }
449 
450   // Finds the eigen values and associated eigen vectors of a
451   // symmetric positive definite 3x3 matrix,eigen values are
452   // sorted in decreasing order, eig_val corresponds to the
453   // columns of the eig_vec matrix.
454   // Note: The routine will only use the lower diagonal part
455   // of the matrix, i.e.
456   // |  a00,          |
457   // |  a10, a11,     |
458   // |  a20, a21, a22 |
459   static if (traits.isFloatingPoint!ElemT) {
460     void symmetricEigenSolver(out VectorT eig_val, out Matrix3x3!ElemT eig_vec) const {
461       // Compute characteristic polynomial coefficients
462       double c2 = -(_m[0][0] + _m[1][1] + _m[2][2]);
463       double c1 = -(_m[1][0] * _m[1][0] - _m[0][0] * _m[1][1]
464           - _m[0][0] * _m[2][2] - _m[1][1] * _m[2][2]
465           + _m[2][0] * _m[2][0] + _m[2][1] * _m[2][1]);
466       double c0 = -(_m[0][0] * _m[1][1] * _m[2][2] - _m[2][0]
467           * _m[2][0] * _m[1][1] - _m[1][0] * _m[1][0]
468           * _m[2][2] - _m[0][0] * _m[2][1] * _m[2][1]
469           + 2 * _m[1][0] * _m[2][0] * _m[2][1]);
470 
471       // Root finding
472       double q = (c2*c2-3*c1)/9.0;
473       double r = (2*c2*c2*c2-9*c2*c1+27*c0)/54.0;
474       // Assume R^3 <Q^3 so there are three real roots
475       if (q < 0) q = 0;
476       double sqrt_q = -2.0 * math.sqrt(q);
477       double theta = math.acos(r / math.sqrt(q * q * q));
478       double c2_3 = c2 / 3;
479       eig_val[0] = conv.to!ElemT(sqrt_q * math.cos(theta / 3.0) - c2_3);
480       eig_val[1] = conv.to!ElemT(sqrt_q * math.cos((theta + 2.0 * M_PI)/3.0) - c2_3);
481       eig_val[2] = conv.to!ElemT(sqrt_q * math.cos((theta - 2.0 * M_PI)/3.0) - c2_3);
482 
483       // Sort eigen value in decreasing order
484       Vector3!int d_order = eig_val.componentOrder();
485       eig_val = VectorT(eig_val[d_order[2]], eig_val[d_order[1]], eig_val[d_order[0]]);
486       // Compute eigenvectors
487       for (int i = 0; i < 3; ++i) {
488         VectorT r1 , r2 , r3 , e1 , e2 , e3;
489         r1[0] = _m[0][0] - eig_val[i];
490         r2[0] = _m[1][0];
491         r3[0] = _m[2][0];
492         r1[1] = _m[1][0];
493         r2[1] = _m[1][1] - eig_val[i];
494         r3[1] = _m[2][1];
495         r1[2] = _m[2][0];
496         r2[2] = _m[2][1];
497         r3[2] = _m[2][2] - eig_val[i];
498 
499         e1 = r1.crossProd(r2);
500         e2 = r2.crossProd(r3);
501         e3 = r3.crossProd(r1);
502 
503         // Make e2 and e3 point in the same direction as e1
504         if (e2.dotProd(e1) < 0) e2 = -e2;
505         if (e3.dotProd(e1) < 0) e3 = -e3;
506         VectorT e = (e1 + e2 + e3).normalize();
507         eig_vec.setCol(i, e);
508       }
509     }
510   }
511 
512   // Return true is one of the elements of the matrix is NaN
513   static if (traits.isFloatingPoint!ElemT) {
514     bool isNaN() const {
515       for (int i = 0; i < 3; ++i ) {
516         for (int j = 0; j < 3; ++j ) {
517           if (math.isNaN(_m[i][j]) ) {
518             return true;
519           }
520         }
521       }
522       return false;
523     }
524   }
525 
526   bool opEquals(in Matrix3x3!ElemT v) const {
527     return _m[0][0] == v._m[0][0]
528         && _m[0][1] == v._m[0][1]
529         && _m[0][2] == v._m[0][2]
530         && _m[1][0] == v._m[1][0]
531         && _m[1][1] == v._m[1][1]
532         && _m[1][2] == v._m[1][2]
533         && _m[2][0] == v._m[2][0]
534         && _m[2][1] == v._m[2][1]
535         && _m[2][2] == v._m[2][2];
536   }
537 
538   string toString() const {
539     import std.format;
540     string val = "";
541     int i, j;
542     for (i = 0; i < 3; i++) {
543       if (i ==0) {
544         val ~= "[";
545       } else {
546         val ~= " ";
547       }
548       for (j = 0; j < 3; j++) {
549         val ~= conv.to!string(_m[i][j]) ~ " ";
550       }
551       if (i == 2) {
552         val ~= "]";
553       } else {
554         val ~= "\n";
555       }
556     }
557     return val;
558   }
559 }
560 
561 alias Matrix3x3_i = Matrix3x3!int;
562 alias Matrix3x3_f = Matrix3x3!float;
563 alias Matrix3x3_d = Matrix3x3!double;