Commit Graph
3793 Commits
Author SHA1 Message Date
caldiag e0f25c77bb when double-clicking an effect property, it reveals that effect in the timeline 2026-10-14 18:27:25 -03:00
midasmatterhorn 0f38f507ee ui-egui: a click that opens the colour picker no longer closes it in the same frame (#1476) 2026-10-15 00:10:49 +03:00
DWAA1660andClaude Opus 5.5 642eed0802 Properties/Timeline/Effect Controls: a typed component edits only that component of the other selected layers (#1434)
With several layers selected, typing a value into one dimension (say Z
Position) sent the edited layer's whole value with selected: true, and
prop.set gave every selected layer that same value: they all took the first
layer's X and Y and stacked on top of it. Scrubbing was fine (it sends an
offset).

prop.set takes dims: with selected and a typed value, the other selected
layers get only those components and keep the rest of their own value (Position
with Separate Dimensions included). The three panels send the components the
edit changed (both for linked Scale). Without dims, agents still set the whole
value on every selected layer.

Fixes #1434

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kwMcw2pYQ5fSjKa3ZPnh
2026-10-14 14:39:26 -06:00
midasmatterhorn c6736d8fc0 pdf: checked_add for CFF Private DICT and Type 1 RD offsets from the file 2026-10-14 22:57:37 +03:00
midasmatterhorn 790e2387d6 pdf: endchar takes its width by operand parity like the stems and rmoveto 2026-10-14 22:56:27 +03:00
midasmatterhorn 769a019e7d pdf: bound CFF endchar seac recursion so a cyclic font can't overflow the stack 2026-10-14 22:55:46 +03:00
arnav-hiwarkar 75b75f8463 fix(properties): reveal transform rows on double-click (#1456) 2026-10-15 01:25:46 +05:30
Davit MatchakhelidzeandClaude Opus 5.5 fb51427ec0 i18n: Georgian (ka) interface language
Adds Georgian (ქართული, code ka) to Settings > General > Language, with
Match System for ka / ka-GE / ka_GE.UTF-8 locales:

- i18n.rs: the menu catalog, the same 628 (command, English label) keys as
  the other languages, plus the Undo/Redo entries, the workspace and 3D
  view shortcut submenus, the Reveal labels on Windows and Linux and the
  macOS Services items.
- i18n/ui.rs: GEORGIAN, every row of the Chinese panel catalogs (687), and
  GEORGIAN_PANELS, 195 rows for text the panels send through tr() that no
  catalog has yet (form dialogs, drop messages, screen-reader names,
  context menus, Project and Timeline column headers). The tests hold
  them to the same rules (unique, same placeholders, used by a converted
  panel) and keep them from shadowing a Chinese row; prefs_live.rs joins
  the converted files.
- i18n/effects.rs: Georgian names for all 307 effects and 21 categories;
  the CC series and brand names stay in English.
- Properties > Text Animation: the Add Animator and Presets buttons grow
  with their translated labels (English keeps 116 / 84 px).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-14 22:50:28 +04:00
Davit Matchakhelidze 5539bb4a95 web: Match System follows ?lang= and the browser's languages
Match System (the default) stayed English in the browser build, which has
no system-locale probe (#229). The page now passes ?lang= from the address,
then navigator.languages, and Match System takes the first of them with a
catalog (an English tag counts), else English. A language chosen in
Settings still wins; native builds are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
(cherry picked from commit 73c064e3858068ed19fbfafbdd0b3c8cc229a42e)
2026-10-14 22:50:03 +04:00
Davit Matchakhelidze a1a1a36a41 i18n: context menus, tab titles, workspaces and column headers reach the catalogs
Text drawn outside the menu bar stayed English in every language:

- i18n::command_label: the Timeline / Composition layer and property
  context menus now take the menu bar's row for the same command (… or
  ...), else the panel catalog's row; the layer menu's Effects submenu
  draws effect and category names through the effect catalog.
- The dock's tab titles (and the dragged tab), the Composition, Effect
  Controls and Properties tabs, and the workspace bar and menus use the
  window.panel and window.workspace rows every menu catalog already has.
- The Project panel's search hint, details hint and column headers, and
  the Timeline's column headers and Columns menu go through tr().

The Simplified and Traditional Chinese rows for "Type" read 文字 (text, the
Type tool); the string is the Camera Settings field and now the Project
column, so they read 类型 / 類型. dock_ui.rs joins the converted files. Languages without a row for a
string keep the English, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
(cherry picked from commit b335f1e2764929b78d7b678ec026f680b1a8129f)
2026-10-14 22:50:03 +04:00
dotCooCoo 2f437ea551 EPS and PDF: huge arcs, long paths and loop passes count against the import's limits
The EPS interpreter counted one operation against its budget for each
name it ran, whatever the operator did, and paths had no length limit.
Parsing one arc at a radius of 10^300 passed 3 GiB of resident memory
within 43 seconds. A loop of arc at a radius of 2.2 x 10^18 was still
running after 60 seconds, and a loop of pathbbox on a path of 100,001
elements and { {} loop } loop did not finish within 20 seconds. PDF
content had no path limit either: parsing a page that repeats
0 0 1 1 re 4,000,000 times peaked at 2.3 GiB of resident memory.

A path now holds at most 1,048,576 elements (MAX_PATH, the limit of
VectorCraft's EPS interpreter). In EPS, moveto, lineto, curveto,
closepath, arc, charpath and the rect operators check it, and an
operator that would take a path past it stops the program with
"PostScript (path limit)". In PDF, m, l, c, v, y, h and re (each adds
at most five elements) stop the content stream with
"content (path limit)" when the path already holds more than 1,048,571.
Each path element an operator makes, reads or copies (arc, rectangle
arrays, charpath, pathbbox, clip, eoclip, gsave, save) now counts
against the EPS budget. Each pass of repeat, for, loop and forall
counts too, and so does each procedure item that is not a name. A
moveto right after a moveto replaces it, as the PostScript Language
Reference describes, so text placed with moveto and show leaves one
element.

Five tests in tests_import.rs cover the loops, the path limit in both
interpreters and runs of moveto and show.
2026-10-14 10:19:05 -07:00
Hamza a213b7f23a i18n (de): fix two labels that name the wrong thing, settle six terms (#1443)
Two German menu labels described a different command:

- `layer.timeReverse` was "Ebenenreihenfolge umkehren", which reads as reversing
  the stacking order rather than time -- now "Ebene zeitlich umkehren", matching
  `keys.timeReverse`;
- `layer.style.convertToEditable` was "In editierbare Formate umwandeln", and
  "Formate" are formats, not layer styles -- now "In bearbeitbare Stile
  umwandeln".

Where the same English term had more than one German rendering, the one already
in the majority wins, so this settles rather than re-translates:

- "Expression" is "Ausdruck" in seven entries and "Expressions" in the two
  `layer.expressions` ones, which now read "Ausdrücke aktivieren/deaktivieren";
- "Reveal" is "anzeigen" in eleven of twelve entries, so `layer.revealInFinder`
  joins `file.revealInFinder` at "Im Finder anzeigen" -- in every other
  catalogue both commands already share one string;
- "Track" as a verb is "verfolgen" in the other four entries, so `track.mask`
  is "Maske verfolgen" rather than "Maske tracken";
- one spelling of "nach vorne", matching the neighbouring arrange entry.

German also had no platform variants for the file manager. The engine puts the
platform's name into the label before the catalogue sees it, so on Windows the
source is "Reveal in Explorer" and on Linux "Reveal in File Manager"; only
Ukrainian and Czech special-cased those, and a German UI showed the English
string. Added, with a test mirroring `czech_…` and `ukrainian_…`.

Two of the reported six are left alone: "Essential Graphics" is a 3-3 split and
a panel's proper name, and "Null" partly mirrors the English, which itself says
both "Null Object" and "Nulls". Both are translator's choices with no majority
to follow, not corrections.
2026-10-14 21:21:39 +05:00
Nigel Ihza Firdausy ba37283aa4 fix(lottie): serialize Offset Paths miter limits as animatable values 2026-10-14 23:14:18 +07:00
tylerjenningsw 8b536e597b Export: millisecond-rounded footage renders at its rate, each frame once (#1442) 2026-10-14 11:37:36 -04:00
tylerjenningsw 36a905293b Media: tests for the averaged MP4 frame rate (#1442) 2026-10-14 11:33:49 -04:00
tylerjenningsw eb95f732e5 Media: millisecond-rounded MP4 frame times read at the rate they average to (#1442)
Phones and messaging apps store frame times rounded to whole milliseconds
(33/34 ms at 30 fps, 16/17 ms at 60 fps). FilmCraft's reader takes the
median duration, so such clips imported at 30.303 or 58.824 fps, and New
Comp from Selection and renders followed that rate. When the median is no
standard rate, the durations near it are averaged and snapped to the
standard rate within 0.5 %; exact and variable rates keep the reader's
estimate.
2026-10-14 11:33:49 -04:00
zitann2020andDeepSeek V4.1 Flash 66a29505a5 i18n(ja): the Settings dialog in Japanese
Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
2026-10-15 00:19:47 +09:00
midasmatterhorn c3b88d0d76 pdf: don't slice glyph names at a non-char boundary in name_to_unicode 2026-10-14 18:01:41 +03:00
wixar172andClaude Opus 5.5 9846c7255d Shapes: a stroke's buffer is padded by the miter limit only where it has miter joins
draw_bounds padded every stroke by width × miter limit / 2 on each side, joins or
not. A straight 1080 px wide wipe used as a track matte became a 4326×6366 layer
buffer (~440 MB as RGBA f32), made anew every frame while its trim animates, then
cached on the CPU and uploaded to the GPU. With 12 frames exported in parallel a
16 GB Mac ran out of memory and froze.

Now the miter limit applies only to miter-joined paths with a join (two or more
segments, or closed); square caps reach √2 half widths, other strokes one; Taper
and Wave keep the widest padding. Rendered pixels are unchanged.

A full-res frame of the affected ad: 1.7 GB → 0.7 GB peak.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:20:02 +03:00
Claude 25a752baa0 #1436: Solid, Camera, Light and Composition Settings preview live
The Gradient Editor's Preview now covers the other settings dialogs that edit
something that exists: Solid Settings on a solid layer, Camera Settings, Light
Settings and Composition Settings. A change of the draft reaches the project at
once, so the canvas shows it while it is made.

- One shared mechanism (`panels::live_preview`): each step goes back to a
  checkpoint of the project and undo history taken when the dialog opened, then
  runs the dialog's command once. So the project is always "as it opened, plus
  one command": OK leaves one undo step, Cancel and Escape restore the
  checkpoint, Preview off applies nothing until OK, and switching it off takes
  back what was applied.
- A previewing dialog opens at the lower right, without the dark backdrop, and
  the window behind it is no longer greyed out; it can still be dragged by its
  title. The same dialogs creating something new (New Solid, New Camera, New
  Light, New Composition) are unchanged and have no Preview.
- `Placement` is public, and `EffectcraftApp::dialog_state` gives tests and
  automation the drafts.

Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
2026-10-14 14:04:30 +00:00
Claude 59b144439e #1436: the Gradient Editor previews live and leaves the window undimmed
The Gradient Editor edited a draft and set the property only on OK, in a
centred dialog over a dimmed, greyed-out window, so the canvas could not show
the gradient while it was edited. In After Effects the dialog sits at the lower
right, the window stays at full brightness, and the canvas updates live
(Preview checkbox).

- Preview (on by default): each edit is applied with `prop.set` and one merged
  undo step; OK keeps it, Cancel and Escape roll back to a checkpoint of the
  project and undo history taken on open (the Layer Styles dialog's mechanism,
  now shared). Preview off sets the property on OK only, as before; switching
  it off takes back what it applied.
- The editor opens at the lower right (`Placement` for modal dialogs), without
  the dark backdrop, and the window behind is no longer faded
  (`Dialog::leaves_window_undimmed`); clicks still don't reach it.
- Other dialogs are unchanged.

Claude-Session: https://claude.ai/code/session_013fTnVxnXTv2MBW6uj2PPyU
2026-10-14 13:31:10 +00:00
Marco Arnulfo cc29d90943 Import: item.metadata counts a WebM's last frame when its duration is not a whole millisecond (fixes #1429) 2026-10-14 14:22:15 +02:00
Hamza 449e9edd59 Render Queue: resolve a bare project filename against the working directory
`resolve_output` takes the project's folder from `Path::new(path).parent()`.
For a project saved under a bare filename that is the empty path, which the
following `filter` discarded, so a saved project fell through to the
unsaved-project default and relative outputs went to ~/Movies (the user profile
folder on Windows) instead of next to the project.

    --project proj.ecproj      ~/Movies/clip.mp4
    --project ./proj.ecproj    ./clip.mp4

Both spellings name a project in the working directory, so both now resolve
there, as the doc comment on `resolve_output` already describes.

Closes #1427
2026-10-14 17:21:58 +05:00
Marco Arnulfo 78b4fe4170 Import: AIFF files import, including EffectCraft's own AIFF renders (fixes #1428) 2026-10-14 13:44:59 +02:00
Marco Arnulfo 69d17d89af Keyframes: a path pasted onto Position becomes a motion path (fixes #1425) 2026-10-14 13:30:45 +02:00
arnav-hiwarkar 68d928aff3 fix(render): preserve preceding effects in GPU Echo (#1120) 2026-10-14 13:42:59 +05:30
Marco Arnulfo 1b44c5c98a Scripting: Stroke and Hue/Saturation parameters use After Effects' match-name numbers (part of #1405) 2026-10-14 09:15:12 +02:00
arnav-hiwarkar bed289e2a1 fix(presets): identify copy folder action by label
Follow up on #1210: remove the fixed menu index from automation and click handling, and move the preset-folder documentation into the Effects & Presets section.
2026-10-14 12:09:48 +05:30
arnav-hiwarkar 8041329df8 feat(media-browser): add Copy Source Path 2026-10-14 11:42:13 +05:30
Marco Arnulfo ce437747ed Expressions: accepting a function completion inserts the call parentheses (fixes #1393) 2026-10-14 05:28:14 +02:00
tylerjenningsw 8d00db7d90 GPU: test CC Particle Systems II against the CPU with a keyed Birth Rate (#1373)
The keyed Birth Rate comparison is shared (keyed_agree) and now runs on
CC Particle Systems II too. The same two histories bear 90 particles by
0.9 s at different steps: the GPU's particles match the CPU's, and each
state really came from the GPU.
2026-10-13 22:35:37 -04:00
tylerjenningsw 6df1fc17b9 Render: tests for CC Particle Systems II and CC Mr. Mercury bursts and their layer cache (#1373)
Through the renderer, a keyed burst draws as the constant rate during it,
still draws after the rate drops to 0 (unlike a rate of 0 throughout),
and draws as nothing born once Longevity has run out. A Birth Rate
expression on any of the three stepped particle systems keeps the layer
out of the layer cache; without one it is cached.
2026-10-13 22:35:37 -04:00
tylerjenningsw 6fcfc355e3 Render: CC Particle Systems II and CC Mr. Mercury layers with expressions aren't layer cached (#1373)
They now read their Birth Rate at earlier times too, so
effects::reads_param_history names them along with CC Particle World. A
layer key can't see what an expression returned then.
2026-10-13 22:35:37 -04:00
tylerjenningsw 16cc290a47 Particles: tests for CC Particle Systems II and CC Mr. Mercury with a Birth Rate over time (#1373)
For both: a burst lives on after Birth Rate drops to 0, and is drawn
(particles, blobs) where a constant 0 draws nothing. Its particles move
and age, and are gone after their Longevity. Seeking matches a
simulation from 0, and moving an earlier key with the same current rate
and cache key isn't served from stale checkpoints. A constant rate gives
exactly the constant-rate simulation's particles, with or without a
host.
2026-10-13 22:35:36 -04:00
tylerjenningsw f4ab728c0a Particles: CC Particle Systems II and CC Mr. Mercury bear at the Birth Rate of each moment (#1373)
Both simulate from layer time 0, as CC Particle World does, and used
the Birth Rate of the rendered frame for every step. A rate keyed down
to 0 emptied them the frame it got there, although their particles (and
Mercury's blobs) were within their Longevity.

CC Particle World's simulation path becomes Stepped: Birth Rate sampled
at every step (rate_history), a cache key without it, and checkpoints
resumed only with the same rate history. Particle World, Particle
Systems II (60 particles a second per unit, CPU or GPU) and Mr. Mercury
(10 blobs a second per unit, CPU) now all use it. A constant Birth Rate
gives exactly the particles it did.

The constant-rate step, key and simulation (Phys::step, sim_key,
simulate) are now only the tests' reference. Mr. Mercury's physics move
into mercury_phys.
2026-10-13 22:35:36 -04:00
tylerjenningsw d04f88750e GPU: the keyed Birth Rate test tells birth timing from birth count and checks the GPU ran (#1373)
The two histories now bear the same 180 particles by 0.9 s at different
steps (3/s from 0 to 0.5 s, and from 0.25 to 0.75 s). So a checkpoint
check by birth count alone fails the test, not only no check. A
test-only dispatch counter asserts each GPU state really came from the
GPU rather than the CPU fallback.
2026-10-13 21:59:03 -04:00
tylerjenningsw f22ce3d839 Render: tests for a Hold Birth Rate cut under fractional stretch and an expression's history (#1373)
A Hold cut at 0.5 s reads 3 just before and 0 at 0.5 s under 33.3 %,
133.33 % and 200 % stretch, and the frame matches the unstretched one.
A Birth Rate expression reading a Controller layer's Opacity before
0.5 s: changing the Controller changes the 0.9 s frame, and the cached
render matches the uncached one.
2026-10-13 21:59:03 -04:00
tylerjenningsw aa300d7f2e Render: CC Particle World layers with expressions aren't layer cached (#1373)
The simulation checks its checkpoints against the Birth Rate history,
but a cached layer is returned before the effect runs. The layer key
sees the layer's keyframes and its current values, not what an
expression returned at earlier times: a Birth Rate expression reading
another layer could change the history while the current rate, 0, stayed
the same.

effects::reads_param_history names CC Particle World. The renderer
already declines to cache layers whose time effects (ec.time.*) meet
expressions. It now treats Particle World the same way, in the layer key,
the input key (only for the effects that input runs) and the dependency
walk.
2026-10-13 21:59:03 -04:00
tylerjenningsw f263a32ab7 Render: an effect parameter's history is read at its exact layer time (#1373)
FxHost::param_at mapped the requested layer time to comp time, and
EvalCtx::value mapped it back. Under a fractional stretch (33.3 %,
133.33 %) that round trip truncates a tick early, so a Hold key at
0.5 s read its previous value at the step starting there: a Birth Rate
cut to 0 at 0.5 s bore one more step of particles (six).

EvalCtx::value_at_layer_time reads the keyframes at the given layer
time exactly, and still runs an expression at the comp time.
EvalCtx::value now uses it, and param_at reads through it.

EffectHost::param_is_static says when a parameter has no keyframes and
no expression. CC Particle World then skips sampling its Birth Rate at
every step, since it's the same throughout.
2026-10-13 21:59:03 -04:00
tylerjenningsw 25fc51f3ed Timeline: Alt held as a pick whip drops refers by index (refs #1072)
A property or expression pick whip dropped with Alt held, onto the
Timeline or Effect Controls, now sends byIndex: an effect's property is
referred to by its index, as in After Effects.

Test: an_alt_pick_whip_refers_to_an_effect_property_by_index (Alt and
plain drops onto Effect Controls and the Timeline, into the expression
being edited with a dimension, one undo step each).
2026-10-13 21:57:19 -04:00
tylerjenningsw f2cdad9edb Expressions: pick-whip references to effect properties by index (refs #1072)
In After Effects, holding Alt while dropping the expression pick whip
refers to an effect's property by its index, effect("Color Control")(1),
instead of its name, ("Color").

prop.pickWhip takes byIndex: the effect keeps its name and each
property inside it is selected by its one-based place among its group's
children (nested groups too), which is what an expression's (1) reads.
Other properties, dimensions ([1]), insertion at the cursor and match
names (Compact English off) are as before. An index still reads a
renamed property, but another one if properties are reordered.

Tests: pick_whip_by_index_selects_effect_properties_by_their_index
(engine, the references); effectcraft-host pick_whip (by index and by
name read the same values, nested and per dimension).
2026-10-13 21:57:19 -04:00
Vikram Bhaskaran b51c91a126 Render: ask the effect spec whether a stack can skip the layer cover
Matej's follow-up kept the known-margin set as SKIPS_COVER and the renderer looked the ids up. Ask EffectSpec::skips_cover instead so a new margin effect opts in on the spec rather than in run_effect_stack. Tests pin align_plan to cover_shift mod 64 and that Twirl still takes the full cover.
2026-10-14 12:14:27 +11:00
Marco Arnulfo 7df16a5e20 Effects: 4-Color Gradient keeps the layer's alpha with Blending Mode None (refs #1381) 2026-10-14 02:56:17 +02:00
tylerjenningsw 1cea7d774f GPU: test GPU particles against the CPU with a keyed Birth Rate (#1373)
With a Birth Rate keyed into a burst, the GPU's particles match the CPU
simulation through the burst and after it. A second history with the
same system key, whose burst starts later (so the same ids are born at
other steps), isn't resumed from the first history's GPU checkpoints.
2026-10-13 20:45:21 -04:00
tylerjenningsw 8f03f24247 Render: tests for a CC Particle World burst through the renderer (#1373)
The issue's scene: a Birth Rate keyed 3 until 0.5 s then 0 matches the
constant rate during the burst, still draws the burst at 0.9 s with no
new births, and is empty once Longevity has run out. Moving the earlier
key changes the 0.9 s frame through the layer cache. Layers offset by
0.5 s or stretched 200 % sample their own layer times.
2026-10-13 20:45:20 -04:00
tylerjenningsw d5a1268ead CC Particle World: tests for a Birth Rate that changes over time (#1373)
A burst lives on after the rate drops to 0: as a constant rate during
it, only its particles afterwards, still moving, gone after their
Longevity. Another effect's history isn't used. A ramp bears what each
moment asked for. Seeking backward and forward matches a simulation
from 0. Moving an earlier key isn't served from stale checkpoints with
the same current rate. A constant rate is unchanged. The birth schedule
matches the CPU's ids.
2026-10-13 20:45:20 -04:00
tylerjenningsw bb0e2b57db CC Particle World: Birth Rate decides the births of its own time, so a burst lives on (#1373)
The simulation runs from layer time 0, but it used the Birth Rate of the
rendered frame for every step. A rate keyed from 3 down to 0 simulated
no births at all once it reached 0, so the burst's particles vanished
that frame although their Longevity had not run out. A ramped rate was
simulated as if its current value had always applied.

Each step now samples Birth Rate at its own layer time through
EffectHost::param_at (with the effect's own index). Without such a host
the current value applies throughout, as before. Particles already alive
keep moving and ageing whatever the rate. The carry and particle ids
continue across rate changes.

Birth Rate is left out of the simulation cache key, so an animated rate
no longer restarts the simulation every frame. In its place:

- The CPU state keeps a hash of the rates it was stepped with (hist). A
  checkpoint is resumed only when that matches the current history, so
  moving an earlier key is never served from a stale checkpoint.
- The GPU gets the birth schedule of the sampled rates (births_at). Its
  checkpoints record which births they hold, and are resumed only when
  the request's births before that step are the same.

A constant Birth Rate gives exactly the particles it did. CC Particle
Systems II and CC Mr. Mercury keep their constant-rate simulation.
2026-10-13 20:45:20 -04:00
tylerjenningsw a00a76e7ed Particles: a birth schedule from a rate per step (#1373)
psim::births_at builds the GPU's birth schedule (the birth step of every
particle id) from the rate of each step, with the CPU step's carry
arithmetic. births() for a constant rate now uses it. It stops as soon
as the cap would be passed, before adding a step's births, so a huge
rate can't build a huge list first.
2026-10-13 20:45:20 -04:00
tylerjenningsw 2e654f92c1 Effects: SimCache can resume only from checkpoints reached with the same inputs (#1373)
SimCache::run_where takes a check on each checkpoint (its step and
state). A simulation whose steps depend on inputs outside its key, such
as a parameter's values over time, resumes only from a checkpoint
reached with the same inputs. A fresh checkpoint now replaces one at the
same step, so a changed history doesn't leave stale checkpoints behind.
run() is run_where with no check.
2026-10-13 20:45:20 -04:00
tylerjenningsw 7b0771fe5a Effects: a host evaluates one effect parameter at another layer time (#1373)
EffectHost::param_at(effect_index, id, layer_time) evaluates a single
parameter (by its flattened id, such as birthRate or group/param) of the
effect at that index in the layer's stack. A simulation can then sample
one parameter at every step without flattening the whole effect each
time.

The effect index is passed in, rather than read from the renderer's
running-effect index as params_at does: GPU-batched effects never update
that index, so a batched effect would read another effect's parameters.
The renderer's implementation walks the effect group by match id and
evaluates just that property (keyframes and expressions) at the comp
time of the layer time.
2026-10-13 20:45:20 -04:00