Skip to content

Fix rotation-invariant gesture matching - #9420

Merged
kuzeyron merged 3 commits into
kivy:masterfrom
charan-rathore:fix/gesture-rigid-rotation
Oct 18, 2026
Merged

kuzeyron merged 3 commits into
kivy:masterfrom
charan-rathore:fix/gesture-rigid-rotation

Conversation

@charan-rathore

@charan-rathore charan-rathore commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Gesture.get_rotation() always returned 0 degrees: it built both the target and the source vector from the other gesture's first point, so it measured the angle between that point and itself instead of the angle between the two gestures' first points. Rotation-invariant matching therefore treated every gesture as unrotated.

Fix

  • kivy/gesture.py: use this gesture's own first point as the source vector. One line; no API change.

Testing

  • Six tests in kivy/tests/test_gesture.py cover identical gestures, rotation-sensitive and rotation-invariant scores, rigid rotation using both gestures, database matching of a rotated gesture by default and empty gestures.
  • Ran here: on master the three rotation-dependent tests fail (score is 0 regardless of rotation) while the three control tests pass; with the fix all 6 pass.
  • No full compiled build or GL suite run.

@ElliotGarbus

Copy link
Copy Markdown
Contributor

Thanks for the PR and for the tests. The fix looks correct to me.

I'd like to share a general note about priorities, because maintainer review time is limited. This PR doesn't link to an issue, and I couldn't find a user report for this bug. A change like this still costs a full review.

If you'd like to contribute more, these are the most useful places to put your effort:

  • Issues that users have reported. These are real problems people hit, and a fix has an immediate payoff.
  • Items on the Kivy 3.0 project list. That's where the project needs help right now.

I'm not asking you to close this PR. If you'd like to keep going with it, please add a short reproducer (wrong result on master, right result with the fix) and a note about the behavior change. Thanks again for helping out.

Please add a migration note. find() and == default to rotation_invariant=True, and until now that did nothing. After this fix, gestures that differ only by rotation match under the defaults. For example, a swipe right and a swipe left:

stored vs drawn master this PR this PR, rotation_invariant=False
swipe right vs swipe left -3.17 0.98 -3.17

At the default minscore=0.9, an app that tells left from right this way would now match the wrong gesture. The new behavior is what the API promises, so I think the fix is right. But apps that worked by accident need to know. Could you add a short entry to doc/sources/migration.rst (and a versionchanged note on find) saying that matching is now rotation-invariant by default, and that rotation_invariant=False keeps the old behavior?

Optional: p is reused for two different points. src and dst would read better.

dst = dstpts.strokes[0].points[0]
target = Vector([dst.x, dst.y])
src = self.strokes[0].points[0]
source = Vector([src.x, src.y])
return source.angle(target)

…oints

Add a migration note and a versionchanged on find(): matching is now
rotation-invariant by default, and rotation_invariant=False keeps the
old behavior. Rename the reused p variable to dst/src for readability.

Signed-off-by: Charan Rathore <[email protected]>
@charan-rathore

Copy link
Copy Markdown
Contributor Author

All three in 923b1a3: migration note added to doc/sources/migration.rst, a versionchanged on find() documenting the new default, and the p rename is now dst/src exactly as you sketched.

On the reproducer: the tests in kivy/tests/test_gesture.py double as one. On master, test_rotation_invariant_score fails (a 90-degree rotated copy scores well under 1.0 because the rotation is never compensated) and test_database_finds_rotated_gesture_by_default fails to find the stored gesture at all; with the fix both pass, and your swipe table matches what the fix produces.

Behavior change, stated plainly: gestures that differ only by rotation now match under the defaults (minscore 0.9), so an app that told a swipe left from a swipe right this way will now see them as the same gesture. That is what the API always promised; rotation_invariant=False keeps the old behavior, and the migration note says so.

Point taken on priorities. I will link future PRs to user-reported issues or the 3.0 list.

@ElliotGarbus
ElliotGarbus requested a review from kuzeyron October 18, 2026 11:55
@ElliotGarbus

Copy link
Copy Markdown
Contributor

The migration note renders incorrectly. The trailing ``` line (it was already at the end of the file upstream) swallows the next line: docutils joins it to the "For complete documentation" paragraph, shows literal backticks, and warns "Inline literal start-string without end-string". The note also has no section heading, so it ends up inside "Application Storage Directories".

Please delete the stray fence and give the note its own section, in the same layout as the other entries:


========
Gestures
========

*Gesture matching is now rotation-invariant by default*

**Behavior Changes**

``GestureDatabase.find()`` and gesture equality (``==``) default to
``rotation_invariant=True``, and that flag now works as documented. Gestures
that differ only by rotation, such as a swipe right and a swipe left, now
match at the default ``minscore`` of 0.9 where they previously did not.

**Migration Impact**

Apps that relied on the old, uncompensated behavior should pass
``rotation_invariant=False`` to keep it.

kuzeyron
kuzeyron previously approved these changes Oct 18, 2026
@kuzeyron

Copy link
Copy Markdown
Contributor

The migration note renders incorrectly. The trailing ``` line (it was already at the end of the file upstream) swallows the next line: docutils joins it to the "For complete documentation" paragraph, shows literal backticks, and warns "Inline literal start-string without end-string". The note also has no section heading, so it ends up inside "Application Storage Directories".

Please delete the stray fence and give the note its own section, in the same layout as the other entries:


========
Gestures
========

*Gesture matching is now rotation-invariant by default*

**Behavior Changes**

``GestureDatabase.find()`` and gesture equality (``==``) default to
``rotation_invariant=True``, and that flag now works as documented. Gestures
that differ only by rotation, such as a swipe right and a swipe left, now
match at the default ``minscore`` of 0.9 where they previously did not.

**Migration Impact**

Apps that relied on the old, uncompensated behavior should pass
``rotation_invariant=False`` to keep it.

Please ping me when it's done.

@ElliotGarbus

Copy link
Copy Markdown
Contributor

The other component sections (AliasProperty, Mouse Input Provider, SVG, ...) use an overline and an underline, so please use the same style.

The "Application Storage Directories" heading just above is missing its overline, which is why it currently renders as a top-level section next to the page title. Please add it in this PR too, otherwise docutils reports "Title level inconsistent"

for the new heading:

================================
Application Storage Directories
================================

Thanks!

Drop the stray trailing fence that swallowed the next paragraph, add the
missing overline to "Application Storage Directories", and place the
rotation-invariance note under its own overline/underlined Gestures
heading with Behavior Changes and Migration Impact subsections.

Signed-off-by: Charan Rathore <[email protected]>
@charan-rathore

Copy link
Copy Markdown
Contributor Author

Thanks @ElliotGarbus - both fixed in 644bf98:

  • dropped the stray trailing fence and gave the note its own Gestures section with the overline+underline style and the Behavior Changes / Migration Impact subsections from your suggestion
  • added the missing overline to Application Storage Directories

Local docutils pass no longer reports the inline-literal warning, and heading levels are consistent now.

1 similar comment
@charan-rathore

Copy link
Copy Markdown
Contributor Author

Thanks @ElliotGarbus - both fixed in 644bf98:

  • dropped the stray trailing fence and gave the note its own Gestures section with the overline+underline style and the Behavior Changes / Migration Impact subsections from your suggestion
  • added the missing overline to Application Storage Directories

Local docutils pass no longer reports the inline-literal warning, and heading levels are consistent now.

@ElliotGarbus

Copy link
Copy Markdown
Contributor

@kuzeyron This is ready to merge.

@kuzeyron
kuzeyron merged commit 3da25f2 into kivy:master Oct 18, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants