From c9f6f9e3bed731b02b558ec159a331b1bd7901cf Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 1 Oct 2026 23:07:08 -0400 Subject: [PATCH] M0.11: ROADMAP, CONTRIBUTING, docs/architecture and docs/testing Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- CONTRIBUTING.md | 42 +++++++++++++++++++++ ROADMAP.md | 25 +++++++++++++ docs/architecture.md | 89 ++++++++++++++++++++++++++++++++++++++++++++ docs/testing.md | 44 ++++++++++++++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md create mode 100644 ROADMAP.md create mode 100644 docs/architecture.md create mode 100644 docs/testing.md diff --git a/AGENTS.md b/AGENTS.md index ff6e109b..12e44f60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,7 @@ a command, everything is agent-drivable, and the quality gates (`cargo xtask ci` ## See also -- [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/contributing.md](docs/contributing.md): setup, gates, commits, how to add things +- [CONTRIBUTING.md](CONTRIBUTING.md): setup, gates, commits, how to add things - [docs/architecture.md](docs/architecture.md): layers, data model, commands, pipeline - [docs/testing.md](docs/testing.md): oracle tests, criteria, benchmarks - [docs/agents.md](docs/agents.md): driving EffectCraft over MCP / the control channel, and the agent work loop diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..118bc1d6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contributing to EffectCraft + +Thanks for helping. Please read [AGENTS.md](AGENTS.md) first: its rules on assets and clean-room +work apply to everyone, people and AI agents alike, and they are not negotiable. + +## Setup + +You need [Rust](https://rustup.rs/) 1.95 or newer. + +```sh +cargo run -p effectcraft # the desktop app, with the demo project +cargo test --workspace +cargo xtask ci # what every commit must pass +``` + +## Ground rules + +- **Clean room.** Work from After Effects' public documentation and its observable behaviour only. + Never use Adobe artwork, presets or code, and never copy GPL/LGPL/AGPL code. +- **Every asset is attributed.** Add a `.attribution` sidecar and an [ATTRIBUTION.md](ATTRIBUTION.md) + row; `cargo xtask assets` checks it. +- **Layering.** Nothing below the interface layer depends on UI or OS crates; see + [docs/architecture.md](docs/architecture.md). +- **Everything is a command.** New features are engine commands with an id, label, menu path, + shortcut and parameters, so the UI, the CLI and the MCP server get them at once. Interactive + widgets register an automation id. +- **Tests with every change**, and green `cargo xtask ci` before every commit. One task per commit. + +## How to add… + +- **An effect:** add an `EffectSpec` in `crates/effects` (AE display name, category, parameters + with AE's names, defaults and ranges) and its render function, with a determinism test and a + behaviour test. If it reads the clock directly, add it to `TIME_DEPENDENT`. +- **A command:** register it with `cmd!` in `crates/engine/src/commands/`, place it in the menu + tree in `crates/engine/src/menus.rs`, and test it including undo. +- **A panel or dialog:** `crates/ui-egui/src/panels/`; look at the result with the headless + snapshot tool described in [docs/testing.md](docs/testing.md). + +## Talk to us + +Questions, ideas and bug reports are welcome on [Discord](https://discord.gg/artcraft) and in +GitHub issues. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000..39c0f0e7 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,25 @@ +# Roadmap + +EffectCraft aims to do what After Effects does, with the same panels, menus and behaviour, written +from scratch in Rust. This is where it stands. The milestones overlap; several are worked on at +once. + +| | Milestone | State | +|---|---|---| +| M0 | Skeleton: crates, compositor, the After Effects style shell, control channel, `cargo xtask` | Done (web build pending) | +| M1 | Keyframes: temporal and spatial interpolation, Easy Ease, roving, velocity | Done | +| M2 | Compositing: 38 blend modes, track mattes, parenting, adjustment layers | Done | +| M3 | Project operations: settings dialogs, layer commands, `.ecproj`, undo, After Effects menu bar | Mostly done | +| M4 | Preview: precomps, motion blur, cached playback | Mostly done | +| M5 | Timeline depth: graph editor, keyframe clipboard and dialogs, time remapping, pick-whips, expression editor | Done | +| M6 | Shapes, masks and footage: shape operators, masks and the pen tool, video and image import | Done | +| M7 | 3D: 3D layers, cameras, lights, shadows, depth of field, 3D views and camera tools | In review | +| M8 | Expressions with the After Effects object model | Done | +| M9 | Text and effects: text animators, 241 effects | In progress (layer styles, more selectors) | +| M10 | Export: render queue, H.264, ProRes, image sequences, GIF, audio | Done | +| M11 | Animation tools: presets, Motion Sketch, Wiggler, Lottie, audio playback | Next | +| M12 | Performance: layer cache and parallel compositing (done), GPU compositing, disk cache, motion tracking | In progress | +| M13+ | Time effects, puppet, paint, roto, preferences and shortcut editor, the web app, a plugin API | Planned | +| M14 | Built for agents: MCP server, command-line tool, control channel | Done | + +Come tell us what matters most to you on [Discord](https://discord.gg/artcraft). diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..4c8646b3 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,89 @@ +# Architecture + +EffectCraft is a stack of small Rust crates. The engine knows nothing about the user interface; +the egui frontend, the command-line tool and the MCP server all sit on top of the same `Session` +and drive it through the same command registry. + +## 1. Crates and layering + +Dependencies only point downward (or along the few same-layer edges listed below). `cargo xtask +layers` enforces this, and fails the build if a crate below L5 depends on egui, eframe, winit, rfd, +cpal or muda. Everything in L0 to L4 also builds for `wasm32-unknown-unknown` (`cargo xtask wasm`). + +| Layer | Crate (`effectcraft-…`) | Responsibility | +|---|---|---| +| L0 | `time` | `Tick` (254 016 000 000 per second), rational frame rates incl. NTSC, SMPTE and drop-frame timecode | +| L0 | `geom` | Vectors, matrices, quaternions, the layer transform (anchor, position, scale, orientation, rotation) | +| L0 | `color` | sRGB and linear, HSL/HSV, luminance, the 38 blend modes, label colors | +| L1 | `raster` | Premultiplied float images, sampling, affine and projective warps, blurs, compositing (parallel with rayon) | +| L1 | `keyframe` | Animated values, keyframes with temporal ease and spatial Bezier, roving, hold, velocity | +| L1 | `path` | Bezier paths, path operators (trim, offset, round corners, zig zag, twist, merge…), stroking, coverage masks | +| L2 | `project` | The document: items, compositions, layers, the property tree, render queue model, `.ecproj` serde | +| L2 | `text` | Fonts, shaping, layout, per-glyph geometry, text animators and selectors | +| L2 | `effects` | The effect registry (241 effects) and their CPU implementations | +| L3 | `render` | Evaluation and compositing: sources, masks, effects, transforms, 3D, motion blur, mattes, blending, layer cache, audio mixdown | +| L3 | `media` | Footage decoding (FilmCraft's pure-Rust codecs), image sequences, frame cache | +| L3 | `expr` | The expression engine (JavaScript via boa) with the After Effects object model | +| L3 | `export` | Render queue encoding: H.264, ProRes, PNG/JPEG/TIFF/EXR sequences, GIF, audio | +| L4 | `engine` | `Session`: project, undo history, editor state, the command registry and menus | +| L4 | `host` | A fully wired `Session` (media, expressions, exporter) for the frontends | +| L5 | `ui-egui` | The desktop interface: docking, panels, viewer, timeline, graph editor, dialogs, control channel | +| L5 | `automation` | The MCP server, headless or bridged to the running app | +| L6 | apps `effectcraft`, `effectcraft-cli` | Desktop app; command-line tool (render, exec, get/set, MCP) | + +Allowed same-layer edges: `path → keyframe, raster`, `text → path`, `effects → project, text, path`, +`media / expr / export → render`, `export → media`, `host → engine`. + +## 2. Time + +All time is an integer `Tick`, 254 016 000 000 per second, the least common multiple of every +broadcast frame rate (×1001) and audio sample rate, so frame and sample positions are exact. +Keyframe times are in **layer time**; `layer_time = (comp_time - start_time) / stretch`, and a +Time Remap property overrides the source time. Commands snap layer times, keyframe times and +comp durations to whole frames, as After Effects does. Timecode is only a display. + +## 3. Document model + +``` +Project { settings, items, render_queue } +Item { id, name, label, parent folder, kind: Folder | Comp | Footage | Solid } +Comp { size, pixel aspect, frame rate, duration, start timecode, background, work area, + layers (index 0 = layer #1), markers, motion blur shutter, renderer } +Layer { id, name, source (footage | comp | solid | text | shape | null | camera | light), + in/out/start, stretch, switches, blend mode, track matte, parent, props } +``` + +Everything animatable is a `Property` in the layer's `PropGroup` tree, addressed by a path such as +`transform/position`, `effects/#1/blurriness`, `masks/#1/feather`, or by `@uid`. Effects, masks, +text animators and shape contents are groups in the same tree. + +## 4. Rendering + +`Renderer::comp_frame(comp, t)` walks the layers bottom to top. For each visible layer it renders +the **source** (solid, footage frame, text, shape contents, or a nested comp), applies **masks**, +then **effects** in order, then the **transform** into comp space (with motion blur sub-samples +when enabled), the **track matte**, and finally **blends** into the accumulator. Adjustment layers +apply their effects to the accumulator. Runs of 3D layers are composited per pixel through the +active camera, with lights, shadows and depth of field. + +A **layer cache** keeps each layer's finished pixels (source, masks and effects) keyed by a hash of +its evaluated inputs, excluding the transform. Static and transform-only layers render once; +editing one layer re-renders only that layer. Effects that read the clock directly are declared in +`effects::TIME_DEPENDENT`, and a test checks every registered effect against that list. + +Half, Third and Quarter resolution render proportionally fewer pixels end to end. + +## 5. Expressions + +Expressions are JavaScript, run by boa, with After Effects' object model (`thisComp`, `thisLayer`, +`time`, `value`, `wiggle`, `loopOut`, vector maths on arrays, and so on). A syntax error keeps the +text, disables the expression and shows a warning, like After Effects. + +## 6. Commands, the UI seam and automation + +Every action is a command: an id, a label, a menu path, a shortcut, a parameter description, +`enabled()` and `run()`. The menu bar is a tree of command ids. The desktop UI, the command line, +the JSON control channel and the MCP server all dispatch by id, so anything a person can do from a +menu, an agent can do too. Every interactive widget registers an automation id, so agents can also +inspect and click the interface. See [agents.md](agents.md) and +[control-protocol.md](control-protocol.md). diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 00000000..cd5e1d2a --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,44 @@ +# Testing + +```sh +cargo test --workspace # everything +cargo xtask ci # fmt, clippy -D warnings, tests, layers, assets, wasm +``` + +## What the tests cover + +- **Engine commands** (`crates/engine/src/tests*.rs`): every command family, including undo and + redo, and that every menu entry resolves to a registered command. +- **Rendering** (`crates/render`): pixel checks on small compositions, for blend modes, masks, + mattes, time remapping, 3D projection, lighting and shadows. Fast paths (the layer cache, the + affine compositor, blurs, motion blur accumulation) are checked against their reference + implementations. +- **Effects** (`crates/effects`): each effect has a determinism test and at least one behaviour + test (identity at neutral settings, known pixel results, or simulations giving the same frame + whether you seek straight to it or play up to it). A registry-wide test checks that effects + which read the clock are declared time-dependent. +- **Export** (`crates/export/tests`): every format is encoded and decoded back, checking frame + count, size and pixels. When `ffmpeg`/`ffprobe` are installed they are used as an outside + check; they are never linked or shipped. +- **Automation** (`crates/automation`, `apps/effectcraft-cli/tests`): MCP protocol round trips and + the command-line tool's JSON output. +- **Interface** (`crates/ui-egui`): headless egui_kittest tests for panels and dialogs. + +## Looking at the interface without a window + +```sh +cargo run -p effectcraft-ui-egui --example snapshot -- --out ui.png \ + --step '{"method":"engine.execute","params":{"command":"layer.select","params":{"layers":["#2"]}}}' +``` + +Each `--step` is a control-channel request; `{"method":"snap","params":{"path":"x.png"}}` writes an +intermediate image. + +## Benchmarks + +```sh +cargo run --release -p effectcraft-cli -- bench --n 10 --play 30 +``` + +prints per-layer and per-effect timings for one frame, and playback timings with and without the +layer cache.