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

Help creating script from pseudo-code?

Engaged ,
Sep 04, 2024 Sep 04, 2024

Copy link to clipboard

Copied

The basic idea that I want to try is this:
Given a series of elements, I want to create a vertically striped mask that will cover all of the elements with some overlap on either side.

Each of the vertical stripes will be placed "C" pixels apart from each other horizontally  where C=number of images. The vertical stripes will cover all of the images.

How I do it now, is I create a compound path of vertical 1px wide stripes separated by "C" pixels for the entire width (plus 2xC  for overlap). I then place a copy of the compound path aligned with the first image (offset horizontally by "-C" pixels) and select both the compound path and the layer to create a clipping mask.

I repeat this for each shape, but offset the compound path by 1 px horizontally each times (for a total of "C" clipping masks.

 

I have tried to duplicate this in the code below but I'm not that great at JS so I used the Jquery library (which I am also not that great at). I fgure there are easier ways to accomplish this, so please let me know. I am famous for making things much harder than they need to be. 

 

Thanks for taking the time to consider this. 

 

//PLEASE REMEMBER THIS IS PSUEDO CODE USING JQUERY-ishness
//There may be much better ways to do with the Illustrator API.
//assumes a series of shapes of class shape
/*  Overview: Given a series of shapes, find the min and max x and y coordinates to determine the sie of the mask (subtract the count of shapes from min and and to max for overlap of mask).
Draw a series of rectangles 1px wide every (shapecount) pixels for the entire width.

Combine those into a compound shape (no idea how)
For each shape, clone the mask compound shape and place it above the shape. Select the shape and the compound shape and make a clipping mask.
DONE!*/

//getDims -- simple util for getting the x,y,w,h for all shapes combined
	function getDims(lst) {
		let xmin = 0,
			ymin = 0,
			xmax = 0,
			ymax = 0;
		lst.each(function () {
			let elem = this;
			let dims = elem.getBoundingClientRect();
			xmin = Math.floor(Math.min(xmin, dims.left));
			ymin = Math.floor(Math.min(ymin, dims.top));
			xmax = Math.ciel(Math.max(dims.right, xmax));
			ymax = Math.ciel(Math.max(dims.bottom, ymax));
		});
		return { x: xmin, y: ymin, w: xmax - xmin, h: ymax - ymin };
	}
	let shapesList = $(".shape");
	let c = shapesList.length;
	let dims = getDims(shapesList);
	let w = dims.width + 2 * c;
	let h = dims.height + 2 * c;
//simple function for building the mask svg element to cover the width/height with vertical stripes separated by the number of pixels=count of images
	function buildMask() {
		let maskSVG =
			"<svg height='" +
			h +
			"' width='" +
			w +
			"' viewBox='0 0 " +
			w +
			" " +
			h +
			"'> ";
//interior function to repeat the lenticule
		function drawLent(x) {
			maskSVG +=
				"<rect x='" + x + "' width='1' height='" + h + "' fill='#000000'/>";
		}
		let x = 0;
		let lc = Math.floor(w / c);

		for (let i = 0; i < lc; i++) {
			x += c * i;
			drawLent(x);
		}
		maskSVG += "</svg>";
		let mask = $("<div></div>");
		mask.innerHTML(maskSVG);
		return mask;
	}
	let mask = buildMask();
	shapesList.each(function (i) {
		let shape = $(this);
		let shapeMask = mask.clone();
		shapeMask.offset({ left: i + (dims.x - c), top: dims.y });
		let z = shape.css("z-index");
		z -= 1;
		shapeMask.css("z-index", z);
		//do something magical to define a mask using shapeMask and shape
//probably something like shape.applyMask(shapeMask);
	});
});

 

 

TOPICS
How-to , Scripting

Views

259

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

correct answers 1 Correct answer

Community Expert , Sep 05, 2024 Sep 05, 2024

Hi @Jason Burnett, I made this script, that *maybe* does something like what you are asking. Basically clips each selected page item with a compound path made of vertical bars spaced out according to (a) settings.barWidth, and (b) the number of page items selected. Select a few items and try it.

- Mark

With 3 Items selected.With 3 Items selected.With 6 Items selected. And changing the barWidth to 0.25With 6 Items selected. And changing the barWidth to 0.25

 

/**
 * @file Add Clipping Bars.js
 *
 * Usage:
 *   0. Make adjustments to the `settings` object below, if necessar
...

Votes

Translate

Translate
Adobe
Community Expert ,
Sep 04, 2024 Sep 04, 2024

Copy link to clipboard

Copied

Hi @Jason Burnett, can you show us a visual of the expected result? Or even better, a sample file we can look at?

- Mark

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
Contributor ,
Sep 04, 2024 Sep 04, 2024

Copy link to clipboard

Copied

I do not understand your question entirely. Can you sketch it. 
so far I understood, the solution will be found in the use of compound paths and masks. 

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 ,
Sep 05, 2024 Sep 05, 2024

Copy link to clipboard

Copied

Hi @Jason Burnett, I made this script, that *maybe* does something like what you are asking. Basically clips each selected page item with a compound path made of vertical bars spaced out according to (a) settings.barWidth, and (b) the number of page items selected. Select a few items and try it.

- Mark

With 3 Items selected.With 3 Items selected.With 6 Items selected. And changing the barWidth to 0.25With 6 Items selected. And changing the barWidth to 0.25

 

/**
 * @file Add Clipping Bars.js
 *
 * Usage:
 *   0. Make adjustments to the `settings` object below, if necessary.
 *   1. Select multiple page items in Illustrator.
 *   2. Run script.
 *
 * @author m1b
 * @version 2024-09-05
 * @discussion https://community.adobe.com/t5/illustrator-discussions/help-creating-script-from-pseudo-code/m-p/14842810
 */
(function () {

    var settings = {

        // only set this if you want to force the number of slices
        sliceCount: undefined,

        // the width of each bar, in points
        barWidth: 1,

        // if you want to choose the bar width each time
        showUI: false,

    };

    if (
        0 === app.documents.length
        || 0 === app.activeDocument.selection.length
    )
        return alert('Please select some items and try again.');

    if (settings.showUI) {

        var barWidth = Number(prompt('Width of each bar:', settings.barWidth || 1));

        if (isNaN(barWidth))
            return;
        else
            settings.barWidth = barWidth;

    }

    var doc = app.activeDocument,
        items = doc.selection;

    settings.sliceCount = Math.min(settings.sliceCount || Infinity, items.length);

    // calculate the outer bounds
    var everyBound = [];
    for (var i = settings.sliceCount - 1; i >= 0; i--)
        everyBound.push(getItemBounds(items[i]));

    var outerBounds = combineBounds(everyBound),
        step = settings.sliceCount * settings.barWidth;

    // add margins
    outerBounds[0] -= step;
    outerBounds[1] += step;
    outerBounds[2] += step;
    outerBounds[3] -= step;

    // add the clipping bars
    for (var i = settings.sliceCount - 1; i >= 0; i--)
        addClippingBars(items[i], outerBounds, i, settings.sliceCount, settings.barWidth);



})();

/**
 * Adds vertical bars compound path to clip `item`.
 * @author m1b
 * @version 2024-09-05
 * @param {PageItem} item - an Illustrator PageItem.
 * @param {bounds} bounds - the outer bounds [l,t,r,b].
 * @param {Number} index - the slice index.
 * @param {Number} sliceCount - the number of slices.
 * @param {Number} barWidth - the width of each bar.
 */
function addClippingBars(item, bounds, index, sliceCount, barWidth) {

    var x = 0,
        step = sliceCount * barWidth;

    var container = item.parent,
        bars = container.compoundPathItems.add(),
        fullLength = Math.ceil((bounds[2] - bounds[0]) / step) * step;

    while (x <= fullLength) {
        drawBar(bars, bounds, x + (index*barWidth), barWidth);
        x += step;
    }

    // do the clipping
    var group = bars.parent.groupItems.add();
    bars.move(group, ElementPlacement.PLACEATEND);
    item.move(group, ElementPlacement.PLACEATEND);

    // cannot create the clipping mask with code, so ...
    app.selection = [group];
    app.executeMenuCommand('makeMask');

    return bars;

    /**
     * Draws one bar.
     * @param {Document|GroupItem|Layer} container - the place to draw the bar.
     * @param {bounds} bounds - the outer bounds [l,t,b,r].
     * @param {Number} x - the x offset, in points.
     * @param {Number} barWidth - the bar width, in points.
     */
    function drawBar(container, bounds, x, barWidth) {

        return drawRectangle(container,
            [
                bounds[0] + x,
                bounds[1],
                bounds[0] + x + barWidth,
                bounds[3],
            ]
        );

    };

};


/**
 * Draws a rectangle to the document.
 * @param {Document|Layer|GroupItem} container - an Illustrator container.
 * @param {Array<Number>} bounds - [T, L, B, R]
 * @param {Object} props - properties to assign to the rectangle.
 * @return {PathItem}
 */
function drawRectangle(container, bounds, properties) {

    properties = properties || {};

    var rectangle = container.pathItems.rectangle(bounds[1], bounds[0], bounds[2] - bounds[0], -(bounds[3] - bounds[1])); // TLWH

    // defaults
    rectangle.filled = true;
    rectangle.stroked = false;

    // apply properties
    for (var key in properties)
        if (properties.hasOwnProperty(key))
            rectangle[key] = properties[key];

    return rectangle;

};

/**
 * 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 getItemBounds(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(getItemBounds(child, geometric, bounds));

        }

        newBounds = combineBounds(contentBounds);

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

    }

    else if (item.typename == 'TextFrame') {

        // 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 (/illustrator/i.test(app.name))
        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 (/illustrator/i.test(app.name))
        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 (!Mittens.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]
            )
    );

};

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
Engaged ,
Sep 06, 2024 Sep 06, 2024

Copy link to clipboard

Copied

WOW. I got so excited to play with this code that I didn't even get the chance to reply earlier. This is great. There's a lot of great information in here about how to create masks and shapes. Thank you so much. This is really wonderful.

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 ,
Sep 06, 2024 Sep 06, 2024

Copy link to clipboard

Copied

LATEST

Great to hear it was helpful! Yeah ExtendScript is like JavaScript from 1999 with a few quirks of its own mixed in. All the best with your project!

- Mark

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