Skip to content

FrictionSimulation decays faster than physics, and isn't ballistic #120340

Open

Description

@gnprice

The simulation in FrictionSimulation doesn't correctly model the physics it's described as modeling, when constantDeceleration is nonzero. This is the simulation used for fling-scrolling in BouncingScrollPhysics / BouncingScrollSimulation, which is used by default on macOS.

(On iOS we also use BouncingScrollSimulation by default, but with a zero constantDeceleration, which doesn't trigger this issue.)

This has two consequences:

  • I suspect that the actual macOS behavior this simulation is intended to match does correctly model the physics. If so, then this is a fidelity issue.

  • The physically-incorrect behavior is not ballistic/restartable. (For context on that invariant, see Scroll protocol assumes ballistic scroll physics, and ClampingScrollPhysics isn't ballistic #120338.) The excess deceleration compared to the real physics starts at zero and grows over time, so restarting the simulation makes it decelerate less, and go farther in total.

    • This is the inverse of the problem seen with ClampingScrollSimulation / on Android and other non-Apple platforms, in that other issue.

    When the simulation is restarted very frequently, its behavior will closely approximate the real physics. If my suspicion above is correct, then this means frequent restarts actually give this scroll physics nearly the desired behavior, and it's when it doesn't get restarted that it behaves incorrectly.

Steps to Reproduce

I haven't yet tested this out empirically or compared to the native macOS scroll behavior, because I wanted to get this issue written down and posted for comparison with the related issues.

Here's why I say it doesn't correctly model the physics it's described as modeling:

  • This class models "a particle affected by fluid drag." If the particle's current velocity is $v$, then one way that could behave is that its acceleration is $-kv$, where $k$ is a drag coefficient.
    • (I think it's typically much steeper than that with real fluids, growing much faster than linearly in the velocity, like $- k v^4$. But let's leave that aside.)
  • With constantDeceleration: 0, this is indeed what FrictionSimulation does:
    • dx(time) is _v * math.pow(_drag, time), or $x'(t) = v_0 e^{-kt}$, where $v_0$ is the initial velocity.
    • Taking the derivative, we find $x''(t) = -k v_0 e^{-kt}$.
    • These satisfy $x''(t) = - k x'(t)$.
  • With a positive constantDeceleration… well, this parameter isn't really documented. But presumably it should do what its name says: it adds a constant amount of deceleration.
  • This means we should have $x''(t) = -k x'(t) - c$, where $c$ is constantDeceleration.
  • But the actual deceleration is greater than that (meaning $x''(t)$ is less than that):
    • Now dx(time) is _v * math.pow(_drag, time) - _constantDeceleration * time, or $x'(t) = v_0 e^{-kt} - c t$.

    • So $x''(t) = - k v_0 e^{-kt} - c$.

    • But we were supposed to have an acceleration of $-k x'(t) - c$, which, calling it $x''_*$, would be

      $x''_*(t) = - k v_0 e^{-kt} + k c t - c$.

    • So the deceleration is too big, by $x''_*(t) - x''(t) = k c t$, i.e. _constantDeceleration * (-_dragLog) * time.


The crux of the bug is that we've made it so that the effect of constantDeceleration is to make the deceleration at time t be a constant amount more than it would otherwise have been at time t.

But the physics doesn't care how long the simulation has been running: from a physical perspective, adding a constant deceleration means the deceleration at time t should be a constant amount more than it would otherwise have been at velocity dx(t). (And position x(t), but this simulation isn't affected by position.)

Because over time the velocity and deceleration both decrease, this means that at time t we're starting from the deceleration that a simulation with zero constantDeceleration would have had at time t… when that simulation would have been going faster than this one is at time t, and therefore decelerating harder than it will when it later gets down to this one's current speed… which means that we take too large a deceleration as the baseline, and add constantDeceleration to that.

Activity

  1. darshankawar commented on Feb 9, 2023

    @darshankawar
    Member

    Thanks for the report @gnprice
    Can you please take a look at this issue and see if it resembles your case ?
    If not, can you provide minimal code sample that helps to show current behavior you are reporting ?

  2. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 9, 2023
  3. gnprice commented on Feb 9, 2023

    @gnprice
    MemberAuthor

    Thanks. That is an interesting issue report, but I think it does not involve this issue. That one is about scrolling on iOS:

    On iOS, the default Flutter scrolling uses FrictionSimulation but with a constantDeceleration of zero. This issue only applies when constantDeceleration is nonzero. The one place the framework uses that is in the default scrolling for macOS.

    If not, can you provide minimal code sample that helps to show current behavior you are reporting ?

    That would be a useful step. I haven't done it yet, but I'm working on related scrolling issues, and I may come back to this one later and do so if nobody does it first.

    I discovered this issue from reading the code. As part of investigating #120338, I went and audited all the simulations the framework uses for scroll physics to try to confirm that each one was consistent with the scroll protocol's invariant #120341 which requires the scroll physics to make at least a certain kind of sense physically. One consequence of this bug is that this physics (Flutter's default macOS scroll physics) does not satisfy that invariant.

    So the way to reproduce this issue would be with steps similar to #120338, comparing scroll_overlay with and without itemCount: 1000, but with the scroll physics Flutter defaults to on macOS.

    That can be done by either of:

    • Running it on a Mac, using a touchpad to scroll and fling. (Looks like this will require adding macOS support to the scroll_overlay app, using flutter create.)
    • Running it on any touchscreen device, but adding to the ListView constructor call in lib/main.dart a line like physics: BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast, parent: RangeMaintainingScrollPhysics()). (Which I've copied from the framework's ScrollBehavior._bouncingDesktopPhysics.)

    Either approach should exercise this issue, but the first has the added usefulness that it would allow comparison to the native macOS behavior.


    Anyway, that isn't a full repro recipe, just some notes for how to produce one. If someone would like to pick those up and investigate, that'd be great. If not, I may do so later.

    I filed this issue because I'd found it in that audit related to #120338, and didn't want to leave it a mystery what bugs I had in mind when I said that everything else in the framework satisfies the invariant "modulo a few small bugs".

  4. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 9, 2023
  5. darshankawar commented on Feb 10, 2023

    @darshankawar
    Member

    Thanks for the update @gnprice
    I added macOS support to scroll_overlay project and ran it on macos desktop, but getting below error in console although the app compiles and runs properly, but I guess due to the error, the platform velocity value is not getting triggered. I tried with and without itemCount property.

    ══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
    The following MissingPluginException was thrown while activating platform stream on channel
    scroll_overlay.flutter.io/velocity:
    MissingPluginException(No implementation found for method listen on channel
    scroll_overlay.flutter.io/velocity)
    
    When the exception was thrown, this was the stack:
    #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:308:7)
    <asynchronous suspension>
    #1      EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:655:9)
    <asynchronous suspension>
    ════════════════════════════════════════════════════════════════════════════════════════════════════
    
    
    
    120340.mov

    Maybe if you could help to fix the platform error, we can properly replicate this and make it actionable for scrolling team to take a look further.

  6. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 10, 2023
  7. gnprice commented on Feb 10, 2023

    @gnprice
    MemberAuthor

    Excellent, thanks for trying that!

    I realize now that I wrote this part confusingly:

    • (Looks like this will require adding macOS support to the scroll_overlay app, using flutter create.)

    It looks like you got the app running on macOS, with the Flutter-driven list working. So the next step is to try a fling with a non-null itemCount present, and with it absent, and compare how far the fling goes.

    That should exercise this bug:

    • The expected behavior is that a fling goes the same distance with or without itemCount.
    • My prediction from the code is that the actual behavior will be that a fling with itemCount (so that the scroll simulation gets frequently restarted) goes further than a fling without itemCount (where it doesn't get restarted).

    This part shouldn't be affected by the error message you saw.


    Then the other question for experiment would be to compare with the native macOS scroll physics. This bug is just as much a bug regardless of how the native physics compares, but that comparison would help decide what the right fix is.

    That part will be more work, because it requires getting a native macOS list to compare with. The error message you saw is because the Dart code is expecting to talk to some platform code that's (a) showing a list in the background, (b) making that list respond in parallel to the same swipe gestures that Flutter gets, and (c) reporting its list's velocity to be displayed under "Platform velocity". The scroll_overlay app contains versions of that platform code for Android and iOS, but not other platforms.

    This probably isn't a ton of code — it looks like the iOS implementation is a little over a hundred lines, mostly in ios/Runner/OverlayScrollView.m and ios/Runner/PassiveGestureRecognizer.m. And it's possible that most of that can be reused for macOS, though I don't really know anything about macOS programming so that's a guess at best. But it will take some writing new code, and it would take me some time to figure out exactly how to write that code because I've never used the macOS SDK before.

  8. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Feb 10, 2023
  9. darshankawar commented on Feb 10, 2023

    @darshankawar
    Member
    1. Without itemCount or itemCount: null:
    120340.mov
    1. With itemCount: 1000:
    120340_1.mov
    stable, master flutter doctor -v
    [!] Flutter (Channel stable, 3.7.0, on macOS 12.2.1 21D62 darwin-x64, locale
        en-GB)
        • Flutter version 3.7.0 on channel stable at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision b06b8b2710 (28 hours ago), 2023-01-23 16:55:55 -0800
        • Engine revision b24591ed32
        • Dart version 2.19.0
        • DevTools version 2.20.1
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
    
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    [!] Flutter (Channel master, 3.8.0-9.0.pre.20, on macOS 12.2.1 21D62 darwin-x64,
        locale en-GB)
        • Flutter version 3.8.0-9.0.pre.20 on channel master at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 001c4951b6 (4 hours ago), 2023-02-09 20:14:10 -0500
        • Engine revision e1d0032029
        • Dart version 3.0.0 (build 3.0.0-218.0.dev)
        • DevTools version 2.21.1
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
          
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    
    
    
  10. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    f: scrollingViewports, list views, slivers, etc.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    on Feb 10, 2023
  11. 2 remaining items

  12. added
    a: fidelityMatching the OEM platforms better
    and removed
    in triagePresently being triaged by the triage team
    on Feb 10, 2023
  13. added
    P2Important issues not at the top of the work list
    on Feb 16, 2023
  14. added
    team-macosOwned by the macOS platform team
    and removed on Jun 5, 2024
  15. flutter-triage-bot commented on Jun 5, 2024

    @flutter-triage-bot

    The triaged-desktop label is irrelevant if there is no team-desktop label or fyi-desktop label.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: desktopRunning on desktopa: fidelityMatching the OEM platforms betterf: scrollingViewports, list views, slivers, etc.found in release: 3.7Found to occur in 3.7found in release: 3.8Found to occur in 3.8frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-macosBuilding on or for macOS specificallyteam-macosOwned by the macOS platform teamtriaged-macosTriaged by the macOS platform team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions