Grouping only specific paths using a Script.
Copy link to clipboard
Copied
I have a script that looks at the active selection. Always an outlined letter with dots inside. It then checks to see if there are two paths that are on the exact same Y axis. If it finds any paths it creates groups of two path items. I'm having two issues I could use help with.
1. I'm getting an error - Error 1302: No Such Element
2. I'd love to just select the whole layout and have it loop through each outlined character.
The dots are always black, but to illustrate which dots would get grouped I colored them in the example file.
Thank you!
if (app.documents.length > 0) {
if (app.activeDocument.selection.length > 0) {
// group items by vertical separation (line)
var groups = groupObjectsByLine(app.activeDocument.selection);
if (groups) {
var paths;
// iterate over each group of objects
for (var i = 0; i < groups.length; i++) {
paths = [];
// capture all pageItems within the group
// so that a standard array can be passed to adjustOffset
for (var j = 0; j < groups[i].pageItems.length; j++) {
paths.push(groups[i].pageItems[j]);
}
// if a group has 2 items already, create a new group
if (groups[i].pageItems.length === 2) {
var matchingSize = groups[i].pageItems.every(function(p) {
return p.width === groups[i].pageItems[0].width && p.height === groups[i].pageItems[0].height;
});
if (matchingSize) {
var g = app.activeDocument.groupItems.add();
groups.push(g);
g.pageItems.add(paths[0]);
g.pageItems.add(paths[1]);
groups[i].remove();
i--;
}
}
}
}
}
}
/**
* Take an array of Adobe Illustrator pageItems and group them by vertical separation.
* @Param {Array} sel Adobe Illustrator pageItems
* @Returns {Array} Array of Adobe Illustrator groupItems
*/
function groupObjectsByLine(sel) {
var groups = [];
// sort the selected page items by their position (left to right, top to bottom)
sel.sort(function (a, b) {
if (a.top === b.top) {
return a.left - b.left;
} else {
return a.top - b.top;
}
});
// check if each page item shares bounds with others
var item, placed;
while (sel.length > 0) {
item = sel.pop();
placed = false;
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
if (overlappingBounds(item, group)) {
item.move(group, ElementPlacement.PLACEATEND);
placed = true;
group.pageItems.add(item);
break;
}
}
// if an item didn't fit into any current groups make a new group
if (!placed) {
var g = app.activeDocument.groupItems.add();
groups.push(g);
item.move(g, ElementPlacement.PLACEATEND);
}
}
return groups;
}
/**
* Check if a pageItems bounds overlaps with a groupItem.
* @Param {pageItem} item Adobe Illustrator pageItem
* @Param {groupItem} group Adobe Illustrator groupItem
* @Returns {Boolean}
*/
function overlappingBounds(item, group) {
var top = item.geometricBounds[1];
var bottom = item.geometricBounds[3];
var gTop = group.geometricBounds[1];
var gBottom = group.geometricBounds[3];
if (gTop !== top) {
return false;
}
return true;
}
Explore related tutorials & articles
Copy link to clipboard
Copied
So, the reason for the "No Such Element" error is that line 64 `group.pageItems.add(item)` shouldn't be there. The item was already added to the group 2 lines before that `item.move(group, ElementPlacement.PLACEATEND)` and the `add()` method is not for moving paths into groups, it is for adding a new path into the group. There's a cleaner version of the function you are using below.
As a side note, since the function was originally designed to group all of your text characters so it will need some help to work for this application if you just want to select everything. You have a few options, if you only want the dots/studs grouped by line, then you could just ignore any paths with large areas (characters), or if the characters are always outlined like in the example file, you can ignore paths that have a stroke.
/*
GroupObjectsByRow.jsx for Adobe Illustrator
-------------------------------------------
Take a selection of objects that are separated by a unknown
vertical gap and group all that vertically overlap.
Note: This script was designed to group text characters but
works just fine for most types of pageItems.
*/
(function () {
var doc = app.activeDocument;
groups = groupObjectsByRow(doc.selection);
if (groups) {
alert("Groups Created:\n" + groups.length);
}
/**
* Take an array of Adobe Illustrator pageItems and group them by vertical separation.
* @Param {Array} sel Adobe Illustrator pageItems
* @Returns {Array} Array of Adobe Illustrator groupItems
*/
function groupObjectsByRow(sel) {
var groups = [];
// sort the selected page items by their height (tallest to shortest)
sel.sort(function (a, b) {
var aHeight = a.geometricBounds[3] - a.geometricBounds[1];
var bHeight = b.geometricBounds[3] - b.geometricBounds[1];
return bHeight - aHeight;
});
// check if each page item shares bounds with others
var item, placed;
while (sel.length > 0) {
item = sel.pop();
placed = false;
for (var i = 0; i < groups.length; i++) {
group = groups[i];
// check if item bounds overlaps a groups bounds
if (
item.geometricBounds[3] <= group.geometricBounds[1] &&
item.geometricBounds[1] >= group.geometricBounds[3]
) {
item.move(group, ElementPlacement.PLACEATEND);
placed = true;
}
}
// if an item didn't fit into any current groups make a new group
if (!placed) {
g = app.activeDocument.groupItems.add();
groups.push(g);
item.move(g, ElementPlacement.PLACEATEND);
}
}
return groups;
}
})();

