Skip to content

Instantly share code, notes, and snippets.

@shspage
Forked from moluapple/find visual center.jsx
Last active May 18, 2016 13:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shspage/daf5b265a01b97171aa3f8f83c2c2ada to your computer and use it in GitHub Desktop.
Save shspage/daf5b265a01b97171aa3f8f83c2c2ada 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, 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 = getMinVisibleSize(b) > 20 ? // two or more visual center exists, as the bounds width is too big
[b[0] + 2, b[1] - 2] : // use the top left position with a little adjust
[b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
add_nums(i - 1, xy);
}
}
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);
var offset = Math.min(p.width, p.height) / 10 > 1 ? 1 : 0.5,
xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
.replace('#offset', '-' + offset),
TIMES = 50; // if shapes are too large, should increase times to 100 or even bigger.
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) > 20){
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) {
var txt = NUM_LAY.textFrames.add();
txt.contents = n;
txt.textRange.justification = Justification.CENTER;
txt.textRange.characterAttributes.size = 6;
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;
};
}
})();
@moluapple
Copy link

Wow, thank you for the more precise version.

@moluapple
Copy link

Hello,
One more question: as this problem comes from a forum question, and some of us wonder there must be a prue Mathematical solution. Do you think that's possible? or maybe you can port some exists library to Illustrator as you have done with Delaunay diagrams?
here are 2 links I have searched:1, 2

@shspage
Copy link
Author

shspage commented May 17, 2016

I see. I didn't know that topic! Very interesting discussion.
If "more speed" is what they are concerned with, "more simple" way (= your Gist) may be better, even if it (rarely) produces minor errors.
There may be a pure Mathematical solution, but it's too complex for me to implement.

I tried adding some simple modifications to jsclipper, but I couldn't include it in Illustrator script for now.
At least it seems that its manipulation is based on polygons, not bezier curves.
So you must convert curves into polygons before you use its function.
It will take much time.

@moluapple
Copy link

Got it. Thank you for taking your time and the detailed explanation.

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