/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "track_animation.h"
#include
META_BEGIN_NAMESPACE()
namespace Internal {
AnimationState::AnimationStateParams TrackAnimation::GetParams()
{
AnimationState::AnimationStateParams params;
params.owner = GetSelf();
params.runningProperty = META_ACCESS_PROPERTY(Running);
params.progressProperty = META_ACCESS_PROPERTY(Progress);
params.totalDuration = META_ACCESS_PROPERTY(TotalDuration);
return params;
}
bool TrackAnimation::Build(const IMetadata::Ptr& data)
{
if (Super::Build(data)) {
TrackAnimationState::TrackDataParams params { META_ACCESS_PROPERTY(Timestamps) };
GetState().SetTrackDataParams(BASE_NS::move(params));
auto updateKf = MakeCallback(this, &TrackAnimation::UpdateKeyframes);
META_ACCESS_PROPERTY(Timestamps)->OnChanged()->AddHandler(updateKf);
constexpr BASE_NS::string_view name = "Keyframes";
keyframes_ = GetObjectRegistry().GetPropertyRegister().Create(ClassId::StackProperty, name);
if (keyframes_) {
keyframes_->OnChanged()->AddHandler(updateKf);
AddProperty(keyframes_);
}
UpdateKeyframes();
return keyframes_ != nullptr;
}
return false;
}
void TrackAnimation::Initialize()
{
ResetTrack();
}
void TrackAnimation::OnAnimationStateChanged(const IAnimationInternal::AnimationStateChangedInfo& info)
{
using AnimationTargetState = IAnimationInternal::AnimationTargetState;
if (auto p = GetTargetProperty()) {
switch (info.state) {
case AnimationTargetState::FINISHED:
[[fallthrough]];
case AnimationTargetState::STOPPED:
// Evaluate current value
Evaluate();
// Remove ourselves from the target property's stack
RemoveModifier(p.stack);
// Then set the correct keyframe value to the underlying property
if (auto value = GetState().GetCurrentValue()) {
p.property->SetValue(*value);
}
break;
case AnimationTargetState::RUNNING:
// Evaluate current value
Evaluate();
// Add ourselves to the target property's stack
p.stack->AddModifier(GetSelf());
break;
default:
break;
}
}
}
EvaluationResult TrackAnimation::ProcessOnGet(IAny& value)
{
if (auto& currentValue = GetState().GetCurrentValue()) {
if (auto result = value.CopyFrom(*currentValue)) {
return result == AnyReturn::NOTHING_TO_DO ? EvaluationResult::EVAL_CONTINUE
: EvaluationResult::EVAL_VALUE_CHANGED;
}
}
return EvaluationResult::EVAL_CONTINUE;
}
void TrackAnimation::Start()
{
GetState().Start();
}
void TrackAnimation::Stop()
{
GetState().Stop();
}
void TrackAnimation::Pause()
{
GetState().Pause();
}
void TrackAnimation::Restart()
{
GetState().Restart();
}
void TrackAnimation::Finish()
{
GetState().Finish();
}
void TrackAnimation::Seek(float position)
{
GetState().Seek(position);
}
void TrackAnimation::Step(const IClock::ConstPtr& clock)
{
GetState().Step(clock);
}
void TrackAnimation::Evaluate()
{
float progress = META_ACCESS_PROPERTY_VALUE(Progress);
if (auto curve = META_ACCESS_PROPERTY_VALUE(Curve)) {
progress = curve->Transform(progress);
}
const auto trackState = GetState().UpdateIndex(progress);
const PropertyAnimationState::EvaluationData data { GetState().GetCurrentValue(), GetState().GetCurrentTrackStart(),
GetState().GetCurrentTrackEnd(), trackState.second,
META_ACCESS_PROPERTY(KeyframeCurves)->GetValueAt(trackState.first) };
const auto status = GetState().EvaluateValue(data);
UpdateCurrentTrack(trackState.first);
if (status == AnyReturn::SUCCESS) {
NotifyChanged();
}
}
void TrackAnimation::RemoveModifier(const IStackProperty::Ptr& stack)
{
if (stack) {
stack->RemoveModifier(GetSelf());
}
}
void TrackAnimation::OnPropertyChanged(const TargetProperty& property, const IStackProperty::Ptr& previous)
{
if (previous && GetState().IsRunning()) {
// Property changed while running, clean up previous property's stack
RemoveModifier(previous);
if (auto p = interface_cast(previous)) {
p->SetValue(*GetState().GetCurrentValue());
}
}
Initialize();
if (auto p = GetTargetProperty()) {
auto& property = p.property;
auto& value = property->GetValue();
bool alreadyCompatible = value.GetTypeId() == GetState().GetKeyframeItemTypeId();
if (!alreadyCompatible) {
IAny::Ptr array {};
if (!value.IsArray()) {
// Clone the target property's value to an array of the value's underlying type
array = value.Clone(AnyCloneOptions { CloneValueType::DEFAULT_VALUE, TypeIdRole::ARRAY });
} else {
CORE_LOG_E("TrackAnimation: Cannot animate array types");
}
if (!array) {
CORE_LOG_E("TrackAnimation: Failed to create an array of target property type");
}
if (auto kf = interface_pointer_cast(array)) {
keyframes_->SetValue(*kf);
} else {
keyframes_->ResetValue();
}
}
}
UpdateValid();
}
size_t TrackAnimation::AddKeyframe(float timestamp, const IAny::ConstPtr& value)
{
auto index = GetState().AddKeyframe(timestamp, value);
if (index != ITrackAnimation::INVALID_INDEX) {
keyframes_->NotifyChange();
}
return index;
}
bool TrackAnimation::RemoveKeyframe(size_t index)
{
bool success = false;
if (GetState().RemoveKeyframe(index)) {
keyframes_->NotifyChange();
success = true;
} else {
CORE_LOG_E("TrackAnimation: Cannot remove keyframe from index %u.", static_cast(index));
}
return success;
}
void TrackAnimation::RemoveAllKeyframes()
{
Stop();
META_ACCESS_PROPERTY(Timestamps)->Reset();
keyframes_->ResetValue();
UpdateValid();
}
void TrackAnimation::UpdateKeyframes()
{
IArrayAny::Ptr kfArray;
if (auto stack = interface_cast(keyframes_)) {
// Get the topmost IArrayAny value, that should be our keyframe array
auto values = stack->GetValues({}, true);
for (auto it = values.rbegin(); it != values.rend(); ++it) {
if (auto arr = interface_pointer_cast(*it)) {
kfArray = arr;
break;
}
}
}
GetState().SetKeyframes(kfArray);
UpdateValid();
}
void TrackAnimation::UpdateValid()
{
bool valid = false;
auto& timestamps = META_ACCESS_PROPERTY(Timestamps);
if (const auto p = GetTargetProperty(); p && timestamps) {
valid = GetState().UpdateValid();
}
if (valid != META_ACCESS_PROPERTY_VALUE(Valid)) {
if (!valid) {
Stop();
ResetTrack();
}
SetValue(META_ACCESS_PROPERTY(Valid), valid);
}
GetState().ResetCurrentTrack();
}
void TrackAnimation::ResetTrack()
{
GetState().ResetCurrentTrack();
SetValue(META_ACCESS_PROPERTY(CurrentKeyframeIndex), -1);
}
void TrackAnimation::UpdateCurrentTrack(uint32_t index)
{
auto currentIndex = META_ACCESS_PROPERTY_VALUE(CurrentKeyframeIndex);
if (currentIndex != index) {
SetValue(META_ACCESS_PROPERTY(CurrentKeyframeIndex), index);
if (auto f = META_ACCESS_PROPERTY(KeyframeHandlers)->GetValueAt(index)) {
CallMetaFunction(f);
}
}
}
ReturnError TrackAnimation::Export(IExportContext& c) const
{
return Serializer(c) & AutoSerialize() & NamedValue("Keyframes", keyframes_);
}
ReturnError TrackAnimation::Import(IImportContext& c)
{
return Serializer(c) & AutoSerialize() & NamedValue("Keyframes", keyframes_);
}
ReturnError TrackAnimation::Finalize(IImportFunctions& f)
{
auto res = Super::Finalize(f);
if (res) {
UpdateKeyframes();
}
return res;
}
} // namespace Internal
META_END_NAMESPACE()