blob: 1423adea2c64ed40c3b543aebc59d3bf8100d6e7 [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
#include <cmath>
#include <exception>
#include <iomanip>
#include <stdexcept>
#include <math/quat.h>
#include <math/TVecHelpers.h>
#include <utils/String8.h>
#ifndef LIKELY
#define LIKELY_DEFINED_LOCAL
#ifdef __cplusplus
# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
#else
# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
#endif
#endif
#define PURE __attribute__((pure))
#if __cplusplus >= 201402L
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif
namespace android {
namespace details {
// -------------------------------------------------------------------------------------
/*
* No user serviceable parts here.
*
* Don't use this file directly, instead include ui/mat*.h
*/
/*
* Matrix utilities
*/
namespace matrix {
inline constexpr int transpose(int v) { return v; }
inline constexpr float transpose(float v) { return v; }
inline constexpr double transpose(double v) { return v; }
inline constexpr int trace(int v) { return v; }
inline constexpr float trace(float v) { return v; }
inline constexpr double trace(double v) { return v; }
/*
* Matrix inversion
*/
template<typename MATRIX>
MATRIX PURE gaussJordanInverse(const MATRIX& src) {
typedef typename MATRIX::value_type T;
static constexpr unsigned int N = MATRIX::NUM_ROWS;
MATRIX tmp(src);
MATRIX inverted(1);
for (size_t i = 0; i < N; ++i) {
// look for largest element in i'th column
size_t swap = i;
T t = std::abs(tmp[i][i]);
for (size_t j = i + 1; j < N; ++j) {
const T t2 = std::abs(tmp[j][i]);
if (t2 > t) {
swap = j;
t = t2;
}
}
if (swap != i) {
// swap columns.
std::swap(tmp[i], tmp[swap]);
std::swap(inverted[i], inverted[swap]);
}
const T denom(tmp[i][i]);
for (size_t k = 0; k < N; ++k) {
tmp[i][k] /= denom;
inverted[i][k] /= denom;
}
// Factor out the lower triangle
for (size_t j = 0; j < N; ++j) {
if (j != i) {
const T d = tmp[j][i];
for (size_t k = 0; k < N; ++k) {
tmp[j][k] -= tmp[i][k] * d;
inverted[j][k] -= inverted[i][k] * d;
}
}
}
}
return inverted;
}
//------------------------------------------------------------------------------
// 2x2 matrix inverse is easy.
template <typename MATRIX>
CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b |
// | c d |
//
// The analytic inverse is
// | d -b |
// | -c a | / (a d - b c)
//
// Importantly, our matrices are column-major!
MATRIX inverted(MATRIX::NO_INIT);
const T a = x[0][0];
const T c = x[0][1];
const T b = x[1][0];
const T d = x[1][1];
const T det((a * d) - (b * c));
inverted[0][0] = d / det;
inverted[0][1] = -c / det;
inverted[1][0] = -b / det;
inverted[1][1] = a / det;
return inverted;
}
//------------------------------------------------------------------------------
// From the Wikipedia article on matrix inversion's section on fast 3x3
// matrix inversion:
// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
template <typename MATRIX>
CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
typedef typename MATRIX::value_type T;
// Assuming the input matrix is:
// | a b c |
// | d e f |
// | g h i |
//
// The analytic inverse is
// | A B C |^T
// | D E F |
// | G H I | / determinant
//
// Which is
// | A D G |
// | B E H |
// | C F I | / determinant
//
// Where:
// A = (ei - fh), B = (fg - di), C = (dh - eg)
// D = (ch - bi), E = (ai - cg), F = (bg - ah)
// G = (bf - ce), H = (cd - af), I = (ae - bd)
//
// and the determinant is a*A + b*B + c*C (The rule of Sarrus)
//
// Importantly, our matrices are column-major!
MATRIX inverted(MATRIX::NO_INIT);
const T a = x[0][0];
const T b = x[1][0];
const T c = x[2][0];
const T d = x[0][1];
const T e = x[1][1];
const T f = x[2][1];
const T g = x[0][2];
const T h = x[1][2];
const T i = x[2][2];
// Do the full analytic inverse
const T A = e * i - f * h;
const T B = f * g - d * i;
const T C = d * h - e * g;
inverted[0][0] = A; // A
inverted[0][1] = B; // B
inverted[0][2] = C; // C
inverted[1][0] = c * h - b * i; // D
inverted[1][1] = a * i - c * g; // E
inverted[1][2] = b * g - a * h; // F
inverted[2][0] = b * f - c * e; // G
inverted[2][1] = c * d - a * f; // H
inverted[2][2] = a * e - b * d; // I
const T det(a * A + b * B + c * C);
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
inverted[col][row] /= det;
}
}
return inverted;
}
/**
* Inversion function which switches on the matrix size.
* @warning This function assumes the matrix is invertible. The result is
* undefined if it is not. It is the responsibility of the caller to
* make sure the matrix is not singular.
*/
template <typename MATRIX>
inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
gaussJordanInverse<MATRIX>(matrix));
}
template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
// pre-requisite:
// lhs : D columns, R rows
// rhs : C columns, D rows
// res : C columns, R rows
static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
"matrices can't be multiplied. invalid dimensions.");
static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
"invalid dimension of matrix multiply result.");
static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
"invalid dimension of matrix multiply result.");
MATRIX_R res(MATRIX_R::NO_INIT);
for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
res[col] = lhs * rhs[col];
}
return res;
}
// transpose. this handles matrices of matrices
template <typename MATRIX>
CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
// for now we only handle square matrix transpose
static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
MATRIX result(MATRIX::NO_INIT);
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
result[col][row] = transpose(m[row][col]);
}
}
return result;
}
// trace. this handles matrices of matrices
template <typename MATRIX>
CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
typename MATRIX::value_type result(0);
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
result += trace(m[col][col]);
}
return result;
}
// diag. this handles matrices of matrices
template <typename MATRIX>
CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
result[col] = m[col][col];
}
return result;
}
//------------------------------------------------------------------------------
// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
template <typename MATRIX>
TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
typedef typename MATRIX::value_type T;
TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
// Compute the trace to see if it is positive or not.
const T trace = mat[0][0] + mat[1][1] + mat[2][2];
// check the sign of the trace
if (LIKELY(trace > 0)) {
// trace is positive
T s = std::sqrt(trace + 1);
quat.w = T(0.5) * s;
s = T(0.5) / s;
quat.x = (mat[1][2] - mat[2][1]) * s;
quat.y = (mat[2][0] - mat[0][2]) * s;
quat.z = (mat[0][1] - mat[1][0]) * s;
} else {
// trace is negative
// Find the index of the greatest diagonal
size_t i = 0;
if (mat[1][1] > mat[0][0]) { i = 1; }
if (mat[2][2] > mat[i][i]) { i = 2; }
// Get the next indices: (n+1)%3
static constexpr size_t next_ijk[3] = { 1, 2, 0 };
size_t j = next_ijk[i];
size_t k = next_ijk[j];
T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
quat[i] = T(0.5) * s;
if (s != 0) {
s = T(0.5) / s;
}
quat.w = (mat[j][k] - mat[k][j]) * s;
quat[j] = (mat[i][j] + mat[j][i]) * s;
quat[k] = (mat[i][k] + mat[k][i]) * s;
}
return quat;
}
template <typename MATRIX>
String8 asString(const MATRIX& m) {
String8 s;
for (size_t c = 0; c < MATRIX::COL_SIZE; c++) {
s.append("| ");
for (size_t r = 0; r < MATRIX::ROW_SIZE; r++) {
s.appendFormat("%7.2f ", m[r][c]);
}
s.append("|\n");
}
return s;
}
} // namespace matrix
// -------------------------------------------------------------------------------------
/*
* TMatProductOperators implements basic arithmetic and basic compound assignments
* operators on a vector of type BASE<T>.
*
* BASE only needs to implement operator[] and size().
* By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
* get all the functionality here.
*/
template <template<typename T> class BASE, typename T>
class TMatProductOperators {
public:
// multiply by a scalar
BASE<T>& operator *= (T v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
lhs[col] *= v;
}
return lhs;
}
// matrix *= matrix
template<typename U>
const BASE<T>& operator *= (const BASE<U>& rhs) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
lhs = matrix::multiply<BASE<T> >(lhs, rhs);
return lhs;
}
// divide by a scalar
BASE<T>& operator /= (T v) {
BASE<T>& lhs(static_cast< BASE<T>& >(*this));
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
lhs[col] /= v;
}
return lhs;
}
// matrix * matrix, result is a matrix of the same type than the lhs matrix
template<typename U>
friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
return matrix::multiply<BASE<T> >(lhs, rhs);
}
};
/*
* TMatSquareFunctions implements functions on a matrix of type BASE<T>.
*
* BASE only needs to implement:
* - operator[]
* - col_type
* - row_type
* - COL_SIZE
* - ROW_SIZE
*
* By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
* get all the functionality here.
*/
template<template<typename U> class BASE, typename T>
class TMatSquareFunctions {
public:
/*
* NOTE: the functions below ARE NOT member methods. They are friend functions
* with they definition inlined with their declaration. This makes these
* template functions available to the compiler when (and only when) this class
* is instantiated, at which point they're only templated on the 2nd parameter
* (the first one, BASE<T> being known).
*/
friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
return matrix::inverse(matrix);
}
friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
return matrix::transpose(m);
}
friend inline constexpr T PURE trace(const BASE<T>& m) {
return matrix::trace(m);
}
};
template<template<typename U> class BASE, typename T>
class TMatHelpers {
public:
constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; }
constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; }
constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; }
constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; }
constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<>
// array access
constexpr T const* asArray() const {
return &static_cast<BASE<T> const &>(*this)[0][0];
}
// element access
inline constexpr T const& operator()(size_t row, size_t col) const {
return static_cast<BASE<T> const &>(*this)[col][row];
}
inline T& operator()(size_t row, size_t col) {
return static_cast<BASE<T>&>(*this)[col][row];
}
template <typename VEC>
static CONSTEXPR BASE<T> translate(const VEC& t) {
BASE<T> r;
r[BASE<T>::NUM_COLS-1] = t;
return r;
}
template <typename VEC>
static constexpr BASE<T> scale(const VEC& s) {
return BASE<T>(s);
}
friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
m[col] = abs(m[col]);
}
return m;
}
};
// functions for 3x3 and 4x4 matrices
template<template<typename U> class BASE, typename T>
class TMatTransform {
public:
inline constexpr TMatTransform() {
static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
}
template <typename A, typename VEC>
static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
BASE<T> r;
T c = std::cos(radian);
T s = std::sin(radian);
if (about.x == 1 && about.y == 0 && about.z == 0) {
r[1][1] = c; r[2][2] = c;
r[1][2] = s; r[2][1] = -s;
} else if (about.x == 0 && about.y == 1 && about.z == 0) {
r[0][0] = c; r[2][2] = c;
r[2][0] = s; r[0][2] = -s;
} else if (about.x == 0 && about.y == 0 && about.z == 1) {
r[0][0] = c; r[1][1] = c;
r[0][1] = s; r[1][0] = -s;
} else {
VEC nabout = normalize(about);
typename VEC::value_type x = nabout.x;
typename VEC::value_type y = nabout.y;
typename VEC::value_type z = nabout.z;
T nc = 1 - c;
T xy = x * y;
T yz = y * z;
T zx = z * x;
T xs = x * s;
T ys = y * s;
T zs = z * s;
r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys;
r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs;
r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c;
// Clamp results to -1, 1.
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
}
}
}
return r;
}
/**
* Create a matrix from euler angles using YPR around YXZ respectively
* @param yaw about Y axis
* @param pitch about X axis
* @param roll about Z axis
*/
template <
typename Y, typename P, typename R,
typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
>
static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
return eulerZYX(roll, pitch, yaw);
}
/**
* Create a matrix from euler angles using YPR around ZYX respectively
* @param roll about X axis
* @param pitch about Y axis
* @param yaw about Z axis
*
* The euler angles are applied in ZYX order. i.e: a vector is first rotated
* about X (roll) then Y (pitch) and then Z (yaw).
*/
template <
typename Y, typename P, typename R,
typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
>
static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
BASE<T> r;
T cy = std::cos(yaw);
T sy = std::sin(yaw);
T cp = std::cos(pitch);
T sp = std::sin(pitch);
T cr = std::cos(roll);
T sr = std::sin(roll);
T cc = cr * cy;
T cs = cr * sy;
T sc = sr * cy;
T ss = sr * sy;
r[0][0] = cp * cy;
r[0][1] = cp * sy;
r[0][2] = -sp;
r[1][0] = sp * sc - cs;
r[1][1] = sp * ss + cc;
r[1][2] = cp * sr;
r[2][0] = sp * cc + ss;
r[2][1] = sp * cs - sc;
r[2][2] = cp * cr;
// Clamp results to -1, 1.
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
}
}
return r;
}
TQuaternion<T> toQuaternion() const {
return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
}
};
template <template<typename T> class BASE, typename T>
class TMatDebug {
public:
friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
if (row != 0) {
stream << std::endl;
}
if (row == 0) {
stream << "/ ";
} else if (row == BASE<T>::NUM_ROWS-1) {
stream << "\\ ";
} else {
stream << "| ";
}
for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
stream << std::setw(10) << std::to_string(m[col][row]);
}
if (row == 0) {
stream << " \\";
} else if (row == BASE<T>::NUM_ROWS-1) {
stream << " /";
} else {
stream << " |";
}
}
return stream;
}
String8 asString() const {
return matrix::asString(static_cast<const BASE<T>&>(*this));
}
};
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace android
#ifdef LIKELY_DEFINED_LOCAL
#undef LIKELY_DEFINED_LOCAL
#undef LIKELY
#undef UNLIKELY
#endif //LIKELY_DEFINED_LOCAL
#undef PURE
#undef CONSTEXPR