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_) {
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 auto size = timestamps->GetSize();
79 if (timestamps && size > 1 && keyframeArray_->GetSize() == size) {
80 // Update our timestamp range
81 startProgress_ = timestamps->GetValueAt(0);
82 endProgress_ = timestamps->GetValueAt(size - 1);
83 return true;
84 }
85 return false;
86 }
87
ResetCurrentTrack()88 void TrackAnimationState::ResetCurrentTrack()
89 {
90 currentRangeStartTs_ = std::numeric_limits<float>::max();
91 currentRangeEndTs_ = {};
92 currentIndex_ = ITrackAnimation::INVALID_INDEX;
93 }
94
Reset()95 void TrackAnimationState::Reset()
96 {
97 trackStart_.reset();
98 trackEnd_.reset();
99 currentValue_.reset();
100 }
101
ValidateValues()102 bool TrackAnimationState::ValidateValues()
103 {
104 if (!keyframeArray_) {
105 return false;
106 }
107 if (trackStart_ && trackStart_->GetTypeId() != keyframeArray_->GetTypeId(TypeIdRole::ITEM)) {
108 Reset();
109 }
110 if (!trackStart_) {
111 if (auto size = keyframeArray_->GetSize()) {
112 if (trackStart_ = keyframeArray_->Clone({ CloneValueType::DEFAULT_VALUE, TypeIdRole::ITEM }); trackStart_) {
113 trackEnd_ = trackStart_->Clone(false);
114 keyframeArray_->GetAnyAt(0, *trackStart_);
115 keyframeArray_->GetAnyAt(size - 1, *trackEnd_);
116 currentValue_ = trackStart_->Clone(true);
117 }
118 } else {
119 Reset();
120 }
121 }
122 return trackStart_ != nullptr;
123 }
124
SetKeyframes(const IArrayAny::Ptr & keyframes)125 bool TrackAnimationState::SetKeyframes(const IArrayAny::Ptr& keyframes)
126 {
127 bool valid = false;
128 if (keyframes) {
129 if (keyframes != keyframeArray_) {
130 keyframeArray_ = keyframes;
131 valid = ValidateValues();
132 } else {
133 valid = true;
134 }
135 }
136 if (auto& timestamps = GetTimeStamps()) {
137 if (const auto size = timestamps->GetSize()) {
138 startProgress_ = timestamps->GetValueAt(0);
139 endProgress_ = timestamps->GetValueAt(size - 1);
140 }
141 }
142 if (!valid) {
143 Reset();
144 }
145 return valid;
146 }
147
IsBetween(float value,float rangeStart,float rangeEnd,bool includeEnd)148 inline static constexpr bool IsBetween(float value, float rangeStart, float rangeEnd, bool includeEnd)
149 {
150 if (includeEnd) {
151 return value >= rangeStart && value <= rangeEnd;
152 }
153 return value >= rangeStart && value < rangeEnd;
154 }
155
IsInCurrentRange(float progress) const156 bool TrackAnimationState::IsInCurrentRange(float progress) const noexcept
157 {
158 if (currentIndex_ == ITrackAnimation::INVALID_INDEX || !keyframeArray_) {
159 return false;
160 }
161
162 // Include also end in the range if last keyframe
163 return IsBetween(
164 progress, currentRangeStartTs_, currentRangeEndTs_, currentIndex_ == keyframeArray_->GetSize() - 1);
165 }
166
UpdateIndex(float progress)167 BASE_NS::pair<uint32_t, float> TrackAnimationState::UpdateIndex(float progress)
168 {
169 uint32_t index = static_cast<uint32_t>(currentIndex_);
170 if (IsInCurrentRange(progress)) {
171 return { index, GetCurrentTrackProgress(progress) };
172 }
173
174 const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
175 auto& timestamps = GetTimeStamps();
176 if (!size || !timestamps || timestamps->GetSize() != size) {
177 index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX));
178 } else {
179 size_t lo = 0;
180 auto hi = size - 1;
181 auto startTs = timestamps->GetValueAt(lo);
182 auto endTs = timestamps->GetValueAt(hi);
183 if (progress < startTs || progress > endTs) {
184 index = JumpTo(size_t(ITrackAnimation::INVALID_INDEX));
185 } else {
186 while (lo <= hi) {
187 const auto mid = lo + (hi - lo) / 2;
188 const auto endIndex = mid < size - 1 ? mid + 1 : mid;
189 startTs = timestamps->GetValueAt(mid);
190 endTs = timestamps->GetValueAt(endIndex);
191 if (IsBetween(progress, startTs, endTs, mid >= endIndex)) {
192 // Found correct keyframe
193 index = JumpTo(mid);
194 break;
195 }
196 if (progress < startTs) {
197 hi = mid - 1;
198 } else {
199 lo = mid + 1;
200 }
201 }
202 }
203 }
204 return { index, GetCurrentTrackProgress(progress) };
205 }
206
JumpTo(size_t index)207 uint32_t TrackAnimationState::JumpTo(size_t index)
208 {
209 if (index == currentIndex_) {
210 return currentIndex_;
211 }
212
213 if (index == ITrackAnimation::INVALID_INDEX) {
214 currentIndex_ = index;
215 ResetCurrentTrack();
216 } else {
217 const auto size = keyframeArray_ ? keyframeArray_->GetSize() : 0;
218 auto& timestamps = GetTimeStamps();
219 if (!size || !timestamps || timestamps->GetSize() != size) {
220 return ITrackAnimation::INVALID_INDEX;
221 }
222 index = BASE_NS::Math::min(index, size - 1);
223
224 bool last = index == size - 1;
225 auto start = last ? index - 1 : index;
226 auto end = start + 1; // < size - 1 ? start + 1 : start;
227
228 keyframeArray_->GetAnyAt(end, *trackEnd_);
229 keyframeArray_->GetAnyAt(start, *trackStart_);
230 currentRangeStartTs_ = timestamps->GetValueAt(start);
231 currentRangeEndTs_ = timestamps->GetValueAt(end);
232 currentIndex_ = last ? end : index;
233 }
234 return static_cast<uint32_t>(currentIndex_);
235 }
236
GetCurrentTrackProgress(float progress) const237 float TrackAnimationState::GetCurrentTrackProgress(float progress) const noexcept
238 {
239 if (currentIndex_ == ITrackAnimation::INVALID_INDEX) {
240 return 0.f;
241 }
242 float currentTrackRange = currentRangeEndTs_ - currentRangeStartTs_;
243 auto rangeProgress = BASE_NS::Math::clamp(progress, startProgress_, endProgress_);
244 if (currentTrackRange < BASE_NS::Math::EPSILON) {
245 return 1.f;
246 }
247 return 1.f / currentTrackRange * (rangeProgress - currentRangeStartTs_);
248 }
249
250 } // namespace Internal
251
252 META_END_NAMESPACE()
253