| /* | 
 |  * 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. | 
 |  */ | 
 |  | 
 | #ifndef UI_TMATHELPERS_H_ | 
 | #define UI_TMATHELPERS_H_ | 
 |  | 
 | #include <math.h> | 
 | #include <stdint.h> | 
 | #include <sys/types.h> | 
 |  | 
 | #include <cmath> | 
 | #include <exception> | 
 | #include <iomanip> | 
 | #include <stdexcept> | 
 |  | 
 | #include <ui/quat.h> | 
 | #include <ui/TVecHelpers.h> | 
 |  | 
 | #include  <utils/String8.h> | 
 |  | 
 | #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 | 
 |  | 
 | #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 | 
 |  | 
 | #undef LIKELY | 
 | #undef UNLIKELY | 
 | #undef PURE | 
 | #undef CONSTEXPR | 
 |  | 
 | #endif  // UI_TMATHELPERS_H_ |