Files
effectcraft/examples/ae-spatial-tangents.jsx
T
KattandClaude Opus 5.5 6240ed646e Keyframes: auto-Bezier spatial tangents follow After Effects
Measured against After Effects 26.3 with the original
examples/ae-spatial-tangents.jsx (three paths, ten keys, even and uneven
timing): an auto-Bezier key's out tangent is (next - previous) / 6 and
the in tangent its mirror, whatever the key times; an end key uses its
one neighbour, (neighbour - key) / 6. EffectCraft gave end keys none and
scaled middle tangents by segment length, so paths with auto-Bezier keys
ran up to 28 px from AE's. All ten tangents now match; path samples are
within 0.05 px.

Tests that relied on end keys having no tangent now expect AE's: Spatial
Bezier keeps auto keys' handles (linear keys still get a third), the
motion-path handle is drawn at a sixth, and a straight loopOut path is
compared within 1e-9. docs/fidelity records the method and scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:44:49 +05:30

28 lines
1.8 KiB
React

// Original behavior probe: the spatial tangents a host gives auto-Bezier Position keys.
// Runs unchanged in After Effects (File > Scripts > Run Script File, or AfterFX.exe -r) and in
// EffectCraft (effectcraft-cli script examples/ae-spatial-tangents.jsx --empty). Builds three
// original comps with one solid each; run it in a scratch project. EffectCraft prints one JSON line
// per key; After Effects writes them to ae-spatial-tangents.jsonl next to this script.
// No Adobe assets, presets, application internals or implementation code are read.
var EC = String(app.version).indexOf("EffectCraft") >= 0;
var OUT = null;
if (!EC) {
OUT = new File(File($.fileName).parent.fsName + "/ae-spatial-tangents.jsonl");
OUT.encoding = "UTF-8";
OUT.open("w");
}
function emit(s) { if (EC) { writeLn(s); } else { OUT.writeln(s); } }
function r(v) { var o = []; for (var i = 0; i < v.length; i++) o.push(Math.round(v[i] * 1000) / 1000); return "[" + o.join(",") + "]"; }
function dump(name, pts, times) {
var c = app.project.items.addComp(name, 1920, 1080, 1, 2, 30);
var p = c.layers.addSolid([1, 1, 1], "S", 100, 100, 1).property("Transform").property("Position");
for (var i = 0; i < pts.length; i++) p.setValueAtTime(times[i], pts[i]);
// Pin what a preference could decide: AE can default new keys to linear spatial interpolation.
for (var k = 1; k <= p.numKeys; k++) p.setSpatialAutoBezierAtKey(k, true);
for (var k2 = 1; k2 <= p.numKeys; k2++) emit('{"case":"' + name + '","key":' + k2 + ',"in":' + r(p.keyInSpatialTangent(k2)) + ',"out":' + r(p.keyOutSpatialTangent(k2)) + "}");
}
dump("T1 symmetric", [[200, 200], [960, 900], [1700, 200]], [0, 1, 2]);
dump("T2 uneven", [[100, 540], [400, 700], [1800, 540]], [0, 0.5, 2]);
dump("T3 four keys", [[100, 100], [500, 900], [900, 100], [1800, 600]], [0, 0.6, 1.2, 2]);
if (OUT) OUT.close();