Extract animation operation planning

This commit is contained in:
2026-06-03 10:32:06 +02:00
parent fdc1defaba
commit 4f0909f30c
8 changed files with 812 additions and 27 deletions

View File

@@ -1,5 +1,6 @@
#include "pch.h"
#include "node_panel_animation.h"
#include "app_core/document_animation.h"
#include "node_button.h"
#include "node_button_custom.h"
#include "renderer_gl/opengl_capabilities.h"
@@ -45,62 +46,180 @@ void NodePanelAnimation::init_controls()
m_frame_label = find<NodeText>("frame-index");
btn_add->on_click = [this](Node*) {
const auto plan = pp::app::plan_animation_add_frame(
Canvas::I->layer().frames_count(),
Canvas::I->m_anim_frame);
if (!plan)
return;
Canvas::I->layer().add_frame();
load_layers();
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_update();
if (plan.value().reloads_animation_layers)
load_layers();
};
btn_duplicate->on_click = [this](Node*) {
Canvas::I->layer().duplicate_frame(m_selected_frame_index);
load_layers();
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
{
const auto plan = pp::app::plan_animation_duplicate_frame(
layer->frames_count(),
m_selected_frame_index);
if (!plan)
return;
layer->duplicate_frame(plan.value().selected_frame);
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_update();
if (plan.value().reloads_animation_layers)
load_layers();
}
};
btn_remove->on_click = [this](Node*) {
Canvas::I->layer_with_id(m_selected_frame_layer_id)->remove_frame(m_selected_frame_index);
load_layers();
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
{
const auto plan = pp::app::plan_animation_remove_frame(
layer->frames_count(),
m_selected_frame_index);
if (!plan)
return;
layer->remove_frame(plan.value().selected_frame);
m_selected_frame_index = plan.value().target_frame;
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(plan.value().target_frame);
if (plan.value().reloads_animation_layers)
load_layers();
}
};
btn_up->on_click = [this](Node*) {
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
layer->set_frame_duration(m_selected_frame_index, glm::max(layer->frame_duration(m_selected_frame_index) + 1, 1));
load_layers();
{
const auto index_status = pp::app::validate_animation_frame_index(
layer->frames_count(),
m_selected_frame_index);
if (!index_status.ok())
return;
const auto plan = pp::app::plan_animation_adjust_duration(
layer->frames_count(),
m_selected_frame_index,
layer->frame_duration(m_selected_frame_index),
1);
if (!plan)
return;
layer->set_frame_duration(plan.value().selected_frame, plan.value().frame_duration);
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_update();
if (plan.value().reloads_animation_layers)
load_layers();
}
};
btn_down->on_click = [this](Node*) {
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
layer->set_frame_duration(m_selected_frame_index, glm::max(layer->frame_duration(m_selected_frame_index) - 1, 1));
load_layers();
{
const auto index_status = pp::app::validate_animation_frame_index(
layer->frames_count(),
m_selected_frame_index);
if (!index_status.ok())
return;
const auto plan = pp::app::plan_animation_adjust_duration(
layer->frames_count(),
m_selected_frame_index,
layer->frame_duration(m_selected_frame_index),
-1);
if (!plan)
return;
layer->set_frame_duration(plan.value().selected_frame, plan.value().frame_duration);
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_update();
if (plan.value().reloads_animation_layers)
load_layers();
}
};
btn_left->on_click = [this](Node*) {
if (!m_selected_frame)
return;
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
m_selected_frame_index = layer->move_frame_offset(m_selected_frame_index, -1);
Canvas::I->anim_goto_frame(m_selected_frame_index);
load_layers();
{
const auto plan = pp::app::plan_animation_move_frame(
layer->frames_count(),
m_selected_frame_index,
-1);
if (!plan)
return;
m_selected_frame_index = layer->move_frame_offset(plan.value().selected_frame, plan.value().move_offset);
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(m_selected_frame_index);
if (plan.value().reloads_animation_layers)
load_layers();
}
};
btn_right->on_click = [this](Node*) {
if (!m_selected_frame)
return;
if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id))
m_selected_frame_index = layer->move_frame_offset(m_selected_frame_index, +1);
Canvas::I->anim_goto_frame(m_selected_frame_index);
load_layers();
{
const auto plan = pp::app::plan_animation_move_frame(
layer->frames_count(),
m_selected_frame_index,
1);
if (!plan)
return;
m_selected_frame_index = layer->move_frame_offset(plan.value().selected_frame, plan.value().move_offset);
if (plan.value().marks_unsaved)
Canvas::I->m_unsaved = true;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(m_selected_frame_index);
if (plan.value().reloads_animation_layers)
load_layers();
}
};
m_onion->on_select = [this] (Node* target, int index) {
m_timeline->m_onion_size = m_onion->get_int();
Canvas::I->anim_update();
const auto plan = pp::app::plan_animation_onion_size(m_onion->get_int());
if (!plan)
return;
m_timeline->m_onion_size = plan.value().onion_size;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_update();
};
m_timeline->on_frame_changed = [this] (NodeAnimationTimeline* target, int frame) {
LOG("goto frame %d", frame);
Canvas::I->anim_goto_frame(frame);
load_layers();
const auto plan = pp::app::plan_animation_goto_frame(Canvas::I->anim_duration(), frame);
if (!plan)
return;
LOG("goto frame %d", plan.value().target_frame);
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(plan.value().target_frame);
if (plan.value().reloads_animation_layers)
load_layers();
};
btn_next->on_click = [this] (Node* target) {
Canvas::I->anim_goto_next();
load_layers();
const auto plan = pp::app::plan_animation_step_frame(Canvas::I->anim_duration(), Canvas::I->m_anim_frame, 1);
if (!plan)
return;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(plan.value().target_frame);
if (plan.value().reloads_animation_layers)
load_layers();
};
btn_prev->on_click = [this](Node* target) {
Canvas::I->anim_goto_prev();
load_layers();
const auto plan = pp::app::plan_animation_step_frame(Canvas::I->anim_duration(), Canvas::I->m_anim_frame, -1);
if (!plan)
return;
if (plan.value().updates_canvas_animation)
Canvas::I->anim_goto_frame(plan.value().target_frame);
if (plan.value().reloads_animation_layers)
load_layers();
};
btn_play->on_click = [this] (Node* target) {
static auto mode = Canvas::I->m_current_mode;
@@ -183,9 +302,13 @@ void NodePanelAnimation::on_tick(float dt)
if (m_playback_timer > (1.f / m_fps->get_float()))
{
m_playback_timer = 0;
Canvas::I->anim_goto_next();
m_timeline->m_frame = Canvas::I->m_anim_frame;
update_frames();
const auto plan = pp::app::plan_animation_step_frame(Canvas::I->anim_duration(), Canvas::I->m_anim_frame, 1);
if (plan)
{
Canvas::I->anim_goto_frame(plan.value().target_frame);
m_timeline->m_frame = Canvas::I->m_anim_frame;
update_frames();
}
}
}
}