Route animation panel view through app core

This commit is contained in:
2026-06-05 00:37:11 +02:00
parent a9e12f2219
commit bd6cdc20c5
8 changed files with 469 additions and 28 deletions

View File

@@ -6,6 +6,10 @@
#include <cmath>
#include <cstdint>
#include <limits>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace pp::app {
@@ -80,6 +84,39 @@ struct DocumentAnimationTimelineScrubPlan {
int target_frame = 0;
};
struct DocumentAnimationLayerInput {
int layer_index = 0;
std::uint32_t layer_id = 0;
std::string name;
bool visible = true;
std::vector<int> frame_durations;
};
struct DocumentAnimationFrameView {
int frame_index = 0;
int duration = document_animation_default_frame_duration;
bool selected = false;
};
struct DocumentAnimationLayerView {
int layer_index = 0;
std::uint32_t layer_id = 0;
std::string name;
bool visible = true;
bool current = false;
std::vector<DocumentAnimationFrameView> frames;
};
struct DocumentAnimationPanelView {
int total_duration = 1;
int current_frame = 0;
int onion_size = 0;
std::uint32_t selected_layer_id = 0;
int selected_frame = -1;
bool has_selected_frame = false;
std::vector<DocumentAnimationLayerView> layers;
};
class DocumentAnimationServices {
public:
virtual ~DocumentAnimationServices() = default;
@@ -140,6 +177,86 @@ public:
return pp::foundation::Status::success();
}
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationPanelView> plan_animation_panel_view(
const std::vector<DocumentAnimationLayerInput>& layers,
int total_duration,
int current_layer_index,
int current_frame,
std::uint32_t selected_layer_id,
int selected_frame,
int onion_size)
{
if (layers.empty()) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(
pp::foundation::Status::invalid_argument("animation panel requires at least one layer"));
}
const auto timeline_status = validate_animation_frame_index(total_duration, current_frame);
if (!timeline_status.ok()) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(timeline_status);
}
if (current_layer_index < 0 || current_layer_index >= static_cast<int>(layers.size())) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(
pp::foundation::Status::out_of_range("current animation layer index is outside the document"));
}
if (onion_size < 0) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(
pp::foundation::Status::invalid_argument("animation onion size must not be negative"));
}
DocumentAnimationPanelView view;
view.total_duration = total_duration;
view.current_frame = current_frame;
view.onion_size = onion_size;
view.selected_layer_id = selected_layer_id;
view.selected_frame = selected_frame;
view.layers.reserve(layers.size());
for (std::size_t i = 0; i < layers.size(); ++i) {
const auto& input = layers[i];
if (input.layer_index < 0) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(
pp::foundation::Status::out_of_range("animation layer index must not be negative"));
}
if (input.frame_durations.empty()) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(
pp::foundation::Status::invalid_argument("animation layer must contain at least one frame"));
}
DocumentAnimationLayerView layer;
layer.layer_index = input.layer_index;
layer.layer_id = input.layer_id;
layer.name = input.name;
layer.visible = input.visible;
layer.current = input.layer_index == current_layer_index;
layer.frames.reserve(input.frame_durations.size());
for (std::size_t frame_index = 0; frame_index < input.frame_durations.size(); ++frame_index) {
const int duration = input.frame_durations[frame_index];
const auto duration_status = validate_animation_frame_duration(duration);
if (!duration_status.ok()) {
return pp::foundation::Result<DocumentAnimationPanelView>::failure(duration_status);
}
const bool selected = selected_frame >= 0
&& input.layer_id == selected_layer_id
&& static_cast<int>(frame_index) == selected_frame;
view.has_selected_frame = view.has_selected_frame || selected;
layer.frames.push_back(DocumentAnimationFrameView {
.frame_index = static_cast<int>(frame_index),
.duration = duration,
.selected = selected,
});
}
view.layers.push_back(std::move(layer));
}
return pp::foundation::Result<DocumentAnimationPanelView>::success(std::move(view));
}
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationOnionFrameRange> plan_animation_onion_frame_range(
int frame_count,
int current_frame,