blob: c8cb01ad1897148d87811fa4dffffb31f1c9de60 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/renderer/smoothed_value.h"
#include <algorithm>
namespace cobalt {
namespace renderer {
SmoothedValue::SmoothedValue(base::TimeDelta time_to_converge)
: time_to_converge_(time_to_converge), previous_derivative_(0) {
DCHECK(base::TimeDelta() < time_to_converge_);
}
void SmoothedValue::SetTarget(double target, const base::TimeTicks& time) {
// Determine the current derivative and value.
double current_derivative = GetCurrentDerivative(time);
base::optional<double> current_value;
if (target_) {
current_value = GetValueAtTime(time);
}
// Set the previous derivative and value to the current derivative and value.
previous_derivative_ = current_derivative;
previous_value_ = current_value;
target_ = target;
target_set_time_ = time;
}
void SmoothedValue::SnapToTarget() {
previous_value_ = base::nullopt;
previous_derivative_ = 0;
}
double SmoothedValue::GetValueAtTime(const base::TimeTicks& time) const {
if (!previous_value_) {
// If only one target has ever been set, simply return it.
return *target_;
}
// Compute the current value based off of a cubic bezier curve.
double t = SmoothedValue::t(time);
double one_minus_t = 1 - t;
double P0 = SmoothedValue::P0();
double P1 = SmoothedValue::P1();
double P2 = SmoothedValue::P2();
double P3 = SmoothedValue::P3();
return one_minus_t * one_minus_t * one_minus_t * P0 +
3 * one_minus_t * one_minus_t * t * P1 + 3 * one_minus_t * t * t * P2 +
t * t * t * P3;
}
double SmoothedValue::t(const base::TimeTicks& time) const {
DCHECK(target_) << "SetTarget() must have been called previously.";
base::TimeDelta time_diff = time - target_set_time_;
double t = time_diff.InMillisecondsF() / time_to_converge_.InMillisecondsF();
DCHECK_LE(0, t);
return std::max(std::min(t, 1.0), 0.0);
}
double SmoothedValue::P1() const {
// See comments in header for why P1() is calculated this way.
return *previous_value_ + previous_derivative_ / 3.0f;
}
double SmoothedValue::P2() const {
// See comments in header for why P2() is calculated this way.
return P3();
}
double SmoothedValue::GetCurrentDerivative(const base::TimeTicks& time) const {
if (!previous_value_) {
// If only one target has ever been set, return 0 as our derivative.
return 0;
}
double t = SmoothedValue::t(time);
double one_minus_t = 1 - t;
double P0 = SmoothedValue::P0();
double P1 = SmoothedValue::P1();
double P2 = SmoothedValue::P2();
double P3 = SmoothedValue::P3();
return 3 * one_minus_t * one_minus_t * (P1 - P0) +
6 * one_minus_t * t * (P2 - P1) + 3 * t * t * (P3 - P2);
}
} // namespace renderer
} // namespace cobalt