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