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