Hi All, Is there any way to access the current transform properties of a smart object? I’m trying to make a real-time slider for rotating and scaling multiple smart objects… But I’m finding that using relative rotation and scale makes for unreliable results. Thanks!
I used to do this with the old ActionDescriptor method. Looks like the “smartObjectMore” property is still accessible via Batchplay.
function getSmartObjectInfo(layerId, docId) {
const res = require("photoshop").action.batchPlay(
[
{
_obj: 'get',
_target: [
{_ref: "layer", _id: layerId},
{_ref: "document",_id: docId}
]
}
],
{ synchronousExecution: true }
)[0];
if (res.hasOwnProperty("smartObjectMore")) {
console.log(res.smartObjectMore);
} else {
console.error("Layer with id " + layerId + " is not a smart object");
}
}
This object has a ton of info but it’s not presented in the user-friendly format that the UI displays. .transform has the info you need. They don’t give you rotation or scale. You’ll need to calculate that by comparing the .size to the transform points for scale, and write or find a function that calculates rotation based on four points.
Oh. amazing. I genuinely didn’t think there’d be an answer. Greatly appreciated.
No prob. Although I should mention I read your original post in haste. I’m not certain how reliably you can set Smart Object transforms, but I know you can get all the smart object data.
Also, actually, I can’t find the rotation data in there. But useful to access position and height width. It looks like there were people trying to figure this out for scripts a few years ago with no luck.
Yeah no rotation. But you should be able to reliably calculate rotation by using the smartObjectMore.transform array. Also, Photoshop doesn’t give smart objects an anchor point, so the UI shows the position as the center (average) of the 4 corners:
function rotationFromCorners(points) {
var A = points.upperLeft;
var B = points.upperRight;
var radians = Math.atan2(B.y - A.y, B.x - A.x);
return radians * (180/Math.PI);
}
function getAvgPointPosition(points) {
var xa = (points.upperLeft.x + points.upperRight.x + points.lowerRight.x + points.lowerLeft.x)/4;
var ya = (points.upperLeft.y + points.upperRight.y + points.lowerRight.y + points.lowerLeft.y)/4;
return {x:xa, y:ya};
}
// BatchPlay smartObjectMore.transform array of a 1920x1080 image
var t = [
0,
0,
1920,
0,
1920,
1080,
0,
1080
];
// Parse points from .transform into something more readable
var smartObjectPoints = {
upperLeft: {x:t[0], y:t[1]},
upperRight: {x:t[2], y:t[3]},
lowerRight: {x:t[4], y:t[5]},
lowerLeft: {x:t[6], y:t[7]}
};
var smartObjectRotation = rotationFromCorners(smartObjectPoints);
var smartObjectPosition = getAvgPointPosition(smartObjectPoints);
Keep in mind you may start to get inaccuracies from a simple 2-point rotation calculation if you start to throw stuff like Transform > Skew/Distort/Perspective into the mix. Then you may need to compare .transform to the smartObjectMore.nonAffineTransform data. It can get a little tricky, but if you know you’ll be dealing with mostly just basic SO transforms, using the corners should be consistent.
In case someone needs it, I made a function to get the scale in percentage using the points info so we can have the complete transformation properties:
function getScaleFromPoints(points, fullSize) {
const A = points.upperLeft;
const B = points.upperRight;
const C = points.lowerLeft;
const w = distance([A.x, A.y], [B.x, B.y]);
const h = distance([A.x, A.y], [C.x, C.y]);
const fullWidth = fullSize.width;
const fullHeight = fullSize.height;
const wP = (w * 100) / fullWidth;
const hP = (h * 100) / fullHeight;
return {
width: wP,
height: hP
}
function distance(a, b) {
var AB = [b[0] - a[0], b[1] - a[1]];
return Math.sqrt(AB[0] * AB[0] + AB[1] * AB[1]);
}
}