From 88045641f32a1b880beb70f506f752bbe668d9e0 Mon Sep 17 00:00:00 2001 From: Anubhav-Rai Date: Tue, 20 Oct 2026 20:38:58 +0530 Subject: [PATCH] script: comp.width and comp.height resize the frame without moving the layers (#1942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The setters go through `comp.settings`, which defaults to the Composition Settings dialog's centre anchor, so `comp.width = 800` on a 400-wide comp shifted every unparented layer — and all of its position keys — by 200 px. A script that resized a precomp then found its pixel coordinates had moved under it. After Effects' width and height setters only change the frame; moving the contents is what "resize from centre" scripts do for themselves. The setters now pass `anchor: 0` (top left). The dialog, which is where the anchor belongs, is untouched. Co-Authored-By: Claude Opus 5 --- crates/script/src/prelude.js | 7 +++++-- crates/script/src/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/script/src/prelude.js b/crates/script/src/prelude.js index 52712bc0..2354830c 100644 --- a/crates/script/src/prelude.js +++ b/crates/script/src/prelude.js @@ -1506,8 +1506,11 @@ function AVItem() {} AVItem.prototype = Object.create(Item.prototype); (function () { function def(name, get, set) { Object.defineProperty(AVItem.prototype, name, { get: get, set: set }); } - def("width", function () { return this.__info().width; }, function (v) { this.__settings({ width: Math.round(__num(v, "width")) }); }); - def("height", function () { return this.__info().height; }, function (v) { this.__settings({ height: Math.round(__num(v, "height")) }); }); + // `anchor: 0` (top left): After Effects' width and height setters resize the frame and leave the + // layers at their coordinates. Re-anchoring the old frame in the new one belongs to the + // Composition Settings dialog, which is what `comp.settings` defaults to (#1942). + def("width", function () { return this.__info().width; }, function (v) { this.__settings({ width: Math.round(__num(v, "width")), anchor: 0 }); }); + def("height", function () { return this.__info().height; }, function (v) { this.__settings({ height: Math.round(__num(v, "height")), anchor: 0 }); }); def("pixelAspect", function () { return this.__info().pixelAspect; }, function (v) { this.__settings({ pixelAspect: __num(v) }); }); def("duration", function () { return this.__info().duration; }, function (v) { this.__settings({ duration: __num(v, "duration") }); }); def("frameRate", function () { return this.__info().frameRate; }, function (v) { this.__settings({ frameRate: __num(v, "frameRate") }); }); diff --git a/crates/script/src/tests.rs b/crates/script/src/tests.rs index a1e96194..a32289aa 100644 --- a/crates/script/src/tests.rs +++ b/crates/script/src/tests.rs @@ -1623,3 +1623,30 @@ fn set_value_rejects_an_array_of_the_wrong_dimension() { let (_, c) = comp_named(&s, "Dims"); assert_eq!(layer(c, "s").props.prop("transform/position").unwrap().value, KV::Vec3([120.0, 34.0, 0.0])); } + +/// #1942: the scripting width and height setters resize the frame only. Re-anchoring the old frame +/// in the new one, which moves every unparented layer, belongs to the Composition Settings dialog. +#[test] +fn setting_comp_width_and_height_leaves_the_layers_where_they_are() { + let mut s = session(); + let o = ok( + &mut s, + r#" + var c = app.project.items.addComp("w", 400, 200, 1, 1, 25); + var l = c.layers.addSolid([1, 1, 1], "s", 50, 50, 1, 1); + var pos = l.property("ADBE Transform Group").property("ADBE Position"); + pos.setValue([100, 100]); + var k = c.layers.addSolid([1, 1, 1], "k", 50, 50, 1, 1); + var kpos = k.property("ADBE Transform Group").property("ADBE Position"); + kpos.setValueAtTime(0, [10, 20]); + kpos.setValueAtTime(1, [30, 40]); + var before = "" + pos.value; + c.width = 800; + c.height = 400; + [before, "" + pos.value, "" + kpos.keyValue(1), "" + kpos.keyValue(2), c.width, c.height] + "#, + ); + assert_eq!(o.result, json!(["100,100,0", "100,100,0", "10,20,0", "30,40,0", 800, 400])); + let (_, c) = comp_named(&s, "w"); + assert_eq!(layer(c, "s").props.prop("transform/position").unwrap().value, KV::Vec3([100.0, 100.0, 0.0])); +}