9 Replies Latest reply on May 3, 2013 2:56 PM by CarlosCanto

# Sorting the order of points in a pathPoints array

Okay, why would I want to do this? Well, I've got areaText rectangles that were created by various means, and the 0 index is sometimes the top left, other times the bottom right. I need to do a lot of resizing/aligning/spacing/sizing. To script the movement of the top left point of each (or any point for that matter), I'm trying to clean up the sequence of points in each objects array of pathPoints so I can move pathPoint[0].anchor for each rectangle and be sure that the top left is moving, not some other random corner.

So, testing in script alone, and I came up with the following (don't laugh at how I called out the array), which works great in extendScript:

var testArray = new Array;

testArray.push({xVal: 5, yVal:0});

testArray.push({xVal: 0, yVal:-2});

testArray.push({xVal: 5, yVal:-2});

testArray.push({xVal: 0, yVal:0});

function primarySort(){  testArray.sort(function (a,b){return b.yVal-a.yVal});   }

function secondarySort(){

//reorder the first pair of points  based on xVal

if (Number(testArray[0].xVal) > Number(testArray[1].xVal)){

moveMe = testArray[1];

testArray.splice(1,1);

testArray.unshift(moveMe);}

// reorder second pair of points  based on xVal

if (Number(testArray[2].xVal) < Number(testArray[3].xVal)){

moveMe = testArray[2];

testArray.splice(2,1);

testArray.push(moveMe);}    }

function printArray(){

for (i =0; i<testArray.length; i++){

var writeMe = testArray[i].xVal.toString()+ "," + testArray[i].yVal.toString() ;

\$.writeln (writeMe);  }}

primarySort();

printArray();

\$.writeln (".........");

secondarySort();

printArray();

So, then I tried to do the same for Illustrator:

var objArray = app.activeDocument.textFrames;

var objPoints = new Array;

for (i=0; i<objArray.length ; i++){

var pathObj = objArray[i].textPath;

objPoints = pathObj.pathPoints;

XYpairSort();}

function XYpairSort(){

//writes the current sequence of pathPoint anchor x y coordinates to the console

for (i =0; i<objPoints.length; i++){

var writeMe = objPoints[i].anchor[0].toString()+ "," + objPoints[i].anchor[1].toString() + "," + i ;

\$.writeln (writeMe);}

//sort four points into top and bottom pairs

objPoints.sort(function (a,b){return b.anchor[1]-a.anchor[1]});

//reorder the first pair of points  based on xVal

if (Number(objPoints[0].anchor[0]) > Number(objPoints[1].anchor[0])){

moveMe = objPoints[1];

objPoints.splice(1,1);

objPoints.unshift(moveMe);}

// reorder second pair of points  based on xVal

if (Number(objPoints[2].anchor[0]) < Number(objPoints[3].anchor[0])){

moveMe = objPoints[2];

objPoints.splice(2,1);

objPoints.push(moveMe);}

//writes the new sequence of pathPoint anchor x y coordinates to the console

for (i =0; i<objPoints.length; i++){

var writeMe = objPoints[i].anchor[0].toString()+ "," + objPoints[i].anchor[1].toString() + "," + i ;

\$.writeln (writeMe);}

}

Which throws an error "objPoints.sort is not a function"

Unlike the JS only version, my AI script is trying to sort all of the points of all of of the textAreas, which I'll fix shortly so it sorts the 4 points of each object. The problem is that the script isn't sorting the points, but providing a helpful error message instead.

Any ideas? Internet searches for this error don't explain why it happens, just other ways of doing sorts.

• ###### 1. Re: Sorting the order of points in a pathPoints array

Where is your function objPoints.sort() ?

• ###### 2. Re: Sorting the order of points in a pathPoints array

Slightly revised code, closer to being the same as the working code. The sort code is inside the function primarySort, the first function. This is the same structure as the working code in the original post, yet yeilds the error "objPoints.sort is not a function."

var objArray = app.activeDocument.textFrames;

var pathObj = objArray[0].textPath;

var objPoints = pathObj.pathPoints;

primarySort();

printArray();

\$.writeln (".........");

secondarySort();

printArray();

//sort four points into top and bottom pairs

function primarySort(){

objPoints.sort(function (a,b){return b.anchor[1]-a.anchor[1]});}

function secondarySort(){

//reorder the first pair of points  based on xVal

if (Number(objPoints[0].anchor[0]) > Number(objPoints[1].anchor[0])){

moveMe = objPoints[1];

objPoints.splice(1,1);

objPoints.unshift(moveMe);}

// reorder second pair of points  based on xVal

if (Number(objPoints[2].anchor[0]) < Number(objPoints[3].anchor[0])){

moveMe = objPoints[2];

objPoints.splice(2,1);

objPoints.push(moveMe);}

}

function printArray(){

for (i =0; i<objPoints.length; i++){

var writeMe = objPoints[i].anchor[0].toString()+ "," + objPoints[i].anchor[1].toString() ;

\$.writeln (writeMe);

}

}

• ###### 3. Re: Sorting the order of points in a pathPoints array

Two things:

1) It appears that if I make a new array with copies of the x and y data for each anchor, not the anchor[0] and anchor[1] objects, then I can sort that data instead of using the original objects in the sort. I can then write the new positions using the copy of the data. Problem solved. Looks like sort just won't work on objects, but will on values and strings.

2) Does illustrator always evaluate the top left corner of any rectangle be at index[0] for the pathPoints? I may have been making a bad assumption that is taking me down an unneeded path.

Thanks,
Alex

• ###### 4. Re: Sorting the order of points in a pathPoints array

On 1) That's what I would guess. These arrays are not "copied", they point in a very real sense to the actual objects. So even a simple sorting subroutine to "swap" two objects is, in fact, a complicated procedure.

I was actually going to suggest working on a copy of the values :-)

On 2) If you only need the top left anchor point for sorting, and you want to be sure you got the correct point, you might want to inspect each and every point per path. Store the first anchor point's x and y as [x,y] in your new array; then loop over all other points, updating this if you find a lower value.

I think you are after the lowest value for both x and y here (an easy test). Comparing both x and y per point may give you troubles if you consider, for example, a 45 degree rotated square, None of its reported points is "the" top left anchor, but a combination of its lowest x and its lowest y are.

(Disregarding the Cartesian conundrum since Illy went all Mathematically Correct and inverted its y-axis. Adjust signs when applicable for your version.)

• ###### 5. Re: Sorting the order of points in a pathPoints array

Thanks Jongware, that's more or less the approach I'm taking to describe predictably the corners of the rectangle. What I'm working on is aligning, spacing, and sizing a set of rectangular orthogonal text areas, so I'm not concerned with irregular shapes.

In the first script I posted, I take an array of 4 X/Y pairs (substituted in the now working script with copies of the textArea.pathPoints XY coordinate of the anchor) and use the function primarySort to move the pairs with the two highest Y values (the top points) to indices 0 and 1 and the two lowest Y values (bottom points) to indices 2 and 3. The function secondarySort moves the top point of with the lowest X value to index 0 and moves the bottom point with the lowest X value to index 4.

Combined, this gives a set of coordinates for a rectangle, counting through the index from top left corner clockwise. My goal was to to have all of my textArea rectangles follow this predictable index structure regardless of how they were constructed originally.

• ###### 6. Re: Sorting the order of points in a pathPoints array

inanealex2010 wrote:

… Combined, this gives a set of coordinates for a rectangle, counting through the index from top left corner clockwise. My goal was to to have all of my textArea rectangles follow this predictable index structure regardless of how they were constructed originally.

If you only need the cordinates, why you don't tried a way like this:

#target Illustrator
// find coordinates of textframes
var Sel = app.selection.length;
var mmFactor = 0.3527778;
for (i = 0; i < Sel; i++) {
if (app.selection[i].typename == "TextFrame") {
var xL = app.selection[i].position[0];
var yT = app.selection[i].position[1];
var xR = app.selection[i].position[0] + app.selection[i].width;
var yB = app.selection[i].position[1] - app.selection[i].height;
alert("xLeft von Objekt " + i + " = " + xL*mmFactor + " mm","Links");
alert("yTop von Objekt " + i + "  = " + yT*mmFactor  + " mm","Oben");
alert("xRight von Objekt " + i + "  = " + xR*mmFactor  + " mm","Rechts");
alert("yBottom von Objekt " + i + "  = " + yB*mmFactor  + " mm","Unten");
}
}

xL/yT is the pair of the left-top corner.

• ###### 7. Re: Sorting the order of points in a pathPoints array

I'm not trying to get the values of the coordinates at all.

Because I'm going to be moving and resizing the text frames at the same time by writing a new XY coordinate for the pathPoints, what I need is to make sure the index of the coordinates for each text area follow the same pattern, thus all of the sorting I'm doing. We've got scripts that create textAreas where the the index counts clockwise from the bottom right. A textArea drawn with the text tool counts clockwise from the top left. If I assigned new values for pathPoint[1].anchor[0] and pathPoint[1].anchor[1] for textAreas constructed differently I'd get unpredictable results. Might move the top right corner for some, bottom left for others... as I found out the hard way.

• ###### 8. Re: Sorting the order of points in a pathPoints array

Okay, if you made it this far in the discussion, the approach I've been taking is completely unnecessary.

You see, I knew about the .top, .left, .width and .height properties of rectangles, and that if you changed the width of a textFrame, it would scale the text within it:

var obj = app.activeDocument.textFrames[0] // assume it's narrower than 2 inches

obj.width = 144; //scales the width of the block and its contents too.

So, to avoid the scaling problem I had to make sure the textPaths of what I had selected had the same index order so I could grab each point individually and always grab the same point for each textFrame. I was changing the height and width of each textFrame by assigning new XY coordinates to each corner of the path. Great to know how to do this, but not needed, because the .top, .left, .width and .height attributes are available to the textPath object.

What I didn't know (and do now), is that you can scale the path without scaling the content:

var obj = app.activeDocument.textFrames[0] // assume it's narrower than 2 inches

var objPath =obj.textPath;

objPath.width = 144;  //doesn't scale the text inside the frame.

• ###### 9. Re: Sorting the order of points in a pathPoints array

ah, that's what you wanted!