1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/animation/spring_model.h"
17 
18 #include "base/utils/utils.h"
19 
20 namespace OHOS::Ace {
21 namespace {
22 
23 constexpr double HIGH_RATIO = 4.0;
24 constexpr double LOW_RATIO = 2.0;
25 
26 } // namespace
27 
IsValid() const28 bool SpringProperty::IsValid() const
29 {
30     if (LessOrEqual(mass_, 0.0) || LessOrEqual(stiffness_, 0.0) || LessOrEqual(damping_, 0.0)) {
31         return false;
32     }
33     return true;
34 }
35 
SetMass(double mass)36 void SpringProperty::SetMass(double mass)
37 {
38     if (mass > 0.0) {
39         mass_ = mass;
40     }
41 }
42 
Mass() const43 double SpringProperty::Mass() const
44 {
45     return mass_;
46 }
47 
SetStiffness(double stiffness)48 void SpringProperty::SetStiffness(double stiffness)
49 {
50     if (stiffness > 0.0) {
51         stiffness_ = stiffness;
52     }
53 }
54 
Stiffness() const55 double SpringProperty::Stiffness() const
56 {
57     return stiffness_;
58 }
59 
SetDamping(double damping)60 void SpringProperty::SetDamping(double damping)
61 {
62     if (damping > 0.0) {
63         damping_ = damping;
64     }
65 }
66 
Damping() const67 double SpringProperty::Damping() const
68 {
69     return damping_;
70 }
71 
Build(double distance,double velocity,const RefPtr<SpringProperty> & spring)72 RefPtr<SpringModel> SpringModel::Build(double distance, double velocity, const RefPtr<SpringProperty>& spring)
73 {
74     if (!spring || !spring->IsValid()) {
75         LOGE("SpringProperty can not be nullptr.");
76         return nullptr;
77     } else {
78         double cmk = spring->Damping() * spring->Damping() - HIGH_RATIO * spring->Mass() * spring->Stiffness();
79         if (NearZero(cmk)) {
80             if (NearZero(distance)) {
81                 LOGE("create CriticalDamped failed, distance can not be zero.");
82                 return nullptr;
83             }
84             return AceType::MakeRefPtr<CriticalDampedModel>(distance, velocity, spring);
85         } else if (cmk > 0.0) {
86             return AceType::MakeRefPtr<OverdampedModel>(distance, velocity, spring);
87         } else {
88             return AceType::MakeRefPtr<UnderdampedModel>(distance, velocity, spring);
89         }
90     }
91 }
92 
93 // Overdamping calculation model.
CriticalDampedModel(double distance,double velocity,const RefPtr<SpringProperty> & spring)94 CriticalDampedModel::CriticalDampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
95 {
96     if (spring && spring->IsValid() && !NearZero(distance)) {
97         r_ = -spring->Damping() / (LOW_RATIO * spring->Mass());
98         c1_ = distance;
99         c2_ = velocity / (r_ * distance);
100     }
101 }
102 
Position(double time) const103 double CriticalDampedModel::Position(double time) const
104 {
105     return (c1_ + c2_ * time) * exp(r_ * time);
106 }
107 
Velocity(double time) const108 double CriticalDampedModel::Velocity(double time) const
109 {
110     const double power = exp(r_ * time);
111     return r_ * (c1_ + c2_ * time) * power + c2_ * power;
112 }
113 
GetType() const114 SpringModelType CriticalDampedModel::GetType() const
115 {
116     return SpringModelType::CRITICAL_DAMPED;
117 }
118 
119 // Overdamping calculation model.
OverdampedModel(double distance,double velocity,const RefPtr<SpringProperty> & spring)120 OverdampedModel::OverdampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
121 {
122     if (spring && spring->IsValid()) {
123         double cmk = spring->Damping() * spring->Damping() - HIGH_RATIO * spring->Mass() * spring->Stiffness();
124         r1_ = (-spring->Damping() - sqrt(cmk)) / (LOW_RATIO * spring->Mass());
125         r2_ = (-spring->Damping() + sqrt(cmk)) / (LOW_RATIO * spring->Mass());
126         if (!NearEqual(r2_, r1_)) {
127             c2_ = (velocity - r1_ * distance) / (r2_ - r1_);
128             c1_ = distance - c2_;
129         }
130     }
131 }
132 
Position(double time) const133 double OverdampedModel::Position(double time) const
134 {
135     return c1_ * exp(r1_ * time) + c2_ * exp(r2_ * time);
136 }
137 
Velocity(double time) const138 double OverdampedModel::Velocity(double time) const
139 {
140     return c1_ * r1_ * exp(r1_ * time) + c2_ * r2_ * exp(r2_ * time);
141 }
142 
GetType() const143 SpringModelType OverdampedModel::GetType() const
144 {
145     return SpringModelType::OVER_DAMPED;
146 }
147 
148 // Underdamped calculation model
UnderdampedModel(double distance,double velocity,const RefPtr<SpringProperty> & spring)149 UnderdampedModel::UnderdampedModel(double distance, double velocity, const RefPtr<SpringProperty>& spring)
150 {
151     if (spring && spring->IsValid()) {
152         w_ = sqrt(HIGH_RATIO * spring->Mass() * spring->Stiffness() - spring->Damping() * spring->Damping()) /
153              (LOW_RATIO * spring->Mass());
154         r_ = -spring->Damping() / (LOW_RATIO * spring->Mass());
155         c1_ = distance;
156         if (!NearEqual(w_, 0.0)) {
157             c2_ = (velocity - r_ * distance) / w_;
158         }
159     }
160 }
161 
Position(double time) const162 double UnderdampedModel::Position(double time) const
163 {
164     return exp(r_ * time) * (c1_ * cos(w_ * time) + c2_ * sin(w_ * time));
165 }
166 
Velocity(double time) const167 double UnderdampedModel::Velocity(double time) const
168 {
169     double power = exp(r_ * time);
170     double cosine = cos(w_ * time);
171     double sine = sin(w_ * time);
172     return power * (c2_ * w_ * cosine - c1_ * w_ * sine) + r_ * power * (c2_ * sine + c1_ * cosine);
173 }
174 
GetType() const175 SpringModelType UnderdampedModel::GetType() const
176 {
177     return SpringModelType::UNDER_DAMPED;
178 }
179 
180 } // namespace OHOS::Ace