mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Scripting: Google Earth Studio camera scripts key the camera (fixes #1077)
CameraLayer gets the transform and camera-options shortcuts (position, xRotation, zoom...) that AVLayer already had, and Convert Expression to Keyframes run without a property converts the selected ones, as app.executeCommand does it.
This commit is contained in:
1 parent
470ea7a3af
commit
a179714108
3 files changed
+91
-1
No files matched your search
@@ -1144,7 +1144,27 @@ fn velocity(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
|
||||
fn convert_expr_to_keys(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let (cid, lid, uid) = prop_ref(s, p, "prop.convertExpressionToKeyframes")?;
|
||||
const CMD: &str = "prop.convertExpressionToKeyframes";
|
||||
// Without a property (the menu item, a script's `app.executeCommand`) it converts the selected ones.
|
||||
if p.get("path").is_none() && p.get("prop").is_none() && p.get("layer").is_none() {
|
||||
let cid = comp_id(s, p)?;
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?;
|
||||
let sel: Vec<(LayerId, Uid)> =
|
||||
selected_leaf_props(s).into_iter().filter(|(l, u)| comp.layer(*l).and_then(|l| l.props.find(*u)).is_some_and(Property::has_expression)).collect();
|
||||
if sel.is_empty() {
|
||||
return Err(bad(CMD, "no `path` given and no selected property has an expression"));
|
||||
}
|
||||
let mut n = 0;
|
||||
for (lid, uid) in sel {
|
||||
n += convert_one_expr(s, cid, lid, uid)?.as_u64().unwrap_or(0);
|
||||
}
|
||||
return Ok(json!(n));
|
||||
}
|
||||
let (cid, lid, uid) = prop_ref(s, p, CMD)?;
|
||||
convert_one_expr(s, cid, lid, uid)
|
||||
}
|
||||
|
||||
fn convert_one_expr(s: &mut Session, cid: ItemId, lid: LayerId, uid: Uid) -> Result<Value> {
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?.clone();
|
||||
let layer = comp.layer(lid).ok_or(EngineError::NoComp)?.clone();
|
||||
let pr = layer.props.find(uid).ok_or_else(|| bad("prop.convertExpressionToKeyframes", "no property"))?.clone();
|
||||
|
||||
@@ -1304,6 +1304,14 @@ Object.defineProperty(ShapeLayer.prototype, "source", { get: function () { retur
|
||||
function CameraLayer(comp, id) { return this.__init(comp, id, "CameraLayer"); }
|
||||
CameraLayer.prototype = Object.create(Layer.prototype);
|
||||
CameraLayer.prototype.constructor = CameraLayer;
|
||||
// Camera scripts (Google Earth Studio exports among them) key `cameraLayer.position`,
|
||||
// `.xRotation` and `.zoom` directly, as on AVLayer: return the live Property objects.
|
||||
["pointOfInterest", "position", "orientation", "xRotation", "yRotation", "zRotation"].forEach(function (name) {
|
||||
Object.defineProperty(CameraLayer.prototype, name, { get: function () { return this.transform[name]; } });
|
||||
});
|
||||
["zoom", "depthOfField", "focusDistance", "aperture", "blurLevel"].forEach(function (name) {
|
||||
Object.defineProperty(CameraLayer.prototype, name, { get: function () { return this.cameraOptions[name]; } });
|
||||
});
|
||||
|
||||
function LightLayer(comp, id) { return this.__init(comp, id, "LightLayer"); }
|
||||
LightLayer.prototype = Object.create(Layer.prototype);
|
||||
|
||||
@@ -1010,3 +1010,65 @@ fn scripted_text_range_selector_resolves_and_is_the_only_selector() {
|
||||
);
|
||||
assert_eq!(o.result, json!([1, "ADBE Text Selector", 50, 100, "ADBE Text Percent Offset"]));
|
||||
}
|
||||
|
||||
/// A Google Earth Studio camera export: transform and camera shortcuts on the camera layer, keys
|
||||
/// set through them, and Convert Expression to Keyframes run on the selected property (#1077).
|
||||
#[test]
|
||||
fn earth_studio_style_script_keys_the_camera() {
|
||||
let mut s = session();
|
||||
let o = ok(
|
||||
&mut s,
|
||||
r#"
|
||||
var comp = app.project.items.addComp("Orbit", 1920, 1080, 1, 1, 10);
|
||||
comp.openInViewer();
|
||||
var cam = comp.layers.addCamera("Camera", ["960", "540"]);
|
||||
cam.locked = true;
|
||||
var pos = cam.position, rx = cam.xRotation, ry = cam.yRotation, rz = cam.zRotation, zoom = cam.zoom;
|
||||
cam.autoOrient = AutoOrientType.NO_AUTO_ORIENT;
|
||||
var ints = comp.layers.addNull();
|
||||
ints.name = "ints";
|
||||
ints.threeDLayer = true;
|
||||
ints.position.setValuesAtTimes([0, 0.5], [[100, 200, 300], [110, 220, 330]]);
|
||||
rx.setValuesAtTimes([0, 0.5], [181, 182]);
|
||||
ry.setValuesAtTimes([0, 0.5], [39, 38]);
|
||||
rz.setValuesAtTimes([0, 0.5], [87, 86]);
|
||||
pos.expression = 'thisComp.layer("ints").transform.position';
|
||||
var id = app.findMenuCommandId("Convert Expression to Keyframes");
|
||||
if (id && pos.canSetExpression && pos.expressionEnabled) {
|
||||
pos.selected = true;
|
||||
app.executeCommand(id);
|
||||
pos.selected = false;
|
||||
ints.remove();
|
||||
}
|
||||
zoom.setValueAtTime(0, 1500);
|
||||
zoom.setValueAtTime(0.5, 1600);
|
||||
[pos.numKeys, rx.numKeys, ry.numKeys, rz.numKeys, zoom.numKeys, comp.numLayers,
|
||||
cam.pointOfInterest.matchName, cam.orientation.matchName, cam.depthOfField.matchName,
|
||||
cam.focusDistance.matchName, cam.aperture.matchName, cam.blurLevel.matchName]
|
||||
"#,
|
||||
);
|
||||
assert_eq!(
|
||||
o.result,
|
||||
json!([
|
||||
10,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
"ADBE Anchor Point",
|
||||
"ADBE Orientation",
|
||||
"ADBE Camera Depth of Field",
|
||||
"ADBE Camera Focus Distance",
|
||||
"ADBE Camera Aperture",
|
||||
"ADBE Camera Blur Level"
|
||||
])
|
||||
);
|
||||
let (_, c) = comp_named(&s, "Orbit");
|
||||
let cam = layer(c, "Camera");
|
||||
let p = cam.props.prop("transform/position").unwrap();
|
||||
assert_eq!(p.keys[5].value, KV::Vec3([110.0, 220.0, 330.0]));
|
||||
assert!(!p.expr.as_ref().unwrap().enabled);
|
||||
assert_eq!(cam.props.prop("transform/rotationX").unwrap().keys[1].value, KV::Scalar(182.0));
|
||||
assert_eq!(cam.props.prop("cameraOptions/zoom").unwrap().keys[1].value, KV::Scalar(1600.0));
|
||||
}
|
||||
Reference in new issue
Block a user