1 /*
2 * Copyright (c) 2022 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 "animation/rs_spring_model.h"
17
18 #include <algorithm>
19 #include <cmath>
20
21 #include "common/rs_vector2.h"
22 #include "common/rs_vector4.h"
23 #include "modifier/rs_render_property.h"
24 #include "platform/common/rs_log.h"
25
26 namespace OHOS {
27 namespace Rosen {
28 namespace {
29 constexpr float SPRING_MIN_DAMPING_RATIO = 1e-4f;
30 constexpr float SPRING_MAX_DAMPING_RATIO = 1e4f;
31 constexpr float SPRING_MIN_DURATION = 0.001f;
32 constexpr float SPRING_MAX_DURATION = 300.0f;
33 constexpr float SPRING_MIN_RESPONSE = 1e-8;
34 constexpr float SPRING_MIN_AMPLITUDE_RATIO = 0.001f;
35 // helper function to simplify estimation of spring duration
36 template<typename RSAnimatableType>
toFloat(RSAnimatableType value)37 float toFloat(RSAnimatableType value)
38 {
39 return 1.f;
40 }
41 template<>
toFloat(float value)42 float toFloat(float value)
43 {
44 return std::fabs(value);
45 }
46 template<>
toFloat(Vector4f value)47 float toFloat(Vector4f value)
48 {
49 return value.GetLength();
50 }
51 template<>
toFloat(Quaternion value)52 float toFloat(Quaternion value)
53 {
54 return value.GetLength();
55 }
56 template<>
toFloat(Vector2f value)57 float toFloat(Vector2f value)
58 {
59 return value.GetLength();
60 }
61 } // namespace
62
63 template<typename RSAnimatableType>
RSSpringModel(float response,float dampingRatio,const RSAnimatableType & initialOffset,const RSAnimatableType & initialVelocity,float minimumAmplitude)64 RSSpringModel<RSAnimatableType>::RSSpringModel(float response, float dampingRatio,
65 const RSAnimatableType& initialOffset, const RSAnimatableType& initialVelocity, float minimumAmplitude)
66 : response_(response), dampingRatio_(dampingRatio), initialOffset_(initialOffset),
67 initialVelocity_(initialVelocity), minimumAmplitudeRatio_(minimumAmplitude)
68 {
69 CalculateSpringParameters();
70 }
71
72 template<typename RSAnimatableType>
CalculateSpringParameters()73 void RSSpringModel<RSAnimatableType>::CalculateSpringParameters()
74 {
75 // sanity check
76 dampingRatio_ = std::clamp(dampingRatio_, SPRING_MIN_DAMPING_RATIO, SPRING_MAX_DAMPING_RATIO);
77 if (response_ <= 0) {
78 response_ = SPRING_MIN_RESPONSE;
79 }
80 if (minimumAmplitudeRatio_ <= 0) {
81 minimumAmplitudeRatio_ = SPRING_MIN_AMPLITUDE_RATIO;
82 }
83
84 // calculate internal parameters
85 double naturalAngularVelocity = 2 * M_PI / response_;
86 if (dampingRatio_ < 1) { // Under-damped Systems
87 dampedAngularVelocity_ = naturalAngularVelocity * sqrt(1.0f - dampingRatio_ * dampingRatio_);
88 coeffDecay_ = -dampingRatio_ * naturalAngularVelocity;
89 coeffScale_ =
90 (initialVelocity_ + initialOffset_ * dampingRatio_ * naturalAngularVelocity) * (1 / dampedAngularVelocity_);
91 } else if (dampingRatio_ == 1) { // Critically-Damped Systems
92 coeffDecay_ = -naturalAngularVelocity;
93 coeffScale_ = initialVelocity_ + initialOffset_ * naturalAngularVelocity;
94 } else { // Over-damped Systems
95 double coeffTmp = sqrt(dampingRatio_ * dampingRatio_ - 1);
96 coeffDecay_ = (-dampingRatio_ + coeffTmp) * naturalAngularVelocity;
97 coeffScale_ = (initialOffset_ * ((dampingRatio_ + coeffTmp) * naturalAngularVelocity) + initialVelocity_) *
98 (0.5f / (naturalAngularVelocity * coeffTmp));
99 coeffScaleAlt_ = (initialOffset_ * ((coeffTmp - dampingRatio_) * naturalAngularVelocity) - initialVelocity_) *
100 (0.5f / (naturalAngularVelocity * coeffTmp));
101 coeffDecayAlt_ = (-dampingRatio_ - coeffTmp) * naturalAngularVelocity;
102 }
103 }
104
105 template<typename RSAnimatableType>
EstimateDuration()106 void RSSpringModel<RSAnimatableType>::EstimateDuration()
107 {
108 if (dampingRatio_ <= 0.0f) {
109 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
110 return;
111 }
112
113 // convert templated type to float, simplify estimation of spring duration
114 float coeffScale = toFloat(coeffScale_);
115 float initialOffset = toFloat(initialOffset_);
116 float estimatedDuration = 0.0f;
117 float minimumAmplitude = initialOffset * minimumAmplitudeRatio_;
118
119 if (dampingRatio_ < 1) { // Under-damped
120 estimatedDuration = log(fmax(coeffScale, initialOffset) / minimumAmplitude) / -coeffDecay_;
121 } else if (dampingRatio_ == 1) { // Critically-damped
122 // critical damping spring will rest at 2 * natural period
123 estimatedDuration_ = response_ * 2;
124 } else { // Over-damped
125 float coeffScaleAlt = toFloat(coeffScaleAlt_);
126 double durationMain =
127 (coeffScale <= minimumAmplitude) ? 0 : (log(coeffScale / minimumAmplitude) / -coeffDecay_);
128 double durationAlt =
129 (coeffScaleAlt <= minimumAmplitude) ? 0 : (log(coeffScaleAlt / minimumAmplitude) / -coeffDecayAlt_);
130 estimatedDuration = fmax(durationMain, durationAlt);
131 }
132 estimatedDuration_ = std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
133 ROSEN_LOGD("RSSpringModel::%s estimated duration = %.5f, clamped duration = %.5f", __func__, estimatedDuration,
134 estimatedDuration_);
135 }
136
137 template<typename RSAnimatableType>
CalculateDisplacement(double time) const138 RSAnimatableType RSSpringModel<RSAnimatableType>::CalculateDisplacement(double time) const
139 {
140 if (dampingRatio_ <= 0.0f) {
141 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
142 return {};
143 }
144 double coeffDecay = exp(coeffDecay_ * time);
145 if (dampingRatio_ < 1) {
146 // under-damped
147 double rad = dampedAngularVelocity_ * time;
148 RSAnimatableType coeffPeriod = initialOffset_ * cos(rad) + coeffScale_ * sin(rad);
149 return coeffPeriod * coeffDecay;
150 } else if (dampingRatio_ == 1) {
151 // critical-damped
152 return (initialOffset_ + coeffScale_ * time) * coeffDecay;
153 } else {
154 // over-damped
155 double coeffDecayAlt = exp(coeffDecayAlt_ * time);
156 return coeffScale_ * coeffDecay + coeffScaleAlt_ * coeffDecayAlt;
157 }
158 }
159
160 template<typename RSAnimatableType>
GetEstimatedDuration()161 float RSSpringModel<RSAnimatableType>::GetEstimatedDuration()
162 {
163 if (estimatedDuration_ < SPRING_MIN_DURATION) {
164 // during not estimated yet
165 EstimateDuration();
166 }
167 return estimatedDuration_;
168 }
169
170 template<>
CalculateSpringParameters()171 void RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateSpringParameters()
172 {
173 // sanity check
174 dampingRatio_ = std::clamp(dampingRatio_, SPRING_MIN_DAMPING_RATIO, SPRING_MAX_DAMPING_RATIO);
175 if (response_ <= 0) {
176 response_ = SPRING_MIN_RESPONSE;
177 }
178 if (minimumAmplitudeRatio_ <= 0) {
179 minimumAmplitudeRatio_ = SPRING_MIN_AMPLITUDE_RATIO;
180 }
181 if (initialOffset_ == nullptr) {
182 return;
183 }
184
185 // calculate internal parameters
186 double naturalAngularVelocity = 2 * M_PI / response_;
187 if (dampingRatio_ < 1) { // Under-damped Systems
188 dampedAngularVelocity_ = naturalAngularVelocity * sqrt(1.0f - dampingRatio_ * dampingRatio_);
189 coeffDecay_ = -dampingRatio_ * naturalAngularVelocity;
190 coeffScale_ = (initialVelocity_ + initialOffset_ * dampingRatio_ * naturalAngularVelocity) *=
191 (1 / dampedAngularVelocity_);
192 } else if (dampingRatio_ == 1) { // Critically-Damped Systems
193 coeffDecay_ = -naturalAngularVelocity;
194 coeffScale_ = initialVelocity_ + initialOffset_ * naturalAngularVelocity;
195 } else { // Over-damped Systems
196 double coeffTmp = sqrt(dampingRatio_ * dampingRatio_ - 1);
197 coeffDecay_ = (-dampingRatio_ + coeffTmp) * naturalAngularVelocity;
198 coeffScale_ = (initialOffset_ * ((dampingRatio_ + coeffTmp) * naturalAngularVelocity) += initialVelocity_) *=
199 (0.5f / (naturalAngularVelocity * coeffTmp));
200 coeffScaleAlt_ = (initialOffset_ * ((coeffTmp - dampingRatio_) * naturalAngularVelocity) -= initialVelocity_) *=
201 (0.5f / (naturalAngularVelocity * coeffTmp));
202 coeffDecayAlt_ = (-dampingRatio_ - coeffTmp) * naturalAngularVelocity;
203 }
204 }
205
206 template<>
EstimateDuration()207 void RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::EstimateDuration()
208 {
209 if (dampingRatio_ <= 0.0f) {
210 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
211 return;
212 }
213
214 // convert templated type to float, simplify estimation of spring duration
215 float coeffScale = coeffScale_->ToFloat();
216 float initialOffset = initialOffset_->ToFloat();
217 float estimatedDuration = 0.0f;
218 float minimumAmplitude = initialOffset * minimumAmplitudeRatio_;
219
220 if (dampingRatio_ < 1) { // Under-damped
221 estimatedDuration = log(fmax(coeffScale, initialOffset) / minimumAmplitude) / -coeffDecay_;
222 } else if (dampingRatio_ == 1) { // Critically-damped
223 // critical damping spring will rest at 2 * natural period
224 estimatedDuration_ = response_ * 2;
225 } else { // Over-damped
226 float coeffScaleAlt = coeffScaleAlt_->ToFloat();
227 double durationMain =
228 (coeffScale <= minimumAmplitude) ? 0 : (log(coeffScale / minimumAmplitude) / -coeffDecay_);
229 double durationAlt =
230 (coeffScaleAlt <= minimumAmplitude) ? 0 : (log(coeffScaleAlt / minimumAmplitude) / -coeffDecayAlt_);
231 estimatedDuration = fmax(durationMain, durationAlt);
232 }
233 estimatedDuration_ = std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
234 ROSEN_LOGD("RSSpringModel::%s estimated duration = %.5f, clamped duration = %.5f", __func__, estimatedDuration,
235 estimatedDuration_);
236 }
237
238 template<>
CalculateDisplacement(double time) const239 std::shared_ptr<RSRenderPropertyBase> RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateDisplacement(
240 double time) const
241 {
242 if (dampingRatio_ <= 0.0f) {
243 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
244 return {};
245 }
246 double coeffDecay = exp(coeffDecay_ * time);
247 if (dampingRatio_ < 1) {
248 // under-damped
249 double rad = dampedAngularVelocity_ * time;
250 auto coeffPeriod = (initialOffset_ * cos(rad)) += (coeffScale_ * sin(rad));
251 return coeffPeriod *= coeffDecay;
252 } else if (dampingRatio_ == 1) {
253 // critical-damped
254 return (initialOffset_ + coeffScale_ * time) *= coeffDecay;
255 } else {
256 // over-damped
257 double coeffDecayAlt = exp(coeffDecayAlt_ * time);
258 return (coeffScale_ * coeffDecay) += (coeffScaleAlt_ * coeffDecayAlt);
259 }
260 }
261
262 template class RSSpringModel<float>;
263 template class RSSpringModel<Color>;
264 template class RSSpringModel<Matrix3f>;
265 template class RSSpringModel<Vector2f>;
266 template class RSSpringModel<Vector4f>;
267 template class RSSpringModel<Quaternion>;
268 template class RSSpringModel<Vector4<Color>>;
269 template class RSSpringModel<std::shared_ptr<RSFilter>>;
270 template class RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>;
271 } // namespace Rosen
272 } // namespace OHOS
273