4

I'm not that familiar with AI scripting, but I think what I'm looking for is simple. I need a script that can examine a path and find the two most distant anchor points. Then create an axis between those points (interior or exterior, doesn't matter) and then orient the entire path based on that line.

1 1 2 1
3 1 4 1

So what we would end up with is a rotated path with its height representing the maximum caliper diameter (or Feret diameter). In other words, the new bounding box for this path would have height dimension equal to the longest line between points.

The problem is that I have a lot of these objects and I would like to automate to save time.

If anyone can point me in the right direction.

4
  • 1
    What do you want to happen if there is more than one maximum caliber diameter?
    – Geoff Ball
    Commented Jan 14, 2017 at 21:19
  • It should just pick one, randomly or whatever is first. I'm not too concerned about that happening. The paths are defined in an organic way so it's like 1:10000 that it will be an issue. Commented Jan 14, 2017 at 22:35
  • 1
    This should be possible. Pick a point, measure distance to all points, move to next point, do the same, store all, find the largest. Having found the largest distance, pick both points, determine the most southerly (lowest on screen) and set it as anchor. Rotate until other point is straight up, checking against the y position of the southern point... bit messy. But should be possible. I don't know enough about Illustrator's scripting to do it. Nor would I want to. Illustrator is a fiddly mess of oldness, legacy and outright archaic operations and procedures.
    – Confused
    Commented Jan 15, 2017 at 6:19
  • @BenHoagland if you hit up the Adobe Illustrator forums, they might have someone that knows not only enough of how to do this, but can partially write some of this for you. Some of the older guys that have grown up with Illustrator and After Effects have astonishing familiarity with the quirks and foibles of them, and their scripting.
    – Confused
    Commented Jan 15, 2017 at 6:21

1 Answer 1

8

This took about an hour to write, one step at a time:

  1. Handle either one single item, or a selection of items.
  2. Loop through the single path (for a simple object) or through all component paths (for a compound object) and gather all anchor points into a single array.
  3. Test every point against every other. I've thought about it and I don't think there is clever solution that may avoid this.
  4. Calculate the angle between the 2 most distant points.
  5. If the angle exceeds ±180°, clip it to avoid rotating too much.
  6. Rotate the selected object(s).

Result: above the dotted line the original, below after running the script. The leftmost object is your example. The vertical cyan lines are added manually to indicate the longest vertical distance points.

before and after running the script

Some very complicated objects defy simple path exploration – and I have no idea why. See the fish bones for an example; it did not move, because the path finding functions returned nothing at all. The underlying problem could be that it originated as a Symbol, rather than drawn, but the reason eludes me.

//DESCRIPTION:Align object(s) on its longest axis

if (app.documents.length == 0 || app.selection.length < 1)
    alert ("Please make sure to have something useful selected");
else
{
    for (i=0; i<app.selection.length; i++)
    {
        if (app.selection[i].constructor.name=="PathItem" || app.selection[i].constructor.name=="CompoundPathItem")
            realign (app.selection[i]);
    }
}

function realign (obj)
{
    var result, distx,disty,angle;

    result = furthestSet (obj);
    if (result.length == 3)
    {
        /* result[1] is point #1, result[2] = pt #2 */
        /* now calculate angle and rotate */

        disty = result[1][0] - result[2][0];
        distx = result[1][1] - result[2][1];

        angle = -Math.atan2 (distx, disty) - Math.PI/2;
        angle = angle*180.0/Math.PI;
        if (angle <= -180) angle += 180;
        if (angle >=  180) angle -= 180;

        obj.rotate (angle,true,true,true,false, Transformation.CENTER);
    }
}

function distanceFromPointToPoint (A, B)
{
/*  since we only need to know what point is furthest, the squared result is okay as well */

/*  return Math.sqrt ( ((A[0]-B[0]) * (A[0]-B[0])) + ((A[1]-B[1]) * (A[1]-B[1])) ); */

    return ((A[0]-B[0]) * (A[0]-B[0])) + ((A[1]-B[1]) * (A[1]-B[1]));
}

function pathToArray (obj)
{
    var pt;
    var flatpath = [];

    if (!obj.hasOwnProperty ("pathPoints"))
        return null;

    for (pt=0; pt<obj.pathPoints.length; pt++)
    {
        flatpath.push (obj.pathPoints[pt].anchor);
    }
    /* once more for good luck */
    flatpath.push (obj.pathPoints[0].anchor);
    return flatpath;
}

function furthestSet (obj)
{
    var flatpath = [], i,j, d, distance = -1, result = [];

    if (obj.constructor.name == "CompoundPathItem")
    {
        for (p=0; p<obj.pathItems.length; p++)
        {
            flatpath = flatpath.concat(pathToArray (obj.pathItems[p]));
        }
    } else
    {
        flatpath = pathToArray (obj);
    }
    if (flatpath == [])
        return [0, [0,0], [0,0]];

    for (i=0; i < flatpath.length-1; i++)
    {
        for (j=i+1; j < flatpath.length; j++)
        {
            d = distanceFromPointToPoint (flatpath[i], flatpath[j]);
            if (d > distance)
            {
                distance = d;
                result = [d, flatpath[i], flatpath[j]];
            }
        }
    }
    return result;
}
3
  • Instead of atan2 you could have used dot product. Or just dropped the vectors to a matrix. But yes this is pretty simple, computer graphics programming one on one in fact. Only finding furthest point in a nonpolygonal object is actually a bit complicated.
    – joojaa
    Commented Jan 16, 2017 at 6:46
  • The fish may fail because its possible that you have compound paths in compound paths and groups etc. try ungrouping then releasong compound paths and rebuild it.
    – joojaa
    Commented Jan 16, 2017 at 7:20
  • 1
    @Rad Lexus Sorry, I didn't mean to offend by saying the code was simple. I meant to say that the desired result was simple enough for me to explain. Thank you so much! It works perfectly on 500+ objects so far :) Commented Jan 16, 2017 at 14:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.