Route animation timeline scrubbing through app core
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
@@ -70,6 +71,15 @@ struct DocumentAnimationOnionFrameRange {
|
||||
int last_frame = 0;
|
||||
};
|
||||
|
||||
inline constexpr float document_animation_timeline_frame_width = 35.0F;
|
||||
|
||||
struct DocumentAnimationTimelineScrubPlan {
|
||||
int total_duration = 1;
|
||||
float cursor_x = 0.0F;
|
||||
float frame_width = document_animation_timeline_frame_width;
|
||||
int target_frame = 0;
|
||||
};
|
||||
|
||||
class DocumentAnimationServices {
|
||||
public:
|
||||
virtual ~DocumentAnimationServices() = default;
|
||||
@@ -162,6 +172,37 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationTimelineScrubPlan> plan_animation_timeline_scrub(
|
||||
int total_duration,
|
||||
float cursor_x,
|
||||
float frame_width = document_animation_timeline_frame_width)
|
||||
{
|
||||
if (total_duration <= 0) {
|
||||
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("animation timeline duration must be greater than zero"));
|
||||
}
|
||||
|
||||
if (!std::isfinite(cursor_x)) {
|
||||
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("animation timeline cursor position must be finite"));
|
||||
}
|
||||
|
||||
if (!std::isfinite(frame_width) || frame_width <= 0.0F) {
|
||||
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("animation timeline frame width must be positive and finite"));
|
||||
}
|
||||
|
||||
const auto raw_frame = static_cast<std::int64_t>(std::floor(cursor_x / frame_width));
|
||||
const auto target_frame = std::clamp<std::int64_t>(raw_frame, 0, total_duration - 1);
|
||||
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::success(
|
||||
DocumentAnimationTimelineScrubPlan {
|
||||
.total_duration = total_duration,
|
||||
.cursor_x = cursor_x,
|
||||
.frame_width = frame_width,
|
||||
.target_frame = static_cast<int>(target_frame),
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] inline float animation_onion_frame_alpha(
|
||||
const DocumentAnimationOnionFrameRange& range,
|
||||
int frame) noexcept
|
||||
|
||||
@@ -347,10 +347,13 @@ kEventResult NodeAnimationTimeline::handle_event(Event* e)
|
||||
parent::handle_event(e);
|
||||
static int signaled_frame = -1;
|
||||
auto me = static_cast<MouseEvent*>(e);
|
||||
auto ge = static_cast<GestureEvent*>(e);
|
||||
auto update = [&](){
|
||||
auto loc = me->m_pos - m_pos;
|
||||
m_frame = glm::clamp((int)glm::floor(loc.x / 35.f), 0, Canvas::I->anim_duration() - 1);
|
||||
const int total_duration = Canvas::I ? Canvas::I->anim_duration() : 0;
|
||||
const auto scrub = pp::app::plan_animation_timeline_scrub(total_duration, loc.x);
|
||||
if (!scrub)
|
||||
return;
|
||||
m_frame = scrub.value().target_frame;
|
||||
if (on_frame_changed && signaled_frame != m_frame)
|
||||
on_frame_changed(this, m_frame);
|
||||
signaled_frame = m_frame;
|
||||
|
||||
Reference in New Issue
Block a user