Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fabric] Fixes findNodeAtPoint when views were inverted #45519

Closed

Conversation

zhongwuzw
Copy link
Contributor

@zhongwuzw zhongwuzw commented Jul 18, 2024

Summary:

FIxes #45502 . cc @realsoelynn

Changelog:

[GENERAL] [FIXED] - Fixes findNodeAtPoint when views were inverted

Test Plan:

Demo in #45502 .

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Jul 18, 2024
@facebook-github-bot
Copy link
Contributor

@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@realsoelynn
Copy link
Contributor

Thank you so much for quickly looking into the fix for this

Since this is relying on scaleX and scaleY, it will have issue when rotate is involved. (I added a new test case in the reproducer app as shown in the screenshot)

I am thinking of consolidating this findNodeAtPoint with computeRelativeLayoutMetrics as that is being used in measure function to know where in the screen it's being rendered as findNodeAtPoint has been a major area for bugs due to this two different approaches.

@zhongwuzw
Copy link
Contributor Author

I'm not familiar with measurement, will find some time to try to figure it out :)

@zhongwuzw
Copy link
Contributor Author

@realsoelynn Hi, sorry I have no solution to consolidate them right now. But I tried to fix your rotated repro demo :) .

@facebook-github-bot
Copy link
Contributor

@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@realsoelynn
Copy link
Contributor

C++ Test for findNodeAtPoint

Just dumping this test case i wrote a while back. Please help me add to the PR

TEST(FindNodeAtPointTest, invertedList) {
  auto builder = simpleComponentBuilder();
  
  // clang-format off
  auto element =
    Element<ScrollViewShadowNode>()
      .props([] {
        auto sharedProps = std::make_shared<ScrollViewProps>();
        sharedProps->transform = Transform::VerticalInversion();
        return sharedProps;
      })
      .tag(1)
      .finalize([](ScrollViewShadowNode &shadowNode){
        auto layoutMetrics = EmptyLayoutMetrics;
        layoutMetrics.frame.size = {100, 200};
        shadowNode.setLayoutMetrics(layoutMetrics);
      })
      .children({
        Element<ViewShadowNode>()
        .tag(2)
        .finalize([](ViewShadowNode &shadowNode){
          auto layoutMetrics = EmptyLayoutMetrics;
          layoutMetrics.frame.origin = {0, 0};
          layoutMetrics.frame.size = {100, 100};
          shadowNode.setLayoutMetrics(layoutMetrics);
        }),
        Element<ViewShadowNode>()
        .tag(3)
        .finalize([](ViewShadowNode &shadowNode){
          auto layoutMetrics = EmptyLayoutMetrics;
          layoutMetrics.frame.origin = {0, 100};
          layoutMetrics.frame.size = {100, 100};
          shadowNode.setLayoutMetrics(layoutMetrics);
        })
    });
  // clang-format on

  auto parentShadowNode = builder.build(element);

  EXPECT_EQ(
      LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {10, 10})
          ->getTag(),
      3);
  EXPECT_EQ(
      LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {10, 105})
          ->getTag(),
      2);
}

@realsoelynn
Copy link
Contributor

realsoelynn commented Jul 29, 2024

@zhongwuzw Please take a look at this other author PR #45571

Your current solution handled perfectly for horizontal and vertical inversion.

But, we want to to be able to handle different type of rotation transformation.

#45571 I don't agree with the Inversion check in that PR. But, the added new Point transformation function has the right idea to solve rotational transform

Cc: @coado

@coado
Copy link
Collaborator

coado commented Jul 30, 2024

@realsoelynn Is there any way to run cpp tests on emulator locally?

@coado
Copy link
Collaborator

coado commented Jul 30, 2024

@zhongwuzw Please take a look at this other author PR #45571

Your current solution handled perfectly for horizontal and vertical inversion.

But, we want to to be able to handle different type of rotation transformation.

#45571 I don't agree with the Inversion check in that PR. But, the added new Point transformation function has the right idea to solve rotational transform

Cc: @coado

Do you mean this inversion check?

@zhongwuzw
Copy link
Contributor Author

Maybe we can combine the two PRs: one for a quick path to handle horizontal and vertical inversion, and another for a transform fallback to matrix inversion calculation ?

@realsoelynn
Copy link
Contributor

@zhongwuzw Please take a look at this other author PR #45571
Your current solution handled perfectly for horizontal and vertical inversion.
But, we want to to be able to handle different type of rotation transformation.
#45571 I don't agree with the Inversion check in that PR. But, the added new Point transformation function has the right idea to solve rotational transform
Cc: @coado

Do you mean this inversion check?

I mean bool isInverted = currentTransform.inv();. inv() function is not only returning bool result for if it's inverted, but also mutating the current transform.inv() function should not mutate.

@realsoelynn
Copy link
Contributor

Maybe we can combine the two PRs: one for a quick path to handle horizontal and vertical inversion, and another for a transform fallback to matrix inversion calculation ?

@zhongwuzw yep. do you mind pulling in @coado commit to this PR?

Comment on lines 501 to 569
bool Transform::inv() {
if (*this == Transform::Identity()) {
return true;
}

double inv[16], det;
int i;
auto m = this->matrix;

inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] +
m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];

inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] -
m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];

inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] +
m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];

inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] -
m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];

inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] -
m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];

inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] +
m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];

inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] -
m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];

inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] +
m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];

inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] +
m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6];

inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] -
m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6];

inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] +
m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5];

inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] -
m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5];

inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] -
m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6];

inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] +
m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6];

inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] -
m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5];

inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] +
m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5];

det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];

if (det == 0)
return false;

det = 1.0 / det;

for (i = 0; i < 16; i++)
this->matrix[i] = inv[i] * det;

return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we need this.

@coado
Copy link
Collaborator

coado commented Jul 31, 2024

I think right now changes are merged in a way that omits handling rotations. I don't see my part of the code in findNodeAtPoint function, that's why the inv is not being invoked. Also, does it make sense to handle horizontal and vertical inversion separately? The inversion matrix will handle all cases.

@zhongwuzw
Copy link
Contributor Author

@coado Because of conflict I just merged some parts of the code from you, after I combine them, I'll push the whole code. Also I would remove the overflow part which we can concentrate on transform things.

@zhongwuzw zhongwuzw force-pushed the features/fix_inverted_inspection branch from 5b655d3 to cec195a Compare August 1, 2024 06:29
@zhongwuzw
Copy link
Contributor Author

cc @realsoelynn @coado

@facebook-github-bot
Copy link
Contributor

@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@realsoelynn
Copy link
Contributor

Sorry about my late reply

It's failing UT on FindNodeAtPointTest/viewIsScaled test case

Failure: Expected equality of these values:
  LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag()
    Which is: 3
  2

I am figuring out how to run C++ Unit Test like above to run without BUCK. (I will keep you folks posted about it once i learn more about it.)

Comment on lines 506 to 554
inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] +
m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];

inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] -
m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];

inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] +
m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];

inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] -
m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];

inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] -
m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];

inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] +
m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];

inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] -
m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];

inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] +
m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];

inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] +
m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6];

inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] -
m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6];

inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] +
m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5];

inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] -
m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5];

inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] -
m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6];

inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] +
m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6];

inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] -
m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5];

inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] +
m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5];

det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coado Can i have a link to details explanation about this matrix calculation?

I would like to understand more about it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opened a draft, just to check if the idea is correct. So, for now I just copied the implementation from the stackoverflow. There is a more detailed implementation of this in mesa library here.

@zhongwuzw
Copy link
Contributor Author

Sorry about my late reply

It's failing UT on FindNodeAtPointTest/viewIsScaled test case

Failure: Expected equality of these values:
  LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag()
    Which is: 3
  2

I am figuring out how to run C++ Unit Test like above to run without BUCK. (I will keep you folks posted about it once i learn more about it.)

Seems this test is wrong, the tag should be 3, not 2 ?

@coado
Copy link
Collaborator

coado commented Aug 8, 2024

Sorry about my late reply
It's failing UT on FindNodeAtPointTest/viewIsScaled test case

Failure: Expected equality of these values:
  LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag()
    Which is: 3
  2

I am figuring out how to run C++ Unit Test like above to run without BUCK. (I will keep you folks posted about it once i learn more about it.)

Seems this test is wrong, the tag should be 3, not 2 ?

Isn't the view with tag 3 scaled down? It seems like the touch point is within the boundaries of unscaled view with tag 2, so the test is correct, but I might be missing something.

@zhongwuzw
Copy link
Contributor Author

@realsoelynn I just translate C++ ut to React code like below: I hit the breakpoint and it returns child1.

import React from 'react';
import { View, StyleSheet } from 'react-native';

const FindNodeAtPointTest = () => {
  return (
    <View style={styles.parent}>
      <View style={styles.child1}>
        <View style={styles.child2} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  parent: {
    width: 1000,
    height: 1000,
  },
  child1: {
    position: 'absolute',
    top: 100,
    left: 100,
    width: 100,
    height: 100,
    backgroundColor: 'blue'
  },
  child2: {
    position: 'absolute',
    top: 10,
    left: 10,
    width: 10,
    height: 10,
    backgroundColor: 'red',
    transform: [{ scale: 0.5 }],
  },
});

export default FindNodeAtPointTest;

image

@realsoelynn
Copy link
Contributor

@zhongwuzw sorry about my delay. Let's go with your original solution for now to do a simple inversion (without @coado changes). As 0.76 cutoff is near, we want to at least get this major usecase bugfix went in first.

@coado For your changes, let me work with you more later after this PR is merged as i need to prioritize and research into the algorithms more.

Thank you both for your patient.

@zhongwuzw zhongwuzw force-pushed the features/fix_inverted_inspection branch from cec195a to 3aa37d5 Compare August 15, 2024 06:19
@facebook-github-bot
Copy link
Contributor

@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@realsoelynn
Copy link
Contributor

Hmm strange CircleCI failure. @zhongwuzw please help us rebase it.

@zhongwuzw
Copy link
Contributor Author

@realsoelynn Done. :)

@facebook-github-bot
Copy link
Contributor

@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Aug 16, 2024
@facebook-github-bot
Copy link
Contributor

@realsoelynn merged this pull request in 1d1646a.

@react-native-bot
Copy link
Collaborator

This pull request was successfully merged by @zhongwuzw in 1d1646a

When will my fix make it into a release? | How to file a pick request?

@coado coado mentioned this pull request Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inspector Tools issue when view are transformed in Fabric
5 participants