Illustrator: I need a script that manages paths based on maximum caliper diameter

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.

Answer

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

Attribution
Source : Link , Question Author : Ben Hoagland , Answer Author : Jongware

Leave a Comment