Strokes carry an object (0 for older projects, nothing written for 0). The
tracker follows every object in one pass and the mattes are united; the
confidence of a frame is the least sure object's. Object picker in the Layer
panel's bar; roto.options/stroke/clearStrokes/status take or report objects.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Chain::track tracks the frames of the span without a tracked result, in a window
that restarts at the nearest prompted frame; unchanged spans are not tracked
again. Test with a recording tracker: first pass 0..20, no second pass, then a
correction on frame 8 tracks 8..20 with that prompt only.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
TrackRequest takes a list of objects (each with its prompts on its frames) and
the tracker reports (object, frame, result); frame encodings are shared, objects
may start on different frames. VideoTracker::propagate_objects runs a subset.
Limits: 32 objects. Test: two discs, one prompted on frame 0 and one on frame 2.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On real footage the stroke frame came out partial (clicks make SAM 2 pick a
part of the object). Measured on a multi-part figure: clicks only IoU 0.41,
with a box around the figure IoU 1.00. Chain::track now adds the box of the
classic engine's matte of the strokes to each prompt; a box stroke wins.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The tracked frames lived only in memory, so a saved project lost them: a render in another
process (effectcraft-cli render-frame) segmented the frames again with the classic engine.
A propagate with a tracker now saves the frames it followed with the instance, in a hidden
parameter beside Freeze's (TrackCache: each frame's matte, Refine Edge band, colour models and
confidence, keyed by the inputs it was tracked from).
- They apply while the strokes and span they came from are unchanged, whichever model Settings
choose, so a project renders the same when it reopens, without the model loaded or chosen.
- A stroke or span change makes them stale until the next Propagate. A Propagate replaces them;
with the classic engine it leaves none.
- roto.status reports tracker.saved (the model they came from) and a notice when Settings
choose another model.
- The save at job completion is not an undo step (the frames are a cache of the strokes) and is
skipped if the strokes changed while tracking. Decoding checks the saved size, so a corrupt
project is ignored rather than overflowing.
Object Matte saves nothing. Projects saved before this change open unchanged (the parameter is
filled in on open).
Tests: a saved project reopens with the model unloaded and renders the saved frames through
Chain::ensure; choosing the classic engine keeps them until a classic Propagate replaces them; a
stroke change makes them stale. The SAM 2 tests and the failing-tracker test take the Roto test
lock.
Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
With SAM 2 (or MobileSAM) chosen, a propagate that started before the model had loaded ran
the whole job on the classic engine and reported nothing. The chosen model is now in use
before a job that segments with it starts (Roto Brush 2.0 / 3.0 and Object Matte):
- roto.propagate and the other Roto jobs wait for a load or download under way when called with
wait: true (bounded to 10 minutes). Without wait they refuse while the model is still loading,
and start the load in the background. A model that is not installed, or failed to load, is
refused either way.
- roto.status reports model {selected, installed, active, loading, error} and a notice while the
chosen model is not the one in use. source is "model" for a frame the model segmented on its own,
instead of "classical".
- Auto-propagate keeps edited instances pending while the model loads, and shows errors as toasts
instead of dropping them.
- Docs: what propagate does now; tracked frames live in the session that ran propagate and are not
saved in the project file, so a separate render process does not show them; roto.model.select
lists sam2.1-hiera-tiny.
Tests: a selected model that is not installed refuses to propagate (no weights needed); SAM 2
selected without wait is loaded before propagate (EFFECTCRAFT_SAM2). The SAM 2 tests and the
failing-tracker test take the Roto test lock, since they share the process-wide model slot.
Claude-Session: https://claude.ai/code/session_0136kZtXrYo5VUWEFxWduwUD
When the chosen model has a tracker, roto.propagate runs one tracking pass over
the span: prompts from the strokes (a handful of points, which SAM 2 trusts),
the clip tracked both ways, each frame's probability map refined by the same
graph cut with the strokes as hard constraints. Results are cached under their
own keys with a per-frame confidence and an occluded flag; roto.status reports
them (source: tracker|classical). Without a tracker nothing changes.
Real weights: IoU 0.98 on all 21 frames, confidence 0.99.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With the shutter open the textured squares fell back to the particle
colour. Both branches now draw through the same sprite closure, and the
sharp frame goes through plan.post like the trails.
Refraction samples are clamped to the layer rectangle instead of the buffer, so a sample past the
layer's edge in a buffer with transparent padding (a half-resolution preview's, a text or shape
layer's) repeats the layer's edge pixels rather than reaching the pale colour that stands in for
transparent ones. Transparent pixels inside the layer still show it. Same bounds on the GPU path.
Re-pins the four half-resolution CC Bubbles golden hashes (aarch64 macOS); the full-resolution
ones are unchanged. Adds a CPU test and a GPU-vs-CPU parity test on padded buffers.
The README quoted 306 effects in three places while the registry (and the
generated docs/effects.md) had 307, now 308. A catalog test checks every
effect count the README gives against the registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effects that grow their buffer (CC Radial Fast Blur, CC Lens, Magnify,
the distorts and blurs) could pad a 20 x 20 layer by 4096 px a side, a
~8200 x 8200 f32 buffer plus its clone. Buf::pad (CPU and GPU) now cuts
the pad so the grown buffer stays within 16 Mpx or four times its own
area, whichever is larger: small layers are bounded at 256 MiB, big
layers still grow as before in practice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>