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