Skip to content

Commit 474c147

Browse files
authored
feat: 3D and 2D CSS transform matrix integration (#8195)
## Summary This PR finally adds the integration of 2D and 3D transform matrix in transforms interpolation. The 2D matrix is used when possible to optimize interpolation. If the user uses a transformation that requires a 3D space (like x, y rotation or perspective), the 3D matrix is used instead. 2D transform matrix is automatically converted to the 3D transform matrix when needed. ## Test plan <details> <summary>Code snippet</summary> ```tsx import { useState } from 'react'; import type { ViewStyle } from 'react-native'; import { StyleSheet, View } from 'react-native'; import Animated from 'react-native-reanimated'; import { Button, Screen } from '@/apps/css/components'; const transitionStyles: Array<ViewStyle> = [ { transform: [{ rotate: '45deg' }, { skewX: '45deg' }], }, { transform: [{ translateY: 200 }, { rotate: '45deg' }, { scale: 2 }], }, { transform: [{ rotate: '45deg' }, { translateY: 150 }], width: 200, }, ]; export default function Playground() { const [state, setState] = useState(0); const stateToStyle = (num: number) => { return transitionStyles[num % transitionStyles.length]; }; return ( <Screen> <View style={styles.container}> <Button title="Change state" onPress={() => { setState(state + 1); }} /> <Animated.View style={[ { backgroundColor: 'red', height: 65, marginTop: 60, transitionDuration: '0.5s', width: 65, }, stateToStyle(state), ]} /> </View> </Screen> ); } const styles = StyleSheet.create({ container: { alignItems: 'center', flex: 1, justifyContent: 'center', }, }); ``` </details>
1 parent 80bcd6e commit 474c147

22 files changed

+755
-412
lines changed

apps/fabric-example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3190,10 +3190,10 @@ SPEC CHECKSUMS:
31903190
RNReanimated: 3b47c33660454c6f9700b463e92daa282030866a
31913191
RNScreens: 6ced6ae8a526512a6eef6e28c2286e1fc2d378c3
31923192
RNSVG: 287504b73fa0e90a605225aa9f852a86d5461e84
3193-
RNWorklets: 7119ae08263033c456c80d90794a312f2f88c956
3193+
RNWorklets: 991f94e4fa31fc20853e74d5d987426f8580cb0d
31943194
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
31953195
Yoga: e80c5fabbc3e26311152fa20404cdfa14f16a11f
31963196

3197-
PODFILE CHECKSUM: 5d8c04f461eed0f22e86610877d94f2b8b838b8b
3197+
PODFILE CHECKSUM: db099f48c6dadedd8fc0a430129b75e561867ab9
31983198

31993199
COCOAPODS: 1.15.2

packages/react-native-reanimated/Common/cpp/reanimated/CSS/InterpolatorRegistry.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <reanimated/CSS/common/values/CSSNumber.h>
1111
#include <reanimated/CSS/common/values/CSSValue.h>
1212

13-
#include <reanimated/CSS/common/transforms/TransformMatrix3D.h>
13+
#include <reanimated/CSS/common/transforms/TransformMatrix2D.h>
1414
#include <reanimated/CSS/common/values/complex/CSSBoxShadow.h>
1515

1616
#include <reanimated/CSS/svg/values/SVGLength.h>
@@ -164,8 +164,7 @@ const InterpolatorFactoriesRecord TRANSFORMS_INTERPOLATORS = {
164164
transformOp<TranslateYOperation>(0, {RelativeTo::Self, "height"})},
165165
{"skewX", transformOp<SkewXOperation>("0deg")},
166166
{"skewY", transformOp<SkewYOperation>("0deg")},
167-
{"matrix",
168-
transformOp<MatrixOperation>(TransformMatrix3D::Identity())}})},
167+
{"matrix", transformOp<MatrixOperation>(TransformMatrix2D())}})},
169168
};
170169

171170
const InterpolatorFactoriesRecord VIEW_INTERPOLATORS = mergeInterpolators(

packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/transforms/TransformMatrix.h

Lines changed: 87 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,33 @@
33
#include <reanimated/CSS/common/definitions.h>
44

55
#include <folly/dynamic.h>
6+
#include <memory>
67
#include <string>
78
#include <utility>
89

910
namespace reanimated::css {
1011

1112
class TransformMatrix {
1213
public:
13-
virtual ~TransformMatrix() = default;
14-
15-
virtual double determinant() const = 0;
14+
using Shared = std::shared_ptr<const TransformMatrix>;
1615

17-
virtual double &operator[](size_t index) = 0;
18-
virtual const double &operator[](size_t index) const = 0;
16+
TransformMatrix() = default;
17+
virtual ~TransformMatrix() = default;
1918

2019
virtual size_t getDimension() const = 0;
20+
virtual size_t getSize() const = 0;
21+
22+
virtual bool isSingular() const = 0;
23+
virtual bool normalize() = 0;
24+
virtual void transpose() = 0;
25+
virtual double determinant() const = 0;
2126

2227
virtual std::string toString() const = 0;
2328
virtual folly::dynamic toDynamic() const = 0;
29+
30+
virtual double &operator[](size_t index) = 0;
31+
virtual const double &operator[](size_t index) const = 0;
32+
virtual bool operator==(const TransformMatrix &other) const = 0;
2433
};
2534

2635
template <typename TDerived, size_t TDimension>
@@ -29,51 +38,72 @@ class TransformMatrixBase : public TransformMatrix {
2938
static constexpr size_t SIZE = TDimension * TDimension;
3039
using MatrixArray = std::array<double, SIZE>;
3140

32-
explicit TransformMatrixBase(MatrixArray matrix)
33-
: matrix_(std::move(matrix)) {}
34-
35-
explicit TransformMatrixBase(jsi::Runtime &rt, const jsi::Value &value) {
36-
const auto array = value.asObject(rt).asArray(rt);
37-
if (array.size(rt) != SIZE) {
38-
throw std::invalid_argument(
39-
"[Reanimated] Matrix array should have " + std::to_string(SIZE) +
40-
" elements");
41-
}
42-
43-
for (size_t i = 0; i < SIZE; ++i) {
44-
matrix_[i] = array.getValueAtIndex(rt, i).asNumber();
41+
TransformMatrixBase() : TransformMatrix(), matrix_{} {
42+
// Create an identity matrix
43+
for (size_t i = 0; i < TDimension; ++i) {
44+
matrix_[i * (TDimension + 1)] = 1;
4545
}
4646
}
4747

48-
explicit TransformMatrixBase(const folly::dynamic &array) {
49-
if (!array.isArray() || array.size() != SIZE) {
50-
throw std::invalid_argument(
51-
"[Reanimated] Matrix array should have " + std::to_string(SIZE) +
52-
" elements");
53-
}
48+
explicit TransformMatrixBase(MatrixArray matrix)
49+
: TransformMatrix(), matrix_(std::move(matrix)) {}
5450

55-
for (size_t i = 0; i < SIZE; ++i) {
56-
matrix_[i] = array[i].asDouble();
57-
}
51+
TransformMatrixBase(const TransformMatrixBase &other)
52+
: TransformMatrix(), matrix_(other.matrix_) {}
53+
54+
TransformMatrixBase(TransformMatrixBase &&other) noexcept
55+
: TransformMatrix(), matrix_(std::move(other.matrix_)) {}
56+
57+
inline bool operator==(const TDerived &other) const {
58+
return matrix_ == other.matrix_;
5859
}
5960

60-
virtual bool operator==(const TDerived &other) const = 0;
61+
inline bool operator==(const TransformMatrix &other) const override {
62+
return TDimension == other.getDimension() &&
63+
matrix_ == static_cast<const TDerived &>(other).matrix_;
64+
}
6165

62-
double &operator[](size_t index) override {
66+
inline double &operator[](size_t index) override {
6367
return matrix_[index];
6468
}
6569

66-
const double &operator[](size_t index) const override {
70+
inline const double &operator[](size_t index) const override {
6771
return matrix_[index];
6872
}
6973

70-
TDerived operator*(const TDerived &rhs) const {
71-
return TDerived(multiply(rhs));
74+
size_t getDimension() const override {
75+
return TDimension;
7276
}
7377

74-
TDerived &operator*=(const TDerived &rhs) {
75-
matrix_ = multiply(rhs);
76-
return static_cast<TDerived &>(*this);
78+
size_t getSize() const override {
79+
return SIZE;
80+
}
81+
82+
bool isSingular() const override {
83+
return determinant() == 0;
84+
}
85+
86+
bool normalize() override {
87+
const auto last = matrix_[SIZE - 1];
88+
if (last == 0) {
89+
return false;
90+
}
91+
if (last == 1) {
92+
return true;
93+
}
94+
95+
for (size_t i = 0; i < SIZE; ++i) {
96+
matrix_[i] /= last;
97+
}
98+
return true;
99+
}
100+
101+
void transpose() override {
102+
for (size_t i = 0; i < TDimension; ++i) {
103+
for (size_t j = i + 1; j < TDimension; ++j) {
104+
std::swap(matrix_[i * TDimension + j], matrix_[j * TDimension + i]);
105+
}
106+
}
77107
}
78108

79109
std::string toString() const override {
@@ -96,50 +126,46 @@ class TransformMatrixBase : public TransformMatrix {
96126
return result;
97127
}
98128

99-
size_t getDimension() const override {
100-
return TDimension;
129+
inline TDerived operator*(const TDerived &rhs) const {
130+
return TDerived(multiply(rhs));
101131
}
102132

103-
bool isSingular() const {
104-
return determinant() == 0;
133+
inline TDerived &operator*=(const TDerived &rhs) {
134+
matrix_ = multiply(rhs);
135+
return static_cast<TDerived &>(*this);
105136
}
106137

107-
bool normalize() {
108-
const auto last = matrix_[SIZE - 1];
109-
if (last == 0) {
110-
return false;
111-
}
112-
if (last == 1) {
113-
return true;
114-
}
115-
116-
for (size_t i = 0; i < SIZE; ++i) {
117-
matrix_[i] /= last;
138+
TransformMatrixBase &operator=(const TransformMatrixBase &other) {
139+
if (this != &other) {
140+
// Note: dimension_ is const, so we can't reassign it
141+
// But since all instances have the same dimension, this is fine
142+
matrix_ = other.matrix_;
118143
}
119-
return true;
144+
return *this;
120145
}
121146

122-
void transpose() {
123-
for (size_t i = 0; i < TDimension; ++i) {
124-
for (size_t j = i + 1; j < TDimension; ++j) {
125-
std::swap(matrix_[i * TDimension + j], matrix_[j * TDimension + i]);
126-
}
147+
TransformMatrixBase &operator=(TransformMatrixBase &&other) noexcept {
148+
if (this != &other) {
149+
matrix_ = std::move(other.matrix_);
127150
}
151+
return *this;
128152
}
129153

130154
protected:
131155
std::array<double, SIZE> matrix_;
132156

133157
MatrixArray multiply(const TDerived &rhs) const {
134-
std::array<double, SIZE> result{};
158+
MatrixArray result{};
159+
135160
for (size_t i = 0; i < TDimension; ++i) {
136-
for (size_t j = 0; j < TDimension; ++j) {
137-
for (size_t k = 0; k < TDimension; ++k) {
138-
result[i * TDimension + j] +=
139-
matrix_[i * TDimension + k] * rhs[k * TDimension + j];
161+
for (size_t k = 0; k < TDimension; ++k) {
162+
double temp = matrix_[i * TDimension + k];
163+
for (size_t j = 0; j < TDimension; ++j) {
164+
result[i * TDimension + j] += temp * rhs[k * TDimension + j];
140165
}
141166
}
142167
}
168+
143169
return result;
144170
}
145171
};

0 commit comments

Comments
 (0)