Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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.14159265358979323846f | |
| 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(const Matrix3F& rhs) { | |
| 56 memcpy(data_, rhs.data_, sizeof(data_)); | |
| 57 } | |
| 58 | |
| 59 Matrix3F::~Matrix3F() { | |
| 60 } | |
| 61 | |
| 62 bool Matrix3F::IsEqual(const Matrix3F& rhs) const { | |
| 63 return 0 == memcmp(data_, rhs.data_, sizeof(data_)); | |
| 64 } | |
| 65 | |
| 66 bool Matrix3F::IsNear(const Matrix3F& rhs, float precision) const { | |
| 67 DCHECK(precision >= 0); | |
| 68 for (int i = 0; i < M_END; ++i) { | |
| 69 if (std::abs(data_[i] - rhs.data_[i]) > precision) | |
| 70 return false; | |
| 71 } | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 void Matrix3F::set(float m00, float m01, float m02, | |
| 76 float m10, float m11, float m12, | |
| 77 float m20, float m21, float m22) { | |
|
danakj
2013/02/01 00:02:34
oh, missed this. i think this belongs in the heade
motek.
2013/02/01 00:14:02
Done.
| |
| 78 data_[0] = m00; | |
| 79 data_[1] = m01; | |
| 80 data_[2] = m02; | |
| 81 data_[3] = m10; | |
| 82 data_[4] = m11; | |
| 83 data_[5] = m12; | |
| 84 data_[6] = m20; | |
| 85 data_[7] = m21; | |
| 86 data_[8] = m22; | |
| 87 } | |
| 88 | |
| 89 Matrix3F Matrix3F::Inverse() const { | |
| 90 Matrix3F inverse = Matrix3F::Zeros(); | |
| 91 double determinant = Determinant3x3(data_); | |
| 92 if (std::numeric_limits<float>::epsilon() > std::abs(determinant)) | |
| 93 return inverse; // Singular matrix. Return Zeros(). | |
| 94 | |
| 95 inverse.set( | |
| 96 (data_[M11] * data_[M22] - data_[M12] * data_[M21]) / determinant, | |
| 97 (data_[M02] * data_[M21] - data_[M01] * data_[M22]) / determinant, | |
| 98 (data_[M01] * data_[M12] - data_[M02] * data_[M11]) / determinant, | |
| 99 (data_[M12] * data_[M20] - data_[M10] * data_[M22]) / determinant, | |
| 100 (data_[M00] * data_[M22] - data_[M02] * data_[M20]) / determinant, | |
| 101 (data_[M02] * data_[M10] - data_[M00] * data_[M12]) / determinant, | |
| 102 (data_[M10] * data_[M21] - data_[M11] * data_[M20]) / determinant, | |
| 103 (data_[M01] * data_[M20] - data_[M00] * data_[M21]) / determinant, | |
| 104 (data_[M00] * data_[M11] - data_[M01] * data_[M10]) / determinant); | |
| 105 return inverse; | |
| 106 } | |
| 107 | |
| 108 float Matrix3F::Determinant() const { | |
| 109 return static_cast<float>(Determinant3x3(data_)); | |
| 110 } | |
| 111 | |
| 112 Vector3dF Matrix3F::SolveEigenproblem(Matrix3F* eigenvectors) const { | |
| 113 // The matrix must be symmetric. | |
| 114 const float epsilon = std::numeric_limits<float>::epsilon(); | |
| 115 if (std::abs(data_[M01] - data_[M10]) > epsilon || | |
| 116 std::abs(data_[M02] - data_[M02]) > epsilon || | |
| 117 std::abs(data_[M12] - data_[M21]) > epsilon) { | |
| 118 NOTREACHED(); | |
| 119 return Vector3dF(); | |
| 120 } | |
| 121 | |
| 122 float eigenvalues[3]; | |
| 123 float p = | |
| 124 data_[M01] * data_[M01] + | |
| 125 data_[M02] * data_[M02] + | |
| 126 data_[M12] * data_[M12]; | |
| 127 | |
| 128 bool diagonal = std::abs(p) < epsilon; | |
| 129 if (diagonal) { | |
| 130 eigenvalues[0] = data_[M00]; | |
| 131 eigenvalues[1] = data_[M11]; | |
| 132 eigenvalues[2] = data_[M22]; | |
| 133 } else { | |
| 134 float q = Trace() / 3.0f; | |
| 135 p = (data_[M00] - q) * (data_[M00] - q) + | |
| 136 (data_[M11] - q) * (data_[M11] - q) + | |
| 137 (data_[M22] - q) * (data_[M22] - q) + | |
| 138 2 * p; | |
| 139 p = sqrt(p / 6); | |
|
danakj
2013/02/01 00:02:34
nit: std::sqrt
motek.
2013/02/01 00:14:02
Done.
| |
| 140 | |
| 141 // The computation below puts B as (A - qI) / p, where A is *this. | |
| 142 Matrix3F matrix_B(*this); | |
|
danakj
2013/02/01 00:02:34
nit: matrix_b
motek.
2013/02/01 00:14:02
Done.
| |
| 143 matrix_B.data_[M00] -= q; | |
| 144 matrix_B.data_[M11] -= q; | |
| 145 matrix_B.data_[M22] -= q; | |
| 146 for (int i = 0; i < M_END; ++i) | |
| 147 matrix_B.data_[i] /= p; | |
| 148 | |
| 149 float half_det_b = matrix_B.Determinant() / 2.0f; | |
| 150 // half_det_b should be in <-1, 1>, but beware of rounding error. | |
| 151 float phi = 0.0f; | |
| 152 if (half_det_b <= -1.0f) | |
| 153 phi = M_PI / 3; | |
| 154 else if (half_det_b < 1.0f) | |
| 155 phi = acos(half_det_b) / 3; | |
| 156 | |
| 157 eigenvalues[0] = q + 2 * p * cos(phi); | |
| 158 eigenvalues[2] = q + 2 * p * cos(phi + 2.0f * M_PI / 3.0f); | |
| 159 eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; | |
| 160 } | |
| 161 | |
| 162 // Put eigenvalues in the descending order. | |
| 163 int indices[3] = {0, 1, 2}; | |
| 164 if (eigenvalues[2] > eigenvalues[1]) { | |
| 165 std::swap(eigenvalues[2], eigenvalues[1]); | |
| 166 std::swap(indices[2], indices[1]); | |
| 167 } | |
| 168 | |
| 169 if (eigenvalues[1] > eigenvalues[0]) { | |
| 170 std::swap(eigenvalues[1], eigenvalues[0]); | |
| 171 std::swap(indices[1], indices[0]); | |
| 172 } | |
| 173 | |
| 174 if (eigenvalues[2] > eigenvalues[1]) { | |
| 175 std::swap(eigenvalues[2], eigenvalues[1]); | |
| 176 std::swap(indices[2], indices[1]); | |
| 177 } | |
| 178 | |
| 179 if (eigenvectors != NULL && diagonal) { | |
| 180 // Eigenvectors are e-vectors, just need to be sorted accordingly. | |
| 181 *eigenvectors = Zeros(); | |
| 182 for (int i = 0; i < 3; ++i) | |
| 183 eigenvectors->set(indices[i], i, 1.0f); | |
| 184 } else if (eigenvectors != NULL) { | |
| 185 // Consult the following for a detailed discussion: | |
| 186 // Joachim Kopp | |
| 187 // Numerical diagonalization of hermitian 3x3 matrices | |
| 188 // arXiv.org preprint: physics/0610206 | |
| 189 // Int. J. Mod. Phys. C19 (2008) 523-548 | |
| 190 | |
| 191 // TODO(motek): expand to handle correctly negative and multiple | |
| 192 // eigenvalues. | |
| 193 for (int i = 0; i < 3; ++i) { | |
| 194 float l = eigenvalues[i]; | |
| 195 // B = A - l * I | |
| 196 Matrix3F matrix_B(*this); | |
| 197 matrix_B.data_[M00] -= l; | |
| 198 matrix_B.data_[M11] -= l; | |
| 199 matrix_B.data_[M22] -= l; | |
| 200 Vector3dF e1 = CrossProduct(matrix_B.get_column(0), | |
| 201 matrix_B.get_column(1)); | |
| 202 Vector3dF e2 = CrossProduct(matrix_B.get_column(1), | |
| 203 matrix_B.get_column(2)); | |
| 204 Vector3dF e3 = CrossProduct(matrix_B.get_column(2), | |
| 205 matrix_B.get_column(0)); | |
| 206 | |
| 207 // e1, e2 and e3 should point in the same direction. | |
| 208 if (DotProduct(e1, e2) < 0) { | |
| 209 e2.set_x(-e2.x()); | |
|
danakj
2013/02/01 00:02:34
can use e2 = -e2; ?
motek.
2013/02/01 00:14:02
Ah, didn't notice the op. Cool. Done.
| |
| 210 e2.set_y(-e2.y()); | |
| 211 e2.set_z(-e2.z()); | |
| 212 } | |
| 213 | |
| 214 if (DotProduct(e1, e3) < 0) { | |
| 215 e3.set_x(-e3.x()); | |
|
danakj
2013/02/01 00:02:34
e3 = -e3; ?
motek.
2013/02/01 00:14:02
Done.
| |
| 216 e3.set_y(-e3.y()); | |
| 217 e3.set_z(-e3.z()); | |
| 218 } | |
| 219 | |
| 220 Vector3dF eigvec = e1 + e2 + e3; | |
| 221 // Normalize. | |
| 222 eigvec.Scale(1.0f / eigvec.Length()); | |
| 223 eigenvectors->set_column(i, eigvec); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 return Vector3dF(eigenvalues[0], eigenvalues[1], eigenvalues[2]); | |
| 228 } | |
| 229 | |
| 230 // static | |
| 231 Matrix3F Matrix3F::Zeros() { | |
| 232 Matrix3F matrix; | |
| 233 matrix.set(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); | |
| 234 return matrix; | |
| 235 } | |
| 236 | |
| 237 // static | |
| 238 Matrix3F Matrix3F::Ones() { | |
| 239 Matrix3F matrix; | |
| 240 matrix.set(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); | |
| 241 return matrix; | |
| 242 } | |
| 243 | |
| 244 // static | |
| 245 Matrix3F Matrix3F::Identity() { | |
| 246 Matrix3F matrix; | |
| 247 matrix.set(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); | |
| 248 return matrix; | |
| 249 } | |
| 250 | |
| 251 // static | |
| 252 Matrix3F Matrix3F::FromOuterProduct(const Vector3dF& a, const Vector3dF& bt) { | |
| 253 Matrix3F matrix; | |
| 254 matrix.set(a.x() * bt.x(), a.x() * bt.y(), a.x() * bt.z(), | |
| 255 a.y() * bt.x(), a.y() * bt.y(), a.y() * bt.z(), | |
| 256 a.z() * bt.x(), a.z() * bt.y(), a.z() * bt.z()); | |
| 257 return matrix; | |
| 258 } | |
| 259 | |
| 260 } // namespace gfx | |
| OLD | NEW |