Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / generic / ScrollAnimationBezierPhysics.cpp
blobffc69a9fa425d7696b8645457c9b29b66d8caafb
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 "ScrollAnimationBezierPhysics.h"
8 #include "mozilla/StaticPrefs_general.h"
10 using namespace mozilla;
12 ScrollAnimationBezierPhysics::ScrollAnimationBezierPhysics(
13 const nsPoint& aStartPos,
14 const ScrollAnimationBezierPhysicsSettings& aSettings)
15 : mSettings(aSettings), mStartPos(aStartPos), mIsFirstIteration(true) {}
17 void ScrollAnimationBezierPhysics::Update(const TimeStamp& aTime,
18 const nsPoint& aDestination,
19 const nsSize& aCurrentVelocity) {
20 if (mIsFirstIteration) {
21 InitializeHistory(aTime);
24 TimeDuration duration = ComputeDuration(aTime);
25 nsSize currentVelocity = aCurrentVelocity;
27 if (!mIsFirstIteration) {
28 // If an additional event has not changed the destination, then do not let
29 // another minimum duration reset slow things down. If it would then
30 // instead continue with the existing timing function.
31 if (aDestination == mDestination &&
32 aTime + duration > mStartTime + mDuration) {
33 return;
36 currentVelocity = VelocityAt(aTime);
37 mStartPos = PositionAt(aTime);
40 mStartTime = aTime;
41 mDuration = duration;
42 mDestination = aDestination;
43 InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
44 aDestination.x);
45 InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
46 aDestination.y);
47 mIsFirstIteration = false;
50 void ScrollAnimationBezierPhysics::ApplyContentShift(
51 const CSSPoint& aShiftDelta) {
52 nsPoint shiftDelta = CSSPoint::ToAppUnits(aShiftDelta);
53 mStartPos += shiftDelta;
54 mDestination += shiftDelta;
57 TimeDuration ScrollAnimationBezierPhysics::ComputeDuration(
58 const TimeStamp& aTime) {
59 // Average last 3 delta durations (rounding errors up to 2ms are negligible
60 // for us)
61 int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
62 mPrevEventTime[2] = mPrevEventTime[1];
63 mPrevEventTime[1] = mPrevEventTime[0];
64 mPrevEventTime[0] = aTime;
66 // Modulate duration according to events rate (quicker events -> shorter
67 // durations). The desired effect is to use longer duration when scrolling
68 // slowly, such that it's easier to follow, but reduce the duration to make it
69 // feel more snappy when scrolling quickly. To reduce fluctuations of the
70 // duration, we average event intervals using the recent 4 timestamps (now +
71 // three prev -> 3 intervals).
72 int32_t durationMS =
73 clamped<int32_t>(eventsDeltaMs * mSettings.mIntervalRatio,
74 mSettings.mMinMS, mSettings.mMaxMS);
76 return TimeDuration::FromMilliseconds(durationMS);
79 void ScrollAnimationBezierPhysics::InitializeHistory(const TimeStamp& aTime) {
80 // Starting a new scroll (i.e. not when extending an existing scroll
81 // animation), create imaginary prev timestamps with maximum relevant
82 // intervals between them.
84 // Longest relevant interval (which results in maximum duration)
85 TimeDuration maxDelta = TimeDuration::FromMilliseconds(
86 mSettings.mMaxMS / mSettings.mIntervalRatio);
87 mPrevEventTime[0] = aTime - maxDelta;
88 mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
89 mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
92 void ScrollAnimationBezierPhysics::InitTimingFunction(
93 SMILKeySpline& aTimingFunction, nscoord aCurrentPos,
94 nscoord aCurrentVelocity, nscoord aDestination) {
95 if (aDestination == aCurrentPos ||
96 StaticPrefs::general_smoothScroll_currentVelocityWeighting() == 0) {
97 aTimingFunction.Init(
98 0, 0, 1 - StaticPrefs::general_smoothScroll_stopDecelerationWeighting(),
99 1);
100 return;
103 const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
104 double slope =
105 aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
106 double normalization = sqrt(1.0 + slope * slope);
107 double dt = 1.0 / normalization *
108 StaticPrefs::general_smoothScroll_currentVelocityWeighting();
109 double dxy = slope / normalization *
110 StaticPrefs::general_smoothScroll_currentVelocityWeighting();
111 aTimingFunction.Init(
112 dt, dxy,
113 1 - StaticPrefs::general_smoothScroll_stopDecelerationWeighting(), 1);
116 nsPoint ScrollAnimationBezierPhysics::PositionAt(const TimeStamp& aTime) {
117 if (IsFinished(aTime)) {
118 return mDestination;
121 double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
122 double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
123 return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x +
124 progressX * mDestination.x),
125 NSToCoordRound((1 - progressY) * mStartPos.y +
126 progressY * mDestination.y));
129 nsSize ScrollAnimationBezierPhysics::VelocityAt(const TimeStamp& aTime) {
130 if (IsFinished(aTime)) {
131 return nsSize(0, 0);
134 double timeProgress = ProgressAt(aTime);
135 return nsSize(VelocityComponent(timeProgress, mTimingFunctionX, mStartPos.x,
136 mDestination.x),
137 VelocityComponent(timeProgress, mTimingFunctionY, mStartPos.y,
138 mDestination.y));
141 nscoord ScrollAnimationBezierPhysics::VelocityComponent(
142 double aTimeProgress, const SMILKeySpline& aTimingFunction, nscoord aStart,
143 nscoord aDestination) const {
144 double dt, dxy;
145 aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
146 if (dt == 0) return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
148 const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
149 double slope = dxy / dt;
150 return NSToCoordRound(slope * (aDestination - aStart) /
151 (mDuration / oneSecond));