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