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 "track_animation_state.h"
17
18 #include <limits>
19
20 META_BEGIN_NAMESPACE()
21
22 namespace Internal {
23
SetTrackDataParams(TrackDataParams && params)24 void TrackAnimationState::SetTrackDataParams(TrackDataParams&& params)
25 {
26 trackParams_ = BASE_NS::move(params);
27 }
28
AddKeyframe(float timestamp,const IAny::ConstPtr & value)29 size_t TrackAnimationState::AddKeyframe(float timestamp, const IAny::ConstPtr& value)
30 {
31 if (!keyframeArray_ || !value) {
32 CORE_LOG_E("Invalid keyframe array target.");
33 return ITrackAnimation::INVALID_INDEX;
34 }
35
36 timestamp = BASE_NS::Math::clamp01(timestamp);
37 size_t index = 0;
38
39 // Find index for timestamp which maintains ascending timestamp order
40 auto timestamps = GetTimeStamps()->GetValue();
41
42 for (auto t : timestamps) {
43 if (t > timestamp) {
44 break;
45 }
46 index++;
47 }
48
49 if (keyframeArray_->InsertAnyAt(index, *value)) {
50 GetTimeStamps()->InsertValueAt(index, timestamp);
51 ValidateValues();
52 } else {
53 CORE_LOG_E("Failed to add keyframe to TrackAnimation");
54 index = ITrackAnimation::INVALID_INDEX;
55 }
56
57 return index;
58 }
59
RemoveKeyframe(size_t index)60 bool TrackAnimationState::RemoveKeyframe(size_t index)
61 {
62 if (!keyframeArray_ || index >= GetTimeStamps()->GetSize()) {
63 return false;
64 }
65 if (keyframeArray_->RemoveAt(index)) {
66 GetTimeStamps()->RemoveAt(index);
67 return true;
68 }
69 return false;
70 }
71
UpdateValid()72 bool TrackAnimationState::UpdateValid()
73 {
74 if (!(keyframeArray_ && trackStart_ && trackEnd_ && currentValue_)) {
75 return false;
76 }
77 auto& timestamps = GetTimeStamps();
78 if (timestamps) {
79 auto size = timestamps->GetSize();
80 if (size > 1 && keyframeArray_->GetSize() == size) {
81 // Update our timestamp range
82 startProgress_ = timestamps->GetValueAt(0);
83 endProgress_ = timestamps->GetValueAt(size - 1);
84 return true;
85 }
86 }
87 return false;
88 }
89
ResetCurrentTrack()90 void TrackAnimationState::ResetCurrentTrack()
91 {
92 currentRangeStartTs_ = std::numeric_limits<float>::max();
93 currentRangeEndTs_ = {};
94 currentIndex_ = ITrackAnimation::INVALID_INDEX;
95 }
96
Reset()97 void TrackAnimationState::Reset()
98 {
99 trackStart_.reset();
100 trackEnd_.reset();
101 currentValue_.reset();
102 }
103
ValidateValues()104 bool TrackAnimationState::ValidateValues()
105 {
106 if (!keyframeArray_) {
107 return false;
108 }
109 if (trackStart_ && trackStart_->GetTypeId() != keyframeArray_->GetTypeId(TypeIdRole::ITEM)) {
110 Reset();
111 }
112 if (!trackStart_) {
113 if (auto size = keyframeArray_->GetSize()) {
114 if (trackStart_ = keyframeArray_->Clone({ CloneValueType::DEFAULT_VALUE, TypeIdRole::ITEM }); trackStart_) {
115 trackEnd_ = trackStart_->Clone(false);
116 keyframeArray_->GetAnyAt(0, *trackStart_);
117 keyframeArray_->GetAnyAt(size - 1, *trackEnd_);
118 currentValue_ = trackStart_->Clone(true);
119 }
120 } else {
121 Reset();
122 }
123 }
124 return trackStart_ != nullptr;
125 }
126
SetKeyframes(const IArrayAny::Ptr & keyframes)127 bool TrackAnimationState::SetKeyframes(const IArrayAny::Ptr& keyframes)
128 {
129 bool valid = false;
130 if (keyframes) {
131 if (keyframes != keyframeArray_) {
132 keyframeArray_ = keyframes;
133 valid = ValidateValues();
134 } else {
135 valid = true;
136 }
137 }
138 if (auto& timestamps = GetTimeStamps()) {
139 if (const auto size = timestamps->GetSize()) {
140 startProgress_ = timestamps->GetValueAt(0);
141 endProgress_ = timestamps->GetValueAt(size - 1);
142 }
143 }
144 if (!valid) {
145 Reset();
146 }
147 return valid;
148 }
149
IsBetween(float value,float rangeStart,float rangeEnd,bool includeEnd)150 inline static constexpr bool IsBetween(float value, float rangeStart, float rangeEnd, bool includeEnd)
151 {
152 if (includeEnd) {
153 return value >= rangeStart && value <= rangeEnd;
154 }
155 return value >= rangeStart && value < rangeEnd;
156 }
157
IsInCurrentRange(float progress) const158 bool TrackAnimationState::IsInCurrentRange(float progress) const noexcept
159 {
160 if (currentIndex_ == ITrackAnimation::INVALID_INDEX || !keyframeArray_) {
161 return false;
162 }
163
164 // Include also end in the range if last keyframe
165 return IsBetween(
166 progress, currentRangeStartTs_, currentRangeEndTs_, currentIndex_ == keyframeArray_->GetSize() - 1);
167 }
168
UpdateIndex(float progress)169 BASE_NS::pair<uint32_t, float> TrackAnimationState::UpdateIndex(float progress)
170 {
171 uint32_t index = static_cast<uint32_t>(currentIndex_);
172 if (IsInCurrentRange(progress)) {
173 return { index, GetCurrentTrackProgress(progress) };
174 }
175
176 const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
177 auto& timestamps = GetTimeStamps();
178 if (!size || !timestamps || timestamps->GetSize() != size) {
179 index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX), progress);
180 } else {
181 size_t lo = 0;
182 auto hi = size - 1;
183 auto startTs = timestamps->GetValueAt(lo);
184 auto endTs = timestamps->GetValueAt(hi);
185 if (progress < startTs || progress > endTs) {
186 index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX), progress);
187 } else {
188 while (lo <= hi) {
189 const auto mid = lo + (hi - lo) / 2;
190 const auto endIndex = mid < size - 1 ? mid + 1 : mid;
191 startTs = timestamps->GetValueAt(mid);
192 endTs = timestamps->GetValueAt(endIndex);
193 if (IsBetween(progress, startTs, endTs, mid >= endIndex)) {
194 // Found correct keyframe
195 index = JumpTo(mid, progress);
196 break;
197 }
198 if (progress < startTs) {
199 hi = mid - 1;
200 } else {
201 lo = mid + 1;
202 }
203 }
204 }
205 }
206 return { index, GetCurrentTrackProgress(progress) };
207 }
208
SetPrePostFrameValues(float progress)209 void TrackAnimationState::SetPrePostFrameValues(float progress)
210 {
211 auto& timestamps = GetTimeStamps();
212 auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
213 if (size && timestamps && timestamps->GetSize() == size) {
214 if (progress < timestamps->GetValueAt(0)) {
215 keyframeArray_->GetAnyAt(0, *trackEnd_);
216 keyframeArray_->GetAnyAt(0, *trackStart_);
217 }
218 if (progress > timestamps->GetValueAt(size - 1)) {
219 keyframeArray_->GetAnyAt(size - 1, *trackEnd_);
220 keyframeArray_->GetAnyAt(size - 1, *trackStart_);
221 }
222 }
223 }
224
JumpTo(size_t index,float progress)225 uint32_t TrackAnimationState::JumpTo(size_t index, float progress)
226 {
227 if (index == ITrackAnimation::INVALID_INDEX) {
228 currentIndex_ = index;
229 ResetCurrentTrack();
230 SetPrePostFrameValues(progress);
231 return static_cast<uint32_t>(currentIndex_);
232 }
233 if (index == currentIndex_) {
234 return currentIndex_;
235 }
236
237 const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
238 auto& timestamps = GetTimeStamps();
239 if (!size || !timestamps || timestamps->GetSize() != size || !trackStart_ || !trackEnd_) {
240 return ITrackAnimation::INVALID_INDEX;
241 }
242 index = BASE_NS::Math::min(index, size - 1);
243
244 bool last = index == size - 1;
245 auto start = last && index > 0 ? index - 1 : index;
246 auto end = BASE_NS::Math::min(start + 1, size - 1);
247
248 keyframeArray_->GetAnyAt(end, *trackEnd_);
249 keyframeArray_->GetAnyAt(start, *trackStart_);
250 currentRangeStartTs_ = timestamps->GetValueAt(start);
251 currentRangeEndTs_ = timestamps->GetValueAt(end);
252 currentIndex_ = last ? end : index;
253
254 return static_cast<uint32_t>(currentIndex_);
255 }
256
GetCurrentTrackProgress(float progress) const257 float TrackAnimationState::GetCurrentTrackProgress(float progress) const noexcept
258 {
259 if (currentIndex_ == ITrackAnimation::INVALID_INDEX) {
260 return 0.f;
261 }
262 float currentTrackRange = currentRangeEndTs_ - currentRangeStartTs_;
263 auto rangeProgress = BASE_NS::Math::clamp(progress, startProgress_, endProgress_);
264 if (currentTrackRange < BASE_NS::Math::EPSILON) {
265 return 1.f;
266 }
267 return 1.f / currentTrackRange * (rangeProgress - currentRangeStartTs_);
268 }
269
270 } // namespace Internal
271
272 META_END_NAMESPACE()
273