1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/gfx/matrix3_f.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <limits> 10 11 #ifndef M_PI 12 #define M_PI 3.14159265358979323846 13 #endif 14 15 namespace { 16 17 // This is only to make accessing indices self-explanatory. 18 enum MatrixCoordinates { 19 M00, 20 M01, 21 M02, 22 M10, 23 M11, 24 M12, 25 M20, 26 M21, 27 M22, 28 M_END 29 }; 30 31 template<typename T> 32 double Determinant3x3(T data[M_END]) { 33 // This routine is separated from the Matrix3F::Determinant because in 34 // computing inverse we do want higher precision afforded by the explicit 35 // use of 'double'. 36 return 37 static_cast<double>(data[M00]) * ( 38 static_cast<double>(data[M11]) * data[M22] - 39 static_cast<double>(data[M12]) * data[M21]) + 40 static_cast<double>(data[M01]) * ( 41 static_cast<double>(data[M12]) * data[M20] - 42 static_cast<double>(data[M10]) * data[M22]) + 43 static_cast<double>(data[M02]) * ( 44 static_cast<double>(data[M10]) * data[M21] - 45 static_cast<double>(data[M11]) * data[M20]); 46 } 47 48 } // namespace 49 50 namespace gfx { 51 52 Matrix3F::Matrix3F() { 53 } 54 55 Matrix3F::~Matrix3F() { 56 } 57 58 // static 59 Matrix3F Matrix3F::Zeros() { 60 Matrix3F matrix; 61 matrix.set(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); 62 return matrix; 63 } 64 65 // static 66 Matrix3F Matrix3F::Ones() { 67 Matrix3F matrix; 68 matrix.set(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); 69 return matrix; 70 } 71 72 // static 73 Matrix3F Matrix3F::Identity() { 74 Matrix3F matrix; 75 matrix.set(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); 76 return matrix; 77 } 78 79 // static 80 Matrix3F Matrix3F::FromOuterProduct(const Vector3dF& a, const Vector3dF& bt) { 81 Matrix3F matrix; 82 matrix.set(a.x() * bt.x(), a.x() * bt.y(), a.x() * bt.z(), 83 a.y() * bt.x(), a.y() * bt.y(), a.y() * bt.z(), 84 a.z() * bt.x(), a.z() * bt.y(), a.z() * bt.z()); 85 return matrix; 86 } 87 88 bool Matrix3F::IsEqual(const Matrix3F& rhs) const { 89 return 0 == memcmp(data_, rhs.data_, sizeof(data_)); 90 } 91 92 bool Matrix3F::IsNear(const Matrix3F& rhs, float precision) const { 93 DCHECK(precision >= 0); 94 for (int i = 0; i < M_END; ++i) { 95 if (std::abs(data_[i] - rhs.data_[i]) > precision) 96 return false; 97 } 98 return true; 99 } 100 101 Matrix3F Matrix3F::Inverse() const { 102 Matrix3F inverse = Matrix3F::Zeros(); 103 double determinant = Determinant3x3(data_); 104 if (std::numeric_limits<float>::epsilon() > std::abs(determinant)) 105 return inverse; // Singular matrix. Return Zeros(). 106 107 inverse.set( 108 (data_[M11] * data_[M22] - data_[M12] * data_[M21]) / determinant, 109 (data_[M02] * data_[M21] - data_[M01] * data_[M22]) / determinant, 110 (data_[M01] * data_[M12] - data_[M02] * data_[M11]) / determinant, 111 (data_[M12] * data_[M20] - data_[M10] * data_[M22]) / determinant, 112 (data_[M00] * data_[M22] - data_[M02] * data_[M20]) / determinant, 113 (data_[M02] * data_[M10] - data_[M00] * data_[M12]) / determinant, 114 (data_[M10] * data_[M21] - data_[M11] * data_[M20]) / determinant, 115 (data_[M01] * data_[M20] - data_[M00] * data_[M21]) / determinant, 116 (data_[M00] * data_[M11] - data_[M01] * data_[M10]) / determinant); 117 return inverse; 118 } 119 120 float Matrix3F::Determinant() const { 121 return static_cast<float>(Determinant3x3(data_)); 122 } 123 124 Vector3dF Matrix3F::SolveEigenproblem(Matrix3F* eigenvectors) const { 125 // The matrix must be symmetric. 126 const float epsilon = std::numeric_limits<float>::epsilon(); 127 if (std::abs(data_[M01] - data_[M10]) > epsilon || 128 std::abs(data_[M02] - data_[M20]) > epsilon || 129 std::abs(data_[M12] - data_[M21]) > epsilon) { 130 NOTREACHED(); 131 return Vector3dF(); 132 } 133 134 float eigenvalues[3]; 135 float p = 136 data_[M01] * data_[M01] + 137 data_[M02] * data_[M02] + 138 data_[M12] * data_[M12]; 139 140 bool diagonal = std::abs(p) < epsilon; 141 if (diagonal) { 142 eigenvalues[0] = data_[M00]; 143 eigenvalues[1] = data_[M11]; 144 eigenvalues[2] = data_[M22]; 145 } else { 146 float q = Trace() / 3.0f; 147 p = (data_[M00] - q) * (data_[M00] - q) + 148 (data_[M11] - q) * (data_[M11] - q) + 149 (data_[M22] - q) * (data_[M22] - q) + 150 2 * p; 151 p = std::sqrt(p / 6); 152 153 // The computation below puts B as (A - qI) / p, where A is *this. 154 Matrix3F matrix_b(*this); 155 matrix_b.data_[M00] -= q; 156 matrix_b.data_[M11] -= q; 157 matrix_b.data_[M22] -= q; 158 for (int i = 0; i < M_END; ++i) 159 matrix_b.data_[i] /= p; 160 161 double half_det_b = Determinant3x3(matrix_b.data_) / 2.0; 162 // half_det_b should be in <-1, 1>, but beware of rounding error. 163 double phi = 0.0f; 164 if (half_det_b <= -1.0) 165 phi = M_PI / 3; 166 else if (half_det_b < 1.0) 167 phi = acos(half_det_b) / 3; 168 169 eigenvalues[0] = q + 2 * p * static_cast<float>(cos(phi)); 170 eigenvalues[2] = q + 2 * p * 171 static_cast<float>(cos(phi + 2.0 * M_PI / 3.0)); 172 eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; 173 } 174 175 // Put eigenvalues in the descending order. 176 int indices[3] = {0, 1, 2}; 177 if (eigenvalues[2] > eigenvalues[1]) { 178 std::swap(eigenvalues[2], eigenvalues[1]); 179 std::swap(indices[2], indices[1]); 180 } 181 182 if (eigenvalues[1] > eigenvalues[0]) { 183 std::swap(eigenvalues[1], eigenvalues[0]); 184 std::swap(indices[1], indices[0]); 185 } 186 187 if (eigenvalues[2] > eigenvalues[1]) { 188 std::swap(eigenvalues[2], eigenvalues[1]); 189 std::swap(indices[2], indices[1]); 190 } 191 192 if (eigenvectors != NULL && diagonal) { 193 // Eigenvectors are e-vectors, just need to be sorted accordingly. 194 *eigenvectors = Zeros(); 195 for (int i = 0; i < 3; ++i) 196 eigenvectors->set(indices[i], i, 1.0f); 197 } else if (eigenvectors != NULL) { 198 // Consult the following for a detailed discussion: 199 // Joachim Kopp 200 // Numerical diagonalization of hermitian 3x3 matrices 201 // arXiv.org preprint: physics/0610206 202 // Int. J. Mod. Phys. C19 (2008) 523-548 203 204 // TODO(motek): expand to handle correctly negative and multiple 205 // eigenvalues. 206 for (int i = 0; i < 3; ++i) { 207 float l = eigenvalues[i]; 208 // B = A - l * I 209 Matrix3F matrix_b(*this); 210 matrix_b.data_[M00] -= l; 211 matrix_b.data_[M11] -= l; 212 matrix_b.data_[M22] -= l; 213 Vector3dF e1 = CrossProduct(matrix_b.get_column(0), 214 matrix_b.get_column(1)); 215 Vector3dF e2 = CrossProduct(matrix_b.get_column(1), 216 matrix_b.get_column(2)); 217 Vector3dF e3 = CrossProduct(matrix_b.get_column(2), 218 matrix_b.get_column(0)); 219 220 // e1, e2 and e3 should point in the same direction. 221 if (DotProduct(e1, e2) < 0) 222 e2 = -e2; 223 224 if (DotProduct(e1, e3) < 0) 225 e3 = -e3; 226 227 Vector3dF eigvec = e1 + e2 + e3; 228 // Normalize. 229 eigvec.Scale(1.0f / eigvec.Length()); 230 eigenvectors->set_column(i, eigvec); 231 } 232 } 233 234 return Vector3dF(eigenvalues[0], eigenvalues[1], eigenvalues[2]); 235 } 236 237 } // namespace gfx 238