Coverage Report

Created: 2018-09-25 14:53

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