| // Copyright 2018 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/blink/prediction/least_squares_predictor.h" |
| #include "ui/events/blink/prediction/predictor_factory.h" |
| |
| #include <algorithm> |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Solve XB = y. |
| static bool SolveLeastSquares(const gfx::Matrix3F& x, |
| const std::deque<double>& y, |
| gfx::Vector3dF& result) { |
| constexpr double kEpsilon = std::numeric_limits<double>::epsilon(); |
| |
| // return last point if y didn't change. |
| if (std::abs(y[0] - y[1]) < kEpsilon && std::abs(y[1] - y[2]) < kEpsilon) { |
| result = gfx::Vector3dF(y[2], 0, 0); |
| return true; |
| } |
| |
| gfx::Matrix3F x_transpose = x.Transpose(); |
| gfx::Matrix3F temp = gfx::MatrixProduct(x_transpose, x).Inverse(); |
| |
| // Return false if x is singular. |
| if (temp == gfx::Matrix3F::Zeros()) |
| return false; |
| |
| result = gfx::MatrixProduct(gfx::MatrixProduct(temp, x_transpose), |
| gfx::Vector3dF(y[0], y[1], y[2])); |
| return true; |
| } |
| |
| } // namespace |
| |
| LeastSquaresPredictor::LeastSquaresPredictor() {} |
| |
| LeastSquaresPredictor::~LeastSquaresPredictor() {} |
| |
| const char* LeastSquaresPredictor::GetName() const { |
| return input_prediction::kScrollPredictorNameLsq; |
| } |
| |
| void LeastSquaresPredictor::Reset() { |
| x_queue_.clear(); |
| y_queue_.clear(); |
| time_.clear(); |
| } |
| |
| void LeastSquaresPredictor::Update(const InputData& cur_input) { |
| if (!time_.empty()) { |
| // When last point is kMaxTimeDelta away, consider it is incontinuous. |
| if (cur_input.time_stamp - time_.back() > kMaxTimeDelta) |
| Reset(); |
| } |
| |
| x_queue_.push_back(cur_input.pos.x()); |
| y_queue_.push_back(cur_input.pos.y()); |
| time_.push_back(cur_input.time_stamp); |
| if (time_.size() > kSize) { |
| x_queue_.pop_front(); |
| y_queue_.pop_front(); |
| time_.pop_front(); |
| } |
| } |
| |
| bool LeastSquaresPredictor::HasPrediction() const { |
| return time_.size() >= kSize; |
| } |
| |
| gfx::Matrix3F LeastSquaresPredictor::GetXMatrix() const { |
| gfx::Matrix3F x = gfx::Matrix3F::Zeros(); |
| double t1 = (time_[1] - time_[0]).InMillisecondsF(); |
| double t2 = (time_[2] - time_[0]).InMillisecondsF(); |
| x.set(1, 0, 0, 1, t1, t1 * t1, 1, t2, t2 * t2); |
| return x; |
| } |
| |
| std::unique_ptr<InputPredictor::InputData> |
| LeastSquaresPredictor::GeneratePrediction(base::TimeTicks predict_time) const { |
| if (!HasPrediction()) |
| return nullptr; |
| |
| float pred_dt = (predict_time - time_[0]).InMillisecondsF(); |
| |
| gfx::Vector3dF b1, b2; |
| gfx::Matrix3F time_matrix = GetXMatrix(); |
| if (SolveLeastSquares(time_matrix, x_queue_, b1) && |
| SolveLeastSquares(time_matrix, y_queue_, b2)) { |
| gfx::Vector3dF prediction_time(1, pred_dt, pred_dt * pred_dt); |
| |
| return std::make_unique<InputData>( |
| gfx::PointF(gfx::DotProduct(prediction_time, b1), |
| gfx::DotProduct(prediction_time, b2)), |
| predict_time); |
| } |
| return nullptr; |
| } |
| |
| base::TimeDelta LeastSquaresPredictor::TimeInterval() const { |
| if (time_.size() > 1) { |
| return std::max(kMinTimeInterval, |
| (time_.back() - time_.front()) / (time_.size() - 1)); |
| } |
| return kTimeInterval; |
| } |
| |
| } // namespace ui |