Skip to content

Instantly share code, notes, and snippets.

@moluapple
Last active September 17, 2023 06:16
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save moluapple/94aaf66070f618641a8a4d34bfb8d4bf to your computer and use it in GitHub Desktop.
Save moluapple/94aaf66070f618641a8a4d34bfb8d4bf to your computer and use it in GitHub Desktop.
[Illustrator] 寻找不规则形状中心点(find visual center of an irregularly shaped polygon with Illustrator)
(function() {
var doc = app.activeDocument,
lays = doc.layers,
WORK_LAY = lays.add(),
NUM_LAY = lays.add(),
i = lays.length - 1,
lay;
// main working loop
for (; i > 1; i--) {
//process each layer
lay = lays[i];
lay.name = lay.name + " Num:" + (i - 1); // i-1 as 2 layers beed added.
process(lay.pathItems, false);
process(lay.compoundPathItems, true); // if any
}
//clean up
NUM_LAY.name = "Numbers";
WORK_LAY.remove();
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
op.filled = false;
op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
add_nums(i - 1, xy, s);
}
}
function getMinVisibleSize(b) {
var s = Math.min(b[2] - b[0], b[1] - b[3]);
return Math.abs(s);
}
function getGeometricCenter(p) {
var b = p.geometricBounds;
return [(b[0] + b[2]) / 2, (b[1] + b[3]) / 2];
}
// returns square of distance between p1 and p2
function getDist2(p1, p2) {
return Math.pow(p1[0] + p2[0], 2) + Math.pow(p1[1] + p2[1], 2);
}
// returns visibleBounds of a path in a compoundPath p
// which is closest to center of the original path op
function findBestBounds(op, p) {
var opc = getGeometricCenter(op);
var idx = 0,
d;
var minD = getDist2(opc, getGeometricCenter(p.pathItems[0]));
for (var i = 0, iEnd = p.pathItems.length; i < iEnd; i++) {
d = getDist2(opc, getGeometricCenter(p.pathItems[i]));
if (d < minD) {
minD = d;
idx = i;
}
}
return p.pathItems[idx].visibleBounds;
}
function applyOffset(op, checkBounds) {
var p = op.duplicate(WORK_LAY, ElementPlacement.PLACEATBEGINNING),
// offset value the small the better, but meantime more slow.
offset = function() {
var minsize = Math.min(p.width, p.height);
if (minsize >= 50) {
return '-1'
} else if (20 < minsize && minsize < 50) {
return '-0.5'
} else {
return '-0.2' // 0.2 * 2 (both side ) * 50 (Times) = 20
}
},
xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
.replace('#offset', offset()),
TIMES = 100; // if shapes are too large, should increase the value.
if (checkBounds) {
// check its size only if it needs, because it's too slow
while (TIMES-- && getMinVisibleSize(p.visibleBounds) > 3) p.applyEffect(xmlstring);
} else {
while (TIMES--) p.applyEffect(xmlstring);
}
return p;
}
function getCenterBounds(op) {
var originalMinSize = getMinVisibleSize(op.visibleBounds);
var p = applyOffset(op, false);
if (getMinVisibleSize(p.visibleBounds) > originalMinSize) {
// in some cases, path p becomes larger for some unknown reason
p.remove();
p = applyOffset(op, true);
}
var b = p.visibleBounds;
if (getMinVisibleSize(b) > 10) {
activeDocument.selection = [p];
executeMenuCommand("expandStyle");
p = activeDocument.selection[0];
if (p.typename == "CompoundPathItem") {
b = findBestBounds(op, p);
}
}
p.remove();
return b;
}
function add_nums(n, xy, s) {
var txt = NUM_LAY.textFrames.add();
txt.contents = n;
txt.textRange.justification = Justification.CENTER;
txt.textRange.characterAttributes.size = s;
txt.position = [xy[0] - txt.width / 2, xy[1] + txt.height / 2];
}
function strokeComPath(compoundPath) {
var p = compoundPath.pathItems,
l = p.length,
i = 0;
for (; i < l; i++) {
!p[i].closed && p[i].closed = true;
p[i].stroked = true;
p[i].filled = false;
};
}
})();
@shspage
Copy link

shspage commented May 16, 2016

Hello,
Very interesting and useful! I was impressed with the way to use LiveEffect in a script. It's so smart.
But I found 2 issues.

  1. There're cases that the offset effect creates multiple separated shapes. It is deemed as a wide shape, despite each shape is small enough.
    ex1
  2. Sometimes the offset effect creates a shape that returns strange visualBounds. It seems it occurs when the source shape is a thin rectangle.
    ex2
    Though these may not be problems in your usage, I tried fixing this. I'll be happy if you integrate it.

@moluapple
Copy link
Author

Many thanks to you, Hiroyuki Sato. I've integrate your code, and adjust some setting values to get better result.

@shspage
Copy link

shspage commented May 17, 2016

Thank you for taking your time to integrate!

@xukeek
Copy link

xukeek commented Oct 15, 2018

@shspage hello, I tried it but lay.pathItems.length is always 0, why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment