Every layer gets the Video switch (eye) in the Timeline (nulls, cameras and lights
were excluded by an is_av() check). A selected layer whose eye is off no longer
draws its box, handles, anchor or paths in the viewer. Alt-click on an eye and
Layer > Switches > Hide Other Video now take nulls along.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round nonzero duration frame counts away from zero while retaining
absolute-time rounding, default argument selection and numeric coercion.
Cover fractional durations, absolute controls, defaults and coercion.
Sort expected paths in the existing footage test for temporary-directory portability.
Co-authored-by: Codex <codex@openai.com>
pixel_form_with borrowed the comp unchanged when it held only comp markers and no layer
markers, so a lone plain comp marker still reached the disk key. Tests: a lone comp marker
keeps the key; a protected marker with a duration (and its duration) on a stretched precomp's
source changes the key of the comp that nests it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A path's vertices and its two tangent arrays deserialize independently,
and nothing on load makes them agree. mask.new pads them, but a path
read from a project file may carry fewer tangents than vertices.
mask.addVertex checked its index against the vertices only and then
inserted into both tangent arrays, and mask.setVertex indexed the
tangent arrays after the same check, so either command panicked on
such a path (Vec insert/index out of bounds).
ShapePath::fit_tangents gives both arrays one entry per vertex (missing
tangents become corners, extras are dropped), and edit_path_counted
calls it on the value it edits, so every mask/shape-path vertex command
works on a consistent path. Other keyframes of an animated path were
already padded by apply_count_op when the vertex count changes.
Tests: two regression tests save a project, cut every mask path's
tangent arrays to one entry (static value and keyframes), reopen it
through Project::from_json and run setVertex and addVertex. Both panic
at mask.rs:209 without the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With several layers set at once from one selected row, a selected
layer locked after it was selected (its Lock switch doesn't deselect
it) had its parent, its compensated transform or its matte changed too.
layer.setParent, layer.setTrackMatte and layer.trackMatte now leave
locked layers as they are, the rule the other layer edits follow
(unlocked): among several, the others change; alone, it is an error
that changes nothing. Done in the engine, a layer locked while a pick
whip is being dragged is covered too.
Test: locked_layers_keep_their_parent_and_matte.
Looped *video* footage had a regression (#1045); a grouped PNG sequence
going through the same path — interpret a fresh placeholder to Loop 15,
drag it onto the timeline — did not. The behaviour is already correct on
main (addItem sizes a sequence layer from looped_duration, the pool's
frame_index wraps per cycle), so this is coverage, not a change:
interpret_footage_loop_extends_sequence_layers asserts a 15-loop of a
0.4 s sequence lands a 6 s layer.
cargo test -p effectcraft-engine --lib interpret_footage: 4 passed.
layer.setTrackMatte took one layer. It now also takes `layers`, set in
one undo step; `layer` and the first selected layer still work as
before. `kind: "preserve"` keeps each layer's own matte mode (Alpha for
a layer without one), so giving several layers a new matte doesn't turn
a mix of Alpha and Luma mattes into one mode; without `kind` the mode
is Alpha, as before. A layer can't be its own matte and an empty list
is refused, before anything changes.
Test: several_layers_take_one_matte_and_keep_their_modes.
File > Dependencies > Find Missing Fonts listed a text layer whenever
its font matched no family name, while text layout finds a font by its
family, PostScript or full name (effectcraft_text::resolve). For a
layer set to the PostScript name Inter-Bold, EffectCraft draws the text
in Inter Bold, and text.fontFeatures reports the layer's font as not
missing. Find Missing Fonts listed that layer and a layer set to the
full name Inter SemiBold.
find_missing now lists a layer when effectcraft_text::resolve, which
text layout uses for each run, reports the layer's font as missing.
fonts_named_by_their_postscript_or_full_name_are_not_missing adds text
layers in Noto Serif, Inter-Bold, Inter SemiBold and a font that no face
has, and checks that Find Missing Fonts lists only the last one.
Upstream's #1167 fix and this branch both stop marker edits from invalidating
cached frames. Kept this branch's shared Project::reads_markers/same_pixels_with/
pixel_form_with (viewer frames, disk cache, nested comps) and folded upstream's
rules into them: any expression counts as reading markers, protected time
markers with a duration still change the frames.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Cryptomatte (#1183): planes shorter than the image read as empty instead of panicking.
- layer.addMarker (#1180): a comp marker no longer replaces one at the same time.
- Desktop (#1005): ignore macOS's -psn_ argument instead of refusing to launch.
- Doc comments put back on their own items (#1153, #1037).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The six PSD PRs merged one by one leave #1131's test calling samples()/to_rgb(), which #1139
replaced; vangel76's integration draft #1149 has the adapted version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single selected_layers list kept ids from every comp, so the active comp looked selected whenever another comp was. Opening a comp and running a script against a different comp now park and restore that selection.
sanitize_state dropped every selected layer not in the active comp at the
end of each script run, so a layer selected by script in a comp that was not
open was gone by the next script call. Every comp keeps its own selection as
in After Effects; only ids gone from every comp are dropped now. UI and
engine lookups resolve selections per comp and tolerate foreign ids.