• 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 FLOAT_NEAR_ZERO_THRESHOLD = 1e-6f;
29 constexpr double DOUBLE_NEAR_ZERO_THRESHOLD = 1e-6;
30 constexpr float SPRING_MIN_THRESHOLD = 5e-5f;
31 
32 template<>
toFloat(float value)33 float toFloat(float value)
34 {
35     return std::fabs(value);
36 }
37 } // namespace
38 
39 template<>
CalculateSpringParameters()40 void RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateSpringParameters()
41 {
42     // sanity check
43     dampingRatio_ = std::clamp(dampingRatio_, SPRING_MIN_DAMPING_RATIO, SPRING_MAX_DAMPING_RATIO);
44     if (response_ <= 0) {
45         response_ = SPRING_MIN_RESPONSE;
46     }
47     if (minimumAmplitudeRatio_ <= 0) {
48         minimumAmplitudeRatio_ = SPRING_MIN_AMPLITUDE_RATIO;
49     }
50     if (initialOffset_ == nullptr) {
51         return;
52     }
53 
54     // calculate internal parameters
55     double naturalAngularVelocity = 2 * FLOAT_PI / response_;
56     if (ROSEN_EQ(naturalAngularVelocity, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
57         naturalAngularVelocity = DOUBLE_NEAR_ZERO_THRESHOLD;
58     }
59     if (dampingRatio_ < 1) { // Under-damped Systems
60         dampedAngularVelocity_ = naturalAngularVelocity * sqrt(1.0f - dampingRatio_ * dampingRatio_);
61         coeffDecay_ = -dampingRatio_ * naturalAngularVelocity;
62         coeffScale_ = (initialVelocity_ + initialOffset_ * dampingRatio_ * naturalAngularVelocity) *=
63             (1 / dampedAngularVelocity_);
64     } else if (ROSEN_EQ(dampingRatio_, 1.0f)) { // Critically-Damped Systems
65         coeffDecay_ = -naturalAngularVelocity;
66         coeffScale_ = initialVelocity_ + initialOffset_ * naturalAngularVelocity;
67     } else { // Over-damped Systems
68         double coeffTmp = sqrt(dampingRatio_ * dampingRatio_ - 1);
69         coeffDecay_ = (-dampingRatio_ + coeffTmp) * naturalAngularVelocity;
70         coeffScale_ = (initialOffset_ * ((dampingRatio_ + coeffTmp) * naturalAngularVelocity) += initialVelocity_) *=
71             (0.5f / (naturalAngularVelocity * coeffTmp));
72         coeffScaleAlt_ = (initialOffset_ * ((coeffTmp - dampingRatio_) * naturalAngularVelocity) -= initialVelocity_) *=
73             (0.5f / (naturalAngularVelocity * coeffTmp));
74         coeffDecayAlt_ = (-dampingRatio_ - coeffTmp) * naturalAngularVelocity;
75     }
76 }
77 
78 template<>
EstimateDuration() const79 float RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::EstimateDuration() const
80 {
81     if (dampingRatio_ <= 0.0f || response_ <= 0.0f) {
82         ROSEN_LOGE("RSSpringModel::%{public}s, uninitialized spring model", __func__);
83         return 0.0f;
84     }
85 
86     // convert templated type to float, simplify estimation of spring duration
87     float coeffScale = coeffScale_->ToFloat();
88     float initialOffset = initialOffset_->ToFloat();
89     float estimatedDuration = 0.0f;
90     float minimumAmplitude = std::max(initialOffset * minimumAmplitudeRatio_, SPRING_MIN_AMPLITUDE);
91 
92     if (dampingRatio_ < 1) { // Under-damped
93         if (ROSEN_EQ(coeffDecay_, 0.0f)) {
94             ROSEN_LOGE("RSSpringModel::%{public}s, coeffDecay_ euqal zero.", __func__);
95             return 0.0f;
96         }
97         estimatedDuration = log(fmax(coeffScale, initialOffset) / minimumAmplitude) / -coeffDecay_;
98     } else if (ROSEN_EQ(dampingRatio_, 1.0f)) { // Critically-damped
99         // critical damping spring use dampingRatio = 0.999 to esimate duration approximately
100         constexpr float dampingRatio = 0.999f;
101         double naturalAngularVelocity = 2 * FLOAT_PI / response_;
102         double dampedAngularVelocity = naturalAngularVelocity * sqrt(1.0f - dampingRatio * dampingRatio);
103         if (ROSEN_EQ(dampedAngularVelocity, 0.0)) {
104             return 0.0f;
105         }
106         double tempCoeffA = 1.0 / (dampingRatio * naturalAngularVelocity);
107         double tempCoeffB = toFloat((initialVelocity_ + initialOffset_ * dampingRatio * naturalAngularVelocity) *
108                                     (1 / dampedAngularVelocity));
109         double tempCoeffC = sqrt(initialOffset * initialOffset + tempCoeffB * tempCoeffB);
110         if (ROSEN_EQ(tempCoeffC, 0.0)) {
111             return 0.0f;
112         }
113         estimatedDuration = log(tempCoeffC / minimumAmplitude) * tempCoeffA;
114     } else { // Over-damped
115         if (ROSEN_EQ(coeffDecay_, 0.0f) || ROSEN_EQ(coeffDecayAlt_, 0.0f)) {
116             ROSEN_LOGE("RSSpringModel::%{public}s, coeffDecay_ or coeffDecayAlt_ euqal zero.", __func__);
117             return 0.0f;
118         }
119         float coeffScaleAlt = coeffScaleAlt_->ToFloat();
120         double durationMain =
121             (coeffScale <= minimumAmplitude) ? 0 : (log(coeffScale / minimumAmplitude) / -coeffDecay_);
122         double durationAlt =
123             (coeffScaleAlt <= minimumAmplitude) ? 0 : (log(coeffScaleAlt / minimumAmplitude) / -coeffDecayAlt_);
124         estimatedDuration = fmax(durationMain, durationAlt);
125     }
126     return std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
127 }
128 
129 template<>
CalculateDisplacement(double time) const130 std::shared_ptr<RSRenderPropertyBase> RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>::CalculateDisplacement(
131     double time) const
132 {
133     if (dampingRatio_ <= 0.0f) {
134         ROSEN_LOGE("RSSpringModel::%{public}s, uninitialized spring model", __func__);
135         return {};
136     }
137     double coeffDecay = exp(coeffDecay_ * time);
138     if (dampingRatio_ < 1) {
139         // under-damped
140         double rad = dampedAngularVelocity_ * time;
141         auto coeffPeriod = (initialOffset_ * cos(rad)) += (coeffScale_ * sin(rad));
142         return coeffPeriod *= coeffDecay;
143     } else if (ROSEN_EQ(dampingRatio_, 1.0f)) {
144         // critical-damped
145         return ((coeffScale_ * time) += initialOffset_) *= coeffDecay;
146     } else {
147         // over-damped
148         double coeffDecayAlt = exp(coeffDecayAlt_ * time);
149         return (coeffScale_ * coeffDecay) += (coeffScaleAlt_ * coeffDecayAlt);
150     }
151 }
152 
153 template<>
EstimateDuration() const154 float RSSpringModel<float>::EstimateDuration() const
155 {
156     if (dampingRatio_ < 0.0f) {
157         ROSEN_LOGE("RSSpringModel::%{public}s, uninitialized spring model", __func__);
158         return 0.0f;
159     }
160     if (response_ < 0.0f || (ROSEN_EQ(initialOffset_, 0.0f) && ROSEN_EQ(initialVelocity_, 0.0f)) ||
161         !std::isfinite(response_) || !std::isfinite(dampingRatio_) || !std::isfinite(initialVelocity_)) {
162         ROSEN_LOGE("RSSpringModel::%{public}s, parameters is invalid", __func__);
163         return 0.0f;
164     }
165     float estimatedDuration = 0.0f;
166     if (dampingRatio_ < 1.0f) { // Under-damped
167         estimatedDuration = EstimateDurationForUnderDampedModel();
168     } else if (ROSEN_EQ(dampingRatio_, 1.0f)) { // Critically-damped
169         estimatedDuration = EstimateDurationForCriticalDampedModel();
170     } else { // Over-damped
171         estimatedDuration = EstimateDurationForOverDampedModel();
172     }
173     return std::clamp(estimatedDuration, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
174 }
175 
176 template<>
BinarySearchTime(float left,float right,float target) const177 float RSSpringModel<float>::BinarySearchTime(float left, float right, float target) const
178 {
179     bool isIncrease = CalculateDisplacement(left) < CalculateDisplacement(right);
180 
181     while (left < right - 1e-3) {
182         float midTime = left + (right - left) / 2.0f;
183         float midValue = CalculateDisplacement(midTime);
184         if (!std::isfinite(midTime) || !std::isfinite(midValue)) {
185             return right;
186         }
187         if (ROSEN_EQ(midValue, target, FLOAT_NEAR_ZERO_THRESHOLD)) {
188             return midTime;
189         }
190         if ((midValue < target) != isIncrease) {
191             right = midTime;
192         } else {
193             left = midTime;
194         }
195     }
196 
197     return right;
198 }
199 
200 template<>
EstimateDurationForUnderDampedModel() const201 float RSSpringModel<float>::EstimateDurationForUnderDampedModel() const
202 {
203     if (response_ <= 0.0f) {
204         ROSEN_LOGE("RSSpringModel<float>::EstimateDurationForUnderDampedModel, uninitialized response.");
205         return 0.0f;
206     }
207 
208     float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_),
209         SPRING_MIN_THRESHOLD); // avoiding 0 in logarithmic expressions
210     double naturalAngularVelocity = 2.0 * FLOAT_PI / response_;
211     double dampingAngularVelocity = sqrt(1.0 - dampingRatio_ * dampingRatio_) * naturalAngularVelocity;
212     if (ROSEN_EQ(dampingAngularVelocity, 0.0)) {
213         ROSEN_LOGE("RSSpringModel<float>::EstimateDurationForUnderDampedModel, dampingAngularVelocity equal zero.");
214         // critical damping spring will almost rest at 2 * natural period
215         return response_ * 2;
216     }
217     double tmpCoeffA = -1.0 / (dampingRatio_ * naturalAngularVelocity);
218     double tmpCoeffB =
219         sqrt(pow(initialOffset_, 2) +
220              (pow((initialVelocity_ + dampingRatio_ * naturalAngularVelocity * initialOffset_) / dampingAngularVelocity,
221                  2)));
222     if (ROSEN_EQ(tmpCoeffB, 0.0)) {
223         return 0.0f;
224     }
225     double tmpCoeffC = std::fabs(threshold / tmpCoeffB);
226     if (ROSEN_EQ(tmpCoeffC, 0.0)) {
227         return 0.0f;
228     }
229     float estimatedDuration = tmpCoeffA * log(tmpCoeffC);
230     return estimatedDuration;
231 }
232 
233 template<>
EstimateDurationForCriticalDampedModel() const234 float RSSpringModel<float>::EstimateDurationForCriticalDampedModel() const
235 {
236     if (response_ <= 0.0f) {
237         ROSEN_LOGE("RSSpringModel::EstimateDurationForCriticalDampedModel, uninitialized response.");
238         return 0.0f;
239     }
240 
241     float estimatedDuration = 0.0f;
242     float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_), SPRING_MIN_THRESHOLD);
243     double naturalAngularVelocity = 2.0 * FLOAT_PI / response_;
244     double tmpCoeff = (initialVelocity_ + naturalAngularVelocity * initialOffset_);
245     if (ROSEN_EQ(tmpCoeff, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
246         if (ROSEN_EQ(naturalAngularVelocity, 0.0) || ROSEN_EQ(initialOffset_ / threshold, 0.0f)) {
247             ROSEN_LOGE("RSSpringModel::EstimateDurationForCriticalDampedModel, invalid parameters.");
248             return 0.0f;
249         }
250         estimatedDuration = 1.0f / naturalAngularVelocity * log(std::fabs(initialOffset_ / threshold));
251         return estimatedDuration;
252     }
253     if (ROSEN_EQ(naturalAngularVelocity * tmpCoeff, 0.0)) {
254         ROSEN_LOGE("RSSpringModel::EstimateDurationForCriticalDampedModel, denominator euqal zero.");
255         return 0.0f;
256     }
257     float extremumTime = initialVelocity_ / (naturalAngularVelocity * tmpCoeff);
258     extremumTime = std::clamp(extremumTime, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
259     float extremumValue = CalculateDisplacement(extremumTime);
260     threshold = extremumValue > 0.0f ? threshold : -threshold;
261     if (std::fabs(extremumValue) < std::fabs(threshold)) {
262         estimatedDuration = BinarySearchTime(SPRING_MIN_DURATION, extremumTime, threshold);
263     } else {
264         estimatedDuration = BinarySearchTime(extremumTime, SPRING_MAX_DURATION, threshold);
265     }
266     return estimatedDuration;
267 }
268 
269 template<>
EstimateDurationForOverDampedModel() const270 float RSSpringModel<float>::EstimateDurationForOverDampedModel() const
271 {
272     if (response_ <= 0.0f) {
273         ROSEN_LOGE("RSSpringModel::EstimateDurationForOverDampedModel, uninitialized response.");
274         return 0.0f;
275     }
276     float estimatedDuration = 0.0f;
277     float threshold = fmax(toFloat(minimumAmplitudeRatio_ * initialOffset_), SPRING_MIN_THRESHOLD);
278     double naturalAngularVelocity = 2.0 * FLOAT_PI / response_;
279     double tmpCoeffA = dampingRatio_ + sqrt(pow(dampingRatio_, 2) - 1.0);
280     double tmpCoeffB = dampingRatio_ - sqrt(pow(dampingRatio_, 2) - 1.0);
281     double tmpCoeffC = initialOffset_ * naturalAngularVelocity + initialVelocity_ * tmpCoeffA;
282     double tmpCoeffD = initialOffset_ * naturalAngularVelocity + initialVelocity_ * tmpCoeffB;
283     double tmpCoeffE = 2.0 * naturalAngularVelocity * sqrt(pow(dampingRatio_, 2) - 1.0);
284     if (ROSEN_EQ(tmpCoeffE, 0.0)) {
285         ROSEN_LOGE("RSSpringModel<float>::EstimateDurationForOverDampedModel(), invalid parameters.");
286         return 0.0f;
287     }
288     double tmpCoeffF = 1.0 / tmpCoeffE;
289     if (ROSEN_EQ(tmpCoeffC, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
290         double tmpCoeff = initialOffset_ * naturalAngularVelocity * tmpCoeffA + initialVelocity_;
291         if (ROSEN_EQ(-tmpCoeffB * naturalAngularVelocity, 0.0) || ROSEN_EQ(tmpCoeff, 0.0)) {
292             ROSEN_LOGE("RSSpringModel<float>::EstimateDurationForOverDampedModel(), denominator equal zero.");
293             return 0.0f;
294         }
295         estimatedDuration =
296             1.0f / (-tmpCoeffB * naturalAngularVelocity) * log(std::fabs(tmpCoeffF * threshold / tmpCoeff));
297         return estimatedDuration;
298     }
299     if (ROSEN_EQ(tmpCoeffD, 0.0, DOUBLE_NEAR_ZERO_THRESHOLD)) {
300         double tmpCoeff = -initialOffset_ * naturalAngularVelocity * tmpCoeffB - initialVelocity_;
301         if (ROSEN_EQ(-tmpCoeffA * naturalAngularVelocity, 0.0) || ROSEN_EQ(tmpCoeff, 0.0)) {
302             return 0.0f;
303         }
304         estimatedDuration =
305             1.0f / (-tmpCoeffA * naturalAngularVelocity) * log(std::fabs(tmpCoeffF * threshold / tmpCoeff));
306         return estimatedDuration;
307     }
308     float extremumTime = (tmpCoeffC / tmpCoeffD > DOUBLE_NEAR_ZERO_THRESHOLD) ? tmpCoeffF * log(tmpCoeffC / tmpCoeffD)
309                                                                               : SPRING_MIN_DURATION;
310     extremumTime = std::clamp(extremumTime, SPRING_MIN_DURATION, SPRING_MAX_DURATION);
311     float extremumValue = CalculateDisplacement(extremumTime);
312     threshold = extremumValue > 0.0f ? threshold : -threshold;
313     if (std::fabs(extremumValue) < std::fabs(threshold)) {
314         estimatedDuration = BinarySearchTime(SPRING_MIN_DURATION, extremumTime, threshold);
315     } else {
316         estimatedDuration = BinarySearchTime(extremumTime, SPRING_MAX_DURATION, threshold);
317     }
318     return estimatedDuration;
319 }
320 
321 template class RSSpringModel<float>;
322 template class RSSpringModel<Color>;
323 template class RSSpringModel<Matrix3f>;
324 template class RSSpringModel<RRect>;
325 template class RSSpringModel<Vector4<Color>>;
326 template class RSSpringModel<std::shared_ptr<RSFilter>>;
327 template class RSSpringModel<std::shared_ptr<RSRenderPropertyBase>>;
328 } // namespace Rosen
329 } // namespace OHOS
330