Skip to content

Avoid sqrt in gesture distance checks - #187895

Open
bernaferrari wants to merge 5 commits into
flutter:masterfrom
bernaferrari:gesture-distance-squared-cleanups
Open

bernaferrari wants to merge 5 commits into
flutter:masterfrom
bernaferrari:gesture-distance-squared-cleanups

Conversation

@bernaferrari

@bernaferrari bernaferrari commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Fix #187894
This started from my curiosity on #187891

Avoids computing square roots for several gesture and geometry distance threshold checks by comparing squared distances against squared thresholds instead. This keeps the same behavior while avoiding unnecessary sqrt work in hot paths.

sqrt(dx² + dy²) <= r is equivalent to: dx² + dy² <= r²

ChatGPT Image Jun 13, 2026, 02_11_38 AM

This updates distance checks in:

  • multi-tap gesture slop handling
  • tap-and-drag double-tap slop handling
  • multi-drag pending delta hit slop checks
  • InteractiveViewer nearest-point and fling threshold logic

I added a micro-benchmark that shows code 9.1% faster (but depending on the bench and how much you isolate x*x vs sqrt it could be up to 73%).

Let me know what you think.

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 11, 2026
@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. f: gestures flutter/packages/flutter/gestures repository. labels Jun 11, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request replaces distance calculations with squared distance comparisons across several gesture recognizers and the InteractiveViewer widget to avoid expensive square root operations, and adds a microbenchmark to evaluate this optimization. The review feedback suggests adding assertions to verify that tolerances and hit slops are non-negative before squaring them, and adding documentation comments to the public functions in the new benchmark file.

@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from 130bb20 to 79d7840 Compare June 11, 2026 23:05
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 11, 2026
@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from 79d7840 to 67e4c49 Compare June 11, 2026 23:15
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 11, 2026
@Renzo-Olivares
Renzo-Olivares self-requested a review June 11, 2026 23:24
@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from 67e4c49 to 1ac0463 Compare June 12, 2026 00:40
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 12, 2026
@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from 1ac0463 to dc7efef Compare June 12, 2026 05:54
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 12, 2026
@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from dc7efef to 55e9df2 Compare June 15, 2026 23:09
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 15, 2026

@Renzo-Olivares Renzo-Olivares left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@bernaferrari Thanks for the contribution and your patience on this one! I left a comment that applies to all the changes here. My main concern is readability which you pointed out in the original issue, but maybe we can improve on it.

if (pendingDelta!.distance > computeHitSlop(kind, gestureSettings)) {
final double hitSlop = computeHitSlop(kind, gestureSettings);
assert(hitSlop >= 0.0);
if (pendingDelta!.distanceSquared > hitSlop * hitSlop) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is tricky because on one hand it is mathematically correct, but while reading this code it took me an extra step or two to figure out what was happening here. I think it would be more difficult to reason about without the git diff. I wonder if we can somehow still get the gains of avoiding sqrt but keep the code readable. Possible example below. A helper for each of these cases doesn't seem ideal though. Let me know what you think.

@override

void checkForResolutionAfterMove() {
  assert(pendingDelta != null); 
  final double hitSlop = computeHitSlop(kind, gestureSettings); 
  assert(hitSlop >= 0.0); 
  if (pendingDelta!.distanceSquared > hitSlop * hitSlop) { 
    resolve(GestureDisposition.accepted); 
  }
}
bool deltaExceedsThreshold(Offset delta, double threshold) {
  assert(threshold >= 0.0);
  return distanceSquared > (threshold * threshold);
}

@override
void checkForResolutionAfterMove() {
  assert(pendingDelta != null);
  final double hitSlop = computeHitSlop(kind, gestureSettings);

  if (deltaExceedsThreshold(pendingDelta!, hitSlop)) { 
    resolve(GestureDisposition.accepted);
  }
}

@bernaferrari

bernaferrari commented Jul 7, 2026 •

Copy link
Copy Markdown
Contributor Author

We could have an extension for Offset (or even modify Offset in Dart(?)) but this optimization happens in like ~6 times (maybe more, I'm not accounting for material since it is frozen) and the BoxDecoration optimization would be a different case (but maybe I can just comment). Any thoughts? Do you want me to draft it?

I'll also try your idea.

@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from 55e9df2 to 9d76848 Compare July 9, 2026 04:57
@bernaferrari

bernaferrari commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Check if you like the idea of the extension to offset (we could also upstream the change to Dart if it makes sense). I just pushed. But I'm open to different ideas.

@justinmc
justinmc requested a review from Renzo-Olivares July 14, 2026 22:23
@Renzo-Olivares

Copy link
Copy Markdown
Contributor

Apologies for the delay on this one @bernaferrari, will take a look this week.

@Renzo-Olivares Renzo-Olivares left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for your patience @bernaferrari, I left a review!

/// Methods for comparing the magnitude of an [Offset], such as a pointer
/// delta or velocity, against a threshold, such as a touch slop or a minimum
/// fling velocity.
extension OffsetDistanceComparison on Offset {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not the biggest fan of using an extension for this since it gets exposed to all IDE users that import the gestures library. I think since we already use this pattern in the framework as pointed out in #187891, i'm okay with this PR in its original state prior to adding this extension method.

…nce checks

Replaces the raw 'distanceSquared > threshold * threshold' comparisons
with a small named extension on Offset in events.dart, so call sites
read like the original code while still avoiding the square root.
ForcePressGestureRecognizer compared event.delta.distanceSquared
against an unsquared hit slop, so the effective move tolerance was
sqrt(18) = ~4.2 logical pixels instead of the intended 18. Present
since the original implementation in flutter#24554.
Tree_analyze failed because the PUBSPEC CHECKSUM was still the old
value after declaring a direct vector_math dependency.
@bernaferrari
bernaferrari force-pushed the gesture-distance-squared-cleanups branch from f3bb30f to 79dcae8 Compare August 26, 2026 02:33
@bernaferrari

Copy link
Copy Markdown
Contributor Author

Check if you like now, naming is a bit tricky (offsetExceedsThreshold for now) and I used distance.dart file for the function, not sure if there is a better place.

@bernaferrari

Copy link
Copy Markdown
Contributor Author

cc @Renzo-Olivares

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flutter could use less sqrt(dx² + dy²) <= r and more dx² + dy² <= r²

2 participants