Copy link to clipboard
Copied
Just for practice I wrote a basic script that would loop through all document.pageItems then replace it's appearance with a stroke while attempting to draw each of it's pathPoint anchors/handles manually. I'm getting unexpected results, though.
On a simple shape, there are no problems. Left is the script result, right is the actual path:
Looks great. But once I try a more complex file like a character, I get strange results:
The left script results produces a bundled group of anchors where there should be none (between the pencil and the clipboard, beneath the left eye). It looks as though it stops executing at this point, because it doesn't continue to the leaf, shadow, eyes, mouth, etc. When I comment out the section that draws handles, all is fine, it strips the appearance just as expected:
Can I get some help here? What am I doing wrong? How can this be done better overall?
//
// Barebones script to convert all paths in current document to permanent Outlines, including handles and anchors.
// This action can be undone with a single Edit > Undo command.
//
var anchorWidth = 1; // number in pixels, width of stroke
var anchorSize = 5; // number in pixels, height/width of rectangle
var handleSize = 4; // number in pixels, size of ellipse/orb where handle is grabbed
var anchorColor = newRGB(50, 50, 200); // RGB values, currently blue
//
var outlineWidth = 1; // number in pixels, width of stroke
var outlineColor = newRGB(0, 0, 0); // The RGB value of color ([0,0,0] = black)
//
var forceOpacity = true; // Boolean (false or true) if true, force all paths to have full opacity
function convertToOutlines() {
for (var i = app.activeDocument.pageItems.length - 1; i >= 0; i--) {
var item = app.activeDocument.pageItems[i];
if (/path/i.test(item.typename)) {
if (item.stroked || item.filled) {
replaceAppearance(item);
// If I comment out the code below, all objects convert appearance exactly as expected.
// But certain parts (leaf, shadow, eyes/mouth) aren't affected by this?
if (item.pathPoints.length)
for (var p = 0; p < item.pathPoints.length; p++) {
var point = item.pathPoints[p];
drawAnchor(point);
if (point.leftDirection) drawHandle(point, "left");
if (point.rightDirection) drawHandle(point, "right");
}
// ---------------
item.opacity = forceOpacity ? 100.0 : item.opacity;
}
}
}
}
convertToOutlines();
function drawAnchor(point) {
var childpos = point.anchor;
var anchor = app.activeDocument.pathItems.rectangle(
point.anchor[1] + anchorSize / 2,
point.anchor[0] - anchorSize / 2,
anchorSize,
anchorSize
);
setAnchorAppearance(anchor);
}
function drawHandle(point, direction) {
var handle = app.activeDocument.pathItems.add();
handle.setEntirePath([point.anchor, point[direction + "Direction"]]);
setAnchorAppearance(handle);
var handleBar = app.activeDocument.pathItems.ellipse(
point[direction + "Direction"][1] + handleSize / 2,
point[direction + "Direction"][0] - handleSize / 2,
handleSize,
handleSize
);
handleBar.stroked = false;
handleBar.filled = true;
handleBar.fillColor = anchorColor;
}
function setAnchorAppearance(item) {
item.filled = false;
item.stroked = true;
item.strokeWidth = anchorWidth;
item.strokeColor = anchorColor;
}
function replaceAppearance(item) {
item.filled = false;
item.stroked = true;
item.strokeWidth = outlineWidth;
item.strokeColor = outlineColor;
}
function newRGB(r, g, b) {
var color = new RGBColor();
color.red = r;
color.green = g;
color.blue = b;
return color;
}
Figured it out:
I had a few problems:
Copy link to clipboard
Copied
I'm including the file (couldn't attach it, said the contents didn't match the file type?) in case any one willing to help wants to verify or test against it. Thanks guys!
Copy link to clipboard
Copied
Figured it out:
I had a few problems:
For posterity I'm including the final script below. I'd still be interested in hearing any ways that it could be made better!
// https://github.com/Inventsable
// contact: tom@inventsable.cc
//
// Barebones script to convert all paths in current document to permanent Outlines, including handles and anchors.
// This action can be undone with a single Edit > Undo command.
//
// You can edit the below settings:
var anchorWidth = 1; // number in pixels, width of stroke
var anchorSize = 5; // number in pixels, height/width of rectangle
var handleSize = 4; // number in pixels, size of ellipse/orb where handle is grabbed
var anchorColor = newRGB(50, 50, 200); // RGB values, currently blue
var anchorIsFilled = true; // Boolean (true or false) if true, anchors are filled, otherwise have only stroke
//
var outlineWidth = 1; // number in pixels, width of stroke
var outlineColor = newRGB(0, 0, 0); // The RGB value of color ([0,0,0] = black)
//
var forceOpacity = true; // Boolean (true or false) if true, force all paths to have full opacity
// --------------------------------------------------- //
//
// Do not edit below unless you know what you're doing!
//
convertAllToOutlines();
function convertAllToOutlines() {
// Need to collect all current pageItems otherwise recurses into it's own drawn anchors
// app.activeDocument.pageItems is not chronological! Adding to it, even if decrementing, causes recursion
var targets = scanCurrentPageItems();
convertListToOutlines(targets);
}
function scanCurrentPageItems() {
var list = [];
for (var i = app.activeDocument.pageItems.length - 1; i >= 0; i--) {
list.push(app.activeDocument.pageItems[i]);
}
return list;
}
function convertListToOutlines(list) {
for (var i = list.length - 1; i >= 0; i--) {
var item = list[i];
if (/compound|group/i.test(item.typename)) {
if (item.pageItems && item.pageItems.length) {
convertListToOutlines(item.pageItems);
}
} else if (/path/i.test(item.typename)) {
if (item.stroked || item.filled) {
replaceAppearance(item);
if (item.pathPoints && item.pathPoints.length) {
for (var p = 0; p < item.pathPoints.length; p++) {
var point = item.pathPoints[p];
drawAnchor(point);
if (point.leftDirection) drawHandle(point, "left");
if (point.rightDirection) drawHandle(point, "right");
}
item.opacity = forceOpacity ? 100.0 : item.opacity;
} else {
alert("Problem with " + item.name + ": " + item.typename);
}
}
} else {
alert(item.name + " " + item.typename);
}
}
}
function drawAnchor(point) {
var anchor = app.activeDocument.pathItems.rectangle(
point.anchor[1] + anchorSize / 2,
point.anchor[0] - anchorSize / 2,
anchorSize,
anchorSize
);
setAnchorAppearance(anchor, false);
}
function drawHandle(point, direction) {
var handle = app.activeDocument.pathItems.add();
handle.setEntirePath([point.anchor, point[direction + "Direction"]]);
setAnchorAppearance(handle, true);
var handleBar = app.activeDocument.pathItems.ellipse(
point[direction + "Direction"][1] + handleSize / 2,
point[direction + "Direction"][0] - handleSize / 2,
handleSize,
handleSize
);
handleBar.stroked = false;
handleBar.filled = true;
handleBar.fillColor = anchorColor;
}
function setAnchorAppearance(item, isHandle) {
if (!isHandle) {
item.filled = anchorIsFilled;
item.stroked = !anchorIsFilled;
if (!anchorIsFilled) {
item.strokeWidth = anchorWidth;
item.strokeColor = anchorColor;
} else {
item.fillColor = anchorColor;
}
} else {
item.filled = false;
item.stroked = true;
item.strokeWidth = anchorWidth;
item.strokeColor = anchorColor;
}
}
function replaceAppearance(item) {
item.filled = false;
item.stroked = true;
item.strokeWidth = outlineWidth;
item.strokeColor = outlineColor;
}
function newRGB(r, g, b) {
var color = new RGBColor();
color.red = r;
color.green = g;
color.blue = b;
return color;
}
Copy link to clipboard
Copied
I think your script is as good as it gets.
Thakns for sharing!
Copy link to clipboard
Copied
Thanks Carlos! Could always be better though 😋
It probably makes more sense to use PathItems instead of PageItems, since then I wouldn't have to compare for groups/compounds, and right now the script still produces handles even when they aren't extended since leftDirection and rightDirection never seem to be null. Initially I tried comparing the anchor position to the handle data directly and only drawing if they aren't equal -- this didn't work (at least in comparing both array entries directly) so I dropped it but didn't look too far into why. Might do a bit of sprucing up and fixes regardless.
Someone had asked if this was possible on reddit and I recommended scripting, but they didn't think that could draw the handles/anchors. But we all know better than that
Copy link to clipboard
Copied
I second Carlos, and I'll just say that leftDirection and rightDirection are going to be the same or within a very small tolerance of your anchor point - which is one way of filtering whether the direction handles are fully retracted and need to be rendered or not.
Copy link to clipboard
Copied
oh I see what you mean, in that case then yes, maybe it could get even better.
That's right, we know better, we have magic wands 🙂
Jongware wrote a similar script years ago, check his script. The link to the forums is dead but Wundes has also posted a link to the direct download
http://js4ai.blogspot.com/2012/03/draw-control-handles-as-geometry.html
here's another version by jooja
Copy link to clipboard
Copied
Updated this quite a bit, source code on GitHub:
Before:
After:
Copy link to clipboard
Copied