| // Copyright 2019 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/base/prediction/linear_predictor.h" |
| |
| #include <algorithm> |
| |
| #include "ui/base/ui_base_features.h" |
| |
| namespace ui { |
| |
| LinearPredictor::LinearPredictor(EquationOrder order) { |
| equation_order_ = order; |
| } |
| |
| LinearPredictor::~LinearPredictor() {} |
| |
| const char* LinearPredictor::GetName() const { |
| return equation_order_ == EquationOrder::kFirstOrder |
| ? features::kPredictorNameLinearFirst |
| : features::kPredictorNameLinearSecond; |
| } |
| |
| void LinearPredictor::Reset() { |
| events_queue_.clear(); |
| } |
| |
| size_t LinearPredictor::NumberOfEventsNeeded() { |
| return static_cast<size_t>(equation_order_); |
| } |
| |
| void LinearPredictor::Update(const InputData& new_input) { |
| // The last input received is at least kMaxDeltaTime away, we consider it |
| // is a new trajectory |
| if (!events_queue_.empty() && |
| new_input.time_stamp - events_queue_.back().time_stamp > kMaxTimeDelta) { |
| Reset(); |
| } |
| |
| // Queue the new event and keep only the number of last events needed |
| events_queue_.push_back(new_input); |
| if (events_queue_.size() > static_cast<size_t>(equation_order_)) { |
| events_queue_.pop_front(); |
| } |
| |
| // Compute the current velocity |
| if (events_queue_.size() >= static_cast<size_t>(EquationOrder::kFirstOrder)) { |
| // Even if cur_velocity is empty the first time, last_velocity is only |
| // used when 3 events are in the queue, so it will be initialized |
| last_velocity_.set_x(cur_velocity_.x()); |
| last_velocity_.set_y(cur_velocity_.y()); |
| |
| // We have at least 2 events to compute the current velocity |
| // Get delta time between the last 2 events |
| // Note: this delta time is also used to compute the acceleration term |
| events_dt_ = (events_queue_.at(events_queue_.size() - 1).time_stamp - |
| events_queue_.at(events_queue_.size() - 2).time_stamp) |
| .InMillisecondsF(); |
| |
| // Get delta pos between the last 2 events |
| gfx::Vector2dF delta_pos = events_queue_.at(events_queue_.size() - 1).pos - |
| events_queue_.at(events_queue_.size() - 2).pos; |
| |
| // Get the velocity |
| if (events_dt_ > 0) { |
| cur_velocity_.set_x(ScaleVector2d(delta_pos, 1.0 / events_dt_).x()); |
| cur_velocity_.set_y(ScaleVector2d(delta_pos, 1.0 / events_dt_).y()); |
| } else { |
| cur_velocity_.set_x(0); |
| cur_velocity_.set_y(0); |
| } |
| } |
| } |
| |
| bool LinearPredictor::HasPrediction() const { |
| // Even if the current equation is second order, we still can predict a |
| // first order |
| return events_queue_.size() >= |
| static_cast<size_t>(EquationOrder::kFirstOrder); |
| } |
| |
| std::unique_ptr<InputPredictor::InputData> LinearPredictor::GeneratePrediction( |
| base::TimeTicks predict_time) const { |
| if (!HasPrediction()) |
| return nullptr; |
| |
| float pred_dt = |
| (predict_time - events_queue_.back().time_stamp).InMillisecondsF(); |
| |
| // Compute the prediction |
| // Note : a first order prediction is computed when only 2 events are |
| // available in the second order predictor |
| if (equation_order_ == EquationOrder::kSecondOrder && events_dt_ > 0 && |
| events_queue_.size() == |
| static_cast<size_t>(EquationOrder::kSecondOrder)) { |
| // Add the acceleration term to the current result |
| return std::make_unique<InputData>(GeneratePredictionSecondOrder(pred_dt), |
| predict_time); |
| } |
| return std::make_unique<InputData>(GeneratePredictionFirstOrder(pred_dt), |
| predict_time); |
| } |
| |
| gfx::PointF LinearPredictor::GeneratePredictionFirstOrder(float pred_dt) const { |
| return events_queue_.back().pos + ScaleVector2d(cur_velocity_, pred_dt); |
| } |
| |
| gfx::PointF LinearPredictor::GeneratePredictionSecondOrder( |
| float pred_dt) const { |
| DCHECK(equation_order_ == EquationOrder::kSecondOrder); |
| |
| gfx::Vector2dF acceleration = |
| ScaleVector2d(cur_velocity_ - last_velocity_, 1.0 / events_dt_); |
| return events_queue_.back().pos + ScaleVector2d(cur_velocity_, pred_dt) + |
| ScaleVector2d(acceleration, 0.5 * pred_dt * pred_dt); |
| } |
| |
| base::TimeDelta LinearPredictor::TimeInterval() const { |
| if (events_queue_.size() > 1) { |
| return std::max(kMinTimeInterval, (events_queue_.back().time_stamp - |
| events_queue_.front().time_stamp) / |
| (events_queue_.size() - 1)); |
| } |
| return kTimeInterval; |
| } |
| |
| } // namespace ui |