15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "ui/events/gestures/fling_curve.h"
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include <algorithm>
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include <cmath>
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/logging.h"
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace {
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const float kDefaultAlpha = -5.70762e+03f;
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const float kDefaultBeta = 1.72e+02f;
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const float kDefaultGamma = 3.7e+00f;
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)inline double GetPositionAtTime(double t) {
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return kDefaultAlpha * exp(-kDefaultGamma * t) - kDefaultBeta * t -
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         kDefaultAlpha;
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)inline double GetVelocityAtTime(double t) {
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return -kDefaultAlpha * kDefaultGamma * exp(-kDefaultGamma * t) -
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         kDefaultBeta;
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)inline double GetTimeAtVelocity(double v) {
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return -log((v + kDefaultBeta) / (-kDefaultAlpha * kDefaultGamma)) /
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         kDefaultGamma;
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace ui {
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)FlingCurve::FlingCurve(const gfx::Vector2dF& velocity,
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       base::TimeTicks start_timestamp)
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    : curve_duration_(GetTimeAtVelocity(0)),
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      start_timestamp_(start_timestamp),
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      time_offset_(0),
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      position_offset_(0) {
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  float max_start_velocity = std::max(fabs(velocity.x()), fabs(velocity.y()));
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (max_start_velocity > GetVelocityAtTime(0))
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    max_start_velocity = GetVelocityAtTime(0);
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  CHECK_GT(max_start_velocity, 0);
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  displacement_ratio_ = gfx::Vector2dF(velocity.x() / max_start_velocity,
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       velocity.y() / max_start_velocity);
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  time_offset_ = GetTimeAtVelocity(max_start_velocity);
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  position_offset_ = GetPositionAtTime(time_offset_);
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  last_timestamp_ = start_timestamp_ + base::TimeDelta::FromSecondsD(
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                           curve_duration_ - time_offset_);
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)FlingCurve::~FlingCurve() {
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)gfx::Vector2dF FlingCurve::GetScrollAmountAtTime(base::TimeTicks current) {
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (current < start_timestamp_)
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return gfx::Vector2dF();
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  float displacement = 0;
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (current < last_timestamp_) {
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    float time = time_offset_ + (current - start_timestamp_).InSecondsF();
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    CHECK_LT(time, curve_duration_);
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    displacement = GetPositionAtTime(time) - position_offset_;
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  } else {
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    displacement = GetPositionAtTime(curve_duration_) - position_offset_;
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  gfx::Vector2dF scroll(displacement * displacement_ratio_.x(),
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                        displacement * displacement_ratio_.y());
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  gfx::Vector2dF scroll_increment(scroll.x() - cumulative_scroll_.x(),
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                  scroll.y() - cumulative_scroll_.y());
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  cumulative_scroll_ = scroll;
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return scroll_increment;
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace ui
81