Merge pull request #1123 from nanox333/fix/808-source-point-convert

# Conflicts:
#	crates/script/src/tests.rs
This commit is contained in:
Brandon Thomas committed 2026-10-13 04:28:30 -04:00
commit 5c0ac48f31
3 files changed
+52 -2

No files matched your search

+24
View File
@@ -44,6 +44,7 @@ pub fn query(s: &mut Session, kind: &str, a: &J) -> R {
"textdoc" => textdoc(s, a),
"markers" => markers(s, a),
"sourceRect" => source_rect(s, a),
"pointConvert" => point_convert(s, a),
"resolveAdd" => resolve_add(s, a),
"menuCommandId" => Ok(menu_command_id(a.get("name").and_then(J::as_str).unwrap_or(""))),
"commandAt" => {
@@ -577,6 +578,29 @@ fn markers(s: &Session, a: &J) -> R {
Ok(J::Array(out))
}
/// A layer's source point in comp space (`toComp`) or the reverse, through the layer's transform
/// and its parents' at comp time (#808: `sourcePointToComp`, `compPointToSource`).
fn point_convert(s: &Session, a: &J) -> R {
let (cid, c, l) = layer_of(s, a)?;
let t = f(a, "time").map(Tick::from_seconds_f64).unwrap_or_else(|| s.time_of(cid));
let p = a.get("point").and_then(J::as_array).ok_or("missing point")?;
let x = p.first().and_then(J::as_f64).unwrap_or(0.0);
let y = p.get(1).and_then(J::as_f64).unwrap_or(0.0);
let mut ctx = EvalCtx::new(&s.project, cid, c, t);
ctx.expr = s.expr.as_deref();
let (layer_space, _) = ctx.layer_to_comp(l);
let m = if a.get("toComp").and_then(J::as_bool).unwrap_or(true) {
layer_space
} else {
layer_space.inverse().ok_or("the layer's transform can't be inverted")?
};
let k = m.0;
let w = k[2][0] * x + k[2][1] * y + k[2][2];
let (ox, oy) = (k[0][0] * x + k[0][1] * y + k[0][2], k[1][0] * x + k[1][1] * y + k[1][2]);
let (ox, oy) = if w != 0.0 && w.is_finite() { (ox / w, oy / w) } else { (ox, oy) };
Ok(json!([ox, oy]))
}
fn source_rect(s: &Session, a: &J) -> R {
let (cid, c, l) = layer_of(s, a)?;
let t = f(a, "time").map(Tick::from_seconds_f64).unwrap_or_else(|| s.time_of(cid));
+6 -2
View File
@@ -1285,8 +1285,12 @@ AVLayer.prototype.constructor = AVLayer;
P.sourceRectAtTime = function (t, extents) {
return __get("sourceRect", { comp: this.__comp, layer: this.__id, time: __num(t, "time"), extents: !!extents });
};
P.sourcePointToComp = function (p) { return p; };
P.compPointToSource = function (p) { return p; };
// Source ↔ comp points, through the layer's transform and its parents' at the comp's time (#808).
function pointIn(layer, p, toComp) {
return __get("pointConvert", { comp: layer.__comp, layer: layer.__id, point: [__num(p[0], "point"), __num(p[1], "point")], toComp: toComp, time: layer.containingComp.time });
}
P.sourcePointToComp = function (p) { return pointIn(this, p, true); };
P.compPointToSource = function (p) { return pointIn(this, p, false); };
P.replaceSource = function () { throw __err("replaceSource is not supported yet"); };
P.openInViewer = function () { __call("layer.openLayer", this.__p1()); return null; };
})();
+22
View File
@@ -1082,3 +1082,25 @@ fn effect_parameter_interpolation_and_ease() {
);
assert_eq!(o.result, json!([true, true, 16.67, 33.33, true]));
}
/// #808: sourcePointToComp and compPointToSource follow the layer's transform.
#[test]
fn source_point_conversions_follow_the_layer_transform() {
let mut s = session();
let o = ok(
&mut s,
r#"
var comp = app.project.items.addComp("Repro", 1920, 1080, 1, 10, 25);
var layer = comp.layers.addSolid([1, 0, 0], "S", 200, 100, 1);
layer.transform.position.setValue([400, 300]);
layer.transform.scale.setValue([50, 50]);
var a = layer.sourcePointToComp([0, 0]);
var b = layer.compPointToSource([400, 300]);
[a[0], a[1], b[0], b[1]]
"#,
);
let v = o.result;
let near = |x: &Value, want: f64| x.as_f64().is_some_and(|g| (g - want).abs() < 1e-6);
assert!(near(&v[0], 350.0) && near(&v[1], 275.0), "{v}");
assert!(near(&v[2], 100.0) && near(&v[3], 50.0), "{v}");
}