1 /*
2 * Copyright (c) 2022-2023 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_rect.h"
22 #include "modifier/rs_render_property.h"
23 #include "platform/common/rs_log.h"
24
25 namespace OHOS {
26 namespace Rosen {
27 namespace {
28 constexpr float SPRING_MIN_DAMPING_RATIO = 1e-4f;
29 constexpr float SPRING_MAX_DAMPING_RATIO = 1e4f;
30 constexpr float SPRING_MIN_DURATION = 0.001f;
31 constexpr float SPRING_MAX_DURATION = 300.0f;
32 constexpr float SPRING_MIN_RESPONSE = 1e-8;
33 constexpr float SPRING_MIN_AMPLITUDE_RATIO = 1e-3f;
34 constexpr float SPRING_MIN_AMPLITUDE = 1e-5f;
35 constexpr float FLOAT_NEAR_ZERO_THRESHOLD = 1e-6f;
36 constexpr double DOUBLE_NEAR_ZERO_THRESHOLD = 1e-6;
37 constexpr float SPRING_MIN_THRESHOLD = 1.0f / 4096.0f;
38 // helper function to simplify estimation of spring duration
39 template<typename RSAnimatableType>
toFloat(RSAnimatableType value)40 float toFloat(RSAnimatableType value)
41 {
42 return 1.f;
43 }
44 template<>
toFloat(float value)45 float toFloat(float value)
46 {
47 return std::fabs(value);
48 }
49 template<>
toFloat(Vector4f value)50 float toFloat(Vector4f value)
51 {
52 return value.GetLength();
53 }
54 template<>
toFloat(Quaternion value)55 float toFloat(Quaternion value)
56 {
57 return value.GetLength();
58 }
59 template<>
toFloat(Vector2f value)60 float toFloat(Vector2f value)
61 {
62 return value.GetLength();
63 }
64 } // namespace
65
66 template<typename RSAnimatableType>
RSSpringModel(float response,float dampingRatio,const RSAnimatableType & initialOffset,const RSAnimatableType & initialVelocity,float minimumAmplitude)67 RSSpringModel<RSAnimatableType>::RSSpringModel(float response, float dampingRatio,
68 const RSAnimatableType& initialOffset, const RSAnimatableType& initialVelocity, float minimumAmplitude)
69 : response_(response), dampingRatio_(dampingRatio), initialOffset_(initialOffset),
70 initialVelocity_(initialVelocity), minimumAmplitudeRatio_(minimumAmplitude)
71 {
72 CalculateSpringParameters();
73 }
74
75 template<typename RSAnimatableType>
CalculateSpringParameters()76 void RSSpringModel<RSAnimatableType>::CalculateSpringParameters()
77 {
78 // sanity check
79 dampingRatio_ = std::clamp(dampingRatio_, SPRING_MIN_DAMPING_RATIO, SPRING_MAX_DAMPING_RATIO);
80 if (response_ <= 0) {
81 response_ = SPRING_MIN_RESPONSE;
82 }
83 if (minimumAmplitudeRatio_ <= 0) {
84 minimumAmplitudeRatio_ = SPRING_MIN_AMPLITUDE_RATIO;
85 }
86
87 // calculate internal parameters
88 double naturalAngularVelocity = 2 * M_PI / response_;
89 if (dampingRatio_ < 1) { // Under-damped Systems
90 dampedAngularVelocity_ = naturalAngularVelocity * sqrt(1.0f - dampingRatio_ * dampingRatio_);
91 coeffDecay_ = -dampingRatio_ * naturalAngularVelocity;
92 coeffScale_ =
93 (initialVelocity_ + initialOffset_ * dampingRatio_ * naturalAngularVelocity) * (1 / dampedAngularVelocity_);
94 } else if (dampingRatio_ == 1) { // Critically-Damped Systems
95 coeffDecay_ = -naturalAngularVelocity;
96 coeffScale_ = initialVelocity_ + initialOffset_ * naturalAngularVelocity;
97 } else { // Over-damped Systems
98 double coeffTmp = sqrt(dampingRatio_ * dampingRatio_ - 1);
99 coeffDecay_ = (-dampingRatio_ + coeffTmp) * naturalAngularVelocity;
100 coeffScale_ = (initialOffset_ * ((dampingRatio_ + coeffTmp) * naturalAngularVelocity) + initialVelocity_) *
101 (0.5f / (naturalAngularVelocity * coeffTmp));
102 coeffScaleAlt_ = (initialOffset_ * ((coeffTmp - dampingRatio_) * naturalAngularVelocity) - initialVelocity_) *
103 (0.5f / (naturalAngularVelocity * coeffTmp));
104 coeffDecayAlt_ = (-dampingRatio_ - coeffTmp) * naturalAngularVelocity;
105 }
106 }
107
108 template<typename RSAnimatableType>
EstimateDuration() const109 float RSSpringModel<RSAnimatableType>::EstimateDuration() const
110 {
111 if (dampingRatio_ <= 0.0f) {
112 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
113 return 0.0f;
114 }
115
116 // convert templated type to float, simplify estimation of spring duration
117 float coeffScale = toFloat(coeffScale_);
118 float initialOffset = toFloat(initialOffset_);
119 float estimatedDuration = 0.0f;
120 float minimumAmplitude = std::max(initialOffset * minimumAmplitudeRatio_, SPRING_MIN_AMPLITUDE);
121
122 if (dampingRatio_ < 1) { // Under-damped
123 estimatedDuration = log(fmax(coeffScale, initialOffset) / minimumAmplitude) / -coeffDecay_;
124 } else if (dampingRatio_ == 1) { // Critically-damped
125 // critical damping spring will rest at 2 * natural period
126 estimatedDuration = response_ * 2;
127 } else { // Over-damped
128 float coeffScaleAlt = toFloat(coeffScaleAlt_);
129 double durationMain =
130 (coeffScale <= minimumAmplitude) ? 0 : (log(coeffScale / minimumAmplitude) / -coeffDecay_);
131 double durationAlt =
132 (coeffScaleAlt <= minimumAmplitude) ? 0 : (log(coeffScaleAlt / minimumAmplitude) / -coeffDecayAlt_);
133 estimatedDuration = fmax(durationMain, durationAlt);
134 }
135 return std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
136 }
137
138 template<typename RSAnimatableType>
CalculateDisplacement(double time) const139 RSAnimatableType RSSpringModel<RSAnimatableType>::CalculateDisplacement(double time) const
140 {
141 if (dampingRatio_ <= 0.0f) {
142 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
143 return {};
144 }
145 double coeffDecay = exp(coeffDecay_ * time);
146 if (dampingRatio_ < 1) {
147 // under-damped
148 double rad = dampedAngularVelocity_ * time;
149 RSAnimatableType coeffPeriod = initialOffset_ * cos(rad) + coeffScale_ * sin(rad);
150 return coeffPeriod * coeffDecay;
151 } else if (dampingRatio_ == 1) {
152 // critical-damped
153 return (initialOffset_ + coeffScale_ * time) * coeffDecay;
154 } else {
155 // over-damped
156 double coeffDecayAlt = exp(coeffDecayAlt_ * time);
157 return coeffScale_ * coeffDecay + coeffScaleAlt_ * coeffDecayAlt;
158 }
159 }
160
161 template<>
CalculateSpringParameters()162 void RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateSpringParameters()
163 {
164 // sanity check
165 dampingRatio_ = std::clamp(dampingRatio_, SPRING_MIN_DAMPING_RATIO, SPRING_MAX_DAMPING_RATIO);
166 if (response_ <= 0) {
167 response_ = SPRING_MIN_RESPONSE;
168 }
169 if (minimumAmplitudeRatio_ <= 0) {
170 minimumAmplitudeRatio_ = SPRING_MIN_AMPLITUDE_RATIO;
171 }
172 if (initialOffset_ == nullptr) {
173 return;
174 }
175
176 // calculate internal parameters
177 double naturalAngularVelocity = 2 * M_PI / response_;
178 if (dampingRatio_ < 1) { // Under-damped Systems
179 dampedAngularVelocity_ = naturalAngularVelocity * sqrt(1.0f - dampingRatio_ * dampingRatio_);
180 coeffDecay_ = -dampingRatio_ * naturalAngularVelocity;
181 coeffScale_ = (initialVelocity_ + initialOffset_ * dampingRatio_ * naturalAngularVelocity) *=
182 (1 / dampedAngularVelocity_);
183 } else if (dampingRatio_ == 1) { // Critically-Damped Systems
184 coeffDecay_ = -naturalAngularVelocity;
185 coeffScale_ = initialVelocity_ + initialOffset_ * naturalAngularVelocity;
186 } else { // Over-damped Systems
187 double coeffTmp = sqrt(dampingRatio_ * dampingRatio_ - 1);
188 coeffDecay_ = (-dampingRatio_ + coeffTmp) * naturalAngularVelocity;
189 coeffScale_ = (initialOffset_ * ((dampingRatio_ + coeffTmp) * naturalAngularVelocity) += initialVelocity_) *=
190 (0.5f / (naturalAngularVelocity * coeffTmp));
191 coeffScaleAlt_ = (initialOffset_ * ((coeffTmp - dampingRatio_) * naturalAngularVelocity) -= initialVelocity_) *=
192 (0.5f / (naturalAngularVelocity * coeffTmp));
193 coeffDecayAlt_ = (-dampingRatio_ - coeffTmp) * naturalAngularVelocity;
194 }
195 }
196
197 template<>
EstimateDuration() const198 float RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::EstimateDuration() const
199 {
200 if (dampingRatio_ <= 0.0f) {
201 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
202 return 0.0f;
203 }
204
205 // convert templated type to float, simplify estimation of spring duration
206 float coeffScale = coeffScale_->ToFloat();
207 float initialOffset = initialOffset_->ToFloat();
208 float estimatedDuration = 0.0f;
209 float minimumAmplitude = std::max(initialOffset * minimumAmplitudeRatio_, SPRING_MIN_AMPLITUDE);
210
211 if (dampingRatio_ < 1) { // Under-damped
212 estimatedDuration = log(fmax(coeffScale, initialOffset) / minimumAmplitude) / -coeffDecay_;
213 } else if (dampingRatio_ == 1) { // Critically-damped
214 // critical damping spring will rest at 2 * natural period
215 estimatedDuration = response_ * 2;
216 } else { // Over-damped
217 float coeffScaleAlt = coeffScaleAlt_->ToFloat();
218 double durationMain =
219 (coeffScale <= minimumAmplitude) ? 0 : (log(coeffScale / minimumAmplitude) / -coeffDecay_);
220 double durationAlt =
221 (coeffScaleAlt <= minimumAmplitude) ? 0 : (log(coeffScaleAlt / minimumAmplitude) / -coeffDecayAlt_);
222 estimatedDuration = fmax(durationMain, durationAlt);
223 }
224 return std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
225 }
226
227 template<>
CalculateDisplacement(double time) const228 std::shared_ptr<RSRenderPropertyBase> RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateDisplacement(
229 double time) const
230 {
231 if (dampingRatio_ <= 0.0f) {
232 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
233 return {};
234 }
235 double coeffDecay = exp(coeffDecay_ * time);
236 if (dampingRatio_ < 1) {
237 // under-damped
238 double rad = dampedAngularVelocity_ * time;
239 auto coeffPeriod = (initialOffset_ * cos(rad)) += (coeffScale_ * sin(rad));
240 return coeffPeriod *= coeffDecay;
241 } else if (dampingRatio_ == 1) {
242 // critical-damped
243 return (initialOffset_ + coeffScale_ * time) *= coeffDecay;
244 } else {
245 // over-damped
246 double coeffDecayAlt = exp(coeffDecayAlt_ * time);
247 return (coeffScale_ * coeffDecay) += (coeffScaleAlt_ * coeffDecayAlt);
248 }
249 }
250
251 template<>
EstimateDuration() const252 float RSSpringModel<float>::EstimateDuration() const
253 {
254 if (dampingRatio_ < 0.0f) {
255 ROSEN_LOGE("RSSpringModel::%s, uninitialized spring model", __func__);
256 return 0.0f;
257 }
258 if (response_ < 0.0f || (ROSEN_EQ(initialOffset_, 0.0f) && ROSEN_EQ(initialVelocity_, 0.0f)) ||
259 !std::isfinite(response_) || !std::isfinite(dampingRatio_) || !std::isfinite(initialVelocity_)) {
260 ROSEN_LOGE("RSSpringModel::%s, parameters is invalid", __func__);
261 return 0.0f;
262 }
263 float estimatedDuration = 0.0f;
264 if (dampingRatio_ < 1.0f) { // Under-damped
265 estimatedDuration = EstimateDurationForUnderDampedModel();
266 } else if (dampingRatio_ == 1.0f) { // Critically-damped
267 estimatedDuration = EstimateDurationForCriticalDampedModel();
268 } else { // Over-damped
269 estimatedDuration = EstimateDurationForOverDampedModel();
270 }
271 return std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
272 }
273
274 template<>
BinarySearchTime(float left,float right,float target) const275 float RSSpringModel<float>::BinarySearchTime(float left, float right, float target) const
276 {
277 float midTime = 0.0f;
278 float midValue = 0.0f;
279 bool isIncrease = CalculateDisplacement(left) < CalculateDisplacement(right);
280
281 while (left < right - 1e-3) {
282 midTime = left + (right - left) / 2.0f;
283 midValue = CalculateDisplacement(midTime);
284 if (!std::isfinite(midTime) || !std::isfinite(midValue)) {
285 return right;
286 }
287 if (ROSEN_EQ(midValue, target, FLOAT_NEAR_ZERO_THRESHOLD)) {
288 return midTime;
289 }
290 if ((midValue < target) != isIncrease) {
291 right = midTime;
292 } else {
293 left = midTime;
294 }
295 }
296
297 return right;
298 }
299
300 template<>
EstimateDurationForUnderDampedModel() const301 float RSSpringModel<float>::EstimateDurationForUnderDampedModel() const
302 {
303 float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_),
304 SPRING_MIN_THRESHOLD); // avoiding 0 in logarithmic expressions
305 double naturalAngularVelocity = 2.0 * M_PI / response_;
306 double dampingAngularVelocity = sqrt(1.0 - dampingRatio_ * dampingRatio_) * naturalAngularVelocity;
307 double tmpCoeffA = -1.0 / (dampingRatio_ * naturalAngularVelocity);
308 double tmpCoeffB =
309 sqrt(pow(initialOffset_, 2) +
310 (pow((initialVelocity_ + dampingRatio_ * naturalAngularVelocity * initialOffset_) / dampingAngularVelocity,
311 2)));
312 double tmpCoeffC = std::fabs(threshold / tmpCoeffB);
313 if (ROSEN_EQ(tmpCoeffC, 0.0)) {
314 return 0.0f;
315 }
316 float estimatedDuration = tmpCoeffA * log(tmpCoeffC);
317 return estimatedDuration;
318 }
319
320 template<>
EstimateDurationForCriticalDampedModel() const321 float RSSpringModel<float>::EstimateDurationForCriticalDampedModel() const
322 {
323 float estimatedDuration = 0.0f;
324 float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_), SPRING_MIN_THRESHOLD);
325 double naturalAngularVelocity = 2.0 * M_PI / response_;
326 double tmpCoeff = (initialVelocity_ + naturalAngularVelocity * initialOffset_);
327 if (ROSEN_EQ(tmpCoeff, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
328 estimatedDuration = 1.0f / naturalAngularVelocity * log(std::fabs(initialOffset_ / threshold));
329 return estimatedDuration;
330 }
331 float extremumTime = initialVelocity_ / (naturalAngularVelocity * tmpCoeff);
332 extremumTime = std::clamp(extremumTime, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
333 float extremumValue = CalculateDisplacement(extremumTime);
334 threshold = extremumValue > 0.0f ? threshold : -threshold;
335 if (std::fabs(extremumValue) < std::fabs(threshold)) {
336 estimatedDuration = BinarySearchTime(SPRING_MIN_DURATION, extremumTime, threshold);
337 } else {
338 estimatedDuration = BinarySearchTime(extremumTime, SPRING_MAX_DURATION, threshold);
339 }
340 return estimatedDuration;
341 }
342
343 template<>
EstimateDurationForOverDampedModel() const344 float RSSpringModel<float>::EstimateDurationForOverDampedModel() const
345 {
346 float estimatedDuration = 0.0f;
347 float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_), SPRING_MIN_THRESHOLD);
348 double naturalAngularVelocity = 2.0 * M_PI / response_;
349 double tmpCoeffA = dampingRatio_ + sqrt(pow(dampingRatio_, 2) - 1.0);
350 double tmpCoeffB = dampingRatio_ - sqrt(pow(dampingRatio_, 2) - 1.0);
351 double tmpCoeffC = initialOffset_ * naturalAngularVelocity + initialVelocity_ * tmpCoeffA;
352 double tmpCoeffD = initialOffset_ * naturalAngularVelocity + initialVelocity_ * tmpCoeffB;
353 double tmpCoeffE = 1.0 / (2.0 * naturalAngularVelocity * sqrt(pow(dampingRatio_, 2) - 1.0));
354 if (ROSEN_EQ(tmpCoeffC, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
355 double tmpCoeff = initialOffset_ * naturalAngularVelocity * tmpCoeffA + initialVelocity_;
356 estimatedDuration =
357 1.0f / (-tmpCoeffB * naturalAngularVelocity) * log(std::fabs(tmpCoeffE * threshold / tmpCoeff));
358 return estimatedDuration;
359 }
360 if (ROSEN_EQ(tmpCoeffD, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
361 double tmpCoeff = -initialOffset_ * naturalAngularVelocity * tmpCoeffB - initialVelocity_;
362 estimatedDuration =
363 1.0f / (-tmpCoeffA * naturalAngularVelocity) * log(std::fabs(tmpCoeffE * threshold / tmpCoeff));
364 return estimatedDuration;
365 }
366 float extremumTime = (tmpCoeffC / tmpCoeffD > DOUBLE_NEAR_ZERO_THRESHOLD) ? tmpCoeffE * log(tmpCoeffC / tmpCoeffD)
367 : SPRING_MIN_DURATION;
368 extremumTime = std::clamp(extremumTime, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
369 float extremumValue = CalculateDisplacement(extremumTime);
370 threshold = extremumValue > 0.0f ? threshold : -threshold;
371 if (std::fabs(extremumValue) < std::fabs(threshold)) {
372 estimatedDuration = BinarySearchTime(SPRING_MIN_DURATION, extremumTime, threshold);
373 } else {
374 estimatedDuration = BinarySearchTime(extremumTime, SPRING_MAX_DURATION, threshold);
375 }
376 return estimatedDuration;
377 }
378 template class RSSpringModel<float>;
379 template class RSSpringModel<Color>;
380 template class RSSpringModel<Matrix3f>;
381 template class RSSpringModel<Vector2f>;
382 template class RSSpringModel<Vector4f>;
383 template class RSSpringModel<RRect>;
384 template class RSSpringModel<Quaternion>;
385 template class RSSpringModel<Vector4<Color>>;
386 template class RSSpringModel<std::shared_ptr<RSFilter>>;
387 template class RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>;
388 } // namespace Rosen
389 } // namespace OHOS
390