Copy link to clipboard
Copied
i have many different Isosceles trapezoids?
listener:
var idTrnf = charIDToTypeID( "Trnf" );
var desc48 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref21 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref21.putEnumerated( idLyr, idOrdn, idTrgt );
desc48.putReference( idnull, ref21 );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc48.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc49 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idPxl = charIDToTypeID( "#Pxl" );
desc49.putUnitDouble( idHrzn, idPxl, -16.066570 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idPxl = charIDToTypeID( "#Pxl" );
desc49.putUnitDouble( idVrtc, idPxl, 16.381048 );
var idOfst = charIDToTypeID( "Ofst" );
desc48.putObject( idOfst, idOfst, desc49 );
var idWdth = charIDToTypeID( "Wdth" );
var idPrc = charIDToTypeID( "#Prc" );
desc48.putUnitDouble( idWdth, idPrc, 134.475806 );
var idSkew = charIDToTypeID( "Skew" );
var desc50 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idAng = charIDToTypeID( "#Ang" );
desc50.putUnitDouble( idHrzn, idAng, 19.127500 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idAng = charIDToTypeID( "#Ang" );
desc50.putUnitDouble( idVrtc, idAng, 0.000000 );
var idPnt = charIDToTypeID( "Pnt " );
desc48.putObject( idSkew, idPnt, desc50 );
var idUsng = charIDToTypeID( "Usng" );
var desc51 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idPrc = charIDToTypeID( "#Prc" );
desc51.putUnitDouble( idHrzn, idPrc, -0.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idPrc = charIDToTypeID( "#Prc" );
desc51.putUnitDouble( idVrtc, idPrc, 0.655242 ); //0.655242 Represents the percentage of the vertical direction?How to calculate it,
var idPnt = charIDToTypeID( "Pnt " );
desc48.putObject( idUsng, idPnt, desc51 );
var idPnt = charIDToTypeID( "Pnt " );
desc48.putObject( idUsng, idPnt, desc51 );
var idIntr = charIDToTypeID( "Intr" );
var idIntp = charIDToTypeID( "Intp" );
var idbicubicAutomatic = stringIDToTypeID( "bicubicAutomatic" );
desc48.putEnumerated( idIntr, idIntp, idbicubicAutomatic );
executeAction( idTrnf, desc48, DialogModes.NO );
Copy link to clipboard
Copied
0.655242 Represents the percentage of the vertical direction?How to calculate it,
Copy link to clipboard
Copied
I tried similar thing here: How to remove Smart Object deformation with scripting? | Photoshop Family Customer Community
Anyway I can't solve this now. I can send you everything what I know now.
I am working on algorithm which start with big value and then try something, measure values and decrease or increase this value.
Binary search algorithm - Wikipedia
Then I could save a lot data for data analysis and find mathematic formula.
Copy link to clipboard
Copied
You also can use "perspective warp" if you have single bitmap layer. Data from script listener here should be easy to change.
Copy link to clipboard
Copied
i have many different Isosceles trapezoids?
And what do we know about them?
Are they all of the same angles and width and height?
How do you intend to automate the task if they should differ?
Copy link to clipboard
Copied
Sorry, the previous formula was nonsense …
// 2017, use it at your own risk;
#target photoshop
// from adobe’s terminology.jsx;
const classChannel = app.charIDToTypeID('Chnl');
const classRectangle = app.charIDToTypeID('Rctn');
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 );
}
////////////////////////////////////
var myDocument = app.activeDocument;
// deselect;
try {
myDocument.selection.deselect();
// =======================================================
var idDslc = charIDToTypeID( "Dslc" );
var desc4 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
ref2.putClass( idPath );
desc4.putReference( idnull, ref2 );
executeAction( idDslc, desc4, DialogModes.NO );
}
catch (e) {};
// switch units to pixels;
app.preferences.rulerUnits = Units.PIXELS;
//////////// transformation ////////////
try {
// paste the image into the document;
var theScreenImage = myDocument.activeLayer;
//////////// corners ////////////
// get the horicontal and vertical coordinates in pixels;
var theBounds = theScreenImage.bounds;
var theWidth = theBounds[2] - theBounds[0];
var thePerc = 58.4;
var theOffset = ((theWidth / thePerc * 100) - theWidth) /2;
var x1 = theBounds[0] - theOffset;
var y1 = theBounds[1];
var x2 = theBounds[2] + theOffset;
var y2 = theBounds[1];
var x3 = theBounds[2];
var y3 = theBounds[3];
var x4 = theBounds[0];
var y4 = theBounds[3];
//////////// transform to the new corners ////////////
transformActiveLayer( [new TPoint(x1,y1), new TPoint(x2,y2), new TPoint(x3,y3), new TPoint(x4,y4)]);
// resets the preferences units;
app.preferences.rulerUnits = originalUnits;
}
catch (e) {};
////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
//////////// the functions ////////////
// 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 );
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] ) );
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.fX );
quadCorners.putUnitDouble( unitPixels, newCorners.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;
};
Copy link to clipboard
Copied
c.pfaffenbichler: wow... that's interesting. I prepared raw AM code for better understaning what's going on and better reusability.
var args = new ActionDescriptor();
var quadRect = new ActionList();
//original left top
quadRect.putUnitDouble(charIDToTypeID('#Pxl'),0);
quadRect.putUnitDouble(charIDToTypeID('#Pxl'),0);
//original right bottom
quadRect.putUnitDouble(charIDToTypeID('#Pxl'),20);
quadRect.putUnitDouble(charIDToTypeID('#Pxl'),20);
//new corners
var quadCorners = new ActionList();
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 0 ); //left top
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 0 );
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 20 ); //right top
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 0 );
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 20 ); //right bottom
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 20 );
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 0 ); //left bottom
quadCorners.putUnitDouble( charIDToTypeID('#Pxl'), 20 );
args.putList( stringIDToTypeID("rectangle"), quadRect );
args.putList( stringIDToTypeID("quadrilateral"), quadCorners );
executeAction( charIDToTypeID('Trnf'), args );
Copy link to clipboard
Copied
c.pfaffenbichler: if I understand correctly this method should transform any rectangle into any quadrilateral (Quadrilateral)
But it doesn't seem to be designed for non-rectangular shape on input. It seems to be possible but a bit harder.
Copy link to clipboard
Copied
if I understand correctly this method should transform any rectangle into any quadrilateral
Alas, if it is run on a Smart Object that already has a transformation applied the result seems to be nonsensical.
Copy link to clipboard
Copied
yes, but if you wrap SO into group you can show group-layer bounds and transform group. And you should see blue rectangle which will be input rectangle. I don't know why but SO sometimes has zero pixels after transformation. But it works with wraping SO into folder. I think undeforming should be possible with some math. And you also can read corner rectangles of SO.
Copy link to clipboard
Copied
Seems to that I will be able improve "Undeform" script. Now always right bottom corner has wrong position.
And with quadrilateral transformation I am able change this corner and turn smart object into pixel perfect rectangle.
First I need find point where is intercept bottom edge of wrapper bounds and right edge of SO.
Then I need to find "q" for multiplication this "x" coordinate of intersection which will be equal to right top "x"
And similar for "y" axis.
More simple is apply one transformation for each axis.
You also can create new empty layer and link this layer with SO and then transform new empty layer. It works same as with group but you can avoid destroing of clipped layers and max group nesting limit
Copy link to clipboard
Copied
Just to make sure credit is not misplaced: That function and the supporting parts are from »stacksupport.jsx« and »terminology.jsx«, I did not think them up myself but they are part of the Photoshop default Scripts.
And that I figured out how to utilise it in a custom Script was lucky …