4839 Commits
Author SHA1 Message Date
arnav-hiwarkar 3bd7de6ad8 #3475: Beam: Starting and Ending Thickness take After Effects' range
Starting and Ending Thickness take 0..4000, After Effects' recorded valid
range in the same units (pixels). The CPU and GPU renderers draw the beam
per pixel from its distance to the segment, within the layer's buffer, so
a thicker beam costs the same.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar c4cd9f1c2e #3477: Scatter: Scatter Amount takes After Effects' range
Scatter Amount takes 0..1000, After Effects' recorded valid range in the
same units (pixels); the slider stays 0..127. The CPU and GPU renderers move
each pixel by one random offset of up to the amount, so a larger amount
costs the same; samples outside the layer are transparent.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar a08240155c #3474: Radial Wipe: Feather takes After Effects' range
Feather takes 0..32000, After Effects' recorded valid range in the same
units. The CPU and GPU renderers evaluate the feather per pixel in closed
form (a Gaussian of the distance to the wedge, radial_wipe_alpha), so a
wide feather costs the same and stays finite.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 29c76bb6b4 #3472, #3473: Venetian Blinds: Width and Feather take After Effects' ranges
Width takes 1..32000 and Feather 0..32000, After Effects' recorded valid
ranges in the same units (pixels). The work stays bounded: the renderers
floor the width at 1 buffer pixel, sum at most 40 Gaussian terms per
pixel, and above 8 widths the feather is the even veil
(venetian_cover; the GPU hands that case to the CPU, as before).
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 85f47ec072 #3476: Grid: Border takes After Effects' range
Border takes 0..2000, After Effects' recorded valid range in the same units
(pixels). The CPU and GPU renderers compute the border per pixel in closed
form, so a wider border costs the same; one wider than the cells covers
them.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar ce4db78896 #3480: Change Color: Hue Transform takes After Effects' range
Hue Transform takes -1800..1800 degrees, After Effects' recorded valid range
in the same units. The CPU and GPU renderers wrap the hue (rem_euclid /
fract_euclid), so five turns render like the hue they end on, at the same
cost.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30
arnav-hiwarkar 244f9bd846 #3483, #3484: Noise HLS: amounts and Grain Size take After Effects' ranges
Hue, Lightness and Saturation go up to 1000 % and Grain Size down to 0 (it
keeps its wider maximum, 100), After Effects' recorded valid ranges in the
same units. Noise HLS Auto shares the controls and widens with them.
Defaults and sliders are unchanged.

The renderers need no change: hue wraps, lightness and saturation clamp per
pixel after the noise is added, and Grain Size is floored at 0.1 px on the
CPU and the GPU, so the new values cost the same and stay finite.

The amounts were written as "percent" in keyframe text because their range
was 0..100, and Grain Size would become "percent" at 0..100; UNITS rows keep
the units they had.
2026-11-12 15:46:48 +05:30
arnav-hiwarkar 36eecad00f #3538: a cached config's size counts the LUTs it holds 2026-11-12 15:30:14 +05:30
arnav-hiwarkar 363a800d3f #3478, #3479: 4-Color Gradient: Blend and Jitter take After Effects' ranges
Blend goes up to 10000 (it keeps its minimum of 1, below After Effects' 5)
and Jitter up to 500 %, After Effects' recorded valid ranges in the same
units. Defaults and sliders are unchanged.

The CPU and GPU renderers capped Jitter's dither at 100 %, so higher values
would be stored but render as 100. Both now take it from one function,
four_color_jitter, which keeps the dither linear up to 500 %. Blend 10000
is a finite softening (Blend squared) at the same cost.

Jitter was written as "percent" in keyframe text because its range was
0..100; a UNITS row keeps that unit.
2026-11-12 15:14:34 +05:30
arnav-hiwarkar 39a3b565d8 #3487, #3488, #3489, #3490, #3491: Levels (Individual Controls): levels and gamma take After Effects' ranges
Input/Output Black and White of every channel take -10000..10000 and Gamma
goes down to 0 (it keeps its wider maximum, 10), After Effects' recorded
valid ranges in the same units (the .aep mapping is Conv::Plain). Defaults
and the 0..1 / 0.1..3 sliders are unchanged.

Gamma 0 renders as the existing 0.01 floor, an exponent of 100, so an HDR
value a few times Input White would overflow f32 and an infinite curve
times an empty output range is NaN. The curve is now bounded at +-1e6 on
the CPU (Lv::apply) and the GPU (fxt_lv), so the output stays finite over
the whole range; values below the bound are unchanged.

The engine regression (tests/effect_ranges.rs) sets both ends of each row
through prop.set, checks undo/redo, save and reopen, and that a project
saved with the narrower ranges opens with the new ones and its values.
2026-11-12 15:11:21 +05:30
arnav-hiwarkar 9831d70bfa #3539: a cache purge gives the viewers' frames new keys
Reload Footage Changed on Disk of a file whose metadata didn't change keeps the
project's revision; PurgeCaches emptied the viewer frame cache but the new
frame came back under the key of the one on screen, so neither the Composition
viewer nor the passive viewers uploaded it. Count purges and mix the count into
the frame key's options, as #3493 does with the LUT generation.
2026-11-12 15:11:13 +05:30
arnav-hiwarkar 4d24233505 #3538: a custom config not read yet is read for its LUT files 2026-11-12 15:07:48 +05:30
arnav-hiwarkar 216fc6052d #3470, #3471, #3472, #3473, #3474, #3475, #3476, #3477, #3478, #3479, #3480, #3483, #3484, #3487, #3488, #3489, #3490, #3491: guard effect hard ranges against After Effects' recorded ranges
A test walks every effect parameter the .aep table maps to a recorded After
Effects definition (fx_defs) and checks that the effect's hard range
(ParamUi::Slider min..max, what prop.set and the value editors clamp to)
covers After Effects' valid range, converted to EffectCraft's units the way
the importer converts values. Conv::number_scale is that conversion, now
shared by the importer and the test.

The parameters still narrower are allow-listed by issue (#3460-#3492); the
test fails on a new narrower parameter and on a row that is no longer
needed, so the list shrinks as the issues are fixed.
2026-11-12 15:05:48 +05:30
arnav-hiwarkar 637d33f796 Merge #3550's branch: the frames key writes names; precomp layer names are keyed too (#3535) 2026-11-12 15:04:14 +05:30
arnav-hiwarkar 385b9530a1 #3535: Warp Stabilizer and 3D Camera Tracker keys cover a precomp's contents
On a precomp layer the key covered only the nested comp's size, rate and
duration, so an edit inside the precomp kept a stale analysis. The key now
writes the precomp layer's Essential Properties and what its comp draws, field
by field and recursively. Each comp is written once per key and nesting is
capped, so shared, deep or cyclic precomps are keyed in bounded time. Docs say
what clears an analysis.
2026-11-12 15:00:14 +05:30
arnav-hiwarkar 51b8521eb3 #3539: test that the viewers show a still reloaded at the same size 2026-11-12 14:53:09 +05:30
arnav-hiwarkar 57990d9a47 #3535: Warp Stabilizer and 3D Camera Tracker keys cover the layer's masks and the effects above them
Their analyses are made from the layer's input to the effect (masks, then the
effects above it), but the key covered only the layer's frames, so a mask or
effect edit kept a stale analysis. frames_key::analysis_key adds the masks,
the effects above the instance and the Effects switch. Keys saved before
(frames only) stay valid while each edit keeps the input.
2026-11-12 14:48:30 +05:30
arnav-hiwarkar 94ab5d77cf #3536: pin the key text of non-default footage, source layer, Cineon, colour space and solid values; every mask mode keys apart 2026-11-12 14:47:05 +05:30
arnav-hiwarkar a700c5c8db #3538: layer keys and render reads include OCIO files and a config's LUTs 2026-11-12 14:43:54 +05:30
arnav-hiwarkar ef137cb2ce #3538: OCIO transform files and configs follow their files through #3493's LUT cache
Move the LUT cache that follows files by size and modification time into
effects::file_cache and use it for ocio::lut::load_file and
ocio_config::load_config. A config's entry depends on the .ocio file, the LUTs
its FileTransforms read and the search-path places it looked for missing ones.
check_lut_files and lut_generation now cover all three caches.
2026-11-12 14:43:54 +05:30
arnav-hiwarkar e04ae226a9 #3536: the frames key writes group and property names, and the uid of a property with an expression
Expressions find effects by name and read names (thisProperty.name,
propertyGroup(1).name), and seed random/wiggle with the property uid, so
both can change the frames. The Debug-based key had them.
2026-11-12 14:37:02 +05:30
arnav-hiwarkar 0547c51e47 #3538: test that OCIO transform files and configs are read again once they change 2026-11-12 14:33:21 +05:30
ash 41585f2aec Merge pull request #3450 from arnav-hiwarkar/fix/3087-aep-effect-placeholders
.aep import: keep effects without a counterpart as placeholders (#3087)
2026-11-12 14:21:58 +05:30
arnav-hiwarkar 1c8d20a12f #3534: test a key 0.24.1 saved while the footage was offline; the frames key doc says what is read in full 2026-11-12 14:19:24 +05:30
arnav-hiwarkar a19ffbc0db #3534, #3536: docs: the Warp Stabilizer and Roto Brush keys come from analysis::frames_key 2026-11-12 14:10:55 +05:30
arnav-hiwarkar 54cf31f841 #3534: footage going offline no longer drops saved analyses
Whether a footage file is there is an observation (footage.check sets
Footage::missing without an edit), not part of the footage. The
signature leaves it out, and keys saved by 0.22-0.24, which hashed it,
are recognised with the file either online or offline. Warp Stabilizer,
3D Camera Tracker and Roto Brush analyses are kept through the next edit
while the footage is offline and after it comes back.
2026-11-12 14:10:23 +05:30
arnav-hiwarkar 54d53fff76 #3536: one explicit analysis signature for Warp Stabilizer, 3D Camera Tracker and Roto Brush
analysis::frames_key writes what a layer's frames are made of field by
field: every struct and enum it writes is destructured or matched in full
and each variant is a fixed word, so a new field doesn't compile until it
is decided and a rename doesn't change keys. Names, labels, UI hints and
markers are left out. Roto Brush's Input Key writes masks and the effects
above it the same way, instead of their Debug text.

Keys saved by 0.22-0.24 are recognised by the old code, moved unchanged
to frames_key::legacy, and kept rather than rewritten (frozen mattes hash
them).
2026-11-12 14:08:57 +05:30
arnav-hiwarkar d361a6cb4e #3534, #3536: pin analysis keys saved by 0.24.1
A fixture keyed by the current release: Warp Stabilizer, 3D Camera Tracker
and Roto Brush keys keep matching through edits that keep the frames, undo
and save/reopen, and real changes still clear them.
2026-11-12 13:56:48 +05:30
arnav-hiwarkar e7a396856d #3087: a placeholder's float slider keeps its range
It arrived as a plain number, so Effect Controls neither dragged it over its
slider range nor clamped an edit to its valid range, as After Effects does.
2026-11-12 13:54:03 +05:30
arnav-hiwarkar 04bbc67528 #3087: test a placeholder found by its match name under ADBE Effect Parade 2026-11-12 13:42:53 +05:30
arnav-hiwarkar b762b4800d Merge remote-tracking branch 'upstream/main' into fix/3087-aep-effect-placeholders
# Conflicts:
#	crates/expr/src/host.rs
2026-11-12 13:42:21 +05:30
Sean Hawkey 24df676af5 Expressions, Scripting: a layer's property numbers and match names follow After Effects (#3528) (#3529)
* Expressions, Scripting: a layer's property numbers follow After Effects

In After Effects a layer's top-level number reaches the same group on every layer of a kind:
Marker is 1, and slots the layer has not used yet (Time Remap, Motion Trackers, Layer Styles,
Data, Sets...) keep their place. EffectCraft numbered only the groups a layer holds, without
Marker, so `layer(n)` and `layer.property(n)` reached a different group, and the numbers moved
as groups were added. Rigs that read a palette by number, such as
`comp("Palette").layer(1)(4)(n)(1)` on a shape layer, read Transform instead of Effects; their
`try` fell back to the stored value and every linked colour rendered near-black, with no
expression error.

`effectcraft_project::ae_order` holds After Effects' slot order per layer kind, observed in
After Effects 26.3 from a script (`layer.property(i).matchName`) and from an expression
(`thisComp.layer(..)(i).name`) on new layers of each kind. The expression host and the
scripting model resolve a layer's numbers through it: 1 is the markers, a used slot is its
group, an unused one is an empty group (never null, so loops over `numProperties` are safe).
`numProperties` at layer level and `propertyIndex` of top-level groups are After Effects'
numbers too. 3D model and primitive layers keep EffectCraft's own numbers.

* Expressions: a layer's top-level groups by After Effects match name

`thisLayer("ADBE Effect Parade")`, as scripts such as Karton write their expressions, reaches the
group through the same table as the layer's numbers: Effects, or an empty group for a slot the
layer has not used, and `("ADBE Marker")` the markers.

* Expressions, Scripting: review follow-ups for After Effects layer numbers

- An enabled Time Remap (a top-level property) reports After Effects' number 2 as its propertyIndex.
- In scripting, an unused slot resolves to the same empty group by match name
  (`property("ADBE Layer Styles")`) as by number (`property(7)`).
- The empty group is callable like any group: `layer.property(7)(1)` is null, not a TypeError.
2026-11-12 13:40:56 +05:30
arnav-hiwarkar a2ada5a5fe #3087: count every parameter a placeholder walks, and popup names by length
A definition of parameters that are not kept, shared by many instances, was
walked again for each one without spending the budget; long popup item
names were kept per instance with each item counted once.
2026-11-12 13:14:16 +05:30
arnav-hiwarkar 6f8c39d5f4 #3087: expressions find an effect by its After Effects match name
effect("Pseudo/0.25") and thisLayer("Effects")("Pseudo/0.25") now match a
placeholder's effect id, now that its match id is one path segment.
2026-11-12 13:06:51 +05:30
arnav-hiwarkar 0da7691d9d #3087: give a placeholder a match id that is one path segment
A pseudo effect's match name (Pseudo/0.25) was its group's match id, so a
match path to its parameters (keyframe paste, pick whip) was cut apart at
'/' and '.' and found nothing. The effect id stays the match name.
2026-11-12 13:06:51 +05:30
arnav-hiwarkar f6e2bdf6a0 Merge remote-tracking branch 'upstream/main' into fix/3087-aep-effect-placeholders
# Conflicts:
#	docs/aep-import.md
2026-11-12 13:01:53 +05:30
ash 9a4d02a827 Merge pull request #3449 from arnav-hiwarkar/fix/3087-aep-psd-layers
.aep import: an item made from one Photoshop layer shows that layer (#3087)
2026-11-12 12:58:31 +05:30
Dylan M. Taylor c43845a962 update tests for shared encoder output 2026-11-12 07:13:43 +00:00
Dylan M. Taylor d71ccdfc9d bound shared mesh scene conversion before allocation 2026-11-12 06:34:42 +00:00
ash 103760584a Merge pull request #3533 from arnav-hiwarkar/fix/3493-lut-cache
Apply Color LUT and Lumetri follow a LUT file that changes on disk (#3493)
2026-11-12 12:00:00 +05:30
Dylan M. Taylor 1ae04027df fix app adapters for shared format readers 2026-11-12 06:23:01 +00:00
ash c9e553266c Merge pull request #3530 from arnav-hiwarkar/fix/3456-tracker-signature
Warp Stabilizer, 3D Camera Tracker: keep analyses saved before 0.23 (#3456)
2026-11-12 11:39:19 +05:30
ash 8a2b4cc633 #2796: ScriptUI panel bars hide while the window fits its dock (#2880)
A docked panel's solid bars, once shown, took their width from the viewport, so a window that exactly filled its dock stayed too large and kept its bars. The bars are now hidden while the window fits its dock (to the whole point, #2585) and stay solid when it overflows (#269).
2026-11-12 11:37:26 +05:30
arnav-hiwarkar 63520598a0 #3493: test the followed-files limit on a cache of its own
The test filled the shared cache past the limit, which could let go of a
file another test running alongside still follows.
2026-11-12 11:36:40 +05:30
arnav-hiwarkar dc3e5245c2 #3493: show the frame again in the viewers once a LUT file changes
The viewers replace their picture only when the frame key changes, and a
frame rendered again after a LUT change had the same key, so the old look
stayed on screen. The viewer frame keys carry the LUT generation (in memory
only), which also leaves frames still rendering with the old LUT unasked
for, so the refusal of late frames is no longer needed.
2026-11-12 11:29:57 +05:30
arnav-hiwarkar d036127bf4 #3493: key a layer reading a precomp on the LUT file its override names
A precomp instance whose Essential Property names another LUT file renders
with that file, but a layer reading the instance through a layer parameter
was keyed on the nested comp's own file, so editing the overriding file left
that layer cached.
2026-11-12 11:29:57 +05:30
arnav-hiwarkar 82c2a23f07 #3493: keep following the LUT file just read past the followed-files limit
Past 1024 followed files, the trim kept only files with a cached LUT and
dropped the one being noted (its LUT is cached right after), so a later
edit to it went unnoticed. The tests start from an empty folder.
2026-11-12 11:29:57 +05:30
arnav-hiwarkar 63aca62574 #3493: follow LUT files whose LUT left the cache; bound it by bytes too
The watcher stamped only the files whose parsed LUT was still cached, so
with more LUTs than the cache keeps, an edit to an evicted one left the
viewer's frames stale. The cache now remembers the stamp each LUT file was
last read with (bounded separately), and keeps at most 64 LUTs and 512 MiB
of tables. Frames finishing while the viewer empties its cache are refused.
2026-11-12 11:13:12 +05:30
arnav-hiwarkar 58fcfeb54d #3456: test that placed-page footage doesn't match a 0.22 key
0.22 keys are hashed without placed_page, so only footage that isn't
placed as a page may match them.
2026-11-12 11:04:21 +05:30
Dylan M. Taylor 3e1965e274 Dependencies: pin craft-libs at current main 2026-11-12 05:34:09 +00:00