Files
react-native/ReactCommon/fabric/graphics/Transform.h
T
Joshua Gross 2e27fb6a97 LayoutAnimations: for all Transform matrices, store decomposed matrix and use that for interpolation
Summary:
Previously, we tried to take a Transform matrix, decompose it into parts, and then interpolate between those parts. This will always be risky at best, and in some cases ambiguous or unsolvable. For example, a scale of -1 is identical to a rotation of 180 degrees.

Another issue is that when decomposing a matrix, it is impossible to tell the sign of scaleX, scaleY, scaleZ. This is a problem - flipping a View over an axis via scale then becomes a non-animatable operation.

This caused a number of issues.

To resolve it, we accumulate the "operations" resulting in a particular transform. This allows us to easily interpolate between two Transform matrices without actually decomposing the matrix, since we have the "path" that resulted in each particular matrix.

This will make LayoutAnimations over transforms, including Skew transforms, look and work much better, and more reliably.

Changelog: [Internal]

Reviewed By: shergin

Differential Revision: D22204559

fbshipit-source-id: 0d88ae77e4399a7ea333afbf6062beea977b854a
2020-06-24 16:36:28 -07:00

215 lines
4.8 KiB
C++

/*
* 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 <array>
#include <vector>
#include <folly/Hash.h>
#include <react/graphics/Float.h>
#include <react/graphics/Geometry.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#endif
namespace facebook {
namespace react {
inline bool isZero(Float n) {
// We use this ternary expression instead of abs, fabsf, etc, because
// Float can be double or float depending on compilation target.
return (n < 0 ? n * (-1) : n) < 0.00001;
}
/**
* Defines operations used to construct a transform matrix.
* An "Arbitrary" operation means that the transform was seeded with some
* arbitrary initial result.
*/
enum class TransformOperationType {
Arbitrary,
Identity,
Perspective,
Scale,
Translate,
Rotate,
Skew
};
struct TransformOperation {
TransformOperationType type;
Float x;
Float y;
Float z;
};
/*
* Defines transform matrix to apply affine transformations.
*/
struct Transform {
std::vector<TransformOperation> operations{};
std::array<Float, 16> 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
/*
* Given a TransformOperation, return the proper transform.
*/
static Transform FromTransformOperation(
TransformOperation transformOperation);
static TransformOperation DefaultTransformOperation(
TransformOperationType type);
/*
* Returns the identity transform (`[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]`).
*/
static Transform Identity();
/*
* Returns a Perspective transform.
*/
static Transform Perspective(Float perspective);
/*
* Returns a Scale transform.
*/
static Transform Scale(Float factorX, Float factorY, Float factorZ);
/*
* Returns a Translate transform.
*/
static Transform Translate(Float x, Float y, Float z);
/*
* Returns a Skew transform.
*/
static Transform Skew(Float x, Float y);
/*
* Returns a transform that rotates by `angle` radians along the given axis.
*/
static Transform RotateX(Float angle);
static Transform RotateY(Float angle);
static Transform RotateZ(Float angle);
static Transform Rotate(Float angleX, Float angleY, Float angleZ);
/**
* 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.
*
* @param progress
* @param lhs
* @param rhs
* @return
*/
static Transform Interpolate(
float animationProgress,
Transform const &lhs,
Transform const &rhs);
/*
* Equality operators.
*/
bool operator==(Transform const &rhs) const;
bool operator!=(Transform const &rhs) const;
/*
* Matrix subscript.
*/
Float &at(int x, int y);
Float const &at(int x, int y) const;
/*
* Concatenates (multiplies) transform matrices.
*/
Transform operator*(Transform const &rhs) const;
/**
* Convert to folly::dynamic.
*/
#ifdef ANDROID
operator folly::dynamic() const {
return folly::dynamic::array(
matrix[0],
matrix[1],
matrix[2],
matrix[3],
matrix[4],
matrix[5],
matrix[6],
matrix[7],
matrix[8],
matrix[9],
matrix[10],
matrix[11],
matrix[12],
matrix[13],
matrix[14],
matrix[15]);
}
#endif
};
/*
* Applies tranformation to the given point.
*/
Point operator*(Point const &point, Transform const &transform);
/*
* Applies tranformation to the given size.
*/
Size operator*(Size const &size, Transform const &transform);
/*
* Applies tranformation to the given rect.
* ONLY SUPPORTS scale and translation transformation.
*/
Rect operator*(Rect const &rect, Transform const &transform);
Vector operator*(Transform const &transform, Vector const &vector);
} // namespace react
} // namespace facebook
namespace std {
template <>
struct hash<facebook::react::Transform> {
size_t operator()(const facebook::react::Transform &transform) const {
return folly::hash::hash_combine(
0,
transform.matrix[0],
transform.matrix[1],
transform.matrix[2],
transform.matrix[3],
transform.matrix[4],
transform.matrix[5],
transform.matrix[6],
transform.matrix[7],
transform.matrix[8],
transform.matrix[9],
transform.matrix[10],
transform.matrix[11],
transform.matrix[12],
transform.matrix[13],
transform.matrix[14],
transform.matrix[15]);
}
};
} // namespace std