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