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 "gfxPrefs.h"
9 
10 using namespace mozilla;
11 
ScrollAnimationMSDPhysics(const nsPoint & aStartPos)12 ScrollAnimationMSDPhysics::ScrollAnimationMSDPhysics(const nsPoint& aStartPos)
13     : mStartPos(aStartPos),
14       mModelX(0, 0, 0, gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant(),
15               1),
16       mModelY(0, 0, 0, gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant(),
17               1),
18       mIsFirstIteration(true) {}
19 
Update(const TimeStamp & aTime,const nsPoint & aDestination,const nsSize & aCurrentVelocity)20 void ScrollAnimationMSDPhysics::Update(const TimeStamp& aTime,
21                                        const nsPoint& aDestination,
22                                        const nsSize& aCurrentVelocity) {
23   double springConstant = ComputeSpringConstant(aTime);
24 
25   // mLastSimulatedTime is the most recent time that this animation has been
26   // "observed" at. We don't want to update back to a state in the past, so we
27   // set mStartTime to the more recent of mLastSimulatedTime and aTime.
28   // aTime can be in the past if we're processing an input event whose internal
29   // timestamp is in the past.
30   if (mLastSimulatedTime && aTime < mLastSimulatedTime) {
31     mStartTime = mLastSimulatedTime;
32   } else {
33     mStartTime = aTime;
34   }
35 
36   if (!mIsFirstIteration) {
37     mStartPos = PositionAt(mStartTime);
38   }
39 
40   mLastSimulatedTime = mStartTime;
41   mDestination = aDestination;
42   mModelX = AxisPhysicsMSDModel(mStartPos.x, aDestination.x,
43                                 aCurrentVelocity.width, springConstant, 1);
44   mModelY = AxisPhysicsMSDModel(mStartPos.y, aDestination.y,
45                                 aCurrentVelocity.height, springConstant, 1);
46   mIsFirstIteration = false;
47 }
48 
ComputeSpringConstant(const TimeStamp & aTime)49 double ScrollAnimationMSDPhysics::ComputeSpringConstant(
50     const TimeStamp& aTime) {
51   if (!mPreviousEventTime) {
52     mPreviousEventTime = aTime;
53     mPreviousDelta = TimeDuration();
54     return gfxPrefs::SmoothScrollMSDPhysicsMotionBeginSpringConstant();
55   }
56 
57   TimeDuration delta = aTime - mPreviousEventTime;
58   TimeDuration previousDelta = mPreviousDelta;
59 
60   mPreviousEventTime = aTime;
61   mPreviousDelta = delta;
62 
63   double deltaMS = delta.ToMilliseconds();
64   if (deltaMS >= gfxPrefs::SmoothScrollMSDPhysicsContinuousMotionMaxDeltaMS()) {
65     return gfxPrefs::SmoothScrollMSDPhysicsMotionBeginSpringConstant();
66   }
67 
68   if (previousDelta &&
69       deltaMS >= gfxPrefs::SmoothScrollMSDPhysicsSlowdownMinDeltaMS() &&
70       deltaMS >= previousDelta.ToMilliseconds() *
71                      gfxPrefs::SmoothScrollMSDPhysicsSlowdownMinDeltaRatio()) {
72     // The rate of events has slowed (the time delta between events has
73     // increased) enough that we think that the current scroll motion is coming
74     // to a stop. Use a stiffer spring in order to reach the destination more
75     // quickly.
76     return gfxPrefs::SmoothScrollMSDPhysicsSlowdownSpringConstant();
77   }
78 
79   return gfxPrefs::SmoothScrollMSDPhysicsRegularSpringConstant();
80 }
81 
SimulateUntil(const TimeStamp & aTime)82 void ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp& aTime) {
83   if (!mLastSimulatedTime || aTime < mLastSimulatedTime) {
84     return;
85   }
86   TimeDuration delta = aTime - mLastSimulatedTime;
87   mModelX.Simulate(delta);
88   mModelY.Simulate(delta);
89   mLastSimulatedTime = aTime;
90 }
91 
PositionAt(const TimeStamp & aTime)92 nsPoint ScrollAnimationMSDPhysics::PositionAt(const TimeStamp& aTime) {
93   SimulateUntil(aTime);
94   return nsPoint(NSToCoordRound(mModelX.GetPosition()),
95                  NSToCoordRound(mModelY.GetPosition()));
96 }
97 
VelocityAt(const TimeStamp & aTime)98 nsSize ScrollAnimationMSDPhysics::VelocityAt(const TimeStamp& aTime) {
99   SimulateUntil(aTime);
100   return nsSize(NSToCoordRound(mModelX.GetVelocity()),
101                 NSToCoordRound(mModelY.GetVelocity()));
102 }
103