• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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