blob: 2d39b6369c5772d4fcab73c2457ee338f3feebb6 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cobalt/math/matrix3_f.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace cobalt {
namespace math {
namespace {
// This is only to make accessing indices self-explanatory.
enum MatrixCoordinates { M00, M01, M02, M10, M11, M12, M20, M21, M22, M_END };
template <typename T>
double Determinant3x3(T data[M_END]) {
// This routine is separated from the Matrix3F::Determinant because in
// computing inverse we do want higher precision afforded by the explicit
// use of 'double'.
return static_cast<double>(data[M00]) *
(static_cast<double>(data[M11]) * data[M22] -
static_cast<double>(data[M12]) * data[M21]) +
static_cast<double>(data[M01]) *
(static_cast<double>(data[M12]) * data[M20] -
static_cast<double>(data[M10]) * data[M22]) +
static_cast<double>(data[M02]) *
(static_cast<double>(data[M10]) * data[M21] -
static_cast<double>(data[M11]) * data[M20]);
}
} // namespace
Matrix3F::Matrix3F() {}
Matrix3F::~Matrix3F() {}
// static
Matrix3F Matrix3F::Zeros() {
Matrix3F matrix;
matrix.SetMatrix(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
return matrix;
}
// static
Matrix3F Matrix3F::Ones() {
Matrix3F matrix;
matrix.SetMatrix(1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
return matrix;
}
// static
Matrix3F Matrix3F::Identity() {
Matrix3F matrix;
matrix.SetMatrix(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
return matrix;
}
// static
Matrix3F Matrix3F::FromOuterProduct(const Vector3dF& a, const Vector3dF& bt) {
Matrix3F matrix;
matrix.SetMatrix(a.x() * bt.x(), a.x() * bt.y(), a.x() * bt.z(),
a.y() * bt.x(), a.y() * bt.y(), a.y() * bt.z(),
a.z() * bt.x(), a.z() * bt.y(), a.z() * bt.z());
return matrix;
}
// static
Matrix3F Matrix3F::FromArray(const float data[9]) {
Matrix3F matrix;
memcpy(matrix.data_, data, sizeof(float) * 9);
return matrix;
}
// static
Matrix3F Matrix3F::FromValues(float m00, float m01, float m02, float m10,
float m11, float m12, float m20, float m21,
float m22) {
Matrix3F matrix;
matrix.SetMatrix(m00, m01, m02, m10, m11, m12, m20, m21, m22);
return matrix;
}
bool Matrix3F::IsZeros() const {
return data_[M00] == 0.0f && data_[M01] == 0.0f && data_[M02] == 0.0f &&
data_[M10] == 0.0f && data_[M11] == 0.0f && data_[M12] == 0.0f &&
data_[M20] == 0.0f && data_[M21] == 0.0f && data_[M22] == 0.0f;
}
bool Matrix3F::IsIdentity() const {
return data_[M00] == 1.0f && data_[M01] == 0.0f && data_[M02] == 0.0f &&
data_[M10] == 0.0f && data_[M11] == 1.0f && data_[M12] == 0.0f &&
data_[M20] == 0.0f && data_[M21] == 0.0f && data_[M22] == 1.0f;
}
bool Matrix3F::IsEqual(const Matrix3F& rhs) const {
return 0 == memcmp(data_, rhs.data_, sizeof(data_));
}
bool Matrix3F::IsNear(const Matrix3F& rhs, float precision) const {
DCHECK_GE(precision, 0);
for (int i = 0; i < M_END; ++i) {
if (std::abs(data_[i] - rhs.data_[i]) > precision) return false;
}
return true;
}
Matrix3F Matrix3F::Inverse() const {
Matrix3F inverse = Matrix3F::Zeros();
double determinant = Determinant3x3(data_);
if (std::numeric_limits<float>::epsilon() > std::abs(determinant))
return inverse; // Singular matrix. Return Zeros().
inverse.SetMatrix(
(data_[M11] * data_[M22] - data_[M12] * data_[M21]) / determinant,
(data_[M02] * data_[M21] - data_[M01] * data_[M22]) / determinant,
(data_[M01] * data_[M12] - data_[M02] * data_[M11]) / determinant,
(data_[M12] * data_[M20] - data_[M10] * data_[M22]) / determinant,
(data_[M00] * data_[M22] - data_[M02] * data_[M20]) / determinant,
(data_[M02] * data_[M10] - data_[M00] * data_[M12]) / determinant,
(data_[M10] * data_[M21] - data_[M11] * data_[M20]) / determinant,
(data_[M01] * data_[M20] - data_[M00] * data_[M21]) / determinant,
(data_[M00] * data_[M11] - data_[M01] * data_[M10]) / determinant);
return inverse;
}
float Matrix3F::Determinant() const {
return static_cast<float>(Determinant3x3(data_));
}
Vector3dF Matrix3F::SolveEigenproblem(Matrix3F* eigenvectors) const {
// The matrix must be symmetric.
const float epsilon = std::numeric_limits<float>::epsilon();
if (std::abs(data_[M01] - data_[M10]) > epsilon ||
std::abs(data_[M02] - data_[M20]) > epsilon ||
std::abs(data_[M12] - data_[M21]) > epsilon) {
NOTREACHED();
return Vector3dF();
}
float eigenvalues[3];
float p = data_[M01] * data_[M01] + data_[M02] * data_[M02] +
data_[M12] * data_[M12];
bool diagonal = std::abs(p) < epsilon;
if (diagonal) {
eigenvalues[0] = data_[M00];
eigenvalues[1] = data_[M11];
eigenvalues[2] = data_[M22];
} else {
float q = Trace() / 3.0f;
p = (data_[M00] - q) * (data_[M00] - q) +
(data_[M11] - q) * (data_[M11] - q) +
(data_[M22] - q) * (data_[M22] - q) + 2 * p;
p = std::sqrt(p / 6);
// The computation below puts B as (A - qI) / p, where A is *this.
Matrix3F matrix_b(*this);
matrix_b.data_[M00] -= q;
matrix_b.data_[M11] -= q;
matrix_b.data_[M22] -= q;
for (int i = 0; i < M_END; ++i) matrix_b.data_[i] /= p;
double half_det_b = Determinant3x3(matrix_b.data_) / 2.0;
// half_det_b should be in <-1, 1>, but beware of rounding error.
double phi = 0.0f;
if (half_det_b <= -1.0)
phi = M_PI / 3;
else if (half_det_b < 1.0)
phi = acos(half_det_b) / 3;
eigenvalues[0] = q + 2 * p * static_cast<float>(cos(phi));
eigenvalues[2] =
q + 2 * p * static_cast<float>(cos(phi + 2.0 * M_PI / 3.0));
eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2];
}
// Put eigenvalues in the descending order.
int indices[3] = {0, 1, 2};
if (eigenvalues[2] > eigenvalues[1]) {
std::swap(eigenvalues[2], eigenvalues[1]);
std::swap(indices[2], indices[1]);
}
if (eigenvalues[1] > eigenvalues[0]) {
std::swap(eigenvalues[1], eigenvalues[0]);
std::swap(indices[1], indices[0]);
}
if (eigenvalues[2] > eigenvalues[1]) {
std::swap(eigenvalues[2], eigenvalues[1]);
std::swap(indices[2], indices[1]);
}
if (eigenvectors != NULL && diagonal) {
// Eigenvectors are e-vectors, just need to be sorted accordingly.
*eigenvectors = Zeros();
for (int i = 0; i < 3; ++i) eigenvectors->Set(indices[i], i, 1.0f);
} else if (eigenvectors != NULL) {
// Consult the following for a detailed discussion:
// Joachim Kopp
// Numerical diagonalization of hermitian 3x3 matrices
// arXiv.org preprint: physics/0610206
// Int. J. Mod. Phys. C19 (2008) 523-548
// TODO(motek): expand to handle correctly negative and multiple
// eigenvalues.
for (int i = 0; i < 3; ++i) {
float l = eigenvalues[i];
// B = A - l * I
Matrix3F matrix_b(*this);
matrix_b.data_[M00] -= l;
matrix_b.data_[M11] -= l;
matrix_b.data_[M22] -= l;
Vector3dF e1 = CrossProduct(matrix_b.column(0), matrix_b.column(1));
Vector3dF e2 = CrossProduct(matrix_b.column(1), matrix_b.column(2));
Vector3dF e3 = CrossProduct(matrix_b.column(2), matrix_b.column(0));
// e1, e2 and e3 should point in the same direction.
if (DotProduct(e1, e2) < 0) e2 = -e2;
if (DotProduct(e1, e3) < 0) e3 = -e3;
Vector3dF eigvec = e1 + e2 + e3;
// Normalize.
eigvec.Scale(1.0f / eigvec.Length());
eigenvectors->set_column(i, eigvec);
}
}
return Vector3dF(eigenvalues[0], eigenvalues[1], eigenvalues[2]);
}
Matrix3F Matrix3F::operator*(const Matrix3F& other) const {
Matrix3F ret;
ret.data_[M00] = data_[M00] * other.data_[M00] +
data_[M01] * other.data_[M10] +
data_[M02] * other.data_[M20];
ret.data_[M01] = data_[M00] * other.data_[M01] +
data_[M01] * other.data_[M11] +
data_[M02] * other.data_[M21];
ret.data_[M02] = data_[M00] * other.data_[M02] +
data_[M01] * other.data_[M12] +
data_[M02] * other.data_[M22];
ret.data_[M10] = data_[M10] * other.data_[M00] +
data_[M11] * other.data_[M10] +
data_[M12] * other.data_[M20];
ret.data_[M11] = data_[M10] * other.data_[M01] +
data_[M11] * other.data_[M11] +
data_[M12] * other.data_[M21];
ret.data_[M12] = data_[M10] * other.data_[M02] +
data_[M11] * other.data_[M12] +
data_[M12] * other.data_[M22];
ret.data_[M20] = data_[M20] * other.data_[M00] +
data_[M21] * other.data_[M10] +
data_[M22] * other.data_[M20];
ret.data_[M21] = data_[M20] * other.data_[M01] +
data_[M21] * other.data_[M11] +
data_[M22] * other.data_[M21];
ret.data_[M22] = data_[M20] * other.data_[M02] +
data_[M21] * other.data_[M12] +
data_[M22] * other.data_[M22];
return ret;
}
PointF Matrix3F::operator*(const PointF& rhs) const {
float x = rhs.x() * data_[M00] + rhs.y() * data_[M01] + data_[M02];
float y = rhs.x() * data_[M10] + rhs.y() * data_[M11] + data_[M12];
float z = rhs.x() * data_[M20] + rhs.y() * data_[M21] + data_[M22];
DCHECK_NE(z, 0.f);
return PointF(x / z, y / z);
}
RectF Matrix3F::MapRect(const RectF& rect) const {
PointF points[4];
points[0] = *this * rect.origin();
points[1] = *this * rect.top_right();
points[2] = *this * rect.bottom_left();
points[3] = *this * rect.bottom_right();
float min_x = std::numeric_limits<float>::max();
float max_x = -std::numeric_limits<float>::max();
float min_y = std::numeric_limits<float>::max();
float max_y = -std::numeric_limits<float>::max();
for (int i = 0; i < 4; ++i) {
min_x = std::min(min_x, points[i].x());
max_x = std::max(max_x, points[i].x());
min_y = std::min(min_y, points[i].y());
max_y = std::max(max_y, points[i].y());
}
return RectF(min_x, min_y, max_x - min_x, max_y - min_y);
}
} // namespace math
} // namespace cobalt