1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ScrollAnimationMSDPhysics.h"
8 #include "mozilla/StaticPrefs_general.h"
9 
10 using namespace mozilla;
11 
ScrollAnimationMSDPhysics(const nsPoint & aStartPos)12 ScrollAnimationMSDPhysics::ScrollAnimationMSDPhysics(const nsPoint& aStartPos)
13     : mStartPos(aStartPos),
14       mModelX(
15           0, 0, 0,
16           StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
17           1),
18       mModelY(
19           0, 0, 0,
20           StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
21           1),
22       mIsFirstIteration(true) {}
23 
Update(const TimeStamp & aTime,const nsPoint & aDestination,const nsSize & aCurrentVelocity)24 void ScrollAnimationMSDPhysics::Update(const TimeStamp& aTime,
25                                        const nsPoint& aDestination,
26                                        const nsSize& aCurrentVelocity) {
27   double springConstant = ComputeSpringConstant(aTime);
28 
29   // mLastSimulatedTime is the most recent time that this animation has been
30   // "observed" at. We don't want to update back to a state in the past, so we
31   // set mStartTime to the more recent of mLastSimulatedTime and aTime.
32   // aTime can be in the past if we're processing an input event whose internal
33   // timestamp is in the past.
34   if (mLastSimulatedTime && aTime < mLastSimulatedTime) {
35     mStartTime = mLastSimulatedTime;
36   } else {
37     mStartTime = aTime;
38   }
39 
40   if (!mIsFirstIteration) {
41     mStartPos = PositionAt(mStartTime);
42   }
43 
44   mLastSimulatedTime = mStartTime;
45   mDestination = aDestination;
46   mModelX = AxisPhysicsMSDModel(mStartPos.x, aDestination.x,
47                                 aCurrentVelocity.width, springConstant, 1);
48   mModelY = AxisPhysicsMSDModel(mStartPos.y, aDestination.y,
49                                 aCurrentVelocity.height, springConstant, 1);
50   mIsFirstIteration = false;
51 }
52 
ApplyContentShift(const CSSPoint & aShiftDelta)53 void ScrollAnimationMSDPhysics::ApplyContentShift(const CSSPoint& aShiftDelta) {
54   nsPoint shiftDelta = CSSPoint::ToAppUnits(aShiftDelta);
55   mStartPos += shiftDelta;
56   mDestination += shiftDelta;
57 }
58 
ComputeSpringConstant(const TimeStamp & aTime)59 double ScrollAnimationMSDPhysics::ComputeSpringConstant(
60     const TimeStamp& aTime) {
61   if (!mPreviousEventTime) {
62     mPreviousEventTime = aTime;
63     mPreviousDelta = TimeDuration();
64     return StaticPrefs::
65         general_smoothScroll_msdPhysics_motionBeginSpringConstant();
66   }
67 
68   TimeDuration delta = aTime - mPreviousEventTime;
69   TimeDuration previousDelta = mPreviousDelta;
70 
71   mPreviousEventTime = aTime;
72   mPreviousDelta = delta;
73 
74   double deltaMS = delta.ToMilliseconds();
75   if (deltaMS >=
76       StaticPrefs::
77           general_smoothScroll_msdPhysics_continuousMotionMaxDeltaMS()) {
78     return StaticPrefs::
79         general_smoothScroll_msdPhysics_motionBeginSpringConstant();
80   }
81 
82   if (previousDelta &&
83       deltaMS >=
84           StaticPrefs::general_smoothScroll_msdPhysics_slowdownMinDeltaMS() &&
85       deltaMS >=
86           previousDelta.ToMilliseconds() *
87               StaticPrefs::
88                   general_smoothScroll_msdPhysics_slowdownMinDeltaRatio()) {
89     // The rate of events has slowed (the time delta between events has
90     // increased) enough that we think that the current scroll motion is coming
91     // to a stop. Use a stiffer spring in order to reach the destination more
92     // quickly.
93     return StaticPrefs::
94         general_smoothScroll_msdPhysics_slowdownSpringConstant();
95   }
96 
97   return StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant();
98 }
99 
SimulateUntil(const TimeStamp & aTime)100 void ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp& aTime) {
101   if (!mLastSimulatedTime || aTime < mLastSimulatedTime) {
102     return;
103   }
104   TimeDuration delta = aTime - mLastSimulatedTime;
105   mModelX.Simulate(delta);
106   mModelY.Simulate(delta);
107   mLastSimulatedTime = aTime;
108 }
109 
PositionAt(const TimeStamp & aTime)110 nsPoint ScrollAnimationMSDPhysics::PositionAt(const TimeStamp& aTime) {
111   SimulateUntil(aTime);
112   return nsPoint(NSToCoordRound(mModelX.GetPosition()),
113                  NSToCoordRound(mModelY.GetPosition()));
114 }
115 
VelocityAt(const TimeStamp & aTime)116 nsSize ScrollAnimationMSDPhysics::VelocityAt(const TimeStamp& aTime) {
117   SimulateUntil(aTime);
118   return nsSize(NSToCoordRound(mModelX.GetVelocity()),
119                 NSToCoordRound(mModelY.GetVelocity()));
120 }
121