1 /*
2 * Copyright (c) 2024 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_state.h"
17
18 #include <meta/api/make_callback.h>
19 #include <meta/api/util.h>
20 #include <meta/interface/animation/intf_animation_modifier.h>
21 #include <meta/interface/builtin_objects.h>
22 #include <meta/interface/interface_macros.h>
23 #include <meta/interface/intf_attachment_container.h>
24 #include <meta/interface/property/property_events.h>
25
26 META_BEGIN_NAMESPACE()
27
28 namespace Internal {
29
Initialize(AnimationStateParams && params)30 bool AnimationState::Initialize(AnimationStateParams&& params)
31 {
32 params_ = BASE_NS::move(params);
33 auto owner = GetOwner();
34 if (!owner) {
35 CORE_LOG_F("AnimationState: Invalid target animation");
36 return false;
37 }
38 auto controllerProp = owner->Controller();
39 if (!controllerProp) {
40 return false;
41 }
42 if (!controllerProp->GetValue().lock()) {
43 // No controller set, init to default
44 controllerProp->SetValue(META_NS::GetAnimationController());
45 }
46
47 updateTotalDuration_ = MakeCallback<IOnChanged>(this, &AnimationState::UpdateTotalDuration);
48 if (auto timed = interface_cast<ITimedAnimation>(owner)) {
49 timed->Duration()->OnChanged()->AddHandler(updateTotalDuration_, uintptr_t(this));
50 }
51
52 auto updateController = MakeCallback<IOnChanged>(this, &AnimationState::UpdateController);
53 controllerProp->OnChanged()->AddHandler(updateController);
54 state_.clock_ = META_NS::GetObjectRegistry().Create<META_NS::IManualClock>(META_NS::ClassId::ManualClock);
55 CORE_ASSERT(state_.clock_);
56 UpdateTotalDuration();
57 UpdateController();
58 return true;
59 }
60
Uninitialize()61 void AnimationState::Uninitialize()
62 {
63 if (auto timed = interface_pointer_cast<ITimedAnimation>(GetOwner())) {
64 timed->Duration()->OnChanged()->RemoveHandler(uintptr_t(this));
65 }
66 }
67
NotifyEvaluationNeeded() const68 void AnimationState::NotifyEvaluationNeeded() const
69 {
70 if (auto internal = interface_pointer_cast<IAnimationInternal>(GetOwner())) {
71 internal->OnEvaluationNeeded();
72 }
73 }
74
NotifyStateChanged(const IAnimationInternal::AnimationStateChangedInfo & info) const75 void AnimationState::NotifyStateChanged(const IAnimationInternal::AnimationStateChangedInfo& info) const
76 {
77 if (auto internal = interface_pointer_cast<IAnimationInternal>(GetOwner())) {
78 internal->OnAnimationStateChanged(info);
79 // Need also evaluation
80 internal->OnEvaluationNeeded();
81 }
82 }
83
UpdateController()84 void AnimationState::UpdateController()
85 {
86 IAnimation::Ptr animation = GetOwner();
87 if (!animation) {
88 CORE_LOG_E("Invalid target animation");
89 return;
90 }
91 auto oldController = controller_.lock();
92 auto newController = animation->Controller()->GetValue().lock();
93
94 if (oldController != newController) {
95 if (oldController) {
96 oldController->RemoveAnimation(animation);
97 }
98 if (newController) {
99 newController->AddAnimation(animation);
100 }
101 controller_ = newController;
102 }
103 }
104
ResetClock()105 void AnimationState::ResetClock()
106 {
107 state_.ResetLastTick();
108 state_.SetTime(TimeSpan::Zero());
109 }
110
Step(const IClock::ConstPtr & clock)111 AnimationState::StepStatus AnimationState::Step(const IClock::ConstPtr& clock)
112 {
113 if (!IsRunning()) {
114 return {};
115 }
116
117 const auto time = clock ? clock->GetTime() : TimeSpan::Zero();
118 float progress = static_cast<float>(state_.Tick(time).ToMilliseconds()) /
119 static_cast<float>(state_.GetBaseDuration().ToMilliseconds());
120
121 return Move(IAnimationInternal::MoveParams::FromProgress(progress));
122 }
123
GetTargetState(const IAnimationInternal::MoveParams & move)124 constexpr IAnimationInternal::AnimationTargetState GetTargetState(const IAnimationInternal::MoveParams& move) noexcept
125 {
126 using AnimationTargetState = IAnimationInternal::AnimationTargetState;
127 const auto& step = move.step;
128 const auto& state = move.state;
129 if (state == AnimationTargetState::UNDEFINED) {
130 // Figure out target state based on step data automatically
131 const float progress = step.progress;
132 const bool reverse = step.reverse;
133 if (progress >= 1.f) {
134 return reverse ? AnimationTargetState::RUNNING : AnimationTargetState::FINISHED;
135 }
136 if (progress <= 0.f) {
137 return reverse ? AnimationTargetState::FINISHED : AnimationTargetState::RUNNING;
138 }
139 return AnimationTargetState::RUNNING;
140 }
141
142 // Just go to the state defined by the caller
143 return state;
144 }
145
Move(const IAnimationInternal::MoveParams & move)146 AnimationState::StepStatus AnimationState::Move(const IAnimationInternal::MoveParams& move)
147 {
148 using AnimationTargetState = IAnimationInternal::AnimationTargetState;
149 auto animationState = GetTargetState(move);
150 const auto& step = move.step;
151 float progress = step.progress;
152
153 if (state_.shouldInit_) {
154 state_.loops = state_.duration.loopCount;
155 state_.shouldInit_ = false;
156 }
157
158 if (animationState == AnimationTargetState::FINISHED) {
159 // Check if we need to loop
160 if (state_.loops && (state_.loops < 0 || --state_.loops)) {
161 animationState = AnimationTargetState::RUNNING;
162 const auto overflow = progress - BASE_NS::Math::floor(progress);
163 state_.SetTime(overflow * state_.GetBaseDuration());
164 if (overflow > 0.f) {
165 // If progress based on clock would be e.g. 1.2, jump to 0.2 to not jank the animation
166 progress = overflow;
167 }
168 }
169 }
170
171 AnimationState::StepStatus status;
172 if (progress = BASE_NS::Math::clamp01(progress); progress != GetProgress()) {
173 SetProgress(progress);
174 status.changed = true;
175 }
176
177 status.changed |= SetState(animationState);
178 status.state = state_.animationState_;
179 status.progress = GetProgress();
180 if (status.changed) {
181 NotifyEvaluationNeeded();
182 }
183 return status;
184 }
185
Seek(float position)186 void AnimationState::Seek(float position)
187 {
188 auto animation = GetOwner();
189 if (!animation) {
190 CORE_LOG_E("Invalid target animation");
191 return;
192 }
193
194 position = BASE_NS::Math::clamp01(position);
195 state_.ResetLastTick();
196 const auto seekedTime = state_.GetBaseDuration().ToSecondsFloat() * position;
197 state_.SetTime(TimeSpan::Seconds(seekedTime));
198 auto state = state_.animationState_;
199 if (position >= 1.f) {
200 state = IAnimationInternal::AnimationTargetState::FINISHED;
201 }
202 Move(IAnimationInternal::MoveParams::FromProgress(position, state));
203 }
204
Pause()205 bool AnimationState::Pause()
206 {
207 return SetState(IAnimationInternal::AnimationTargetState::PAUSED);
208 }
209
Start()210 bool AnimationState::Start()
211 {
212 return SetState(IAnimationInternal::AnimationTargetState::RUNNING);
213 }
214
Stop()215 bool AnimationState::Stop()
216 {
217 return Move(IAnimationInternal::MoveParams::FromProgress(0.f, IAnimationInternal::AnimationTargetState::STOPPED))
218 .StatusChanged();
219 }
220
Finish()221 bool AnimationState::Finish()
222 {
223 return Move(IAnimationInternal::MoveParams::FromProgress(1.f, IAnimationInternal::AnimationTargetState::FINISHED))
224 .StatusChanged();
225 }
226
Restart()227 bool AnimationState::Restart()
228 {
229 if (Stop()) {
230 return Start();
231 }
232 return false;
233 }
234
GetOwner() const235 IAnimation::Ptr AnimationState::GetOwner() const noexcept
236 {
237 return params_.owner.lock();
238 }
239
IsRunning() const240 bool AnimationState::IsRunning() const noexcept
241 {
242 return GetValue(params_.runningProperty, false);
243 }
244
IsPaused() const245 bool AnimationState::IsPaused() const noexcept
246 {
247 return state_.animationState_ == IAnimationInternal::AnimationTargetState::PAUSED;
248 }
249
SetRunning(bool running)250 void AnimationState::SetRunning(bool running) noexcept
251 {
252 SetValue(params_.runningProperty, running);
253 }
254
GetProgress() const255 float AnimationState::GetProgress() const noexcept
256 {
257 return GetValue(params_.progressProperty, 0.f);
258 }
259
SetProgress(float progress)260 void AnimationState::SetProgress(float progress) noexcept
261 {
262 SetValue(params_.progressProperty, progress);
263 }
264
SetState(IAnimationInternal::AnimationTargetState state)265 bool AnimationState::SetState(IAnimationInternal::AnimationTargetState state)
266 {
267 using AnimationTargetState = IAnimationInternal::AnimationTargetState;
268 const auto previous = state_.animationState_;
269 if (previous == state) {
270 return false;
271 }
272 if (const auto owner = GetOwner()) {
273 bool notifyStarted = false;
274 switch (state) {
275 case AnimationTargetState::RUNNING:
276 if (previous != AnimationTargetState::PAUSED) {
277 state_.shouldInit_ = true;
278 notifyStarted = true;
279 if (previous == AnimationTargetState::FINISHED) {
280 SetProgress(0.f);
281 ResetClock();
282 }
283 }
284 break;
285 case AnimationTargetState::PAUSED:
286 state_.ResetLastTick();
287 break;
288 case AnimationTargetState::FINISHED:
289 [[fallthrough]];
290 case AnimationTargetState::STOPPED:
291 ResetClock();
292 break;
293 default:
294 CORE_LOG_E("Invalid target state for animation: AnimationTargetState::UNDEFINED");
295 ResetClock();
296 break;
297 }
298 SetRunning(state == AnimationTargetState::RUNNING);
299
300 state_.animationState_ = state;
301 IAnimationInternal::AnimationStateChangedInfo info;
302 info.source = GetOwner();
303 info.state = state;
304 info.previous = previous;
305 NotifyStateChanged(info);
306
307 if (state == AnimationTargetState::FINISHED) {
308 Invoke<IOnChanged>(owner->OnFinished());
309 }
310 if (notifyStarted) {
311 Invoke<IOnChanged>(owner->OnStarted());
312 }
313 return true;
314 }
315 return false;
316 }
317
GetModifiers() const318 BASE_NS::vector<IAnimationModifier::Ptr> AnimationState::GetModifiers() const
319 {
320 if (!modifierCache_.HasTarget()) {
321 // Do not create an attachment container unless one has already been created by someone
322 if (const auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
323 if (const auto container = attach->GetAttachmentContainer(false)) {
324 modifierCache_.SetTarget(
325 container, { "", TraversalType::NO_HIERARCHY, { IAnimationModifier::UID }, true });
326 }
327 }
328 }
329
330 return modifierCache_.FindAll();
331 }
332
ApplyStepModifiers(float progress) const333 IAnimationModifier::StepData AnimationState::ApplyStepModifiers(float progress) const
334 {
335 IAnimationModifier::StepData step(progress);
336 for (auto&& mod : GetModifiers()) {
337 mod->ProcessOnStep(step);
338 }
339 return step;
340 }
341
ApplyDurationModifiers(TimeSpan duration) const342 IAnimationModifier::DurationData AnimationState::ApplyDurationModifiers(TimeSpan duration) const
343 {
344 using DurationData = IAnimationModifier::DurationData;
345 DurationData durationData;
346 durationData.duration = duration;
347 for (auto&& mod : GetModifiers()) {
348 DurationData data = durationData;
349 if (mod->ProcessOnGetDuration(data)) {
350 durationData = data;
351 }
352 }
353 return durationData;
354 }
355
GetAnimationBaseDuration() const356 TimeSpan AnimationState::GetAnimationBaseDuration() const
357 {
358 if (auto timed = interface_cast<ITimedAnimation>(GetOwner())) {
359 return GetValue(timed->Duration());
360 }
361 CORE_LOG_W("Cannot update total duration of an animation that does not implement ITimedAnimation");
362 return TimeSpan::Zero();
363 }
364
UpdateTotalDuration()365 void AnimationState::UpdateTotalDuration()
366 {
367 if (!params_.totalDuration) {
368 return;
369 }
370 auto durationData = ApplyDurationModifiers(GetAnimationBaseDuration());
371 state_.duration = durationData;
372 state_.totalDuration =
373 durationData.loopCount > 0 ? durationData.duration * durationData.loopCount : TimeSpan::Infinite();
374 SetValue(params_.totalDuration, state_.totalDuration);
375 }
376
Attach(const IObject::Ptr & attachment,const IObject::Ptr & dataContext)377 bool AnimationState::Attach(const IObject::Ptr& attachment, const IObject::Ptr& dataContext)
378 {
379 bool success = false;
380 if (auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
381 if (const auto attachments = interface_cast<IAttachmentContainer>(attach->GetAttachmentContainer(true))) {
382 if (const auto modifier = interface_cast<IAnimationModifier>(attachment)) {
383 if (success = attachments->Attach(attachment, dataContext); success) {
384 if (auto notifyChanged = interface_cast<INotifyOnChange>(modifier)) {
385 notifyChanged->OnChanged()->AddHandler(updateTotalDuration_, uintptr_t(this));
386 }
387 }
388 UpdateTotalDuration();
389 } else {
390 // Attaching something else than a modifier
391 return attachments->Attach(attachment, dataContext);
392 }
393 }
394 }
395 return success;
396 }
397
Detach(const IObject::Ptr & attachment)398 bool AnimationState::Detach(const IObject::Ptr& attachment)
399 {
400 bool success = false;
401 if (auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
402 success = attach->Detach(attachment);
403 if (const auto modifier = interface_cast<IAnimationModifier>(attachment)) {
404 if (auto notifyChanged = interface_cast<INotifyOnChange>(modifier)) {
405 notifyChanged->OnChanged()->RemoveHandler(uintptr_t(this));
406 }
407 }
408 UpdateTotalDuration();
409 }
410 return success;
411 }
412
413 // PropertyAnimationState
414
GetInterpolator() const415 IInterpolator::Ptr PropertyAnimationState::GetInterpolator() const
416 {
417 return interpolator_;
418 }
419
SetInterpolator(const TypeId & id)420 bool PropertyAnimationState::SetInterpolator(const TypeId& id)
421 {
422 interpolator_ = id != TypeId {} ? GetObjectRegistry().CreateInterpolator(id) : nullptr;
423 return interpolator_ != nullptr;
424 }
425
EvaluateValue(const EvaluationData & data) const426 AnyReturnValue PropertyAnimationState::EvaluateValue(const EvaluationData& data) const
427 {
428 if (!data.IsValid()) {
429 return AnyReturn::FAIL;
430 }
431
432 auto step = ApplyStepModifiers(data.progress);
433 auto progress = step.progress;
434
435 if (progress <= 0.f) {
436 return data.target->CopyFrom(*data.from);
437 }
438 if (progress >= 1.f) {
439 return data.target->CopyFrom(*data.to);
440 }
441 if (data.curve) {
442 progress = data.curve->Transform(progress);
443 }
444 if (interpolator_) {
445 return interpolator_->Interpolate(*data.target, *data.from, *data.to, progress);
446 }
447 CORE_LOG_W("No interpolator set for animation state");
448 return AnyReturn::FAIL;
449 }
450
451 } // namespace Internal
452
453 META_END_NAMESPACE()
454