Using Stdlib.getVectorMaskBounds one could only get bounding rect clipped to canvas size, because making selection outside canvas is impossible. Sometimes it is necessary to get real, not clipped bounds. If paths on vector mask consist only of corner points or only corner points determines clipping rect of a path (i.e. no curve stick out of it) one can use the following function to get paths clipping rect NOT clipped to canvas size:
Code: Select all
// by Damian SzopeN Sepczuk <damian[d0t]sepczuk[a7]o2{do7}pl>
// [in] round (bool) -- whether returned values should be rounded to the nearest pixel, def: false
// [in] doc -- document containing layer with vector mask
// [in] layer -- layer with vector mask
// returns array [left, top, right, bottom, width, height]
Stdlib.getVectorMaskBounds_cornerPointsOnly = function(round, doc, layer) {
round = !!round;
function _ftn() {
var ref = new ActionReference();
ref.putEnumerated( cTID('Path'), cTID('Path'), sTID('vectorMask') );
ref.putEnumerated(cTID("Lyr "), cTID("Ordn"), cTID("Trgt"));
var vMaskDescr = executeActionGet(ref);
var pathContents = vMaskDescr.getObjectValue(sTID('pathContents'));
var pathList = pathContents.getList(sTID('pathComponents'));
// for each path in current layer
var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
for ( cPath=0; cPath<pathList.count; ++cPath )
{
var curPath = pathList.getObjectValue(cPath).getList(sTID("subpathListKey"));
var points = curPath.getObjectValue(0).getList(sTID("points"));
// for each point
for (cPoint=0; cPoint < points.count; ++cPoint )
{
var point = points.getObjectValue(cPoint).getObjectValue(sTID("anchor"));
var x = point.getUnitDoubleValue(sTID('horizontal'));
var y = point.getUnitDoubleValue(sTID('vertical'));
if ( x < minX ) minX = x; // it is faster than if/else block (benchmarked on PSCS4)
if ( x > maxX ) maxX = x;
if ( y < minY ) minY = y;
if ( y > maxY ) maxY = y;
}
}
res = [minX, minY, maxX, maxY, maxX-minX, maxY-minY];
if (round)
{
for ( i=0; i<res.length; ++i )
{
res = Math.round(res);
}
}
return res;
}
var bnds = Stdlib.wrapLCLayer(doc, layer, _ftn);
return bnds;
}
Function Stdlib.wrapLCLayer is in stdlib.js file.
Stdlib.getVectorMaskBounds + path partially outside canvas
Stdlib.getVectorMaskBounds + path partially outside canvas
Nice clean code. If you want to get a little bit more out of it, try moving the sTID calls outside of the loops (via additional variables). It may or may not make a noticeable difference, but if your benchmarking noticed a difference for if/else-if it's probably worth checking into.
BTW, do you want/mind me folding your stuff into stdlib.js (with attribution, of course)?
-X
BTW, do you want/mind me folding your stuff into stdlib.js (with attribution, of course)?
-X
Stdlib.getVectorMaskBounds + path partially outside canvas
BTW, do you want/mind me folding your stuff into stdlib.js (with attribution, of course)?
If you think it's a good idea -- go ahead
If you want to get a little bit more out of it, try moving the sTID calls outside of the loops (via additional variables).
Good suggestion I've benchmarked the code w/o using xTID (bare numerical values), gaining ca. 33% but forgot to optimize the loop itself :stupid:. It could be also a good idea to buffer once checked IDs in JS array in xTID function (dynamic programming). Scripts heavily using xTID (for example in loops) could benefit from it. I'll test it today.
Updated code below:
Code: Select all// by Damian SzopeN Sepczuk <damian[d0t]sepczuk[a7]o2{do7}pl>
// [in] round (bool) -- whether returned values should be rounded to the nearest pixel, def: false
// [in] doc -- document containing layer with vector mask
// [in] layer -- layer with vector mask
// returns array [left, top, right, bottom, width, height]
Stdlib.getVectorMaskBounds_cornerPointsOnly = function(round, doc, layer) {
round = !!round;
function _ftn() {
var ref = new ActionReference();
ref.putEnumerated( cTID('Path'), cTID('Path'), sTID('vectorMask') );
ref.putEnumerated(cTID("Lyr "), cTID("Ordn"), cTID("Trgt"));
var vMaskDescr = executeActionGet(ref);
var pathContents = vMaskDescr.getObjectValue(sTID('pathContents'));
var pathList = pathContents.getList(sTID('pathComponents'));
// for each path in current layer
var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
// using separate variables gives speed gain
var _id1 = sTID("subpathListKey"),
_id2 = sTID("points"),
_id3 = sTID("anchor"),
_id4 = sTID('horizontal'),
_id5 = sTID('vertical');
for ( cPath=0; cPath<pathList.count; ++cPath )
{
var curPath = pathList.getObjectValue(cPath).getList(_id1);
var points = curPath.getObjectValue(0).getList(_id2);
// for each point
for (cPoint=0; cPoint < points.count; ++cPoint )
{
var point = points.getObjectValue(cPoint).getObjectValue(_id3);
var x = point.getUnitDoubleValue(_id4);
var y = point.getUnitDoubleValue(_id5);
if ( x < minX ) minX = x; // it is faster than if/else block (benchmarked on PSCS4)
if ( x > maxX ) maxX = x;
if ( y < minY ) minY = y;
if ( y > maxY ) maxY = y;
}
}
res = [minX, minY, maxX, maxY, maxX-minX, maxY-minY];
if (round)
{
for ( i=0; i<res.length; ++i )
{
res = Math.round(res);
}
}
return res;
}
var bnds = Stdlib.wrapLCLayer(doc, layer, _ftn);
return bnds;
}
-- update
I've tested buffered versions of xTID. The conclusion is -- they (generally) do no harm. Code and tests (CS4) below:
Code: Select allcTID_global_array = new Array();
function cTID(s) { return cTID_global_array[s] || cTID_global_array[s]=app.charIDToTypeID(s); };
sTID_global_array = new Array();
function sTID(s) { return sTID_global_array[s] || sTID_global_array[s]=app.stringIDToTypeID(s); };
Test results:
Code: Select alldifferent variations of getVectorMaskBounds_cornerPointsOnly (10000 invokes, layer mask with 3 paths each containing 10 corner points)
===============================================
Version Infinity: (11921003+11902229+11907602)/3=11910278
Version w/o xTID: (7033363+6994813+7054897)/3 = 7027691
Version w/ xTID before loop var: (7600072+7580678+7547901)/3 = 7576217
Version w/ xTID before loop arrNum:(7709093+7725437+7697937)/3 = 7710822
Version w/ xTID before loop arrStr:(7798030+7776608+7772954)/3 = 7782531
Version w/ xTID before loop Obj: (7782374+7793718+7775850)/3 = 7783980
Version Inf w/ buffered xTID: (10440350+10377560+10423510)/3 =10413807
Version w/ buf xTID and opt loop var:(7560101+7567488+7582057)/3= 7569882
Code: Select allBuffered xTID itself
===============================================
Test code
var iter=NUM_OF_ITERATIONS;
for ( var i=0; i<iter; ++i ) {
sTID('vectorMask');
sTID('pathContents');
sTID('subpathListKey');
sTID('anchor');
}
NoOfIter w/o buffering w/ buffering time diff % speedup
1 32 40 -8 -25%
2 40 45 -5 -13%
5 66 58 8 12%
10 111 82 29 26%
100 895 445 450 50%
1000 10255 4298 5957 58%
10000 105075 42208 62867 59%
100000 944331 426956 517375 55%
1000000 8994127 4125196 4868931 54%
If you think it's a good idea -- go ahead
If you want to get a little bit more out of it, try moving the sTID calls outside of the loops (via additional variables).
Good suggestion I've benchmarked the code w/o using xTID (bare numerical values), gaining ca. 33% but forgot to optimize the loop itself :stupid:. It could be also a good idea to buffer once checked IDs in JS array in xTID function (dynamic programming). Scripts heavily using xTID (for example in loops) could benefit from it. I'll test it today.
Updated code below:
Code: Select all// by Damian SzopeN Sepczuk <damian[d0t]sepczuk[a7]o2{do7}pl>
// [in] round (bool) -- whether returned values should be rounded to the nearest pixel, def: false
// [in] doc -- document containing layer with vector mask
// [in] layer -- layer with vector mask
// returns array [left, top, right, bottom, width, height]
Stdlib.getVectorMaskBounds_cornerPointsOnly = function(round, doc, layer) {
round = !!round;
function _ftn() {
var ref = new ActionReference();
ref.putEnumerated( cTID('Path'), cTID('Path'), sTID('vectorMask') );
ref.putEnumerated(cTID("Lyr "), cTID("Ordn"), cTID("Trgt"));
var vMaskDescr = executeActionGet(ref);
var pathContents = vMaskDescr.getObjectValue(sTID('pathContents'));
var pathList = pathContents.getList(sTID('pathComponents'));
// for each path in current layer
var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
// using separate variables gives speed gain
var _id1 = sTID("subpathListKey"),
_id2 = sTID("points"),
_id3 = sTID("anchor"),
_id4 = sTID('horizontal'),
_id5 = sTID('vertical');
for ( cPath=0; cPath<pathList.count; ++cPath )
{
var curPath = pathList.getObjectValue(cPath).getList(_id1);
var points = curPath.getObjectValue(0).getList(_id2);
// for each point
for (cPoint=0; cPoint < points.count; ++cPoint )
{
var point = points.getObjectValue(cPoint).getObjectValue(_id3);
var x = point.getUnitDoubleValue(_id4);
var y = point.getUnitDoubleValue(_id5);
if ( x < minX ) minX = x; // it is faster than if/else block (benchmarked on PSCS4)
if ( x > maxX ) maxX = x;
if ( y < minY ) minY = y;
if ( y > maxY ) maxY = y;
}
}
res = [minX, minY, maxX, maxY, maxX-minX, maxY-minY];
if (round)
{
for ( i=0; i<res.length; ++i )
{
res = Math.round(res);
}
}
return res;
}
var bnds = Stdlib.wrapLCLayer(doc, layer, _ftn);
return bnds;
}
-- update
I've tested buffered versions of xTID. The conclusion is -- they (generally) do no harm. Code and tests (CS4) below:
Code: Select allcTID_global_array = new Array();
function cTID(s) { return cTID_global_array[s] || cTID_global_array[s]=app.charIDToTypeID(s); };
sTID_global_array = new Array();
function sTID(s) { return sTID_global_array[s] || sTID_global_array[s]=app.stringIDToTypeID(s); };
Test results:
Code: Select alldifferent variations of getVectorMaskBounds_cornerPointsOnly (10000 invokes, layer mask with 3 paths each containing 10 corner points)
===============================================
Version Infinity: (11921003+11902229+11907602)/3=11910278
Version w/o xTID: (7033363+6994813+7054897)/3 = 7027691
Version w/ xTID before loop var: (7600072+7580678+7547901)/3 = 7576217
Version w/ xTID before loop arrNum:(7709093+7725437+7697937)/3 = 7710822
Version w/ xTID before loop arrStr:(7798030+7776608+7772954)/3 = 7782531
Version w/ xTID before loop Obj: (7782374+7793718+7775850)/3 = 7783980
Version Inf w/ buffered xTID: (10440350+10377560+10423510)/3 =10413807
Version w/ buf xTID and opt loop var:(7560101+7567488+7582057)/3= 7569882
Code: Select allBuffered xTID itself
===============================================
Test code
var iter=NUM_OF_ITERATIONS;
for ( var i=0; i<iter; ++i ) {
sTID('vectorMask');
sTID('pathContents');
sTID('subpathListKey');
sTID('anchor');
}
NoOfIter w/o buffering w/ buffering time diff % speedup
1 32 40 -8 -25%
2 40 45 -5 -13%
5 66 58 8 12%
10 111 82 29 26%
100 895 445 450 50%
1000 10255 4298 5957 58%
10000 105075 42208 62867 59%
100000 944331 426956 517375 55%
1000000 8994127 4125196 4868931 54%
Stdlib.getVectorMaskBounds + path partially outside canvas
I've added Stdlib.getVectorMaskBounds_cornerPointsOnly (with a couple of 'var's stuck in).
I've also added ID functions as _cTID and _sTID with minor changes to re-scope (and rename) the id cache.
{Edit} I will probably convert cTID and sTID over at a later time after I've done more testing with the cache versions.
These will be in the push of stdlib.js.
-X
I've also added ID functions as _cTID and _sTID with minor changes to re-scope (and rename) the id cache.
{Edit} I will probably convert cTID and sTID over at a later time after I've done more testing with the cache versions.
These will be in the push of stdlib.js.
-X
Stdlib.getVectorMaskBounds + path partially outside canvas
I've added Stdlib.getVectorMaskBounds_cornerPointsOnly (with a couple of 'var's stuck in).
I think, you are speaking of (_id1, ..., _id5) fragment? Isn't the snippet
Code: Select allvar a=2,
b=3,
c=4;
equal to
Code: Select allvar a=2;
var b=3;
var c=4;
Either way, all variables should be in local scop.
I will probably convert cTID and sTID over at a later time after I've done more testing with the cache versions.
I've tested several ways of returning-or-computing cached value (using if, ?:, typeof) but || version turned out to be the best one. Please share your ideas and tests itself when you're done.
I think, you are speaking of (_id1, ..., _id5) fragment? Isn't the snippet
Code: Select allvar a=2,
b=3,
c=4;
equal to
Code: Select allvar a=2;
var b=3;
var c=4;
Either way, all variables should be in local scop.
I will probably convert cTID and sTID over at a later time after I've done more testing with the cache versions.
I've tested several ways of returning-or-computing cached value (using if, ?:, typeof) but || version turned out to be the best one. Please share your ideas and tests itself when you're done.
Stdlib.getVectorMaskBounds + path partially outside canvas
Either way, all variables should be in local scop.
cPoint, cPath, and i were the ones that needed the 'var'.
but || version turned out to be the best one.
I'm not surprised. I use the idiom self, a habit I picked up in my perl days.
I'll post something here when I make the switch.
-X
cPoint, cPath, and i were the ones that needed the 'var'.
but || version turned out to be the best one.
I'm not surprised. I use the idiom self, a habit I picked up in my perl days.
I'll post something here when I make the switch.
-X