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.
5 #include "ui/gfx/geometry/matrix3_f.h"
12 #define M_PI 3.14159265358979323846
17 // This is only to make accessing indices self-explanatory.
18 enum MatrixCoordinates
{
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
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
]);
52 Matrix3F::Matrix3F() {
55 Matrix3F::~Matrix3F() {
59 Matrix3F
Matrix3F::Zeros() {
61 matrix
.set(0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
, 0.0f
);
66 Matrix3F
Matrix3F::Ones() {
68 matrix
.set(1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
, 1.0f
);
73 Matrix3F
Matrix3F::Identity() {
75 matrix
.set(1.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
, 0.0f
, 0.0f
, 0.0f
, 1.0f
);
80 Matrix3F
Matrix3F::FromOuterProduct(const Vector3dF
& a
, const Vector3dF
& bt
) {
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());
88 bool Matrix3F::IsEqual(const Matrix3F
& rhs
) const {
89 return 0 == memcmp(data_
, rhs
.data_
, sizeof(data_
));
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
)
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().
108 static_cast<float>((data_
[M11
] * data_
[M22
] - data_
[M12
] * data_
[M21
]) /
110 static_cast<float>((data_
[M02
] * data_
[M21
] - data_
[M01
] * data_
[M22
]) /
112 static_cast<float>((data_
[M01
] * data_
[M12
] - data_
[M02
] * data_
[M11
]) /
114 static_cast<float>((data_
[M12
] * data_
[M20
] - data_
[M10
] * data_
[M22
]) /
116 static_cast<float>((data_
[M00
] * data_
[M22
] - data_
[M02
] * data_
[M20
]) /
118 static_cast<float>((data_
[M02
] * data_
[M10
] - data_
[M00
] * data_
[M12
]) /
120 static_cast<float>((data_
[M10
] * data_
[M21
] - data_
[M11
] * data_
[M20
]) /
122 static_cast<float>((data_
[M01
] * data_
[M20
] - data_
[M00
] * data_
[M21
]) /
124 static_cast<float>((data_
[M00
] * data_
[M11
] - data_
[M01
] * data_
[M10
]) /
129 float Matrix3F::Determinant() const {
130 return static_cast<float>(Determinant3x3(data_
));
133 Vector3dF
Matrix3F::SolveEigenproblem(Matrix3F
* eigenvectors
) const {
134 // The matrix must be symmetric.
135 const float epsilon
= std::numeric_limits
<float>::epsilon();
136 if (std::abs(data_
[M01
] - data_
[M10
]) > epsilon
||
137 std::abs(data_
[M02
] - data_
[M20
]) > epsilon
||
138 std::abs(data_
[M12
] - data_
[M21
]) > epsilon
) {
143 float eigenvalues
[3];
145 data_
[M01
] * data_
[M01
] +
146 data_
[M02
] * data_
[M02
] +
147 data_
[M12
] * data_
[M12
];
149 bool diagonal
= std::abs(p
) < epsilon
;
151 eigenvalues
[0] = data_
[M00
];
152 eigenvalues
[1] = data_
[M11
];
153 eigenvalues
[2] = data_
[M22
];
155 float q
= Trace() / 3.0f
;
156 p
= (data_
[M00
] - q
) * (data_
[M00
] - q
) +
157 (data_
[M11
] - q
) * (data_
[M11
] - q
) +
158 (data_
[M22
] - q
) * (data_
[M22
] - q
) +
160 p
= std::sqrt(p
/ 6);
162 // The computation below puts B as (A - qI) / p, where A is *this.
163 Matrix3F
matrix_b(*this);
164 matrix_b
.data_
[M00
] -= q
;
165 matrix_b
.data_
[M11
] -= q
;
166 matrix_b
.data_
[M22
] -= q
;
167 for (int i
= 0; i
< M_END
; ++i
)
168 matrix_b
.data_
[i
] /= p
;
170 double half_det_b
= Determinant3x3(matrix_b
.data_
) / 2.0;
171 // half_det_b should be in <-1, 1>, but beware of rounding error.
173 if (half_det_b
<= -1.0)
175 else if (half_det_b
< 1.0)
176 phi
= acos(half_det_b
) / 3;
178 eigenvalues
[0] = q
+ 2 * p
* static_cast<float>(cos(phi
));
179 eigenvalues
[2] = q
+ 2 * p
*
180 static_cast<float>(cos(phi
+ 2.0 * M_PI
/ 3.0));
181 eigenvalues
[1] = 3 * q
- eigenvalues
[0] - eigenvalues
[2];
184 // Put eigenvalues in the descending order.
185 int indices
[3] = {0, 1, 2};
186 if (eigenvalues
[2] > eigenvalues
[1]) {
187 std::swap(eigenvalues
[2], eigenvalues
[1]);
188 std::swap(indices
[2], indices
[1]);
191 if (eigenvalues
[1] > eigenvalues
[0]) {
192 std::swap(eigenvalues
[1], eigenvalues
[0]);
193 std::swap(indices
[1], indices
[0]);
196 if (eigenvalues
[2] > eigenvalues
[1]) {
197 std::swap(eigenvalues
[2], eigenvalues
[1]);
198 std::swap(indices
[2], indices
[1]);
201 if (eigenvectors
!= NULL
&& diagonal
) {
202 // Eigenvectors are e-vectors, just need to be sorted accordingly.
203 *eigenvectors
= Zeros();
204 for (int i
= 0; i
< 3; ++i
)
205 eigenvectors
->set(indices
[i
], i
, 1.0f
);
206 } else if (eigenvectors
!= NULL
) {
207 // Consult the following for a detailed discussion:
209 // Numerical diagonalization of hermitian 3x3 matrices
210 // arXiv.org preprint: physics/0610206
211 // Int. J. Mod. Phys. C19 (2008) 523-548
213 // TODO(motek): expand to handle correctly negative and multiple
215 for (int i
= 0; i
< 3; ++i
) {
216 float l
= eigenvalues
[i
];
218 Matrix3F
matrix_b(*this);
219 matrix_b
.data_
[M00
] -= l
;
220 matrix_b
.data_
[M11
] -= l
;
221 matrix_b
.data_
[M22
] -= l
;
222 Vector3dF e1
= CrossProduct(matrix_b
.get_column(0),
223 matrix_b
.get_column(1));
224 Vector3dF e2
= CrossProduct(matrix_b
.get_column(1),
225 matrix_b
.get_column(2));
226 Vector3dF e3
= CrossProduct(matrix_b
.get_column(2),
227 matrix_b
.get_column(0));
229 // e1, e2 and e3 should point in the same direction.
230 if (DotProduct(e1
, e2
) < 0)
233 if (DotProduct(e1
, e3
) < 0)
236 Vector3dF eigvec
= e1
+ e2
+ e3
;
238 eigvec
.Scale(1.0f
/ eigvec
.Length());
239 eigenvectors
->set_column(i
, eigvec
);
243 return Vector3dF(eigenvalues
[0], eigenvalues
[1], eigenvalues
[2]);