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;