summaryrefslogtreecommitdiff
path: root/chromium/ui/events/gestures/fling_curve.cc
blob: 00d29a46fd56439cf822beb806376a3c53a7377f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/gestures/fling_curve.h"

#include <algorithm>
#include <cmath>

#include "base/logging.h"

namespace {

const float kDefaultAlpha = -5.70762e+03f;
const float kDefaultBeta = 1.72e+02f;
const float kDefaultGamma = 3.7e+00f;

inline double GetPositionAtTime(double t) {
  return kDefaultAlpha * exp(-kDefaultGamma * t) - kDefaultBeta * t -
         kDefaultAlpha;
}

inline double GetVelocityAtTime(double t) {
  return -kDefaultAlpha * kDefaultGamma * exp(-kDefaultGamma * t) -
         kDefaultBeta;
}

inline double GetTimeAtVelocity(double v) {
  return -log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) /
         kDefaultGamma;
}

}  // namespace

namespace ui {

FlingCurve::FlingCurve(const gfx::Vector2dF& velocity,
                       base::TimeTicks start_timestamp)
    : curve_duration_(GetTimeAtVelocity(0)),
      start_timestamp_(start_timestamp),
      previous_timestamp_(start_timestamp_),
      time_offset_(0),
      position_offset_(0) {
  DCHECK(!velocity.IsZero());
  float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y()));
  if (max_start_velocity > GetVelocityAtTime(0))
    max_start_velocity = GetVelocityAtTime(0);
  CHECK_GT(max_start_velocity, 0);

  displacement_ratio_ = gfx::Vector2dF(velocity.x() / max_start_velocity,
                                       velocity.y() / max_start_velocity);
  time_offset_ = GetTimeAtVelocity(max_start_velocity);
  position_offset_ = GetPositionAtTime(time_offset_);
}

FlingCurve::~FlingCurve() {
}

bool FlingCurve::ComputeScrollOffset(base::TimeTicks time,
                                     gfx::Vector2dF* offset,
                                     gfx::Vector2dF* velocity) {
  DCHECK(offset);
  DCHECK(velocity);
  base::TimeDelta elapsed_time = time - start_timestamp_;
  if (elapsed_time < base::TimeDelta()) {
    *offset = gfx::Vector2dF();
    *velocity = gfx::Vector2dF();
    return true;
  }

  bool still_active = true;
  float scalar_offset;
  float scalar_velocity;
  double offset_time = elapsed_time.InSecondsF() + time_offset_;
  if (offset_time < curve_duration_) {
    scalar_offset = GetPositionAtTime(offset_time) - position_offset_;
    scalar_velocity = GetVelocityAtTime(offset_time);
  } else {
    scalar_offset = GetPositionAtTime(curve_duration_) - position_offset_;
    scalar_velocity = 0;
    still_active = false;
  }

  *offset = gfx::ScaleVector2d(displacement_ratio_, scalar_offset);
  *velocity = gfx::ScaleVector2d(displacement_ratio_, scalar_velocity);
  return still_active;
}

bool FlingCurve::ComputeScrollDeltaAtTime(base::TimeTicks current,
                                          gfx::Vector2dF* delta) {
  DCHECK(delta);
  if (current <= previous_timestamp_) {
    *delta = gfx::Vector2dF();
    return true;
  }

  previous_timestamp_ = current;

  gfx::Vector2dF offset, velocity;
  bool still_active = ComputeScrollOffset(current, &offset, &velocity);

  *delta = offset - cumulative_scroll_;
  cumulative_scroll_ = offset;
  return still_active;
}

}  // namespace ui