Selection from subpath

Anyone, especially newbies, asking for help with photoshop scripting and photoshop automation - as opposed to those contributing to discussion about an aspect of photoshop scripting

Moderators: Patrick, Mike Hale, xbytor, Larry Ligon, Andrew, PS-Moderators

Selection from subpath

Postby smathis » Mon Feb 20, 2012 8:30 pm

If I have a path made up of a number of subpaths, is there a way to specify a selection from a particular subpath, rather than the whole path? Such as:


Code: Select all
myPath.subPathItems[1].selection();



Trying scriptListener as an alternate, and I'm getting the whole path instead..not sure what I'm missing on this one...


Code: Select all
var idsetd = charIDToTypeID( "setd" );

    var desc11 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref7 = new ActionReference();

        var idChnl = charIDToTypeID( "Chnl" );

        var idfsel = charIDToTypeID( "fsel" );

        ref7.putProperty( idChnl, idfsel );

    desc11.putReference( idnull, ref7 );

    var idT = charIDToTypeID( "T   " );

        var ref8 = new ActionReference();

        var idPath = charIDToTypeID( "Path" );

        var idOrdn = charIDToTypeID( "Ordn" );

        var idTrgt = charIDToTypeID( "Trgt" );

        ref8.putEnumerated( idPath, idOrdn, idTrgt );

    desc11.putReference( idT, ref8 );

    var idVrsn = charIDToTypeID( "Vrsn" );

    desc11.putInteger( idVrsn, 1 );

    var idvectorMaskParams = stringIDToTypeID( "vectorMaskParams" );

    desc11.putBoolean( idvectorMaskParams, true );

executeAction( idsetd, desc11, DialogModes.NO );

smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Mon Feb 20, 2012 9:24 pm

If you have a subpath selected the scriptlistener code you posted will load only that subpath as a selection. The problem is as you pointed out there is no way to select a subpath with a script. It can only be done in the GUI.

But there are ways around that. If the subpath is made up of only corner points you can get the subpath pathpoint anchors and use those to make the selection. For subpaths with smooth point you can get all of the subpath pathpoints data and make a new temp path then load that path as a selection.

Neither is a simple or as easy as it could be if subPathItem also had all of the methods of pathItem.
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Wed Feb 22, 2012 7:17 pm

So I dug up this code on the forum and I'm noticing that I just really am having problems grasping how the array of arrays is handling info and passing it on for proper retrieval. If I have a range of subpaths I want to copy for the purpose of making selections, how do specify the range in the context of the existing for loop? Could I also use this to split the x,y anchor coordinates and sort? I think I just am not sure how to access different hierarchies of arrays of arrays, with the anchor/subpath arrays thrown on top of it! Really is making my head spin, and I think I'm dancing around the one solution to all my scripting issues.

Code: Select all
function extractSubPathInfo(pathObj){
   var pathArray = new Array();// each element can be used as the second arugment in pathItems.add ie doc.pathItems.add("myPath1", pathArray[0]);
   var pl = pathObj.subPathItems.length;
   //define the path
   for(var s=0;s<pl;s++){
      var pArray = new Array();
      for(var i=0;i<pathObj.subPathItems[s].pathPoints.length;i++){
         pArray[i] = new PathPointInfo;
         pArray[i].kind = pathObj.subPathItems[s].pathPoints[i].kind;
         pArray[i].anchor = pathObj.subPathItems[s].pathPoints[i].anchor;
         pArray[i].leftDirection = pathObj.subPathItems[s].pathPoints[i].leftDirection;
         pArray[i].rightDirection = pathObj.subPathItems[s].pathPoints[i].rightDirection;
      }
      var subPathArray = new Array();
      subPathArray[0] = new SubPathInfo();
      subPathArray[0].operation = ShapeOperation.SHAPEADD;
      subPathArray[0].closed = true;
      subPathArray[0].entireSubPath = pArray;
   
      pathArray.push(subPathArray);
      }
   return pathArray;
}
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Wed Feb 22, 2012 7:25 pm

Someone on the Adobe Photoshop scripting forum had the same question about making a selection from a subpath. c.pfaffenbichler posted about the same answer I did here but with example code. It may help to have a look at the topic. http://forums.adobe.com/thread/965359?tstart=0
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Wed Feb 22, 2012 8:14 pm

That's me hammering at the same issue. Similar to the issues I've been hammering at for a few weeks. You could find a few postings from me where I'm trying to get array of arrays to work for different purposes. This has led me to the conclusion that these arrays all look very similar in architecture and function. I'm not trying to be a bother to the community by posting multiple places, but I feel like I am missing something simple by the responses I've been getting. I'm not a master photoshop script writer or .js programmer but I am trying to learn this stuff rather than just post a "make this code, k, thnx, bye" post.

c.pfaffenbichler has been a huge help getting me this far in how subpaths, anchors, and arrays interplay. I've tried that particular example code and it works, I'm just trying to figure out the whole architecture a bit better so I can specify a subpath range without literal definitions for each subpathItem I need. All these array of arrays codes seem very similar, but I can't digest them in a comparative manner to determine a course of best action. It may be this is as far as I get on the subject but since I'm a novice trying to learn this stuff, but I figured I might ask the community here too as every posting has helped me get to the next step in my knowledge and ability of execution.
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Wed Feb 22, 2012 9:35 pm

To answer how to adapt the code to work with a range of subpaths. It may be more a matter of style but I wouldn't. I would modify that( or similar code ) into a function that creates a new path from one subpath. I then would create another function that can work with a range of subpaths by calling the first function as many time as needed. For example( it would be better if there was more error checking )

Code: Select all
#target photoshop
splitSubPaths( app.activeDocument.pathItems[0], 0, 1 );// assumes there is an open document and the first pathItem has two subpaths.

/*///////////////////////////////////////////////////////////////////////////////////////////////////////
// Function: makeNewPathFromSubpath
// Usage: creates a new path from subpath
// Input: pathItem, Number-index of subpath, String(optional)
// Return: pathItem - the new pathIems or -1 if error
// Notes: adapted from code by c.pfaffenbichler
//              http://forums.adobe.com/thread/965359?tstart=0
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
function makeNewPathFromSubpath( thePath, subPathIndex, pathName ){
   try{
   var theSubPath = thePath.subPathItems[subPathIndex];
   var lineSubPathArray = new Array ();
   var lineArray = new Array ();
   for (c = 0; c < (theSubPath.pathPoints.length); c++) {
      lineArray[c] = new PathPointInfo;
      lineArray[c].kind = theSubPath.pathPoints[c].pointKind;
      lineArray[c].anchor = theSubPath.pathPoints[c].anchor;
      lineArray[c].leftDirection = theSubPath.pathPoints[c].leftDirection;
      lineArray[c].rightDirection = theSubPath.pathPoints[c].rightDirection;
   }
   lineSubPathArray[0] = new SubPathInfo();
   lineSubPathArray[0].closed = theSubPath.closed;
   lineSubPathArray[0].operation = theSubPath.operation;
   lineSubPathArray[0].entireSubPath = lineArray;
   undefined == pathName ? pathName = 'temp':pathName;
   return app.activeDocument.pathItems.add(pathName, lineSubPathArray);
   }catch(e){ return -1;}
};
/*///////////////////////////////////////////////////////////////////////////////////////////////////////
// Function: splitSubPaths
// Usage: creates new paths from subPaths
// Input: pathItem, Number, Number
// Return:  -1 if error
// Notes:
///////////////////////////////////////////////////////////////////////////////////////////////////////////*/
function splitSubPaths( thePath, startSubPathIndex, endSubPathIndex ){
   try{
      for( var sp = startSubPathIndex; sp<= endSubPathIndex;sp++ ){
         var tempPath = makeNewPathFromSubpath( thePath, sp,thePath.name+'_'+sp );
      }
   }catch(e){ return -1;}
};


To try to explain paths in general. What can be scripted when working with paths is very limited. You can work around some of those limits by extracting the path info and creating new paths from that data, modifying it if needed.

When working with existing paths you work with what Adobe call collections. Collections are similar to arrays but are not the same. They can be worked by index like arrays but have different methods. The collections used for paths are pathItems, subPathItems, and pathPoints. The pathItems collection is a collection of path objects. subPathItems is a collection of subPaths of a path. etc. A path can have one or more suppaths. A subPath can have more or more pathPoints.

When creating a path you do work with arrays. You need at least one array to hold the subPathInfo object. That object has properties for pathType(open/closed), pathOperation( add, subtract,etc ) and an array of pathPointInfo object. pathPointInfo objects have properties for kind( smooth, corner ), and arrays for anchor, left handle, and right handle.

It takes some working with paths to get a handle on all that is involved. But it helps to remember that existing paths use collections of objects describing the existing path. To create a path you use arrays of objects that describe the path you want to create.

One final note. Some subPaths also have subPaths( I think those paths come from Illustrator or other vector apps ). The DOM does not work with those 'subSubPaths'. So it is possible that you can run into a path that you can't extract all the data from.
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Wed Feb 22, 2012 10:46 pm

Just to make sure I'm following along correctly..

This line sets up the index value to be passed onto the next function via the arg
Code: Select all
       var theSubPath = thePath.subPathItems[subPathIndex];


to these arg's

Code: Select all
function splitSubPaths( thePath, startSubPathIndex, endSubPathIndex )


What about defining the start as something other than the first index and stop without a literal value? So instead of this:

Code: Select all
splitSubPaths(thePath, 0, 12 )


Passing something like this?

Code: Select all
var startSubPathIndex = myDoc.pathItems[0].subPathItems[1];
var endSubPathIndex = myDoc.pathItems[0].subPathItems.length;



Edit:
Actually got the endSubPathIndex var to pass correctly. Start var isn't doing it's trick but it seems because it needs to start at 0 via the for count, I assume and can't be redefined.

What would be a good method for filtering out subpaths based on width? Filter out those unnecessary values at the splitSubPath step?
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Thu Feb 23, 2012 12:04 am

Code: Select all
var startSubPathIndex = myDoc.pathItems[0].subPathItems[1];// this references a subPath object
var endSubPathIndex = myDoc.pathItems[0].subPathItems.length;// this references a Number


should be

Code: Select all
var startSubPathIndex = 1;// or whatever supPath index you want to start with. Note that it is a Number
var endSubPathIndex = myDoc.pathItems[0].subPathItems.length;// is a number so ok as is


If you wanted to filter the subPaths you would either include code that queries the subPath to see if it matches the criteria or create another function that does the same and call that function inside the sp loop.
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Thu Feb 23, 2012 1:11 am

Great! Thanks again for the help, this is really awesome. So to continue to be a bother about this.. If I am filtering on a subpath's width, then creating a selection and measuring its bounds is the only way to execute this step, correct?
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Thu Feb 23, 2012 2:14 am

Making a selection would be one way. The problem is you can not script making a selection from a subPath. You would have to create the path from the subPath first then make the selection and delete the path if need.

What I had in mind was to get the bounds of the subPath from the subPath directly. That way you wouldn't be making unwanted paths or selections. Something like this
Code: Select all
#target photoshop
Array.prototype.numericSort = function() {
   return this.sort( function (a,b) { return a-b; } );
};
splitSubPaths( app.activeDocument.pathItems[0], 1, 4, exceedsWidth, 200 );


function exceedsWidth( subPathItem, width ){
   var b = getSubPathBounds( subPathItem );
   var w = b[3]-b[0];
   return w>width ? true:false;
};
function getSubPathBounds( subPathItem ){
   var subPathBounds = [];
   var b =[];
   var xValues = [];
   var yValues = [];
   var pointCount = subPathItem.pathPoints.length;
   for( var p=0;p<pointCount;p++ ){
      xValues.push(  subPathItem.pathPoints[p].anchor[0] );
      yValues.push(  subPathItem.pathPoints[p].anchor[1] );
   }
   xValues.numericSort();
   yValues.numericSort();
   subPathBounds.push(xValues[0]);
   subPathBounds.push(xValues[xValues.length-1]);
   subPathBounds.push(yValues[0]);
   subPathBounds.push(yValues[yValues.length-1]);
   return subPathBounds;
};
function makeNewPathFromSubpath( thePath, subPathIndex, pathName ){
   try{
   var theSubPath = thePath.subPathItems[subPathIndex];
   var lineSubPathArray = new Array ();
   var lineArray = new Array ();
   for (c = 0; c < (theSubPath.pathPoints.length); c++) {
      lineArray[c] = new PathPointInfo;
      lineArray[c].kind = theSubPath.pathPoints[c].pointKind;
      lineArray[c].anchor = theSubPath.pathPoints[c].anchor;
      lineArray[c].leftDirection = theSubPath.pathPoints[c].leftDirection;
      lineArray[c].rightDirection = theSubPath.pathPoints[c].rightDirection;
   }
   lineSubPathArray[0] = new SubPathInfo();
   lineSubPathArray[0].closed = theSubPath.closed;
   lineSubPathArray[0].operation = theSubPath.operation;
   lineSubPathArray[0].entireSubPath = lineArray;
   undefined == pathName ? pathName = 'temp':pathName;
   return app.activeDocument.pathItems.add(pathName, lineSubPathArray);
   }catch(e){ return -1;}
};
function splitSubPaths( thePath, startSubPathIndex, endSubPathIndex, ftn, width ){
   try{
      for( var sp = startSubPathIndex; sp<= endSubPathIndex;sp++ ){
         var include = ftn(thePath.subPathItems[sp], width);
         if(include == true ) var tempPath = makeNewPathFromSubpath( thePath, sp,thePath.name+'_'+sp );
      }
   }catch(e){ return -1;}
};
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Thu Feb 23, 2012 3:19 pm

Thanks a bunch! That definitely is a more elegant solution. I have a few questions on this for you though..

What is the array doing here for the width value? Would I reverse these if I wanted to exclude a small path and include a big path? Or is that happening at the sort?
Code: Select all
var w = b[3]-b[0];


Also, is ftn referencing the anonymous prototype function at the top for sorting?

Code: Select all
    function splitSubPaths( thePath, startSubPathIndex, endSubPathIndex, ftn, width ){
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Thu Feb 23, 2012 4:36 pm

I'll start with your last question and work backwards.

You can think of ftn as a helper function for splitSubPaths. That helper functon( in this case exceedsWidth ) is passed by reference as an agrument. The helper function is defined as a standard function object, not by prototype. I did this so splitSubsPaths would be more flexible. Create a function that return true/false based on whatever you criteria for making a new path from the subpath.

In the exceedsWidth function b is the bounds of the subpath. It is similar to the bounds of a selection or layer. So the width is b[2]-b[0] and the height is b[3]-b[1]. I see why you may have been confused. There is a type in exceedsWidth. It should be var w = b[2]-b[0]; With that corrected it should return true when the subpath execceds the width argument. By modifying this function you have have splitSubPaths make paths based on width, height, both width and height, number of pathPoints, etc. You can also set a range such as wider that 100px but less than 400px.

numericSort is used as an easy way to get the x and y anchor values in numeric order. I could have set up vars for top,left,right,bottom before the pathPoint loop and swapping as needed inside the pathPoint loop. The smallest x value is left, the largest is right. with y the smallest is top, the largeset bottom.
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Re: Selection from subpath

Postby smathis » Thu Feb 23, 2012 5:20 pm

That's really awesome Mike, thanks again for all your patient help!! I've got switch gears and put this up for a few hours, but I'll dig into it again later today and let you know if I have any other questions. :D
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby smathis » Tue Feb 28, 2012 5:54 pm

So I've rewritten this and I'm still having some issues constraining range on width, height, or # of points range. Is it because I removed "width" from the function argument list? Is it how I'm trying to constrain the values?

I thought if I defined w or h's ranges like on the third line below, it'd be okay to evaluate properly. Also, I noticed none of these operations can be done if rulers aren't set to points, could you confirm that Mike?

Code lines I've altered-

Code: Select all
var startSubPathIndex = 1; var endSubPathIndex = myDoc.pathItems[0].subPathItems.length;

splitSubPaths( thePath, startSubPathIndex, endSubPathIndex, exceedsWidth);

return (w > 100, w < 120, h > 10) ? true:false;
smathis
 
Posts: 23
Joined: Wed Feb 01, 2012 6:22 pm

Re: Selection from subpath

Postby Mike Hale » Tue Feb 28, 2012 6:50 pm

Xbytor is better at this kind of function passing that I am and I am sure he would have done a better job. But the way I have it set up it needs the width argument( or the function needs to be edited ). That width argument is used by exceedsWidth to determine which subpaths to include.

PathPoints anchors values are in pixels no matter what the ruler settings. So width needs to also be pixels. If you want to use other units such as cm or in you will need to convert before calling the splitSubPaths function and still use pixels as the width argument.
Mike Hale
Site Admin
 
Posts: 4337
Joined: Fri Sep 30, 2005 10:52 pm
Location: USA

Next

Return to Help Me

Who is online

Users browsing this forum: pfaffenbichler and 2 guests