#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 {
constexpr float kAccelerationInfluence = 0.5f;
constexpr float kVelocityInfluence = 1.0f;
}
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()) {
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());
}
}