• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
4

Loop Action to Perspective Warp and Resize

Participant ,
Mar 14, 2024 Mar 14, 2024

Copy link to clipboard

Copied

Hail to all the bright minds out there! 👋🏻

 

I would like help reducing the time it takes to combine a bunch of photos of boxes that were not all taken from the same angle.

 

The current steps are:

  • Open up a template
  • Drag and drop the required jpgs as smart layers
  • Run an action to edit the first photo smart layer and perspective crop them to make all corners 90˚
  • Save and Close
  • Make the image background transparent
  • Resize the layer to a specific size (out of proportion if needed) eg. 600x600px
  • Select the next layer
  • Run the action again
  • etc
  • When all layers are done, arrange them in a grid.

 

Screenshot 2024-03-14 at 15.00.53.png

Already achieved

I have created an action that does almost all steps except the resize to a specific size.

 

Need help with:

  1. Resizing the layer to a specific size
  2. Loop the action; currently, when looping with a script, I get the error "Could not complete the Play command because the action is already playing."
  3. When all is done, distribute all layers in the folder "PHOTOS" on a variable grid. Sometimes, I have 4, sometimes 6, and maybe I need to extend the template in the future to have space for more.

 

Attached is a folder of the action, scripts, and files

Documents 

 

I am unsure what is or is not achievable to automate, but any help is much appreciated! 🙏🏻

TOPICS
Actions and scripting , macOS

Views

539

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Apr 05, 2024 Apr 05, 2024

Screenshot 2024-04-07 at 12.31.33.pngScreenshot 2024-04-07 at 12.31.52.png

 

// convert smart object with four point vector mask into smart object and transform so that the vector mask is square according to the four guides;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
// set to pixels;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
////////////////////////////////////
// determine the target area;
var theID = getLayerId ();
var theTargetArea = getGuidesSquare ();
if (theTargetArea) {var theTarget =
...

Votes

Translate

Translate
Adobe
Community Expert ,
Apr 03, 2024 Apr 03, 2024

Copy link to clipboard

Copied

The guides just exist because they are present in the containing file, the empty space likely has some effect on the filesize, but sizewise more important is that it’s a Smart-Object-in-Smart-Object-situation. 

Theoretically it should be possible to avoid the empty space but to me the empty space is necessary to center the image for the transformation. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 03, 2024 Apr 03, 2024

Copy link to clipboard

Copied

Ahhh Okay okay, Smart-Object-in-Smart-Object-situation I figured was unavoidable for all the magic the script needs to do. But it makes sense now what those seemingly random guidelines are coming from 😄

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 03, 2024 Apr 03, 2024

Copy link to clipboard

Copied

Ideally you should start with untransformed Smart Objects (of the same resolution as the containing document), that way you could simply flatten the resulting Smart Objects at the end without losing quality. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 03, 2024 Apr 03, 2024

Copy link to clipboard

Copied

Not sure what you meant to be honest I am:

- dragging and dropping my original images from the finder (MacOS) into the PSD template file
- Create a 4 point path
- Make it into a vector mask
- Run your script
- and want to move to the next layer, hiding the current, unhiding the next, but the script I found is duplicating the document and seems weird. ChatGPT is not helpful, so a bit stuck there atm 😖)

 

To my understanding, that is an untransformed Smart Objects, but maybe it is lost in translation (not a native English speaker). But really it is only the "autist on the spectrum" in me that is triggered by something that is triggered with this "Smart-Object-in-Smart-Object-situation".

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 03, 2024 Apr 03, 2024

Copy link to clipboard

Copied

Not sure where the edit of posts/replies is, but I see I forgot one step....
- dragging and dropping my original images from the finder (MacOS) into the PSD template file
- Hide all layers except the first
- Select 1st layer
- Create a 4 point path
- Make a vector mask
- Run your script
- and want to move to the next layer, hiding the current, unhiding the next, but the script I found duplicates the document and seems weird. ChatGPT is not helpful, so a bit stuck there atm 😖)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 04, 2024 Apr 04, 2024

Copy link to clipboard

Copied

The images seem to have been scaled. 

Screenshot 2024-04-04 at 09.29.40.png

 

So because the SO is converted to a SO the transformation is applied to a lores version: 

Screenshot 2024-04-04 at 09.31.14.png

But the image would allow for better results: 

Screenshot 2024-04-04 at 09.31.57.png

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 04, 2024 Apr 04, 2024

Copy link to clipboard

Copied

Ahhh, dragging and dropping most likely does that indeed. Do I understand correctly that it would be better to check how if I can avoid this and have the images dropped at 100%? 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 04, 2024 Apr 04, 2024

Copy link to clipboard

Copied

It would, but I expect as long as the placed SO is about twice as big as in the 600px x 600px-based result it should be fine. 

 

As for selecting the next layer down: 

// hide layer then select and show next layer;
// 2024, use ut at your own risk;
if (app.documents.length > 0) {
var myDocument = activeDocument;
var theIndex = getLayerIndex();
myDocument.activeLayer.visible = false;
if (theIndex-2 > 0) {
selectLayerByIndex(theIndex-2, false);
myDocument.activeLayer.visible = true;
};
};
// by mike hale, via paul riggott;
// http://forums.adobe.com/message/1944754#1944754
function selectLayerByIndex(index,add){ 
add = undefined ? add = false:add 
var ref = new ActionReference();
ref.putIndex(charIDToTypeID("Lyr "), index);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref );
if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
try{
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
}catch(e){
alert(e.message); 
}
};
////// based on code by mike hale and via paul riggott //////
function getLayerIndex(){
var ref1 = new ActionReference();
ref1.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var desc = executeActionGet(ref1);
if (desc.getBoolean(stringIDToTypeID("hasBackgroundLayer")) == true) {var theAdd =0}
else {var theAdd = 1};
var ref = new ActionReference(); 
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); 
d = executeActionGet(ref); 
return d.getInteger(stringIDToTypeID('itemIndex'))+theAdd; 
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 04, 2024 Apr 04, 2024

Copy link to clipboard

Copied

🙏🏻🙏🏻🙏🏻🙏🏻🙏🏻 for the Next layer scripts, again saving me from the agony of using a weird script that does a bunch of things unnecessary. 

 

I am not sure I fully understand the SO and the LowRes part. Isn't the SO image "opened" and transformed in its original size? But yes, when dropped into the template, the path created is 2 or 3 times bigger than after all the steps. So I am good?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 04, 2024 Apr 04, 2024

Copy link to clipboard

Copied

The SO contains the fullres content. 

If the SO is scaled down and converted to a SO this resulting SO contains the downsampled pixels plus the original image data; but the visible data in the containing document is the downsampled one. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 05, 2024 Apr 05, 2024

Copy link to clipboard

Copied

Screenshot 2024-04-07 at 12.31.33.pngScreenshot 2024-04-07 at 12.31.52.png

 

// convert smart object with four point vector mask into smart object and transform so that the vector mask is square according to the four guides;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
// set to pixels;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
////////////////////////////////////
// determine the target area;
var theID = getLayerId ();
var theTargetArea = getGuidesSquare ();
if (theTargetArea) {var theTarget = theTargetArea[2]-theTargetArea[0]}
else {var theTarget = 600};
// check for appropriate four point vector mask;
var isSO = isSmartObject();
var thePath = collectPathInfoFromVectorMask();
if (isSO == true && thePath.length == 1 && thePath[0].length == 6) {
// select pen tool;
var desc221 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putClass( stringIDToTypeID( "penTool" ) );
desc221.putReference( stringIDToTypeID( "null" ), ref1 );
executeAction( stringIDToTypeID( "select" ), desc221, DialogModes.NO );
// select vector mask;
var desc11 = new ActionDescriptor();
var ref2 = new ActionReference();
ref2.putEnumerated( stringIDToTypeID( "path" ), stringIDToTypeID( "path" ), stringIDToTypeID( "vectorMask" ) );
ref2.putEnumerated( stringIDToTypeID( "layer" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc11.putReference( stringIDToTypeID( "null" ), ref2 );
executeAction( stringIDToTypeID( "select" ), desc11, DialogModes.NO );
// copy;
var desc12 = new ActionDescriptor();
desc12.putString( stringIDToTypeID( "copyHint" ), "path" );
executeAction( stringIDToTypeID( "copyEvent" ), desc12, DialogModes.NO );
// deselect vector mask;
deselectVectorMask();
// convert to smart object;
executeAction( stringIDToTypeID( "newPlacedLayer" ), undefined, DialogModes.NO );
// paste path;
executeAction( stringIDToTypeID( "paste" ), undefined, DialogModes.NO );
// deselect vector mask;
deselectVectorMask();
var theID = getLayerId ();
////////////////////////////////////
// rotate shortest side to 0˚;
var theX = new Array;
// get lengths and angles;
for (var m = 0; m < 4; m++) {
    var theNext = m+1;
    if (theNext == 4) {theNext = 0};
    theX.push([getDistance(thePath[0][m][0], thePath[0][theNext][0]), getAngle(thePath[0][m][0], thePath[0][theNext][0]), thePath[0][m][0], thePath[0][theNext][0]])
};
theX.sort(sortArrayByIndexedItem);
duplicateMoveRotateScale(theID, 0, 0, 100, 100, 360-theX[0][1], 0, false);
////////////////////////////////////
// skew second shortest side to 90˚;
var thePath = collectPathInfoFromVectorMask();
var theHor = new Array;
for (var m = 0; m < 4; m++) {
    theHor.push(thePath[0][m][0])
};
theHor.sort(sortArrayByIndexedItem);
var theLeft = theHor.slice(0,2).sort(sortArrayByIndexedItem2);
var theRight = theHor.slice(2).sort(sortArrayByIndexedItem2);
var theAngle = getAngle(theLeft[0], theLeft[1]);
duplicateMoveRotateScale(theID, 0, 0, 100, 100, 0, theAngle-90, false);
////////////////////////////////////
// get target corners;
var thePath = collectPathInfoFromVectorMask();
var theHor = new Array;
for (var m = 0; m < 4; m++) {
    theHor.push(thePath[0][m][0])
};
theHor.sort(sortArrayByIndexedItem);
var theLeft = theHor.slice(0,2).sort(sortArrayByIndexedItem2);
var theRight = theHor.slice(2).sort(sortArrayByIndexedItem2);
var theA = intersectionOfLines ([theLeft[1], theRight[1]], [theRight[0], [theRight[0][0], theRight[1][1]]]);
var theB = intersectionOfLines ([theRight[0], theRight[1]], [theLeft[1], [theRight[1][0], theLeft[1][1]]]);
// define the four cornerpoints;
var aTopLeft = [theLeft[0][0], theLeft[0][1]]
var aTopRight = [theB[0], theRight[0][1]];
var aBottomRight = [theRight[0][0], theLeft[1][1]];
var aBottomLeft = [theLeft[1][0], theA[1]];
// transform to points;
enableDisableVectorMask (false);
app.activeDocument.pathItems[app.activeDocument.pathItems.length-1].deselect();
transformByCornerpoints (aTopLeft, aTopRight, aBottomRight, aBottomLeft);
enableDisableVectorMask ();
// scale to 600px by 600px;
var theBounds = activeDocument.activeLayer.bounds;
var currentWidth = (Number(theBounds[2])-Number(theBounds[0]));
var currentHeight = (Number(theBounds[3])-Number(theBounds[1]));
duplicateMoveRotateScale (theID, 0, 0, theTarget/currentWidth*100, theTarget/currentHeight*100, 0, 0, false);
};
////////////////////////////////////
// determine rotation;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var nonAffineTransform = layerDesc.getObjectValue(stringIDToTypeID('smartObjectMore')).getList(stringIDToTypeID("nonAffineTransform"));
var nonAffineTransf = new Array;
for (var n = 0; n < nonAffineTransform.count/2; n++) {
    nonAffineTransf.push([nonAffineTransform.getDouble(n*2), nonAffineTransform.getDouble(n*2+1), n])
};
nonAffineTransf.sort(sortArrayByIndexedItem);
if (nonAffineTransf[0][1] < nonAffineTransf[1][1]) {var theRotation = nonAffineTransf[0][2]}
else {var theRotation = nonAffineTransf[1][2]};
// counter rotate;
duplicateMoveRotateScale (theID, 0, 0, 100, 100, theRotation*90, 0, false);
var currentBounds = getBounds ();
////////////////////////////////////
// unmask the smart object’s content;
var theSO = openSmartObject ();
removeVectorMask();
var theBounds = theSO.activeLayer.bounds;
var horAdd = Math.max(0, Math.max(theBounds[0]*(-1), theBounds[2]-theSO.width))*2;
var verAdd = Math.max(0, Math.max(theBounds[1]*(-1), theBounds[3]-theSO.height))*2;
theSO.resizeCanvas(theSO.width+horAdd, theSO.height+verAdd, AnchorPosition.MIDDLECENTER);
theSO.close(SaveOptions.SAVECHANGES);
enableDisableVectorMask (false);
////////////////////////////////////
duplicateMoveRotateScale (theID, Number(theTargetArea[0])-currentBounds[0][0], Number(theTargetArea[1])-currentBounds[0][1], 100, 100, 0, 0, false);
removeVectorMask();
////////////////////////////////////
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////// duplicate layer and move, rotate and scale it //////
function duplicateMoveRotateScale (theID, theX, theY, theScaleX, theScaleY, theRotation, theSkew, theDuplicate) {
selectLayerByID(theID,false);
try{
var idTrnf = charIDToTypeID( "Trnf" );
var desc10 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
    var ref6 = new ActionReference();
    ref6.putIdentifier( charIDToTypeID( "Lyr " ), theID );
/*        ref6.putEnumerated( charIDToTypeID( "Lyr " ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );*/
desc10.putReference( idnull, ref6 );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc10.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
    var desc11 = new ActionDescriptor();
    var idHrzn = charIDToTypeID( "Hrzn" );
    var idPxl = charIDToTypeID( "#Pxl" );
    desc11.putUnitDouble( idHrzn, idPxl, theX );
    var idVrtc = charIDToTypeID( "Vrtc" );
    var idPxl = charIDToTypeID( "#Pxl" );
    desc11.putUnitDouble( idVrtc, idPxl, theY );
var idOfst = charIDToTypeID( "Ofst" );
desc10.putObject( idOfst, idOfst, desc11 );
var idWdth = charIDToTypeID( "Wdth" );
var idPrc = charIDToTypeID( "#Prc" );
desc10.putUnitDouble( idWdth, idPrc, theScaleX );
var idHght = charIDToTypeID( "Hght" );
var idPrc = charIDToTypeID( "#Prc" );
desc10.putUnitDouble( idHght, idPrc, theScaleY );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc10.putUnitDouble( idAngl, idAng, theRotation );

var idskew = stringIDToTypeID( "skew" );
    var desc358 = new ActionDescriptor();
    var idhorizontal = stringIDToTypeID( "horizontal" );
    var idangleUnit = stringIDToTypeID( "angleUnit" );
    desc358.putUnitDouble( idhorizontal, idangleUnit, theSkew );
    var idvertical = stringIDToTypeID( "vertical" );
    var idangleUnit = stringIDToTypeID( "angleUnit" );
    desc358.putUnitDouble( idvertical, idangleUnit, 0.000000 );
var idpaint = stringIDToTypeID( "paint" );
desc10.putObject( idskew, idpaint, desc358 );

var idIntr = charIDToTypeID( "Intr" );
var idIntp = charIDToTypeID( "Intp" );
var idbicubicAutomatic = stringIDToTypeID( "bicubicAutomatic" );
desc10.putEnumerated( idIntr, idIntp, idbicubicAutomatic );
var idCpy = charIDToTypeID( "Cpy " );
desc10.putBoolean( idCpy, theDuplicate );
executeAction( idTrnf, desc10, DialogModes.NO );
} catch (e) {}
};
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){ 
add = undefined ? add = false:add 
var ref = new ActionReference();
    ref.putIdentifier(charIDToTypeID("Lyr "), id);
    var desc = new ActionDescriptor();
    desc.putReference(charIDToTypeID("null"), ref );
        if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) ); 
        desc.putBoolean( charIDToTypeID( "MkVs" ), false ); 
    try{
    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
}catch(e){
alert(e.message); 
}
};
////// bounds of active layer //////
function getBounds () {
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("bounds"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var layerDesc = executeActionGet(ref);
var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
var theW = theBounds.getUnitDoubleValue(stringIDToTypeID("right")) - theBounds.getUnitDoubleValue(stringIDToTypeID("left"));
var theH = theBounds.getUnitDoubleValue(stringIDToTypeID("bottom")) - theBounds.getUnitDoubleValue(stringIDToTypeID("top"));
var horCenter = theseBounds[0] + theW / 2;
var verCenter = theseBounds[1] + theH / 2;
return ([theseBounds, theW, theH, horCenter, verCenter])
};
////// get active layer’s id //////
function getLayerId () {
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("layerID"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
return executeActionGet(ref).getInteger(stringIDToTypeID("layerID"));
};
////// is smart object //////
function isSmartObject () {
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("smartObject"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var layerDesc = executeActionGet(ref);
var isSmartObject = layerDesc.hasKey(stringIDToTypeID("smartObject"));
return isSmartObject
};
////// get an angle, 3:00 being 0˚, 6:00 90˚, etc. //////
function getAngle (pointOne, pointTwo) {
// calculate the triangle sides;
var width = pointTwo[0] - pointOne[0];
var height = pointTwo[1] - pointOne[1];
var sideC = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); 
// calculate the angles;
if (width+width > width) {theAngle = Math.asin(height / sideC) * 360 / 2 / Math.PI}
else {theAngle = 180 - (Math.asin(height / sideC) * 360 / 2 / Math.PI)};
if (theAngle < 0) {theAngle = (360 + theAngle)};
//	if (theAngle > 180) {theAngle = (360 - theAngle) * (-1)};
return theAngle
};
////// get a distance between two points //////
function getDistance (pointOne, pointTwo) {
// calculate the triangle sides;
var width = pointTwo[0] - pointOne[0];
var height = pointTwo[1] - pointOne[1];
var sideC = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); 
return sideC
};
////// collect path info from actiondescriptor, smooth added //////
function collectPathInfoFromVectorMask () {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// based of functions from xbytor’s stdlib;
var ref = new ActionReference();
var idPath = charIDToTypeID( "Path" );
ref.putEnumerated( idPath, idPath, stringIDToTypeID( "vectorMask" ) );
var desc = app.executeActionGet(ref);
var pname = desc.getString(cTID('PthN'));
// create new array;
var theArray = new Array;
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
// for subpathitems;
for (var m = 0; m < pathComponents.count; m++) {
    var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
    var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));
    switch (operation1) {
        case 1097098272:
        var operation = 1097098272 //cTID('Add ');
        break;
        case 1398961266:
        var operation = 1398961266 //cTID('Sbtr');
        break;
        case 1231975538:
        var operation = 1231975538 //cTID('Intr');
        break;
        default:
//		case 1102:
        var operation = sTID('xor') //ShapeOperation.SHAPEXOR;
        break;
        };
// for subpathitem’s count;
    for (var n = 0; n < listKey.count; n++) {
        theArray.push(new Array);
        var points = listKey.getObjectValue(n).getList(sTID('points'));
        try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}
        catch (e) {var closed = false};
// for subpathitem’s segment’s number of points;
        for (var o = 0; o < points.count; o++) {
            var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));
            var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
            var thisPoint = [anchor];
            try {
                var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));
                var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];
                thisPoint.push(leftDirection)
                }
            catch (e) {
                thisPoint.push(anchor)
                };
            try {
                var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));
                var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];
                thisPoint.push(rightDirection)
                }
            catch (e) {
                thisPoint.push(anchor)
                };
            try {
                var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));
                thisPoint.push(smoothOr)
                }
            catch (e) {thisPoint.push(false)};
            theArray[theArray.length - 1].push(thisPoint);
            };
        theArray[theArray.length - 1].push(closed);
        theArray[theArray.length - 1].push(operation);
        };
    };
// by xbytor, thanks to him;
function cTID (s) { return app.charIDToTypeID(s); };
function sTID (s) { return app.stringIDToTypeID(s); };
// reset;
app.preferences.rulerUnits = originalRulerUnits;
return theArray;
};
////// sort a double array, thanks to sam, http://www.rhinocerus.net/forum/lang-javascript/ //////
function sortArrayByIndexedItem(a,b) {
var theIndex = 0;
if (a[theIndex]<b[theIndex]) return -1;
if (a[theIndex]>b[theIndex]) return 1;
return 0;
};
function sortArrayByIndexedItem2(a,b) {
var theIndex = 1;
if (a[theIndex]<b[theIndex]) return -1;
if (a[theIndex]>b[theIndex]) return 1;
return 0;
};
////// intersection point of linear functions //////
function intersectionOfLines (lineA, lineB) {
try {
// process lineA;
var wA = lineA[1][0] - lineA[0][0];
var hA = lineA[1][1] - lineA[0][1];
var inclineA = hA / wA ;
// process lineB;
var wB = lineB[1][0] - lineB[0][0];
var hB = lineB[1][1] - lineB[0][1];
var inclineB = hB / wB ;
// break if parrallel;
if (inclineA == inclineB) {return undefined};
// check for horizontal or vertical;
if (wA == 0) {var theX = lineA[1][0]};
if (hA == 0) {var theY = lineA[1][1]};
if (wB == 0) {var theX = lineB[1][0]};
if (hB == 0) {var theY = lineB[1][1]};
// do stuff;
var yZeroA = lineA[0][1] - (lineA[0][0] * inclineA);
var yZeroB = lineB[0][1] - (lineB[0][0] * inclineB);
if (theX == undefined) {var theX = (yZeroB - yZeroA) / (inclineA - inclineB)};
if (theY == undefined) {var theY = theX * inclineA + yZeroA};
// result;
return [theX, theY]
}
catch (e) {return undefined}
};
////// transform by corner points //////
function transformByCornerpoints (aTopLeft, aTopRight, aBottomRight, aBottomLeft) {
//////////// transformation ////////////
// from adobe’s terminology.jsx;
const classChannel = app.charIDToTypeID('Chnl');
const enumNone = app.charIDToTypeID('None');
const eventSet = app.charIDToTypeID('setd');
const eventTransform = app.charIDToTypeID('Trnf');
const keySelection = app.charIDToTypeID('fsel');
const krectangleStr = app.stringIDToTypeID("rectangle");
const kquadrilateralStr = app.stringIDToTypeID("quadrilateral");
const keyBottom = app.charIDToTypeID('Btom');
const keyLeft = app.charIDToTypeID('Left');
const keyNull = app.charIDToTypeID('null');
const keyRight = app.charIDToTypeID('Rght');
const keyTo = app.charIDToTypeID('T   ');
const keyTop = app.charIDToTypeID('Top ');
const typeOrdinal = app.charIDToTypeID('Ordn');
const unitPixels = app.charIDToTypeID('#Pxl');
// from adobe’s geometry.jsx;
//
// =================================== TPoint ===================================
//
function TPoint( x, y )
{
this.fX = x;
this.fY = y;
}
// TPoint Constants
const kTPointOrigion = new TPoint( 0, 0 );
TPoint.kOrigin = kTPointOrigion;

const kTPointInfinite = new TPoint( Infinity, Infinity );
TPoint.kInfinite = kTPointInfinite;
const kTPointClassname = "TPoint";
TPoint.prototype.className = kTPointClassname;
// Overloaded math operators
TPoint.prototype["=="] = function( Src )
{
return (this.fX == Src.fX) && (this.fY == Src.fY);
}

TPoint.prototype["+"] = function( b )
{
return new TPoint( this.fX + b.fX, this.fY + b.fY );
}

TPoint.prototype["-"] = function( b, reversed )
{
if (typeof(b) == "undefined")		// unary minus
    return new TPoint( -this.fX, -this.fY )
else
{
    if (reversed)
        return new TPoint( b.fX - this.fX, by.fY - this.fY );
    else
        return new TPoint( this.fX - b.fX, this.fY - b.fY);
}
}
//
// Multiply and divide work with scalars as well as points
//
TPoint.prototype["*"] = function( b )
{
if (typeof(b) == 'number')
    return new TPoint( this.fX * b, this.fY * b );
else
    return new TPoint( this.fX * b.fX, this.fY * b.fY );
}
TPoint.prototype["/"] = function( b, reversed )
{
if (reversed)
{
    if (typeof(b) == "number")
        debugger;
// Can't divide a number by a point
    else
        return new TPoint( b.fX / this.fX, b.fY / this.fY );
}
else
{
    if (typeof(b) == 'number')
        return new TPoint( this.fX / b, this.fY / b );
    else
        return new TPoint( this.fX / b.fX, this.fY / b.fY );
}
}
TPoint.prototype.toString = function()
{
return "[" + this.fX.toString() + "," + this.fY.toString() + "]";
}
TPoint.prototype.vectorLength = function()
{
return Math.sqrt( this.fX * this.fX + this.fY * this.fY );
}
//////////// the new corners ////////////
transformActiveLayer( [new TPoint(aTopLeft[0], aTopLeft[1]), new TPoint(aTopRight[0], aTopRight[1]), new TPoint(aBottomRight[0], aBottomRight[1]), new TPoint(aBottomLeft[0], aBottomLeft[1])]);
// from adobe’s stacksupport.jsx;
// Apply a perspective transform to the current layer, with the
// corner TPoints given in newCorners (starts at top left, in clockwise order)
// Potential DOM fix
function transformActiveLayer( newCorners )
{
function pxToNumber( px )
{
    return px.as("px");
}
var saveUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var i;
var setArgs = new ActionDescriptor();
var chanArg = new ActionReference();
chanArg.putProperty( classChannel, keySelection );
//	setArgs.putReference( keyNull, chanArg );
var boundsDesc = new ActionDescriptor();
var layerBounds = app.activeDocument.activeLayer.bounds;
boundsDesc.putUnitDouble( keyTop, unitPixels, pxToNumber( layerBounds[1] ) );
boundsDesc.putUnitDouble( keyLeft, unitPixels, pxToNumber( layerBounds[0] ) );
boundsDesc.putUnitDouble( keyRight, unitPixels, pxToNumber( layerBounds[2] ) );
boundsDesc.putUnitDouble( keyBottom, unitPixels, pxToNumber( layerBounds[3] ) );
//	executeAction( eventSet, setArgs );
var result = new ActionDescriptor();
var args = new ActionDescriptor();
var quadRect = new ActionList();
quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[0] ) );
// ActionList put is different from ActionDescriptor put
quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[1] ) );
quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[2] ) );
quadRect.putUnitDouble( unitPixels, pxToNumber( layerBounds[3] ) );	
var quadCorners = new ActionList();
for (i = 0; i < 4; ++i)
{
    quadCorners.putUnitDouble( unitPixels, newCorners[i].fX );
    quadCorners.putUnitDouble( unitPixels, newCorners[i].fY );
}
args.putList( krectangleStr, quadRect );
args.putList( kquadrilateralStr, quadCorners );
executeAction( eventTransform, args );	
// Deselect
deselArgs = new ActionDescriptor();
deselRef = new ActionReference();
deselRef.putProperty( classChannel, keySelection );
deselArgs.putReference( keyNull, deselRef );
deselArgs.putEnumerated( keyTo, typeOrdinal, enumNone );
executeAction( eventSet, deselArgs );
app.preferences.rulerUnits = saveUnits;
}
};
////// disable vector mask //////
function enableDisableVectorMask (theBoolean) {
try {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var vmEnabled = layerDesc.getBoolean(stringIDToTypeID( "vectorMaskEnabled" ));
if (theBoolean == undefined) {
if (vmEnabled == false) {var theBoolean = true}
else {var theBoolean = false};
};
// =======================================================
var idlayer = stringIDToTypeID( "layer" );
var desc5 = new ActionDescriptor();
    var ref1 = new ActionReference();
    ref1.putEnumerated( idlayer, stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc5.putReference( stringIDToTypeID( "null" ), ref1 );
    var desc6 = new ActionDescriptor();
    desc6.putBoolean( stringIDToTypeID( "vectorMaskEnabled" ), theBoolean );
desc5.putObject( stringIDToTypeID( "to" ), idlayer, desc6 );
executeAction( stringIDToTypeID( "set" ), desc5, DialogModes.NO );
} catch (e) {}
};
////// deselect vector mask //////
function deselectVectorMask () {
var desc5 = new ActionDescriptor();
var ref2 = new ActionReference();
ref2.putEnumerated( stringIDToTypeID( "path" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc5.putReference( stringIDToTypeID( "null" ), ref2 );
executeAction( stringIDToTypeID( "deselect" ), desc5, DialogModes.NO );
};
////// open smart object //////
function openSmartObject () {
var desc2 = new ActionDescriptor();
executeAction( stringIDToTypeID( "placedLayerEditContents" ), desc2, DialogModes.NO );
return activeDocument;
};
///// thanky to xbytor //////
function removeVectorMask () {
cTID = function(s) { return app.charIDToTypeID(s); };
var desc317 = new ActionDescriptor();
var ref302 = new ActionReference();
ref302.putEnumerated( cTID('Path'), cTID('Path'), stringIDToTypeID('vectorMask') );
ref302.putEnumerated( cTID('Lyr '), cTID('Ordn'), cTID('Trgt') );
desc317.putReference( cTID('null'), ref302 );
executeAction( cTID('Dlt '), desc317, DialogModes.NO );
};
////// disable vector mask //////
function enableDisableVectorMask (theBoolean) {
try {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref);
var vmEnabled = layerDesc.getBoolean(stringIDToTypeID( "vectorMaskEnabled" ));
if (theBoolean == undefined) {
if (vmEnabled == false) {var theBoolean = true}
else {var theBoolean = false};
};
// =======================================================
var idlayer = stringIDToTypeID( "layer" );
var desc5 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putEnumerated( idlayer, stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc5.putReference( stringIDToTypeID( "null" ), ref1 );
var desc6 = new ActionDescriptor();
desc6.putBoolean( stringIDToTypeID( "vectorMaskEnabled" ), theBoolean );
desc5.putObject( stringIDToTypeID( "to" ), idlayer, desc6 );
executeAction( stringIDToTypeID( "set" ), desc5, DialogModes.NO );
} catch (e) {}
};
////// get square circumscribed by four guides //////
function getGuidesSquare () {
// set to pixels;
var myDocument = app.activeDocument;
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// collect guides;
var theVerticalArray = new Array;
var theHorizontalArray = new Array;
for (var m = 0; m < myDocument.guides.length; m++) {
if (myDocument.guides[m].direction == Direction.HORIZONTAL) {theHorizontalArray.push(Number(myDocument.guides[m].coordinate))}
else {theVerticalArray.push(Number(myDocument.guides[m].coordinate))}
};
theHorizontalArray.sort(sortByDate);
theVerticalArray.sort(sortByDate);
// if rectangle;
if (theHorizontalArray.length == 2 && theVerticalArray.length == 2) {
    if (theHorizontalArray[1]-theHorizontalArray[0] == theVerticalArray[1]-theVerticalArray[0]) {
        return [theVerticalArray[0], theHorizontalArray[0], theVerticalArray[1], theHorizontalArray[1]]
    }
};
app.preferences.rulerUnits = originalRulerUnits;
function sortByDate(a,b) {
if (Number(a)<Number(b)) return -1;
if (Number(a)>Number(b)) return 1;
return 0;
};
};

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 10, 2024 Apr 10, 2024

Copy link to clipboard

Copied

A am afraid there is an issue with my transformation approach. 

In some cases the transformation will not result in a rectangle/square.

I am still trying to figure out the issue, please make sure to visually check the results before processing a file further. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 10, 2024 Apr 10, 2024

Copy link to clipboard

Copied

Hey @c.pfaffenbichler 

 

I have been testing the scripts and encountered some weird cases. I did not want to take up more of your time, to be honest, as you already have been so generous in spending plenty of time on this big project.

 

I also got an error this week, which I suspect has something to do with the move based on the guide approach. I did clean up the document but, as far as I know, did not move the guides.

 

Usually, I support one script, but I am debating whether to have separate ones for warp, resize, and move. They would be less complex and easier for me to read and adjust if specifics change in the future or detect which step is failing.

 

The 4 point vector does seem like the golden approach because it contains the information that can be used for all 3 steps (warp, scale, move), but the inconsistent result is worrisome. I Wish there was a way to use the perspective warp function and avoid the complex rotation and scaling currently needed.

 

PS: I have added the final/cleaned PSD template (based on 400x400 pixels box sizes) to the Google drive folder, with the 2 fake images for testing.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 10, 2024 Apr 10, 2024

Copy link to clipboard

Copied

I seem to experience problems accessing those files at current, I’ll try again later. 

Ideal for trouble-shooting would naturally be »unedited« files where you subsequently got those bad results (with everything except the box-element itself blacked out). 

 

I am trying to investigate the issue, but I am not sure if (or when) I can get a grip on it unfortunately. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Apr 10, 2024 Apr 10, 2024

Copy link to clipboard

Copied

LATEST

I could not detect a pattern tbh. It seemed random... But I will try more and hope to encounter a consistent one that goes wrong.

 

The error, suspecting the move based on guides is the issue is blocking me however to do this more fluidly.  Maybe to avoid sudden issues due to guides settings or something by accident changed the move can be changed more static like I had in a different script?

 

// 20-03-2024 // Move Smart Object into position
if (app && app.documents.length > 0) {
    var doc = app.activeDocument;
    var originalRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;
    
    // Show guides
    app.activeDocument.guidesVisibility = true;

    ////// Align Layer Top Left of Selection //////
    var deltaX = doc.selection.bounds[0];
    var deltaY = doc.selection.bounds[1];
    doc.activeLayer.translate(-deltaX, -deltaY);

    doc.activeLayer.translate(2500, 1740)

    // Deselect all
    doc.selection.deselect();

    }

 

sorry to hear you are experiencing issues with downloading maybe separate links would work better?
The Google Drive folder

PSD Template

Box 2.jpg

Box 5.jpg

Current Script

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines