* Copyright (c) 2021 Huawei Device Co., Ltd.
* 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 "core/animation/spring_model.h"
#include "base/log/log_wrapper.h"
#include "base/utils/utils.h"
namespace OHOS::Ace {
namespace {
constexpr double HIGH_RATIO = 4.0;
constexpr double LOW_RATIO = 2.0;
}
bool SpringProperty::IsValid() const
{
if (LessOrEqual(mass_, 0.0) || LessOrEqual(stiffness_, 0.0) || LessOrEqual(damping_, 0.0)) {
return false;
}
return true;
}
void SpringProperty::SetMass(double mass)
{
if (mass > 0.0) {
mass_ = mass;
}
}
double SpringProperty::Mass() const
{
return mass_;
}
void SpringProperty::SetStiffness(double stiffness)
{
if (stiffness > 0.0) {
stiffness_ = stiffness;
}
}
double SpringProperty::Stiffness() const
{
return stiffness_;
}
void SpringProperty::SetDamping(double damping)
{
if (damping > 0.0) {
damping_ = damping;
}
}
double SpringProperty::Damping() const
{
return damping_;
}
RefPtr<SpringModel> SpringModel::Build(double distance, double velocity, const RefPtr<SpringProperty>& spring)
{
if (!spring || !spring->IsValid()) {
LOGE("SpringProperty can not be nullptr.");
return nullptr;
} else {
double cmk = spring->Damping() * spring->Damping() - HIGH_RATIO * spring->Mass() * spring->Stiffness();
if (NearZero(cmk)) {
if (NearZero(distance)) {
LOGE("create CriticalDamped failed, distance can not be zero.");
return nullptr;
}
return AceType::MakeRefPtr<CriticalDampedModel>(distance, velocity, spring);
} else if (cmk > 0.0) {
return AceType::MakeRefPtr<OverdampedModel>(distance, velocity, spring);
} else {
return AceType::MakeRefPtr<UnderdampedModel>(distance, velocity, spring);
}
}
}
CriticalDampedModel::CriticalDampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
{
if (spring && spring->IsValid() && !NearZero(distance)) {
r_ = -spring->Damping() / (LOW_RATIO * spring->Mass());
c1_ = distance;
c2_ = velocity / (r_ * distance);
}
}
double CriticalDampedModel::Position(double time) const
{
return (c1_ + c2_ * time) * exp(r_ * time);
}
double CriticalDampedModel::Velocity(double time) const
{
const double power = exp(r_ * time);
return r_ * (c1_ + c2_ * time) * power + c2_ * power;
}
SpringModelType CriticalDampedModel::GetType() const
{
return SpringModelType::CRITICAL_DAMPED;
}
OverdampedModel::OverdampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
{
if (spring && spring->IsValid()) {
double cmk = spring->Damping() * spring->Damping() - HIGH_RATIO * spring->Mass() * spring->Stiffness();
r1_ = (-spring->Damping() - sqrt(cmk)) / (LOW_RATIO * spring->Mass());
r2_ = (-spring->Damping() + sqrt(cmk)) / (LOW_RATIO * spring->Mass());
if (!NearEqual(r2_, r1_)) {
c2_ = (velocity - r1_ * distance) / (r2_ - r1_);
c1_ = distance - c2_;
}
}
}
double OverdampedModel::Position(double time) const
{
return c1_ * exp(r1_ * time) + c2_ * exp(r2_ * time);
}
double OverdampedModel::Velocity(double time) const
{
return c1_ * r1_ * exp(r1_ * time) + c2_ * r2_ * exp(r2_ * time);
}
SpringModelType OverdampedModel::GetType() const
{
return SpringModelType::OVER_DAMPED;
}
UnderdampedModel::UnderdampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
{
if (spring && spring->IsValid()) {
w_ = sqrt(HIGH_RATIO * spring->Mass() * spring->Stiffness() - spring->Damping() * spring->Damping()) /
(LOW_RATIO * spring->Mass());
r_ = -spring->Damping() / (LOW_RATIO * spring->Mass());
c1_ = distance;
if (!NearEqual(w_, 0.0)) {
c2_ = (velocity - r_ * distance) / w_;
}
}
}
double UnderdampedModel::Position(double time) const
{
return exp(r_ * time) * (c1_ * cos(w_ * time) + c2_ * sin(w_ * time));
}
double UnderdampedModel::Velocity(double time) const
{
double power = exp(r_ * time);
double cosine = cos(w_ * time);
double sine = sin(w_ * time);
return power * (c2_ * w_ * cosine - c1_ * w_ * sine) + r_ * power * (c2_ * sine + c1_ * cosine);
}
SpringModelType UnderdampedModel::GetType() const
{
return SpringModelType::UNDER_DAMPED;
}
}