blob: 55a643040604c2a5450595a6ffe010ff58e73d96 [file] [log] [blame]
// Copyright 2017 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 "remoting/client/ui/fling_tracker.h"
#include <cmath>
namespace remoting {
namespace {
const float kSecToMs = 0.001f;
// TODO(yuweih): May need to tweak these numbers to get better smoothness.
// Stop flinging if the speed drops below this. This is a small speed to make
// sure the animation stops smoothly.
const float kMinTrackSpeed = 0.01f;
// The minimum displacement needed to trigger the fling animation. This is to
// prevent unintentional fling with low velocity.
const float kMinDisplacement = 20.f;
float GetDisplacement(float time_constant,
float initial_speed_rate,
float time_elapsed) {
// x = T * v0 * (1 - e^(-t / T))
// This comes from a solution to the linear drag equation F=-kv
float exp_factor = -std::expm1(-time_elapsed / time_constant);
return time_constant * initial_speed_rate * exp_factor;
}
// Returns the time needed for the object to get to the stable state where the
// speed drops below kMinTrackSpeed.
float GetDuration(float time_constant, float initial_speed_rate) {
// t = -T * ln(kMinTrackSpeed / v0)
// Solution of v(t) = kMinTrackSpeed
return -time_constant * std::log(kMinTrackSpeed / initial_speed_rate);
}
} // namespace
FlingTracker::FlingTracker(float time_constant)
: time_constant_(time_constant) {}
FlingTracker::~FlingTracker() = default;
void FlingTracker::StartFling(float velocity_x, float velocity_y) {
// Convert to pixel/ms
velocity_x *= kSecToMs;
velocity_y *= kSecToMs;
initial_speed_rate_ =
std::sqrt(velocity_x * velocity_x + velocity_y * velocity_y);
if (initial_speed_rate_ < kMinTrackSpeed) {
StopFling();
return;
}
fling_duration_ = GetDuration(time_constant_, initial_speed_rate_);
float final_displacement =
GetDisplacement(time_constant_, initial_speed_rate_, fling_duration_);
if (final_displacement < kMinDisplacement) {
StopFling();
return;
}
velocity_ratio_x_ = velocity_x / initial_speed_rate_;
velocity_ratio_y_ = velocity_y / initial_speed_rate_;
previous_position_x_ = 0;
previous_position_y_ = 0;
}
void FlingTracker::StopFling() {
initial_speed_rate_ = 0.f;
}
bool FlingTracker::IsFlingInProgress() const {
return initial_speed_rate_ > 0;
}
bool FlingTracker::TrackMovement(base::TimeDelta time_elapsed,
float* dx,
float* dy) {
if (!IsFlingInProgress()) {
return false;
}
float time_elapsed_ms = time_elapsed.InMilliseconds();
if (time_elapsed_ms > fling_duration_) {
StopFling();
return false;
}
float displacement =
GetDisplacement(time_constant_, initial_speed_rate_, time_elapsed_ms);
float position_x = displacement * velocity_ratio_x_;
float position_y = displacement * velocity_ratio_y_;
*dx = position_x - previous_position_x_;
*dy = position_y - previous_position_y_;
previous_position_x_ = position_x;
previous_position_y_ = position_y;
return true;
}
} // namespace remoting