910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/prediction/kalman_predictor.h"

#include <algorithm>
#include <cmath>

#include "base/numerics/angle_conversions.h"
#include "base/time/time.h"
#include "ui/base/ui_base_features.h"

namespace {

// Influence of acceleration during each prediction sample
constexpr float kAccelerationInfluence = 0.5f;
// Influence of velocity during each prediction sample
constexpr float kVelocityInfluence = 1.0f;

}  // namespace

namespace ui {

constexpr base::TimeDelta InputPredictor::kMaxTimeDelta;
constexpr base::TimeDelta InputPredictor::kMaxResampleTime;
constexpr base::TimeDelta InputPredictor::kMaxPredictionTime;
constexpr base::TimeDelta InputPredictor::kTimeInterval;
constexpr base::TimeDelta InputPredictor::kMinTimeInterval;
constexpr base::TimeDelta KalmanPredictor::kMaxTimeInQueue;

KalmanPredictor::KalmanPredictor() = default;
KalmanPredictor::~KalmanPredictor() = default;

const char* KalmanPredictor::GetName() const {
  return features::kPredictorNameKalman;
}

void KalmanPredictor::Reset() {
  x_predictor_.Reset();
  y_predictor_.Reset();
  last_points_.clear();
  time_filter_.Reset();
}

void KalmanPredictor::Update(const InputData& cur_input) {
  base::TimeDelta dt;
  if (last_points_.size()) {
    // When last point is kMaxTimeDelta away, consider it is incontinuous.
    dt = cur_input.time_stamp - last_points_.back().time_stamp;
    if (dt > kMaxTimeDelta)
      Reset();
    else
      time_filter_.Update(dt.InMillisecondsF(), 0);
  }

  double dt_ms = time_filter_.GetPosition();
  last_points_.push_back(cur_input);
  x_predictor_.Update(cur_input.pos.x(), dt_ms);
  y_predictor_.Update(cur_input.pos.y(), dt_ms);

  while (last_points_.back().time_stamp - last_points_.front().time_stamp >
         kMaxTimeInQueue) {
    last_points_.pop_front();
  }
}

bool KalmanPredictor::HasPrediction() const {
  return x_predictor_.Stable() && y_predictor_.Stable();
}

std::unique_ptr<InputPredictor::InputData> KalmanPredictor::GeneratePrediction(
    base::TimeTicks predict_time,
    base::TimeDelta frame_interval) {
  if (!HasPrediction())
    return nullptr;

  DCHECK(last_points_.size());
  float pred_dt =
      (predict_time - last_points_.back().time_stamp).InMillisecondsF();

  gfx::PointF position(last_points_.back().pos.x(),
                       last_points_.back().pos.y());
  gfx::Vector2dF velocity = PredictVelocity();
  gfx::Vector2dF acceleration = PredictAcceleration();

  position += ScaleVector2d(velocity, kVelocityInfluence * pred_dt);

  position +=
      ScaleVector2d(acceleration, kAccelerationInfluence * pred_dt * pred_dt);

  return std::make_unique<InputData>(position, predict_time);
}

base::TimeDelta KalmanPredictor::TimeInterval() const {
  return time_filter_.GetPosition()
             ? std::max(kMinTimeInterval,
                        base::Milliseconds(time_filter_.GetPosition()))
             : kTimeInterval;
}

gfx::Vector2dF KalmanPredictor::PredictPosition() const {
  return gfx::Vector2dF(x_predictor_.GetPosition(), y_predictor_.GetPosition());
}

gfx::Vector2dF KalmanPredictor::PredictVelocity() const {
  return gfx::Vector2dF(x_predictor_.GetVelocity(), y_predictor_.GetVelocity());
}

gfx::Vector2dF KalmanPredictor::PredictAcceleration() const {
  return gfx::Vector2dF(x_predictor_.GetAcceleration(),
                        y_predictor_.GetAcceleration());
}

}  // namespace ui