diff --git a/ReactCommon/fabric/graphics/Quaternion.h b/ReactCommon/fabric/graphics/Quaternion.h deleted file mode 100644 index 4233a2c6cb4..00000000000 --- a/ReactCommon/fabric/graphics/Quaternion.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Portions Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -// The following is a modified, stripped-down version of the Quaternion class -// by Frank Astier. Copyright notice below. -// The original has many, many more features, and has been stripped down -// to support the exact data-structures and use-cases we need for React Native. - -/** - * The MIT License (MIT) - * - * Copyright (c) 2015 Frank Astier - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace facebook { -namespace react { - -template -class Quaternion { - public: - /** - * Copy constructor. - */ - Quaternion(const Quaternion &y) : a_(y.a_), b_(y.b_), c_(y.c_), d_(y.d_) {} - - Quaternion(T a, T b, T c, T d) : a_(a), b_(b), c_(c), d_(d) {} - - static Quaternion fromRotationMatrix(std::array const &rm) { - T t = rm[0 * 4 + 0] + rm[1 * 4 + 1] + rm[2 * 4 + 2]; - if (t > 0) { - T s = (T)0.5 / std::sqrt(t + 1); - return {(T)0.25 / s, - (rm[2 * 4 + 1] - rm[1 * 4 + 2]) * s, - (rm[0 * 4 + 2] - rm[2 * 4 + 0]) * s, - (rm[1 * 4 + 0] - rm[0 * 4 + 1]) * s}; - } else if (rm[0 * 4 + 0] > rm[1 * 4 + 1] && rm[0 * 4 + 0] > rm[2 * 4 + 2]) { - T s = (T)2.0 * - std::sqrt( - 1.0 + rm[0 * 4 + 0] - rm[1 * 4 + 1] - rm[2 * 4 + 2]); // S=4*qx - return {(rm[2 * 4 + 1] - rm[1 * 4 + 2]) / s, - (T)0.25 * s, - (rm[0 * 4 + 1] + rm[1 * 4 + 0]) / s, - (rm[0 * 4 + 2] + rm[2 * 4 + 0]) / s}; - } else if (rm[1 * 4 + 1] > rm[2 * 4 + 2]) { - T s = (T)2.0 * - std::sqrt( - 1.0 + rm[1 * 4 + 1] - rm[0 * 4 + 0] - rm[2 * 4 + 2]); // S=4*qy - return {(rm[0 * 4 + 2] - rm[2 * 4 + 0]) / s, - (rm[0 * 4 + 1] + rm[1 * 4 + 0]) / s, - (T)0.25 * s, - (rm[1 * 4 + 2] + rm[2 * 4 + 1]) / s}; - } else { - T s = (T)2.0 * - std::sqrt( - 1.0 + rm[2 * 4 + 2] - rm[0 * 4 + 0] - rm[1 * 4 + 1]); // S=4*qz - return {(rm[1 * 4 + 0] - rm[0 * 4 + 1]) / s, - (rm[0 * 4 + 2] + rm[2 * 4 + 0]) / s, - (rm[1 * 4 + 2] + rm[2 * 4 + 1]) / s, - (T)0.25 * s}; - } - } - - /** - * Returns a 3D, 4x4 rotation matrix. - * This is the "homogeneous" expression to convert to a rotation matrix, - * which works even when the Quaternion is not a unit Quaternion. - */ - inline std::array toRotationMatrix4x4() { - T a2 = a_ * a_, b2 = b_ * b_, c2 = c_ * c_, d2 = d_ * d_; - T ab = a_ * b_, ac = a_ * c_, ad = a_ * d_; - T bc = b_ * c_, bd = b_ * d_; - T cd = c_ * d_; - return {a2 + b2 - c2 - d2, - 2 * (bc - ad), - 2 * (bd + ac), - 0, - 2 * (bc + ad), - a2 - b2 + c2 - d2, - 2 * (cd - ab), - 0, - 2 * (bd - ac), - 2 * (cd + ab), - a2 - b2 - c2 + d2, - 0, - 0, - 0, - 0, - 1}; - } - - inline Quaternion normalize() const { - assert(abs() > 0); // or this is not normalizable - T factor = abs(); - return *this / (factor != 0 ? factor : 1); - } - - inline T dot(const Quaternion &other) { - return a_ * other.a_ + b_ * other.b_ + c_ * other.c_ + d_ * other.d_; - } - - /** - * The square of the norm of the Quaternion. - * (The square is sometimes useful, and it avoids paying for a sqrt). - */ - inline T norm_squared() const { - return a_ * a_ + b_ * b_ + c_ * c_ + d_ * d_; - } - - /** - * The norm of the Quaternion (the l2 norm). - */ - inline T abs() const { - return std::sqrt(norm_squared()); - } - - inline Quaternion operator/=(T y) { - a_ /= y; - b_ /= y; - c_ /= y; - d_ /= y; - return *this; - } - - inline Quaternion operator*=(T y) { - a_ *= y; - b_ *= y; - c_ *= y; - d_ *= y; - return *this; - } - - inline Quaternion operator+=(Quaternion const &other) { - a_ += other.a_; - b_ += other.b_; - c_ += other.c_; - d_ += other.d_; - return *this; - } - - inline Quaternion operator-=(Quaternion const &other) { - a_ -= other.a_; - b_ -= other.b_; - c_ -= other.c_; - d_ -= other.d_; - return *this; - } - - private: - T a_; // AKA w, qw - T b_; // AKA x, qx - T c_; // AKA y, qy - T d_; // AKA z, qz -}; - -template -inline Quaternion operator/(Quaternion const &lhs, T rhs) { - return Quaternion(lhs) /= rhs; -} - -template -inline Quaternion operator*(Quaternion const &lhs, T rhs) { - return Quaternion(lhs) *= rhs; -} - -template -inline Quaternion operator+( - Quaternion const &lhs, - Quaternion const &rhs) { - return Quaternion(lhs) += rhs; -} - -template -inline Quaternion operator-( - Quaternion const &lhs, - Quaternion const &rhs) { - return Quaternion(lhs) -= rhs; -} - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/fabric/graphics/Transform.cpp b/ReactCommon/fabric/graphics/Transform.cpp index 34e8f5ca61b..46575fec31c 100644 --- a/ReactCommon/fabric/graphics/Transform.cpp +++ b/ReactCommon/fabric/graphics/Transform.cpp @@ -7,34 +7,18 @@ #include "Transform.h" -#include #include -#include - namespace facebook { namespace react { -#ifdef RN_DEBUG_STRING_CONVERTIBLE -void Transform::print(Transform const &t, std::string prefix) { - LOG(ERROR) << prefix << "[ " << t.matrix[0] << " " << t.matrix[1] << " " - << t.matrix[2] << " " << t.matrix[3] << " ]"; - LOG(ERROR) << prefix << "[ " << t.matrix[4] << " " << t.matrix[5] << " " - << t.matrix[6] << " " << t.matrix[7] << " ]"; - LOG(ERROR) << prefix << "[ " << t.matrix[8] << " " << t.matrix[9] << " " - << t.matrix[10] << " " << t.matrix[11] << " ]"; - LOG(ERROR) << prefix << "[ " << t.matrix[12] << " " << t.matrix[13] << " " - << t.matrix[14] << " " << t.matrix[15] << " ]"; -} -#endif - Transform Transform::Identity() { return {}; } Transform Transform::Perspective(Float perspective) { auto transform = Transform{}; - transform.matrix[11] = -1 / perspective; + transform.matrix[11] = -1.0 / perspective; return transform; } @@ -102,130 +86,16 @@ Transform Transform::Rotate(Float x, Float y, Float z) { return transform; } -Transform::SRT Transform::ExtractSRT(Transform const &t) { - // First we need to extract translation, rotation, and scale from both - // matrices, in that order. Matrices must be in this form: [a b c d] [e f g h] - // [i j k l] - // [0 0 0 1] - // We also assume that all scale factors are non-negative, because in - assert( - t.matrix[12] == 0 && t.matrix[13] == 0 && t.matrix[14] == 0 && - t.matrix[15] == 1 && "Last row of matrix must be [0,0,0,1]"); - - // lhs: - // Translation: extract the values from the rightmost column - Float translationX = t.matrix[3]; - Float translationY = t.matrix[7]; - Float translationZ = t.matrix[11]; - - // Scale: the length of the first three column vectors - // TODO: do we need to do anything special for negative scale factors? - // the last element is a uniform scale factor - Float scaleX = t.matrix[15] * - sqrt(pow(t.matrix[0], 2) + pow(t.matrix[4], 2) + - pow(t.matrix[8], 2)); // sqrt(a^2 + e^2 + i^2) - Float scaleY = t.matrix[15] * - sqrt(pow(t.matrix[1], 2) + pow(t.matrix[5], 2) + - pow(t.matrix[9], 2)); // sqrt(b^2 + f^2 + j^2) - Float scaleZ = t.matrix[15] * - sqrt(pow(t.matrix[2], 2) + pow(t.matrix[6], 2) + - pow(t.matrix[10], 2)); // sqrt(c^2 + g^2 + k^2) - - Float rScaleFactorX = scaleX == 0 ? 1 : scaleX; - Float rScaleFactorY = scaleY == 0 ? 1 : scaleY; - Float rScaleFactorZ = scaleZ == 0 ? 1 : scaleZ; - - // Construct a rotation matrix and convert that to quaternions - auto rotationMatrix = std::array{t.matrix[0] / rScaleFactorX, - t.matrix[1] / rScaleFactorY, - t.matrix[2] / rScaleFactorZ, - 0, - t.matrix[4] / rScaleFactorX, - t.matrix[5] / rScaleFactorY, - t.matrix[6] / rScaleFactorZ, - 0, - t.matrix[8] / rScaleFactorX, - t.matrix[9] / rScaleFactorY, - t.matrix[10] / rScaleFactorZ, - 0, - 0, - 0, - 0, - 1}; - - Quaternion q = - Quaternion::fromRotationMatrix(rotationMatrix).normalize(); - - return Transform::SRT{ - translationX, translationY, translationZ, scaleX, scaleY, scaleZ, q}; -} - Transform Transform::Interpolate( float animationProgress, Transform const &lhs, Transform const &rhs) { - // Extract SRT for both sides - // This is extracted in the form: X,Y,Z coordinates for translations; X,Y,Z - // coordinates for scale; and a quaternion for rotation. - auto lhsSRT = ExtractSRT(lhs); - auto rhsSRT = ExtractSRT(rhs); - - // Interpolate translation and scale terms linearly (LERP) - Float translateX = - (lhsSRT.translationX + - (rhsSRT.translationX - lhsSRT.translationX) * animationProgress); - Float translateY = - (lhsSRT.translationY + - (rhsSRT.translationY - lhsSRT.translationY) * animationProgress); - Float translateZ = - (lhsSRT.translationZ + - (rhsSRT.translationZ - lhsSRT.translationZ) * animationProgress); - Float scaleX = - (lhsSRT.scaleX + (rhsSRT.scaleX - lhsSRT.scaleX) * animationProgress); - Float scaleY = - (lhsSRT.scaleY + (rhsSRT.scaleY - lhsSRT.scaleY) * animationProgress); - Float scaleZ = - (lhsSRT.scaleZ + (rhsSRT.scaleZ - lhsSRT.scaleZ) * animationProgress); - - // Use the quaternion vectors to produce an interpolated rotation via SLERP - // dot: cos of the angle between the two quaternion vectors - Quaternion q1 = lhsSRT.rotation; - Quaternion q2 = rhsSRT.rotation; - Float dot = q1.dot(q2); - // Clamp dot between -1 and 1 - dot = (dot < -1 ? -1 : (dot > 1 ? 1 : dot)); - // There are two ways of performing an identical slerp: q1 and -q1. - // If the dot-product is negative, we can multiply q1 by -1 and our animation - // will take the "short way" around instead of the "long way". - if (dot < 0) { - q1 = q1 * (Float)-1; - dot = dot * -1; + auto result = Transform{}; + for (size_t i = 0; i < 16; i++) { + result.matrix[i] = + lhs.matrix[i] + (rhs.matrix[i] - lhs.matrix[i]) * animationProgress; } - // Interpolated angle - Float theta = acosf(dot) * animationProgress; - - Transform rotation = Transform::Identity(); - - // Compute orthonormal basis - Quaternion orthonormalBasis = (q2 - q1 * dot); - - if (orthonormalBasis.abs() > 0) { - Quaternion orthonormalBasisNormalized = orthonormalBasis.normalize(); - - // Compute orthonormal basis - // Final quaternion result - slerp! - Quaternion resultingRotationVec = - (q1 * (Float)cos(theta) + - orthonormalBasisNormalized * (Float)sin(theta)) - .normalize(); - - // Convert quaternion to matrix - rotation.matrix = resultingRotationVec.toRotationMatrix4x4(); - } - - // Compose matrices and return - return (Scale(scaleX, scaleY, scaleZ) * rotation) * - Translate(translateX, translateY, translateZ); + return result; } bool Transform::operator==(Transform const &rhs) const { diff --git a/ReactCommon/fabric/graphics/Transform.h b/ReactCommon/fabric/graphics/Transform.h index 357e2863d7d..18e568f5471 100644 --- a/ReactCommon/fabric/graphics/Transform.h +++ b/ReactCommon/fabric/graphics/Transform.h @@ -12,7 +12,6 @@ #include #include #include -#include #ifdef ANDROID #include @@ -21,32 +20,13 @@ namespace facebook { namespace react { -struct ScaleRotationTranslation { - Float translationX; - Float translationY; - Float translationZ; - Float scaleX; - Float scaleY; - Float scaleZ; - Quaternion rotation; -}; - /* * Defines transform matrix to apply affine transformations. */ struct Transform { - using SRT = ScaleRotationTranslation; - std::array matrix{ {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; - /** - * For debugging only. Prints out the matrix. - */ -#ifdef RN_DEBUG_STRING_CONVERTIBLE - static void print(Transform const &t, std::string prefix); -#endif - /* * Returns the identity transform (`[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]`). */ @@ -81,29 +61,10 @@ struct Transform { static Transform Rotate(Float angleX, Float angleY, Float angleZ); /** - * Extract SRT (scale, rotation, transformation) from a Transform matrix. - * - * CAVEATS: - * 1. The input matrix must not have Skew applied. - * 2. Scaling factors must be non-negative. Scaling by a negative factor is - * equivalent to a rotation, and though it is possible to detect if 1 or - * 3 of the scale signs are flipped (but not two), it is not possible - * to detect WHICH of the scales are flipped. Thus, any animation - * that involves a negative scale factor will not crash but will - * interpolate over nonsensical values. - * 3. Another caveat is that if the animation interpolates TO a 90º - * rotation in the X, Y, or Z axis, the View will appear to suddenly - * explode in size. Interpolating THROUGH 90º is fine as long as you don't end - * up at 90º or close to it (89.99). The same is true for 0±90 and 360n+90, - * etc. - */ - static SRT ExtractSRT(Transform const &transform); - - /** - * Perform an interpolation between lhs and rhs, given progress. - * This first decomposes the matrices into translation, scale, and rotation, - * performs slerp between the two rotations, and a linear interpolation - * of scale and translation. + * Perform a simple interpolation between lhs and rhs, given "progress" + * between the two assuming that we are "moving" from lhs to rhs. This is a + * simple linear interpolation between each matrix index and will only work + * for simple scaling or translation; this will not work for rotation. * * @param progress * @param lhs