• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

create script to move on the Y axis using artborad as a reference

New Here ,
Jan 15, 2025 Jan 15, 2025

Copy link to clipboard

Copied

 

I can't create a java script to align all objects to the base of the artboard because it always gives an error on the Y axis. I've tried several approaches but so far nothing
Does anyone have an example script how to align multiple objects to the base?
on the x axis it works perfectly to center without losing the relative distance between the objects, however I have not had success in relation to the Y axis, either to measure the size of the grouping, to center or to move


 

 

TOPICS
Scripting

Views

51

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Community Expert ,
Jan 15, 2025 Jan 15, 2025

Copy link to clipboard

Copied

Can you share your script that works perfectly with the x axis?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jan 15, 2025 Jan 15, 2025

Copy link to clipboard

Copied

LATEST

Hi @Luiz Vigraf there are some complexities to this issue, for example: getting the actual bounds of an item is difficult in many cases, such as text or clipping masked items. I have written a function to help with this (see in script below), which makes things much easier, and made writing this script today super quick. I also wrote a function to get the most likely artboard for a given page item, which will be handy, too. I included an option for left justifying the items (you might not need this, but I did it for fun—turn it off by editing `settings.justifyLeft` to false in the script.

 

Let me know if it's useful.

- Mark

 

demo.gif

 

 

/**
 * @file Move Selected Items To Bottom Of Artboard.js
 *
 * Moves the selected items to the bottom of its artboard,
 * with the option to left justify with space between.
 *
 * @author m1b
 * @version 2025-01-16
 * @discussion https://community.adobe.com/t5/illustrator-discussions/create-script-to-move-on-the-y-axis-using-artborad-as-a-reference/m-p/15091849
 */
(function () {

    var settings = {
        justifyLeft: true,
        spaceBetween: 0,
    };

    var doc = app.activeDocument,
        items = doc.selection,
        xPositions = {};

    if (settings.justifyLeft)
        items.sort(sortByLeft);

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

        var item = items[i],
            bounds = getItemBoundsIllustrator(item, false),
            artboard = getArtboardWithMostOverlap(doc.artboards, bounds);

        if (!artboard)
            // item not on any artboard
            continue;

        var artboardBounds = artboard.artboardRect;

        // keep track of x position per artboard
        xPositions[artboard.name] = undefined == xPositions[artboard.name] ? artboardBounds[0] : xPositions[artboard.name];

        var dx = settings.justifyLeft ? xPositions[artboard.name] - bounds[0] : 0,
            dy = artboardBounds[3] - bounds[3];

        // advance x position for this artboard
        xPositions[artboard.name] += bounds[2] - bounds[0] + settings.spaceBetween;

        // move the item into position
        item.translate(dx, dy, true, true, true, true);

    }

})();

/**
 * Returns the artboard that most overlaps the given bounds.
 * Will return nothing if no overlap at all.
 * @author m1b
 * @version 2025-01-16
 * @param {Array<Artboard>|Artboards} artboards - the artboards to search.
 * @param {Array<Number>} bounds - the bounds to check against [L, T, R, B].
 * @returns {Artboard?} - the artboard with the most overlap.
 */
function getArtboardWithMostOverlap(artboards, bounds) {

    if (
        undefined == artboards
        || 0 === artboards.length
    )
        return;

    var maxOverlap = 0,
        bestArtboard = undefined;

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

        var artboard = artboards[i];
        var artboardBounds = artboard.artboardRect; // [L, T, R, B]

        // calculate the overlap
        var overlapL = Math.max(bounds[0], artboardBounds[0]),
            overlapT = Math.min(bounds[1], artboardBounds[1]),
            overlapR = Math.min(bounds[2], artboardBounds[2]),
            overlapB = Math.max(bounds[3], artboardBounds[3]);

        // overlap area
        var overlapWidth = Math.max(0, overlapR - overlapL),
            overlapHeight = Math.max(0, overlapT - overlapB),
            overlapArea = overlapWidth * overlapHeight;

        if (overlapArea > maxOverlap) {
            // the largest overlapping artboard so far
            maxOverlap = overlapArea;
            bestArtboard = artboard;
        }

    }

    return bestArtboard;

};

/**
 * Sort by left bounds.
 * @author m1b
 * @version 2025-01-16
 * @param {PageItem} a - item to sort.
 * @param {PageItem} b - item to sort.
 * @returns {Number} - the sort code.
 */
function sortByLeft(a, b) {

    return (

        // left bounds value rounded to 3 decimal places
        Math.round((a.visibleBounds[0] - b.visibleBounds[0]) * 1000) / 1000

        // same left, so we'll sort by top bounds value
        || a.visibleBounds[1] - b.visibleBounds[1]

    );

};

// from here down is just code to get a page item's bounds

/**
 * Returns bounds of item(s).
 * @author m1b
 * @version 2024-03-10
 * @param {PageItem|Array<PageItem>} item - an Illustrator PageItem or array of PageItems.
 * @param {Boolean} [geometric] - if false, returns visible bounds.
 * @param {Array} [bounds] - private parameter, used when recursing.
 * @returns {Array} - the calculated bounds.
 */
function getItemBoundsIllustrator(item, geometric, bounds) {

    var newBounds = [],
        boundsKey = geometric ? 'geometricBounds' : 'visibleBounds';

    if (undefined == item)
        return;

    if (
        item.typename == 'GroupItem'
        || item.constructor.name == 'Array'
    ) {

        var children = item.typename == 'GroupItem' ? item.pageItems : item,
            contentBounds = [],
            isClippingGroup = (item.hasOwnProperty('clipped') && item.clipped == true),
            clipBounds;

        for (var i = 0, child; i < children.length; i++) {

            child = children[i];

            if (
                child.hasOwnProperty('clipping')
                && true === child.clipping
            )
                // the clipping item
                clipBounds = child.geometricBounds;

            else
                contentBounds.push(getItemBoundsIllustrator(child, geometric, bounds));

        }

        newBounds = combineBounds(contentBounds);

        if (isClippingGroup)
            newBounds = intersectionOfBounds([clipBounds, newBounds]);

    }

    else if (
        'TextFrame' === item.constructor.name
        && TextType.AREATEXT !== item.kind
    ) {

        // get bounds of outlined text
        var dup = item.duplicate().createOutline();
        newBounds = dup[boundsKey];
        dup.remove();

    }

    else if (item.hasOwnProperty(boundsKey)) {

        newBounds = item[boundsKey];

    }

    // `bounds` will exist if this is a recursive execution
    bounds = (undefined == bounds)
        ? bounds = newBounds
        : bounds = combineBounds([newBounds, bounds]);

    return bounds;

};

/**
 * Returns the combined bounds of all bounds supplied.
 * Works with Illustrator or Indesign bounds.
 * @author m1b
 * @version 2024-03-09
 * @param {Array<bounds>} boundsArray - an array of bounds [L, T, R, B] or [T, L , B, R].
 * @returns {bounds?} - the combined bounds.
 */
function combineBounds(boundsArray) {

    var combinedBounds = boundsArray[0],
        comparator;

    if (/indesign/i.test(app.name))
        comparator = [Math.min, Math.min, Math.max, Math.max];

    else if (APP_IS_ILLUSTRATOR)
        comparator = [Math.min, Math.max, Math.max, Math.min];

    // iterate through the rest of the bounds
    for (var i = 1; i < boundsArray.length; i++) {

        var bounds = boundsArray[i];

        combinedBounds = [
            comparator[0](combinedBounds[0], bounds[0]),
            comparator[1](combinedBounds[1], bounds[1]),
            comparator[2](combinedBounds[2], bounds[2]),
            comparator[3](combinedBounds[3], bounds[3]),
        ];

    }

    return combinedBounds;

};

/**
 * Returns the overlapping rectangle
 * of two or more rectangles.
 * NOTE: Returns undefined if ANY
 * rectangles do not intersect.
 * @author m1b
 * @version 2024-09-05
 * @param {Array<bounds>} arrayOfBounds - an array of bounds [L, T, R, B] or [T, L , B, R].
 * @returns {bounds?} - intersecting bounds.
 */
function intersectionOfBounds(arrayOfBounds) {

    var comparator;

    if (/indesign/i.test(app.name))
        comparator = [Math.max, Math.max, Math.min, Math.min];

    else if (APP_IS_ILLUSTRATOR)
        comparator = [Math.max, Math.min, Math.min, Math.max];

    // sort a copy of array
    var bounds = arrayOfBounds
        .slice(0)
        .sort(function (a, b) { return b[0] - a[0] || a[1] - b[1] });

    // start with first bounds
    var intersection = bounds.shift(),
        b;

    // compare each bounds, getting smaller
    while (b = bounds.shift()) {

        // if doesn't intersect, bail out
        if (!boundsDoIntersect(intersection, b))
            return;

        intersection = [
            comparator[0](intersection[0], b[0]),
            comparator[1](intersection[1], b[1]),
            comparator[2](intersection[2], b[2]),
            comparator[3](intersection[3], b[3]),
        ];

    }

    return intersection;

};

/**
 * Returns true if the two bounds intersect.
 * @author m1b
 * @version 2024-03-10
 * @param {Array} bounds1 - bounds array.
 * @param {Array} bounds2 - bounds array.
 * @param {Boolean} [TLBR] - whether bounds arrays are interpreted as [t, l, b, r] or [l, t, r, b] (default: based on app).
 * @returns {Boolean}
 */
function boundsDoIntersect(bounds1, bounds2, TLBR) {

    if (undefined == TLBR)
        TLBR = (/indesign/i.test(app.name));

    return !(

        TLBR

            // TLBR
            ? (
                bounds2[0] > bounds1[2]
                || bounds2[1] > bounds1[3]
                || bounds2[2] < bounds1[0]
                || bounds2[3] < bounds1[1]
            )

            // LTRB
            : (
                bounds2[0] > bounds1[2]
                || bounds2[1] < bounds1[3]
                || bounds2[2] < bounds1[0]
                || bounds2[3] > bounds1[1]
            )
    );

};

Edit 2025-01-16: improved variable name.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines