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/. */
7 #include "ScrollAnimationMSDPhysics.h"
8 #include "mozilla/StaticPrefs_general.h"
10 using namespace mozilla
;
12 ScrollAnimationMSDPhysics::ScrollAnimationMSDPhysics(const nsPoint
& aStartPos
)
13 : mStartPos(aStartPos
),
16 StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
20 StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
22 mIsFirstIteration(true) {}
24 void ScrollAnimationMSDPhysics::Update(const TimeStamp
& aTime
,
25 const nsPoint
& aDestination
,
26 const nsSize
& aCurrentVelocity
) {
27 double springConstant
= ComputeSpringConstant(aTime
);
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
;
40 if (!mIsFirstIteration
) {
41 mStartPos
= PositionAt(mStartTime
);
44 mLastSimulatedTime
= mStartTime
;
45 mDestination
= aDestination
;
46 mModelX
= NonOscillatingAxisPhysicsMSDModel(
47 mStartPos
.x
, aDestination
.x
, aCurrentVelocity
.width
, springConstant
, 1);
48 mModelY
= NonOscillatingAxisPhysicsMSDModel(
49 mStartPos
.y
, aDestination
.y
, aCurrentVelocity
.height
, springConstant
, 1);
50 mIsFirstIteration
= false;
53 void ScrollAnimationMSDPhysics::ApplyContentShift(const CSSPoint
& aShiftDelta
) {
54 nsPoint shiftDelta
= CSSPoint::ToAppUnits(aShiftDelta
);
55 mStartPos
+= shiftDelta
;
56 mDestination
+= shiftDelta
;
57 TimeStamp currentTime
= mLastSimulatedTime
;
58 nsPoint currentPosition
= PositionAt(currentTime
) + shiftDelta
;
59 nsSize currentVelocity
= VelocityAt(currentTime
);
60 double springConstant
= ComputeSpringConstant(currentTime
);
61 mModelX
= NonOscillatingAxisPhysicsMSDModel(currentPosition
.x
, mDestination
.x
,
62 currentVelocity
.width
,
64 mModelY
= NonOscillatingAxisPhysicsMSDModel(currentPosition
.y
, mDestination
.y
,
65 currentVelocity
.height
,
69 double ScrollAnimationMSDPhysics::ComputeSpringConstant(
70 const TimeStamp
& aTime
) {
71 if (!mPreviousEventTime
) {
72 mPreviousEventTime
= aTime
;
73 mPreviousDelta
= TimeDuration();
75 general_smoothScroll_msdPhysics_motionBeginSpringConstant();
78 TimeDuration delta
= aTime
- mPreviousEventTime
;
79 TimeDuration previousDelta
= mPreviousDelta
;
81 mPreviousEventTime
= aTime
;
82 mPreviousDelta
= delta
;
84 double deltaMS
= delta
.ToMilliseconds();
87 general_smoothScroll_msdPhysics_continuousMotionMaxDeltaMS()) {
89 general_smoothScroll_msdPhysics_motionBeginSpringConstant();
94 StaticPrefs::general_smoothScroll_msdPhysics_slowdownMinDeltaMS() &&
96 previousDelta
.ToMilliseconds() *
98 general_smoothScroll_msdPhysics_slowdownMinDeltaRatio()) {
99 // The rate of events has slowed (the time delta between events has
100 // increased) enough that we think that the current scroll motion is coming
101 // to a stop. Use a stiffer spring in order to reach the destination more
104 general_smoothScroll_msdPhysics_slowdownSpringConstant();
107 return StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant();
110 void ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp
& aTime
) {
111 if (!mLastSimulatedTime
|| aTime
< mLastSimulatedTime
) {
114 TimeDuration delta
= aTime
- mLastSimulatedTime
;
115 mModelX
.Simulate(delta
);
116 mModelY
.Simulate(delta
);
117 mLastSimulatedTime
= aTime
;
120 nsPoint
ScrollAnimationMSDPhysics::PositionAt(const TimeStamp
& aTime
) {
121 SimulateUntil(aTime
);
122 return nsPoint(NSToCoordRound(mModelX
.GetPosition()),
123 NSToCoordRound(mModelY
.GetPosition()));
126 nsSize
ScrollAnimationMSDPhysics::VelocityAt(const TimeStamp
& aTime
) {
127 SimulateUntil(aTime
);
128 return nsSize(NSToCoordRound(mModelX
.GetVelocity()),
129 NSToCoordRound(mModelY
.GetVelocity()));
132 static double ClampVelocityToMaximum(double aVelocity
, double aInitialPosition
,
134 double aSpringConstant
) {
135 // Clamp velocity to the maximum value it could obtain if we started at this
136 // position with zero velocity (see bug 1866904 comment 3). With a damping
137 // ratio >= 1.0, this should be low enough to avoid overshooting the
139 double velocityLimit
=
140 sqrt(aSpringConstant
) * abs(aDestination
- aInitialPosition
);
141 return clamped(aVelocity
, -velocityLimit
, velocityLimit
);
144 ScrollAnimationMSDPhysics::NonOscillatingAxisPhysicsMSDModel::
145 NonOscillatingAxisPhysicsMSDModel(double aInitialPosition
,
146 double aInitialDestination
,
147 double aInitialVelocity
,
148 double aSpringConstant
,
149 double aDampingRatio
)
150 : AxisPhysicsMSDModel(
151 aInitialPosition
, aInitialDestination
,
152 ClampVelocityToMaximum(aInitialVelocity
, aInitialPosition
,
153 aInitialDestination
, aSpringConstant
),
154 aSpringConstant
, aDampingRatio
) {
155 MOZ_ASSERT(aDampingRatio
>= 1.0,
156 "Damping ratio must be >= 1.0 to avoid oscillation");