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 #include "base/logging.h" | |
12 | |
13 namespace { | |
14 | |
15 // This is only to make accessing indices self-explanatory. | |
16 enum MatrixCoordinates { | |
17 kM00, | |
18 kM01, | |
19 kM02, | |
20 kM10, | |
21 kM11, | |
22 kM12, | |
23 kM20, | |
24 kM21, | |
25 kM22, | |
26 kMEnd | |
27 }; | |
28 | |
29 template<typename T> | |
30 double Determinant3x3(T data[kMEnd]) { | |
Alexei Svitkine (slow)
2013/01/31 20:02:38
Add a comment explaining the purpose (same as you
motek.
2013/01/31 22:00:24
Done.
| |
31 return (double)data[kM00] * ((double)data[kM11] * data[kM22] - | |
Alexei Svitkine (slow)
2013/01/31 20:02:38
Use C++ casts, rather than C-style casts.
motek.
2013/01/31 22:00:24
Done.
| |
32 (double)data[kM12] * data[kM21]) + | |
33 data[kM01] * ((double)data[kM12] * data[kM20] - | |
34 (double)data[kM10] * data[kM22]) + | |
35 data[kM02] * ((double)data[kM10] * data[kM21] - | |
36 (double)data[kM11] * data[kM20]); | |
37 } | |
38 | |
39 } // namespace | |
40 | |
41 namespace gfx { | |
42 | |
43 Matrix3F::Matrix3F() { | |
44 } | |
45 | |
46 Matrix3F::Matrix3F(const VectorType& a, const VectorType& bt) { | |
47 set(a.x() * bt.x(), a.x() * bt.y(), a.x() * bt.z(), | |
48 a.y() * bt.x(), a.y() * bt.y(), a.y() * bt.z(), | |
49 a.z() * bt.x(), a.z() * bt.y(), a.z() * bt.z()); | |
50 } | |
51 | |
52 Matrix3F::Matrix3F(const Matrix3F& rhs) { | |
53 memcpy(data_, rhs.data_, sizeof(data_)); | |
54 } | |
55 | |
56 Matrix3F::~Matrix3F() { | |
57 } | |
58 | |
59 bool Matrix3F::IsEqual(const Matrix3F& rhs) const { | |
60 return 0 == memcmp(data_, rhs.data_, sizeof(data_)); | |
61 } | |
62 | |
63 bool Matrix3F::IsNear(const Matrix3F& rhs, ScalarType precision) const { | |
64 DCHECK(precision >= 0); | |
65 for (int i = 0; i < kMEnd; ++i) { | |
66 if (std::abs(data_[i] - rhs.data_[i]) > precision) | |
67 return false; | |
68 } | |
69 return true; | |
70 } | |
71 | |
72 Matrix3F::ScalarType Matrix3F::get(int i, int j) const { | |
73 DCHECK(i >= 0 && i < 3); | |
74 DCHECK(j >= 0 && j < 3); | |
75 return data_[i * 3 + j]; | |
Alexei Svitkine (slow)
2013/01/31 20:02:38
Make a function in the anon namespace that gives y
motek.
2013/01/31 22:00:24
Done.
| |
76 } | |
77 | |
78 void Matrix3F::set(int i, int j, ScalarType v) { | |
79 DCHECK(i >= 0 && i < 3); | |
80 DCHECK(j >= 0 && j < 3); | |
81 data_[i * 3 + j] = v; | |
82 } | |
83 | |
84 void Matrix3F::set(ScalarType v) { | |
85 for (int i = 0; i < kMEnd; i++) | |
86 data_[i] = v; | |
87 } | |
88 | |
89 void Matrix3F::set(ScalarType m00, ScalarType m01, ScalarType m02, | |
90 ScalarType m10, ScalarType m11, ScalarType m12, | |
91 ScalarType m20, ScalarType m21, ScalarType m22) { | |
92 data_[0] = m00; | |
93 data_[1] = m01; | |
94 data_[2] = m02; | |
95 data_[3] = m10; | |
96 data_[4] = m11; | |
97 data_[5] = m12; | |
98 data_[6] = m20; | |
99 data_[7] = m21; | |
100 data_[8] = m22; | |
101 } | |
102 | |
103 Matrix3F::VectorType Matrix3F::GetColumn(int i) const { | |
104 DCHECK(i >= 0 && i < 3); | |
105 return VectorType(data_[i], data_[3 + i], data_[6 + i]); | |
106 } | |
107 | |
108 void Matrix3F::SetColumn(int i, const VectorType& c) { | |
109 DCHECK(i >= 0 && i < 3); | |
110 data_[i] = c.x(); | |
111 data_[3 + i] = c.y(); | |
112 data_[6 + i] = c.z(); | |
113 } | |
114 | |
115 Matrix3F Matrix3F::Inverse() const { | |
116 Matrix3F inverse = Matrix3F::Zeros(); | |
117 double det = Determinant3x3(data_); | |
Alexei Svitkine (slow)
2013/01/31 20:02:38
Nit: det -> determinant
motek.
2013/01/31 22:00:24
Done.
| |
118 if (std::numeric_limits<ScalarType>::epsilon() > std::abs(det)) | |
119 return inverse; // Singular matrix. Return Zeros(). | |
120 | |
121 inverse.set((data_[kM11] * data_[kM22] - data_[kM12] * data_[kM21]) / det, | |
122 (data_[kM02] * data_[kM21] - data_[kM01] * data_[kM22]) / det, | |
123 (data_[kM01] * data_[kM12] - data_[kM02] * data_[kM11]) / det, | |
124 (data_[kM12] * data_[kM20] - data_[kM10] * data_[kM22]) / det, | |
125 (data_[kM00] * data_[kM22] - data_[kM02] * data_[kM20]) / det, | |
126 (data_[kM02] * data_[kM10] - data_[kM00] * data_[kM12]) / det, | |
127 (data_[kM10] * data_[kM21] - data_[kM11] * data_[kM20]) / det, | |
128 (data_[kM01] * data_[kM20] - data_[kM00] * data_[kM21]) / det, | |
129 (data_[kM00] * data_[kM11] - data_[kM01] * data_[kM10]) / det); | |
130 return inverse; | |
131 } | |
132 | |
133 Matrix3F::ScalarType Matrix3F::Determinant() const { | |
134 return ScalarType(Determinant3x3(data_)); | |
135 } | |
136 | |
137 Matrix3F::ScalarType Matrix3F::Trace() const { | |
138 return data_[kM00] + data_[kM11] + data_[kM22]; | |
139 } | |
140 | |
141 Matrix3F::VectorType Matrix3F::SolveEigenproblem( | |
142 Matrix3F* eigenvectors) const { | |
143 // The matrix must be symmetric. | |
144 const ScalarType epsilon = std::numeric_limits<ScalarType>::epsilon(); | |
145 if (std::abs(data_[kM01] - data_[kM10]) > epsilon || | |
146 std::abs(data_[kM02] - data_[kM02]) > epsilon || | |
147 std::abs(data_[kM12] - data_[kM21]) > epsilon) { | |
148 NOTREACHED(); | |
149 return Matrix3F::VectorType(); | |
150 } | |
151 | |
152 ScalarType eigenvalues[3]; | |
153 ScalarType p = | |
154 data_[kM01] * data_[kM01] + | |
155 data_[kM02] * data_[kM02] + | |
156 data_[kM12] * data_[kM12]; | |
157 | |
158 bool diagonal = std::abs(p) < epsilon; | |
159 if (diagonal) { | |
160 eigenvalues[0] = data_[kM00]; | |
161 eigenvalues[1] = data_[kM11]; | |
162 eigenvalues[2] = data_[kM22]; | |
163 } else { | |
164 ScalarType q = Trace() / 3.0; | |
165 p = (data_[kM00] - q) * (data_[kM00] - q) + | |
166 (data_[kM11] - q) * (data_[kM11] - q) + | |
167 (data_[kM22] - q) * (data_[kM22] - q) + | |
168 2 * p; | |
169 p = sqrt(p / 6); | |
170 | |
171 // The computation below puts B as (A - qI) / p, where A is *this. | |
172 Matrix3F matrix_B(*this); | |
173 matrix_B.data_[kM00] -= q; | |
174 matrix_B.data_[kM11] -= q; | |
175 matrix_B.data_[kM22] -= q; | |
176 for (int i = 0; i < kMEnd; ++i) | |
177 matrix_B.data_[i] /= p; | |
178 | |
179 ScalarType half_det_b = matrix_B.Determinant() / 2.0; | |
180 // half_det_b should be in <-1, 1>, but beware of rounding error. | |
181 ScalarType phi = 0.0; | |
182 if (half_det_b <= -1.0) | |
183 phi = M_PI / 3; | |
184 else if (half_det_b < 1.0) | |
185 phi = acos(half_det_b) / 3; | |
186 | |
187 eigenvalues[0] = q + 2 * p * cos(phi); | |
188 eigenvalues[2] = q + 2 * p * cos(phi + 2.0 * M_PI / 3.0); | |
189 eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; | |
190 } | |
191 | |
192 // Put eigenvalues in the descending order. | |
193 int indices[3] = {0, 1, 2}; | |
194 if (eigenvalues[2] > eigenvalues[1]) { | |
195 std::swap(eigenvalues[2], eigenvalues[1]); | |
196 std::swap(indices[2], indices[1]); | |
197 } | |
198 | |
199 if (eigenvalues[1] > eigenvalues[0]) { | |
200 std::swap(eigenvalues[1], eigenvalues[0]); | |
201 std::swap(indices[1], indices[0]); | |
202 } | |
203 | |
204 if (eigenvalues[2] > eigenvalues[1]) { | |
205 std::swap(eigenvalues[2], eigenvalues[1]); | |
206 std::swap(indices[2], indices[1]); | |
207 } | |
208 | |
209 if (eigenvectors != NULL && diagonal) { | |
210 // Eigenvectors are e-vectors, just need to be sorted accordingly. | |
211 *eigenvectors = Zeros(); | |
212 for (int i = 0; i < 3; ++i) | |
213 eigenvectors->set(indices[i], i, 1.0); | |
214 } else if (eigenvectors != NULL) { | |
215 // Consult the following for a detailed discussion: | |
216 // Joachim Kopp | |
217 // Numerical diagonalization of hermitian 3x3 matrices | |
218 // arXiv.org preprint: physics/0610206 | |
219 // Int. J. Mod. Phys. C19 (2008) 523-548 | |
220 | |
221 // TODO(motek): expand to handle correctly negative and multiple | |
222 // eigenvalues. | |
223 for (int i = 0; i < 3; ++i) { | |
224 ScalarType l = eigenvalues[i]; | |
225 // B = A - l * I | |
226 Matrix3F matrix_B(*this); | |
227 matrix_B.data_[kM00] -= l; | |
228 matrix_B.data_[kM11] -= l; | |
229 matrix_B.data_[kM22] -= l; | |
230 VectorType e1 = CrossProduct(matrix_B.GetColumn(0), | |
231 matrix_B.GetColumn(1)); | |
232 VectorType e2 = CrossProduct(matrix_B.GetColumn(1), | |
233 matrix_B.GetColumn(2)); | |
234 VectorType e3 = CrossProduct(matrix_B.GetColumn(2), | |
235 matrix_B.GetColumn(0)); | |
236 | |
237 // e1, e2 and e3 should point in the same direction. | |
238 if (DotProduct(e1, e2) < 0) { | |
239 e2.set_x(-e2.x()); | |
240 e2.set_y(-e2.y()); | |
241 e2.set_z(-e2.z()); | |
242 } | |
243 | |
244 if (DotProduct(e1, e3) < 0) { | |
245 e3.set_x(-e3.x()); | |
246 e3.set_y(-e3.y()); | |
247 e3.set_z(-e3.z()); | |
248 } | |
249 | |
250 VectorType eigvec = e1 + e2 + e3; | |
251 // Normalize. | |
252 eigvec.Scale(1.0 / eigvec.Length()); | |
253 eigenvectors->SetColumn(i, eigvec); | |
254 } | |
255 } | |
256 | |
257 return Matrix3F::VectorType(eigenvalues[0], | |
258 eigenvalues[1], | |
259 eigenvalues[2]); | |
260 } | |
261 | |
262 // static | |
263 Matrix3F Matrix3F::Zeros() { | |
264 Matrix3F matrix; | |
265 matrix.set(0.0); | |
266 return matrix; | |
267 } | |
268 | |
269 // static | |
270 Matrix3F Matrix3F::Ones() { | |
271 Matrix3F matrix; | |
272 matrix.set(1.0); | |
273 return matrix; | |
274 } | |
275 | |
276 // static | |
277 Matrix3F Matrix3F::Identity() { | |
278 Matrix3F matrix; | |
279 matrix.set(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); | |
280 return matrix; | |
281 } | |
282 | |
283 } // namespace gfx | |
OLD | NEW |