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 #include "staggered_animation_state.h"
16 
17 META_BEGIN_NAMESPACE()
18 
19 namespace Internal {
20 
Initialize(AnimationStateParams && params)21 bool StaggeredAnimationState::Initialize(AnimationStateParams&& params)
22 {
23     if (Super::Initialize(BASE_NS::move(params))) {
24         onChildrenChanged_ = CreateShared<META_NS::EventImpl<IOnChanged>>();
25 
26         container_ = CreateContainer();
27         if (auto required = interface_cast<IRequiredInterfaces>(container_)) {
28             // Require all children to implement IAnimation
29             required->SetRequiredInterfaces({ IAnimation::UID, IStartableAnimation::UID });
30         }
31         if (auto proxy = interface_cast<IContainerProxyParent>(container_)) {
32             // The container should use our parent animation as the parent object for children (and not itself)
33             proxy->SetProxyParent(interface_pointer_cast<IContainer>(GetOwner()));
34         }
35         container_->OnAdded()->AddHandler(MakeCallback<IOnChildChanged>(this, &StaggeredAnimationState::ChildAdded));
36         container_->OnRemoved()->AddHandler(
37             MakeCallback<IOnChildChanged>(this, &StaggeredAnimationState::ChildRemoved));
38         container_->OnMoved()->AddHandler(MakeCallback<IOnChildMoved>(this, &StaggeredAnimationState::ChildMoved));
39 
40         childrenChanged_ = MakeCallback<IOnChanged>(this, &StaggeredAnimationState::ChildrenChanged);
41     }
42     return container_ != nullptr;
43 }
44 
Uninitialize()45 void StaggeredAnimationState::Uninitialize()
46 {
47     for (auto& child : children_) {
48         if (child.animation_) {
49             child.durationChanged_.Unsubscribe();
50             child.validChanged_.Unsubscribe();
51             if (auto controller = child.controller_.lock()) {
52                 // If the animation was associated with a controller before it was
53                 // added to this container, add it back to that controller
54                 controller->AddAnimation(child.animation_);
55             }
56         }
57     }
58     Super::Uninitialize();
59 }
60 
UpdateTotalDuration()61 void StaggeredAnimationState::UpdateTotalDuration()
62 {
63     baseDuration_ = TimeSpan::Zero(); // Reset base duration
64     Super::UpdateTotalDuration();
65 }
66 
ChildrenChanged()67 void StaggeredAnimationState::ChildrenChanged()
68 {
69     UpdateTotalDuration();
70     Invoke<IOnChanged>(onChildrenChanged_);
71 }
72 
ChildAdded(const ChildChangedInfo & info)73 void StaggeredAnimationState::ChildAdded(const ChildChangedInfo& info)
74 {
75     // Take a strong reference to the animation
76     IAnimation::Ptr animation = interface_pointer_cast<IAnimation>(info.object);
77     if (!animation) {
78         return;
79     }
80 
81     // Remove the animation from it's controller as the staggered animation is handling it
82     auto controller = GetValue(animation->Controller()).lock();
83     SetValue(animation->Controller(), nullptr);
84 
85     size_t inContainerCount = 0;
86     for (auto& child : GetChildren()) {
87         if (child.animation_ == animation) {
88             inContainerCount++;
89         }
90     }
91 
92     AnimationSegment segment { animation, controller };
93     if (inContainerCount == 1) {
94         segment.durationChanged_.Subscribe(animation->TotalDuration(), childrenChanged_);
95         segment.validChanged_.Subscribe(animation->Valid(), childrenChanged_);
96     }
97     children_.push_back(std::move(segment));
98     ChildrenChanged();
99 }
100 
ChildRemoved(const ChildChangedInfo & info)101 void StaggeredAnimationState::ChildRemoved(const ChildChangedInfo& info)
102 {
103     IAnimation::Ptr animation = interface_pointer_cast<IAnimation>(info.object);
104     for (auto it = children_.begin(); it != children_.end(); it++) {
105         if (it->animation_ == animation) {
106             RemoveChild(it);
107             ChildrenChanged();
108             break;
109         }
110     }
111 }
112 
RemoveChild(typename SegmentVector::iterator & item)113 void StaggeredAnimationState::RemoveChild(typename SegmentVector::iterator& item)
114 {
115     auto& segment = *item;
116     segment.durationChanged_.Unsubscribe();
117     segment.validChanged_.Unsubscribe();
118 
119     // If the animation was associated with a controller before it was
120     // added to this container, return it to that controller
121     SetValue(item->animation_->Controller(), segment.controller_.lock());
122 
123     children_.erase(item);
124 
125     // Empty staggered animation, remove it from it's controller
126     if (children_.empty()) {
127         if (auto controller = controller_.lock()) {
128             if (auto animation = GetOwner()) {
129                 controller->RemoveAnimation(animation);
130             }
131         }
132     }
133 }
134 
ChildMoved(const ChildMovedInfo & info)135 void StaggeredAnimationState::ChildMoved(const ChildMovedInfo& info)
136 {
137     auto fromIndex = info.from;
138     auto toIndex = info.to;
139     const auto size = children_.size();
140     fromIndex = BASE_NS::Math::min(fromIndex, size - 1);
141     toIndex = BASE_NS::Math::min(toIndex, size - 1);
142     if (fromIndex == toIndex) {
143         return;
144     }
145     auto& child = children_[fromIndex];
146     if (fromIndex > toIndex) {
147         const auto first = children_.rbegin() + static_cast<SegmentVector::difference_type>(size - fromIndex - 1);
148         const auto last = children_.rbegin() + static_cast<SegmentVector::difference_type>(size - toIndex);
149         std::rotate(first, first + 1, last);
150     } else {
151         const auto first = children_.begin() + static_cast<SegmentVector::difference_type>(fromIndex);
152         const auto last = children_.begin() + static_cast<SegmentVector::difference_type>(toIndex + 1);
153         std::rotate(first, first + 1, last);
154     }
155     ChildrenChanged();
156 }
157 
IsValid() const158 bool StaggeredAnimationState::IsValid() const
159 {
160     // If any of the children of this container are valid, then
161     // the container is valid
162     for (const auto& child : children_) {
163         auto& animation = child.animation_;
164         if (animation && GetValue(animation->Valid())) {
165             return true;
166         }
167     }
168     return false;
169 }
170 
MapTo01Range(float value,float inputStart,float inputEnd,bool reverse)171 constexpr float MapTo01Range(float value, float inputStart, float inputEnd, bool reverse) noexcept
172 {
173     if (reverse) {
174         const auto offset = 1.f - inputEnd;
175         inputStart += offset;
176         inputEnd += offset;
177     }
178     return (1 / (inputEnd - inputStart)) * (value - inputStart);
179 }
180 
TransformChild(const StaggeredAnimationState::AnimationSegment & segment,float parentProgress,IAnimationInternal::AnimationTargetState parentState,bool reverse)181 constexpr IAnimationInternal::MoveParams TransformChild(const StaggeredAnimationState::AnimationSegment& segment,
182     float parentProgress, IAnimationInternal::AnimationTargetState parentState, bool reverse) noexcept
183 {
184     using AnimationTargetState = IAnimationInternal::AnimationTargetState;
185     IAnimationInternal::MoveParams params;
186     params.step.progress = MapTo01Range(parentProgress, segment.startProgress_, segment.endProgress_, reverse);
187 
188     if (parentState == AnimationTargetState::RUNNING) {
189         params.state = AnimationTargetState::RUNNING;
190         if (params.step.progress < 0.f) {
191             params.state = AnimationTargetState::STOPPED;
192         } else if (params.step.progress >= 1.f) {
193             params.state = AnimationTargetState::FINISHED;
194         }
195     } else {
196         params.state = parentState;
197     }
198 
199     if (reverse) {
200         params.step.progress = 1.f - params.step.progress;
201     }
202     params.step.reverse = reverse;
203     return params;
204 }
205 
206 // ParallelAnimationState
207 
CreateContainer() const208 IContainer::Ptr ParallelAnimationState::CreateContainer() const
209 {
210     return META_NS::GetObjectRegistry().Create<IContainer>(ClassId::ObjectContainer);
211 }
212 
GetAnimationBaseDuration() const213 TimeSpan ParallelAnimationState::GetAnimationBaseDuration() const
214 {
215     if (baseDuration_ != TimeSpan::Zero()) {
216         return baseDuration_;
217     }
218     // Duration of a ParallelAnimation is the max of the total duration of children
219     TimeSpan totalDuration = TimeSpan::Zero();
220     for (const auto& segment : GetChildren()) {
221         const auto& animation = segment.animation_;
222         if (animation) {
223             const TimeSpan childDuration = GetValue(animation->TotalDuration(), TimeSpan::Zero());
224             totalDuration = BASE_NS::Math::max(totalDuration, childDuration);
225         }
226     }
227     return totalDuration;
228 }
229 
ChildrenChanged()230 void ParallelAnimationState::ChildrenChanged()
231 {
232     Super::ChildrenChanged();
233     auto containerDuration = GetAnimationBaseDuration();
234     for (auto&& segment : GetChildren()) {
235         segment.startProgress_ = 0;
236         segment.endProgress_ = 1.f;
237         if (containerDuration.IsFinite()) {
238             const auto duration = segment.animation_ ? GetValue(segment.animation_->TotalDuration()) : TimeSpan::Zero();
239             segment.endProgress_ =
240                 static_cast<float>(duration.ToMilliseconds()) / static_cast<float>(containerDuration.ToMilliseconds());
241         }
242     }
243 }
244 
Evaluate()245 AnyReturnValue ParallelAnimationState::Evaluate()
246 {
247     AnyReturnValue status = AnyReturn::NOTHING_TO_DO;
248     const float containerProgress = GetValue(GetOwner()->Progress());
249     const auto step = ApplyStepModifiers(containerProgress);
250     for (const auto& segment : GetChildren()) {
251         if (const auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
252             if (internal->Move(TransformChild(segment, containerProgress, GetAnimationTargetState(), step.reverse))) {
253                 status = AnyReturn::SUCCESS;
254             }
255         }
256     }
257     return status;
258 }
259 
260 // SequentialAnimationState
261 
CreateContainer() const262 IContainer::Ptr SequentialAnimationState::CreateContainer() const
263 {
264     return META_NS::GetObjectRegistry().Create<IContainer>(ClassId::ObjectFlatContainer);
265 }
266 
GetAnimationBaseDuration() const267 TimeSpan SequentialAnimationState::GetAnimationBaseDuration() const
268 {
269     if (baseDuration_ != TimeSpan::Zero()) {
270         return baseDuration_;
271     }
272     // Duration of a SequentialAnimation is the sum of the total duration of children
273     TimeSpan totalDuration = TimeSpan::Zero();
274     for (const auto& segment : GetChildren()) {
275         const auto& animation = segment.animation_;
276         if (animation) {
277             totalDuration += GetValue(animation->TotalDuration(), TimeSpan::Zero());
278         }
279     }
280     return totalDuration;
281 }
282 
ChildrenChanged()283 void SequentialAnimationState::ChildrenChanged()
284 {
285     Super::ChildrenChanged();
286     auto containerDuration = GetAnimationBaseDuration();
287     float startProgress = 0.f;
288 
289     for (auto&& segment : GetChildren()) {
290         segment.startProgress_ = startProgress;
291         segment.endProgress_ = 1.f;
292         if (containerDuration.IsFinite()) {
293             const auto duration = segment.animation_ ? GetValue(segment.animation_->TotalDuration()) : TimeSpan::Zero();
294             segment.endProgress_ = startProgress + static_cast<float>(duration.ToMilliseconds()) /
295                                                        static_cast<float>(containerDuration.ToMilliseconds());
296             startProgress = segment.endProgress_;
297         } else {
298             CORE_LOG_E("Infinite animation in a sequential animation");
299             startProgress = 1.f;
300         }
301     }
302 }
303 
GetActiveAnimation(float progress,bool reverse) const304 SequentialAnimationState::ActiveAnimation SequentialAnimationState::GetActiveAnimation(
305     float progress, bool reverse) const
306 {
307     const auto& children = GetChildren();
308 
309     int64_t index = 0;
310     for (const auto& anim : GetChildren()) {
311         const auto transform = TransformChild(anim, progress, GetAnimationTargetState(), reverse);
312         if (transform.state == IAnimationInternal::AnimationTargetState::RUNNING) {
313             return { &anim, index };
314         }
315         index++;
316     }
317     return { nullptr, children.empty() ? -1 : index };
318 }
319 
Evaluate()320 AnyReturnValue SequentialAnimationState::Evaluate()
321 {
322     AnyReturnValue status = AnyReturn::NOTHING_TO_DO;
323     const float containerProgress = GetValue(GetOwner()->Progress());
324     const auto step = ApplyStepModifiers(containerProgress);
325     const auto& children = GetChildren();
326 
327     auto active = GetActiveAnimation(containerProgress, step.reverse);
328     if (!active) {
329         // No active animation, make sure all animations are stopped/finished
330         for (const auto& segment : children) {
331             if (auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
332                 internal->Move(TransformChild(segment, containerProgress, GetAnimationTargetState(), step.reverse));
333             }
334         }
335         return children.empty() ? AnyReturn::SUCCESS : AnyReturn::NOTHING_TO_DO;
336     }
337 
338     // Iterate first->last or last->first depending on direction
339     int mod = 1;
340     size_t index = 0;
341     size_t target = children.size();
342     if (step.reverse) {
343         mod = -1;
344         std::swap(index, target);
345     }
346     for (; index != target; index += mod) {
347         const auto& segment = children[index];
348         const auto params = TransformChild(children[index], containerProgress, GetAnimationTargetState(), step.reverse);
349         if (params.state == IAnimationInternal::AnimationTargetState::RUNNING ||
350             segment.animation_ != active.segment->animation_) {
351             // If one instance of the animation is in running state, don't touch the animation for any other state
352             if (const auto internal = interface_cast<IAnimationInternal>(segment.animation_)) {
353                 internal->Move(params);
354             }
355         }
356     }
357 
358     return status;
359 }
360 
361 } // namespace Internal
362 
363 META_END_NAMESPACE()
364