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 "app_core/app_preferences.h"
#include "app_core/app_status.h"
#include "app_core/document_animation.h"
#include "app_core/document_export.h"
#include "app_core/document_cloud.h"
#include "app_core/document_layer.h"
@@ -238,6 +239,18 @@ struct PlanLayerOperationArgs {
int blend_mode = 0;
};
struct PlanAnimationOperationArgs {
std::string kind = "goto";
int frame_count = 1;
int total_duration = 1;
int current_frame = 0;
int selected_frame = 0;
int current_duration = 1;
int delta = 1;
int offset = 1;
int onion_size = 1;
};
struct SimulateAppSessionArgs {
bool has_canvas = true;
bool new_document = false;
@@ -511,6 +524,32 @@ const char* document_layer_operation_name(pp::app::DocumentLayerOperation operat
return "select";
}
const char* document_animation_operation_name(pp::app::DocumentAnimationOperation operation) noexcept
{
switch (operation) {
case pp::app::DocumentAnimationOperation::add_frame:
return "add-frame";
case pp::app::DocumentAnimationOperation::duplicate_frame:
return "duplicate-frame";
case pp::app::DocumentAnimationOperation::remove_frame:
return "remove-frame";
case pp::app::DocumentAnimationOperation::adjust_duration:
return "adjust-duration";
case pp::app::DocumentAnimationOperation::move_frame:
return "move-frame";
case pp::app::DocumentAnimationOperation::goto_frame:
return "goto-frame";
case pp::app::DocumentAnimationOperation::goto_next:
return "goto-next";
case pp::app::DocumentAnimationOperation::goto_previous:
return "goto-previous";
case pp::app::DocumentAnimationOperation::set_onion_size:
return "set-onion-size";
}
return "goto-frame";
}
const char* document_file_write_decision_name(pp::app::DocumentFileWriteDecision decision) noexcept
{
switch (decision) {
@@ -724,6 +763,20 @@ pp::foundation::Result<float> parse_float_arg(std::string_view text)
return pp::foundation::Result<float>::success(value);
}
pp::foundation::Result<int> parse_i32_arg(std::string_view text)
{
int value = 0;
const auto* begin = text.data();
const auto* end = begin + text.size();
const auto [ptr, ec] = std::from_chars(begin, end, value);
if (ec != std::errc {} || ptr != end) {
return pp::foundation::Result<int>::failure(
pp::foundation::Status::invalid_argument("invalid signed integer value"));
}
return pp::foundation::Result<int>::success(value);
}
void print_help()
{
std::cout
@@ -750,6 +803,7 @@ void print_help()
<< " plan-document-resize [--current-resolution N] [--selected-resolution-index N]\n"
<< " plan-layer-rename --old-name NAME --new-name NAME\n"
<< " plan-layer-operation --kind add|duplicate|select|reorder|remove|opacity|visibility|alpha-lock|blend-mode|highlight [--layer-count N] [--index N] [--from-index N] [--to-index N] [--source-index N] [--name NAME] [--opacity N] [--blend-mode N] [--enabled]\n"
<< " plan-animation-operation --kind add|duplicate|remove|duration|move|goto|next|prev|onion [--frame-count N] [--total-duration N] [--current-frame N] [--selected-frame N] [--current-duration N] [--delta N] [--offset N] [--onion-size N]\n"
<< " plan-share-file [--path FILE]\n"
<< " plan-picked-path [--path FILE]\n"
<< " plan-display-file [--path FILE]\n"
@@ -2535,6 +2589,136 @@ int plan_layer_operation(int argc, char** argv)
return 0;
}
pp::foundation::Status parse_plan_animation_operation_args(
int argc,
char** argv,
PlanAnimationOperationArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--kind") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
args.kind = argv[++i];
} else if (key == "--frame-count" || key == "--total-duration" || key == "--current-frame"
|| key == "--selected-frame" || key == "--current-duration" || key == "--delta"
|| key == "--offset" || key == "--onion-size") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
const auto value = parse_i32_arg(argv[++i]);
if (!value) {
return value.status();
}
if (key == "--frame-count") {
args.frame_count = value.value();
} else if (key == "--total-duration") {
args.total_duration = value.value();
} else if (key == "--current-frame") {
args.current_frame = value.value();
} else if (key == "--selected-frame") {
args.selected_frame = value.value();
} else if (key == "--current-duration") {
args.current_duration = value.value();
} else if (key == "--delta") {
args.delta = value.value();
} else if (key == "--offset") {
args.offset = value.value();
} else {
args.onion_size = value.value();
}
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
return pp::foundation::Status::success();
}
pp::foundation::Result<pp::app::DocumentAnimationOperationPlan> make_animation_operation_plan(
const PlanAnimationOperationArgs& args)
{
if (args.kind == "add") {
return pp::app::plan_animation_add_frame(args.frame_count, args.current_frame);
}
if (args.kind == "duplicate") {
return pp::app::plan_animation_duplicate_frame(args.frame_count, args.selected_frame);
}
if (args.kind == "remove") {
return pp::app::plan_animation_remove_frame(args.frame_count, args.selected_frame);
}
if (args.kind == "duration") {
return pp::app::plan_animation_adjust_duration(
args.frame_count,
args.selected_frame,
args.current_duration,
args.delta);
}
if (args.kind == "move") {
return pp::app::plan_animation_move_frame(args.frame_count, args.selected_frame, args.offset);
}
if (args.kind == "goto") {
return pp::app::plan_animation_goto_frame(args.total_duration, args.current_frame);
}
if (args.kind == "next") {
return pp::app::plan_animation_step_frame(args.total_duration, args.current_frame, 1);
}
if (args.kind == "prev") {
return pp::app::plan_animation_step_frame(args.total_duration, args.current_frame, -1);
}
if (args.kind == "onion") {
return pp::app::plan_animation_onion_size(args.onion_size);
}
return pp::foundation::Result<pp::app::DocumentAnimationOperationPlan>::failure(
pp::foundation::Status::invalid_argument("unknown animation operation kind"));
}
int plan_animation_operation(int argc, char** argv)
{
PlanAnimationOperationArgs args;
const auto status = parse_plan_animation_operation_args(argc, argv, args);
if (!status.ok()) {
print_error("plan-animation-operation", status.message);
return 2;
}
const auto plan = make_animation_operation_plan(args);
if (!plan) {
print_error("plan-animation-operation", plan.status().message);
return 2;
}
const auto& value = plan.value();
std::cout << "{\"ok\":true,\"command\":\"plan-animation-operation\""
<< ",\"state\":{\"kind\":\"" << json_escape(args.kind)
<< "\",\"frameCount\":" << args.frame_count
<< ",\"totalDuration\":" << args.total_duration
<< ",\"currentFrame\":" << args.current_frame
<< ",\"selectedFrame\":" << args.selected_frame
<< ",\"currentDuration\":" << args.current_duration
<< ",\"delta\":" << args.delta
<< ",\"offset\":" << args.offset
<< ",\"onionSize\":" << args.onion_size
<< "},\"plan\":{\"operation\":\"" << document_animation_operation_name(value.operation)
<< "\",\"frameCount\":" << value.frame_count
<< ",\"currentFrame\":" << value.current_frame
<< ",\"selectedFrame\":" << value.selected_frame
<< ",\"targetFrame\":" << value.target_frame
<< ",\"frameDuration\":" << value.frame_duration
<< ",\"durationDelta\":" << value.duration_delta
<< ",\"moveOffset\":" << value.move_offset
<< ",\"onionSize\":" << value.onion_size
<< ",\"requiresSelectedFrame\":" << json_bool(value.requires_selected_frame)
<< ",\"mutatesDocument\":" << json_bool(value.mutates_document)
<< ",\"reloadsAnimationLayers\":" << json_bool(value.reloads_animation_layers)
<< ",\"updatesCanvasAnimation\":" << json_bool(value.updates_canvas_animation)
<< ",\"marksUnsaved\":" << json_bool(value.marks_unsaved)
<< "}}\n";
return 0;
}
pp::foundation::Status parse_plan_share_file_args(
int argc,
char** argv,
@@ -4947,6 +5131,10 @@ int main(int argc, char** argv)
return plan_layer_operation(argc, argv);
}
if (command == "plan-animation-operation") {
return plan_animation_operation(argc, argv);
}
if (command == "plan-share-file") {
return plan_share_file(argc, argv);
}