9 Replies Latest reply on Apr 1, 2012 9:05 AM by c.pfaffenbichler

    Selection from subpath

    Steven Mathisen

      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:

       

      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...

       

      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 );

        • 1. Re: Selection from subpath
          c.pfaffenbichler Level 9

          One cannot currectly select a subPathItem via Script as far as I know.

          A workaround is to collect the subPathItem’s properties, create a Path with only one subPathItem and go from there.

           

          In this example it would be the second subPathItem of the first Path.

          // 2012, use it at your own risk;
          #target photoshop
          if (app.documents.length > 0) {
          var myDocument = app.activeDocument;
          if (myDocument.pathItems.length > 0) {
          var theSubPath = myDocument.pathItems[0].subPathItems[1];
          //
          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;
          //
          var myPathItem = app.activeDocument.pathItems.add("test path", lineSubPathArray);
          }
          };
          
          • 2. Re: Selection from subpath
            Steven Mathisen Level 1

            I've switched this to a function, while updating the .subpathItems via a var called currentSubPath, to a second numeral via specificity and it works great. What about passing a value range?

             

            Is that similar to this code you helped me with, passing b and c?

             

            function collectPathPoints (myDoc, thePath) {

             

            var originalRulerUnits = app.preferences.rulerUnits;

             

            app.preferences.rulerUnits = Units.POINTS;

             

            var theArray = [];

             

            for (var b = 0; b < thePath.subPathItems.length; b++) {

             

                theArray[b] = [];

             

                for (var c = 0; c < thePath.subPathItems[b].pathPoints.length; c++) {

             

                     var pointsNumber = thePath.subPathItems[b].pathPoints.length;

             

                     var theAnchor = thePath.subPathItems[b].pathPoints[c].anchor;

             

                     theArray[b][c] = theAnchor;

             

                     };

             

                var theClose = thePath.subPathItems[b].closed;

             

                theArray = theArray.concat(String(theClose))

             

                };

             

            app.preferences.rulerUnits = originalRulerUnits;

             

            return theArray

             

            };

             

            Should I combine this into a universal function and call specific values more effectively? Struggling wrapping my brain around using these arrays of arrays effectively for calling information and wondering if I'm running in circles on the same subject. Is this a more complex version of a universal function that I could use for ?

             

            function extractSubPathInfo(pathObj){
               var pathArray = new Array();
               var pl = pathObj.subPathItems.length;
               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;
                     };
                  pathArray[pathArray.length] = new Array();
                  pathArray[pathArray.length - 1] = new SubPathInfo();
                  pathArray[pathArray.length - 1].operation = pathObj.subPathItems[s].operation;
                  pathArray[pathArray.length - 1].closed = pathObj.subPathItems[s].closed;
                  pathArray[pathArray.length - 1].entireSubPath = pArray;
                  };
               return pathArray;
               };

             

            I've got seperate issues for evalutating x/y ranges independently, and copying/combining subPaths for specific selections but both seem to be related to breaking apart these arrays of arrays to effectively target my paths. I'm not entirely sure at which levels I can best store, seperate, and operate upon certain data ranges in these heirarchies. Any guidance is much appreciated!

            • 3. Re: Selection from subpath
              c.pfaffenbichler Level 9

              I sometimes use these two functions for collecting path info and creating paths – but actually in some cases the subPathItems are not enough … anyway they seem somewhat similar to what you posted.

              ////// function to create path from array with one array per point that holds anchor, leftdirection, etc, 2010 //////
              function createPath (theArray, thePathsName) {
              var originalRulerUnits = app.preferences.rulerUnits;
              app.preferences.rulerUnits = Units.POINTS;
              lineSubPathArray = new Array ();
              if (theArray[theArray.length - 1].constructor != Array) {var numberOfSubPathItems = theArray.length - 1}
              else {var numberOfSubPathItems = theArray.length};
              for (var b = 0; b < numberOfSubPathItems; b++) {
                        var lineArray = new Array ();
                        for (c = 0; c < (theArray[b].length - 2); c++) {
                                  lineArray[c] = new PathPointInfo;
                                  if (!theArray[b][c][3]) {lineArray[c].kind = PointKind.CORNERPOINT}
                                  else {lineArray[c].kind = theArray[b][c][3]};
                                  lineArray[c].anchor = theArray[b][c][0];
                                  if (!theArray[b][c][1]) {lineArray[c].leftDirection = theArray[b][c][0]}
                                  else {lineArray[c].leftDirection = theArray[b][c][1]};
                                  if (!theArray[b][c][2]) {lineArray[c].rightDirection = theArray[b][c][0]}
                                  else {lineArray[c].rightDirection = theArray[b][c][2]}; 
                                  };
                        lineSubPathArray[b] = new SubPathInfo();
                        lineSubPathArray[b].closed = theArray[b][theArray[b].length - 2];
                        lineSubPathArray[b].operation = theArray[b][theArray[b].length - 1];
                        lineSubPathArray[b].entireSubPath = lineArray;
                        };
              var myPathItem = app.activeDocument.pathItems.add(thePathsName, lineSubPathArray);
              app.preferences.rulerUnits = originalRulerUnits;
              return myPathItem
              };
              ////// function to collect path-info as text //////
              function collectPathInfo (myDocument, thePath) {
              var originalRulerUnits = app.preferences.rulerUnits;
              app.preferences.rulerUnits = Units.POINTS;
              var theArray = [];
              for (var b = 0; b < thePath.subPathItems.length; b++) {
                        theArray[b] = [];
                        for (var c = 0; c < thePath.subPathItems[b].pathPoints.length; c++) {
                                  var pointsNumber = thePath.subPathItems[b].pathPoints.length;
                                  var theAnchor = thePath.subPathItems[b].pathPoints[c].anchor;
                                  var theLeft = thePath.subPathItems[b].pathPoints[c].leftDirection;
                                  var theRight = thePath.subPathItems[b].pathPoints[c].rightDirection;
                                  var theKind = thePath.subPathItems[b].pathPoints[c].kind;
                                  theArray[b][c] = [theAnchor, theLeft, theRight, theKind];
                                  };
                        theArray[b][theArray[b].length] = thePath.subPathItems[b].closed;
                        theArray[b][theArray[b].length] = thePath.subPathItems[b].operation;
                        };
              app.preferences.rulerUnits = originalRulerUnits;
              return theArray
              };
              

               

              I’m not quite sure I follow – what are your evaluation needs?

              What poperties of the subPathItems exactly are you trying to assess?

              • 4. Re: Selection from subpath
                Steven Mathisen Level 1

                I'm not following how theArray[b][c] is chaining together in your arrays. Could you explain that a bit more? Currently have a few different path/array needs for different situations, hence the different array postings.

                 

                The first, is fetching a subPath and copying it to a new path so I can evalute it's width/height/# of points and act upon it. I've got a script that's working on copying the paths, courtesy of Mike Hale, but it's not maintaining the proper subPath bounds. Seems to be acting upon the document's bounds, not the subPath.

                 

                var startSubPathIndex = 1;

                var endSubPathIndex = myDoc.pathItems[0].subPathItems.length;

                 

                var originalRulerUnits = app.preferences.rulerUnits;

                app.preferences.rulerUnits = Units.POINTS;

                 

                    Array.prototype.numericSort = function() {

                       return this.sort( function (a,b) { return a-b; } );

                    };

                    splitSubPaths( thePath, startSubPathIndex, endSubPathIndex, exceedsWidth, 100, 500 );

                 

                    function exceedsWidth( subPathItem, minWidth, maxWidth ){

                       var b = getSubPathBounds( subPathItem );

                       var w = b[3]-b[1];

                       var h = b[2]-b[0];

                    return (w > minWidth && w < maxWidth) ? 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, minWidth, maxWidth ){

                       try{

                          for( var sp = startSubPathIndex; sp<= endSubPathIndex;sp++ ){

                             var include = ftn(thePath.subPathItems[sp], minWidth, maxWidth );

                             if(include == true ) var tempPath = makeNewPathFromSubpath( thePath, sp,thePath.name+'_'+sp );

                          }

                       }catch(e){ return -1;}

                    };

                 

                Second is, taking an anchor collection, sorting on Y descending. Then returning these highest Y values up to a specified number of anchors, also while returning its relevant X coordinate. I can sort and return these but can't figure out how to do this while keeping the relevant X coordinate. Currently just using .write for debugging so I can see output in a txt file. So how would I rewrite the code below so I can sort on Y, capture/return 5 or so of the highest values, and return the x coordinate that goes with it?

                 

                    Array.prototype.numericSort = function() {

                       return this.sort( function (a,b) { return b-a; } );

                    };

                 

                 

                function collectPathPoints (myDocument, thePath, log, ftn) {

                 

                var originalRulerUnits = app.preferences.rulerUnits;

                app.preferences.rulerUnits = Units.POINTS;

                 

                var theArray = [];

                var xAnchor  = []

                var yAnchor  = []

                 

                for (var b = 0; b < thePath.subPathItems.length; b++) {

                 

                     theArray[b] = [];

                 

                     for (var c = 0; c < thePath.subPathItems[b].pathPoints.length; c++) {

                 

                       var pointsNumber = thePath.subPathItems[b].pathPoints.length;

                 

                                 xAnchor.push( Math.round(thePath.subPathItems[b].pathPoints[c].anchor[0]*100)/100 );

                                 yAnchor.push( Math.round(thePath.subPathItems[b].pathPoints[c].anchor[1] *100)/100);

                             

                        };

                        xAnchor.numericSort ();

                        yAnchor.numericSort ();

                };

                 

                app.preferences.rulerUnits = originalRulerUnits;

                 

                log.write("Highest Y Vals"+ "\n" + yAnchor[0] + "\n" + yAnchor[1] + "\n" + yAnchor[2] + "\n" + yAnchor[3] + "\n" + yAnchor[4] + "\n")

                 

                };

                • 5. Re: Selection from subpath
                  c.pfaffenbichler Level 9

                  If I understand you correctly you want to sort an Array of Arrays consisting of two Numbers by either the first or second of those Numbers?

                  That would be possible, please try this code (it currently sorts according to the second numbers, to sort for the first numbers change »theIndex« to 0).

                   

                  // sort array of arrays by indexed item of those arrays;
                  // use it at your own risk;
                  var theArray = [[9,2], [6,0], [7,4], [8,1], [5,3]];
                  var theIndex = 1;
                  theArray.sort(sortArrayByIndexedItem);
                  alert (theArray.join("\n"));
                  ////// sort a double array, thanks to sam, http://www.rhinocerus.net/forum/lang-javascript/ //////
                  function sortArrayByIndexedItem(a,b) {
                  //var theIndex = 1;
                  if (a[theIndex]<b[theIndex]) return -1;
                  if (a[theIndex]>b[theIndex]) return 1;
                  return 0;
                  };
                  
                  • 6. Re: Selection from subpath
                    Steven Mathisen Level 1

                    It's what I'm going for but definitely isn't working. Since I'm fetching path points to populate the array via for loop, it is only moving the last value of the array, not accessing the whole array.

                     

                    EDIT:  Looking at this again, I think it will work just not for my current array structure. I'll rewrite my arrays and try it again.

                    • 7. Re: Selection from subpath
                      c.pfaffenbichler Level 9

                      I don’t understand what you want.

                      Please provide an example – like a path’s points in an array and what you really want to get out of that; and possibly a jpg with the source path and the result path for easy visualisation.

                      • 8. Re: Selection from subpath
                        Steven Mathisen Level 1

                        Ok, my confusion about how to use the array of array structure seems to be confusing you.

                         

                        I am trying to build an array structure that stores four values in relation to one another for further operations. The operations I can test and work out, my issue is in the array structure. I need: subPath, pathPoint, x-coordinate, y-coordinate stored in a way that I can sort descending on y-coordinate, while having the other values maintained in relation to their y-coordinate. I need a multidimensional array it seems, but am failing at building one within the code structure you helped me with below. The most essential bit, is to be able to perform sort functions on the array values while maintaining the relation between a point's x and y.

                         

                        function collectPathPoints (myDocument, thePath) {

                         

                        var originalRulerUnits = app.preferences.rulerUnits;

                         

                        app.preferences.rulerUnits = Units.POINTS;

                         

                        var theArray = [];

                         

                        for (var b = 0; b < thePath.subPathItems.length; b++) {

                         

                             theArray[b] = [];

                         

                             for (var c = 0; c < thePath.subPathItems[b].pathPoints.length; c++) {

                         

                                  var pointsNumber = thePath.subPathItems[b].pathPoints.length;

                         

                                  var xAnchor  = Math.round(thePath.subPathItems[b].pathPoints[c].anchor[0]*100)/100;

                         

                                  var yAnchor  = Math.round(thePath.subPathItems[b].pathPoints[c].anchor[1] *100)/100;

                         

                        //         var theAnchor = [xAnchor, yAnchor]

                                 theArray[b][c] = theAnchor;

                                  };

                             };

                         

                        app.preferences.rulerUnits = originalRulerUnits;

                         

                        return theArray

                         

                        };

                         

                        theArray.sort(function (a, b) {

                            if (a.yAnchor > b.yAnchor) {

                               return 1;       

                            } else if (a.yAnchor < b.yAnchor) {

                               return -1;

                            } else {

                               return 0;

                            }

                        });

                        • 9. Re: Selection from subpath
                          c.pfaffenbichler Level 9

                          I think if you were to provide an example it might be easier to understand.

                          Still, maybe this helps; the array that is created of the selected path does not have one array per subPathItem but instead is an array of one array per point that goes like

                          [x-coordinate, y-coordinate, subPathItem-number, pathPoint-number]

                          sortPoints.jpg

                           

                          // sort pathpoints by y-coordinate;
                          // 2012, use it at your own risk;
                          #target photoshop
                          if (app.documents.length > 0) {
                          var originalRulerUnits = app.preferences.rulerUnits;
                          app.preferences.rulerUnits = Units.POINTS;
                          var myDocument = app.activeDocument;
                          var thePath = selectedPath ();
                          if (thePath != undefined) {
                          var thePoints = collectPathPointsInOne (myDocument, thePath);
                          var theIndex = 1;
                          thePoints.sort(sortArrayByIndexedItem);
                          alert (thePoints.join("\n"));
                          };
                          // reset;
                          app.preferences.rulerUnits = originalRulerUnits;
                          };
                          ////// collect path points //////
                          function collectPathPointsInOne (myDocument, thePath) {
                          var originalRulerUnits = app.preferences.rulerUnits;
                          app.preferences.rulerUnits = Units.POINTS;
                          var theArray = [];
                          for (var b = 0; b < thePath.subPathItems.length; b++) {
                          //theArray[b] = [];
                          for (var c = 0; c < thePath.subPathItems[b].pathPoints.length; c++) {
                          var pointsNumber = thePath.subPathItems[b].pathPoints.length;
                          var xAnchor  = Math.round(thePath.subPathItems[b].pathPoints[c].anchor[0]*100)/100;
                          var yAnchor  = Math.round(thePath.subPathItems[b].pathPoints[c].anchor[1] *100)/100;
                          // x.coordinat, y-coordinate, subpathitem-index, pathpoint-index;
                          var theAnchor = [xAnchor, yAnchor, b, c];
                          theArray.push(theAnchor);
                          };
                          };
                          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 = 1;
                          if (a[theIndex]<b[theIndex]) return -1;
                          if (a[theIndex]>b[theIndex]) return 1;
                          return 0;
                          };
                          ////// determine selected path, //////
                          function selectedPath () {
                          try {
                          var ref = new ActionReference(); 
                          ref.putEnumerated( charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
                          var desc = executeActionGet(ref);
                          var theName = desc.getString(charIDToTypeID("PthN"));
                          return app.activeDocument.pathItems.getByName(theName)
                          }
                          catch (e) {return undefined}
                          };