Skip to content

Commit

Permalink
Introduce Transform::Inverse (facebook#42368)
Browse files Browse the repository at this point in the history
Summary:

Changelog: [Internal] - Introduce Transform::Inverse

This is a new static method for `Transform` that makes it possible to invert transforms which will greatly simplify the implementation of transform points between coordinate spaces — something that will be needed to do proper retargeting of Pointer Events.

Differential Revision: D52889917
  • Loading branch information
vincentriemer authored and facebook-github-bot committed Jan 19, 2024
1 parent 08eb985 commit 53e9d1d
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,70 @@ bool Transform::isHorizontalInversion(const Transform& transform) {
return transform.at(0, 0) == -1;
}

Transform Transform::Inverse(const Transform& transform) {
auto m00 = transform.matrix[0];
auto m01 = transform.matrix[1];
auto m02 = transform.matrix[2];
auto m03 = transform.matrix[3];
auto m10 = transform.matrix[4];
auto m11 = transform.matrix[5];
auto m12 = transform.matrix[6];
auto m13 = transform.matrix[7];
auto m20 = transform.matrix[8];
auto m21 = transform.matrix[9];
auto m22 = transform.matrix[10];
auto m23 = transform.matrix[11];
auto m30 = transform.matrix[12];
auto m31 = transform.matrix[13];
auto m32 = transform.matrix[14];
auto m33 = transform.matrix[15];

auto A2323 = m22 * m33 - m23 * m32;
auto A1323 = m21 * m33 - m23 * m31;
auto A1223 = m21 * m32 - m22 * m31;
auto A0323 = m20 * m33 - m23 * m30;
auto A0223 = m20 * m32 - m22 * m30;
auto A0123 = m20 * m31 - m21 * m30;
auto A2313 = m12 * m33 - m13 * m32;
auto A1313 = m11 * m33 - m13 * m31;
auto A1213 = m11 * m32 - m12 * m31;
auto A2312 = m12 * m23 - m13 * m22;
auto A1312 = m11 * m23 - m13 * m21;
auto A1212 = m11 * m22 - m12 * m21;
auto A0313 = m10 * m33 - m13 * m30;
auto A0213 = m10 * m32 - m12 * m30;
auto A0312 = m10 * m23 - m13 * m20;
auto A0212 = m10 * m22 - m12 * m20;
auto A0113 = m10 * m31 - m11 * m30;
auto A0112 = m10 * m21 - m11 * m20;

auto det = m00 * (m11 * A2323 - m12 * A1323 + m13 * A1223) -
m01 * (m10 * A2323 - m12 * A0323 + m13 * A0223) +
m02 * (m10 * A1323 - m11 * A0323 + m13 * A0123) -
m03 * (m10 * A1223 - m11 * A0223 + m12 * A0123);
det = 1 / det;

auto result = Transform{};
result.matrix[0] = det * (m11 * A2323 - m12 * A1323 + m13 * A1223);
result.matrix[1] = det * -(m01 * A2323 - m02 * A1323 + m03 * A1223);
result.matrix[2] = det * (m01 * A2313 - m02 * A1313 + m03 * A1213);
result.matrix[3] = det * -(m01 * A2312 - m02 * A1312 + m03 * A1212);
result.matrix[4] = det * -(m10 * A2323 - m12 * A0323 + m13 * A0223);
result.matrix[5] = det * (m00 * A2323 - m02 * A0323 + m03 * A0223);
result.matrix[6] = det * -(m00 * A2313 - m02 * A0313 + m03 * A0213);
result.matrix[7] = det * (m00 * A2312 - m02 * A0312 + m03 * A0212);
result.matrix[8] = det * (m10 * A1323 - m11 * A0323 + m13 * A0123);
result.matrix[9] = det * -(m00 * A1323 - m01 * A0323 + m03 * A0123);
result.matrix[10] = det * (m00 * A1313 - m01 * A0313 + m03 * A0113);
result.matrix[11] = det * -(m00 * A1312 - m01 * A0312 + m03 * A0112);
result.matrix[12] = det * -(m10 * A1223 - m11 * A0223 + m12 * A0123);
result.matrix[13] = det * (m00 * A1223 - m01 * A0223 + m02 * A0123);
result.matrix[14] = det * -(m00 * A1213 - m01 * A0213 + m02 * A0113);
result.matrix[15] = det * (m00 * A1212 - m01 * A0212 + m02 * A0112);

return result;
}

bool Transform::operator==(const Transform& rhs) const {
for (auto i = 0; i < 16; i++) {
if (matrix[i] != rhs.matrix[i]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ struct Transform {
static bool isVerticalInversion(const Transform& transform);
static bool isHorizontalInversion(const Transform& transform);

static Transform Inverse(const Transform& transform);

/*
* Equality operators.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@

#include <gtest/gtest.h>
#include <cmath>
#include <limits>

using namespace facebook::react;

static inline void checkMatrix(
const Transform& m,
const Transform& exp,
float epsilon = std::numeric_limits<float>::epsilon()) {
for (int i = 0; i < 16; i++) {
EXPECT_NEAR(m.matrix[i], exp.matrix[i], epsilon);
}
}

TEST(TransformTest, transformingSize) {
auto size = facebook::react::Size{100, 200};
auto scaledSize = size * Transform::Scale(0.5, 0.5, 1);
Expand Down Expand Up @@ -90,3 +100,51 @@ TEST(TransformTest, scalingAndTranslatingRect) {
EXPECT_EQ(transformedRect.size.width, 150);
EXPECT_EQ(transformedRect.size.height, 200);
}

// Inverse Transform Tests (adapted from WPT:
// css/geometry/DOMMatrix-invert-invertible)
TEST(TransformTest, inverseIdentity) {
auto m = Transform::Identity();
auto m1 = Transform::Inverse(m);
checkMatrix(m, m1);
}

TEST(TransformTest, inverseTranslate) {
auto m = Transform::Translate(10, -20.5, 0);
auto m1 = Transform::Inverse(m);
checkMatrix(m1, Transform::Translate(-10, 20.5, 0));
checkMatrix(m * m1, Transform::Identity());
}

TEST(TransformTest, inverse3DTranslate) {
auto m = Transform::Translate(10, -20.5, 30.5);
auto m1 = Transform::Inverse(m);
checkMatrix(m1, Transform::Translate(-10, 20.5, -30.5));
checkMatrix(m * m1, Transform::Identity());
}

TEST(TransformTest, inverseScale) {
auto m = Transform::Scale(4, -0.5, 1);
auto m1 = Transform::Inverse(m);
checkMatrix(m1, Transform::Scale(0.25, -2.0, 1));
checkMatrix(m * m1, Transform::Identity());
}

TEST(TransformTest, inverse3DScale) {
auto m = Transform::Scale(4, -0.5, 2);
auto m1 = Transform::Inverse(m);
checkMatrix(m1, Transform::Scale(0.25, -2.0, 0.5));
checkMatrix(m * m1, Transform::Identity());
}

TEST(TransformTest, inverseComplex) {
auto m = Transform::RotateX(20) * Transform::Translate(10, -20.5, 30.5) *
Transform::RotateY(10) * Transform::Scale(10, -0.5, 2.5) *
Transform::RotateZ(-30);
auto expected = Transform::RotateZ(30) * Transform::Scale(0.1, -2.0, 0.4) *
Transform::RotateY(-10) * Transform::Translate(-10, 20.5, -30.5) *
Transform::RotateX(-20);
auto m1 = Transform::Inverse(m);
auto epsilon = 1e-6f;
checkMatrix(m1, expected, epsilon);
}

0 comments on commit 53e9d1d

Please sign in to comment.