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
299 SetRunning(state == AnimationTargetState::RUNNING);
300
301 state_.animationState_ = state;
302 IAnimationInternal::AnimationStateChangedInfo info;
303 info.source = GetOwner();
304 info.state = state;
305 info.previous = previous;
306 NotifyStateChanged(info);
307
308 if (state == AnimationTargetState::FINISHED) {
309 Invoke<IOnChanged>(owner->OnFinished());
310 }
311 if (notifyStarted) {
312 Invoke<IOnChanged>(owner->OnStarted());
313 }
314 return true;
315 }
316 return false;
317 }
318
GetModifiers() const319 BASE_NS::vector<IAnimationModifier::Ptr> AnimationState::GetModifiers() const
320 {
321 if (!modifierCache_.HasTarget()) {
322 // Do not create an attachment container unless one has already been created by someone
323 if (const auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
324 if (const auto container = attach->GetAttachmentContainer(false)) {
325 modifierCache_.SetTarget(
326 container, { "", TraversalType::NO_HIERARCHY, { IAnimationModifier::UID }, true });
327 }
328 }
329 }
330
331 return modifierCache_.FindAll();
332 }
333
ApplyStepModifiers(float progress) const334 IAnimationModifier::StepData AnimationState::ApplyStepModifiers(float progress) const
335 {
336 IAnimationModifier::StepData step(progress);
337 for (auto&& mod : GetModifiers()) {
338 mod->ProcessOnStep(step);
339 }
340 return step;
341 }
342
ApplyDurationModifiers(TimeSpan duration) const343 IAnimationModifier::DurationData AnimationState::ApplyDurationModifiers(TimeSpan duration) const
344 {
345 using DurationData = IAnimationModifier::DurationData;
346 DurationData durationData;
347 durationData.duration = duration;
348 for (auto&& mod : GetModifiers()) {
349 DurationData data = durationData;
350 if (mod->ProcessOnGetDuration(data)) {
351 durationData = data;
352 }
353 }
354 return durationData;
355 }
356
GetAnimationBaseDuration() const357 TimeSpan AnimationState::GetAnimationBaseDuration() const
358 {
359 if (auto timed = interface_cast<ITimedAnimation>(GetOwner())) {
360 return GetValue(timed->Duration());
361 }
362 CORE_LOG_W("Cannot update total duration of an animation that does not implement ITimedAnimation");
363 return TimeSpan::Zero();
364 }
365
UpdateTotalDuration()366 void AnimationState::UpdateTotalDuration()
367 {
368 if (!params_.totalDuration) {
369 return;
370 }
371 auto durationData = ApplyDurationModifiers(GetAnimationBaseDuration());
372 state_.duration = durationData;
373 state_.totalDuration =
374 durationData.loopCount > 0 ? durationData.duration * durationData.loopCount : TimeSpan::Infinite();
375 SetValue(params_.totalDuration, state_.totalDuration);
376 }
377
Attach(const IObject::Ptr & attachment,const IObject::Ptr & dataContext)378 bool AnimationState::Attach(const IObject::Ptr& attachment, const IObject::Ptr& dataContext)
379 {
380 bool success = false;
381 if (auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
382 if (const auto attachments = interface_cast<IAttachmentContainer>(attach->GetAttachmentContainer(true))) {
383 if (const auto modifier = interface_cast<IAnimationModifier>(attachment)) {
384 if (success = attachments->Attach(attachment, dataContext); success) {
385 if (auto notifyChanged = interface_cast<INotifyOnChange>(modifier)) {
386 notifyChanged->OnChanged()->AddHandler(updateTotalDuration_, uintptr_t(this));
387 }
388 }
389 UpdateTotalDuration();
390 } else {
391 // Attaching something else than a modifier
392 return attachments->Attach(attachment, dataContext);
393 }
394 }
395 }
396 return success;
397 }
398
Detach(const IObject::Ptr & attachment)399 bool AnimationState::Detach(const IObject::Ptr& attachment)
400 {
401 bool success = false;
402 if (auto attach = interface_pointer_cast<IAttach>(params_.owner)) {
403 if (const auto attachments = interface_cast<IAttachmentContainer>(attach->GetAttachmentContainer(false))) {
404 success = attachments->Detach(attachment);
405 if (const auto modifier = interface_cast<IAnimationModifier>(attachment)) {
406 if (auto notifyChanged = interface_cast<INotifyOnChange>(modifier)) {
407 notifyChanged->OnChanged()->RemoveHandler(uintptr_t(this));
408 }
409 UpdateTotalDuration();
410 }
411 }
412 }
413 return success;
414 }
415
416 // PropertyAnimationState
417
GetInterpolator() const418 IInterpolator::Ptr PropertyAnimationState::GetInterpolator() const
419 {
420 return interpolator_;
421 }
422
SetInterpolator(const TypeId & id)423 bool PropertyAnimationState::SetInterpolator(const TypeId& id)
424 {
425 interpolator_ = id != TypeId {} ? GetObjectRegistry().CreateInterpolator(id) : nullptr;
426 return interpolator_ != nullptr;
427 }
428
EvaluateValue(const EvaluationData & data) const429 AnyReturnValue PropertyAnimationState::EvaluateValue(const EvaluationData& data) const
430 {
431 if (!data.IsValid()) {
432 return AnyReturn::FAIL;
433 }
434
435 auto step = ApplyStepModifiers(data.progress);
436 auto progress = step.progress;
437
438 if (progress <= 0.f) {
439 return data.target->CopyFrom(*data.from);
440 }
441 if (progress >= 1.f) {
442 return data.target->CopyFrom(*data.to);
443 }
444 if (data.curve) {
445 progress = data.curve->Transform(progress);
446 }
447 if (interpolator_) {
448 return interpolator_->Interpolate(*data.target, *data.from, *data.to, progress);
449 }
450 CORE_LOG_W("No interpolator set for animation state");
451 return AnyReturn::FAIL;
452 }
453
454 } // namespace Internal
455
456 META_END_NAMESPACE()
457