#pragma once #include #include #include #include #include "core/canvas.hpp" #include "core/chain.hpp" #include "core/params.hpp" #include "core/style.hpp" #include "core/video.hpp" namespace effector { // Everything needed to recreate a render: which images went in, the cascade // with its settings, the canvas and the seed. Deliberately no pixels — a // project is a small text file that points at the sources on disk. struct Project { std::vector sources; // absolute paths std::vector stages; // The named looks the stages draw their formatting from. Kept beside the // chain rather than inside it because that is the whole point of a style: // several stages share one, and editing it moves all of them at once. StyleSet styles; int width = 1920; int height = 1080; // The named format the canvas came from, empty for a custom pixel size. // The pixels above stay the truth for the render; this is what the // interface puts back in its dropdown, and what a print format needs its // resolution for. std::string format; int dpi = kDefaultDpi; // Whether the project is a video, and how long and how fast. VideoSettings video; std::optional seed; }; // JSON, written for humans: two-space indent, one source path per line, and // the params of a stage spelled with the same keys the effect declares. // Source paths are written relative to `directory` when they live under it, so // a project folder can be moved or shared whole. std::string projectToJson(const Project& project, const std::string& directory = {}); // Throws EffectError on malformed JSON, an unknown effect or a missing chain. // Relative source paths are resolved against `directory`; params the effect // does not declare are dropped, and missing ones keep the effect's default. A // style of an unknown kind, or a stage naming a style the file does not hold, // is dropped the same way: the stage keeps its own values and still renders. Project projectFromJson(const std::string& text, const std::string& directory = {}); // Both throw EffectError: the file is the user's, so failures are reported // rather than silently swallowed. void saveProject(const std::string& path, const Project& project); Project loadProject(const std::string& path); // The name to offer for the next export after `previous`: its stem with the // trailing `-N` counted on (`city.png` → `city-1.png`, `city-4.png` → // `city-5.png`), skipping any that already exist in its folder. std::string nextExportPath(const std::string& previous); } // namespace effector