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