金曜日, 6月 19, 2026

JavaScriptでAI遊び 96 
Illustratorでオブジェクトのアンカーポイントを均一に(2)

以前作成したバージョンの曲線対応版です。

main();

function main() {

if (app.documents.length == 0) {
alert("ドキュメントを開いてください");
return;
}

var doc = app.activeDocument;

if (doc.selection.length == 0) {
alert("パスを選択してください");
return;
}

var path = doc.selection[0];

if (!(path instanceof PathItem)) {
alert("PathItemを選択してください");
return;
}

var targetCount = Number(
prompt("アンカーポイント数", 30)
);

if (targetCount < 2) {
return;
}

var samples = buildSamples(path, 20);

var totalLength =
samples[samples.length - 1].dist;

var newPts = [];

for (var i = 0; i < targetCount; i++) {

var tdist;

if (path.closed) {
tdist =
totalLength * (i / targetCount);
} else {
tdist =
totalLength *
(i / (targetCount - 1));
}

newPts.push(
getPointAtDistance(samples, tdist)
);
}

createSmoothPath(doc, newPts, path.closed);

alert("完了");
}

// サンプル生成
function buildSamples(path, divs) {

var pts = path.pathPoints;

var result = [];

var accum = 0;

for (var i = 0; i < pts.length; i++) {

var next;

if (i == pts.length - 1) {

if (!path.closed) break;

next = 0;

} else {

next = i + 1;
}

var p0 = pts[i].anchor;
var p1 = pts[i].rightDirection;
var p2 = pts[next].leftDirection;
var p3 = pts[next].anchor;

var prev = null;

for (var j = 0; j <= divs; j++) {

var t = j / divs;

var p = cubicBezier(
p0, p1, p2, p3, t
);

if (prev) {
accum += distance(prev, p);
}

result.push({
pt: p,
dist: accum
});

prev = p;
}
}

return result;
}

// ベジェ評価
function cubicBezier(p0,p1,p2,p3,t){

var mt = 1 - t;

var x =
mt*mt*mt*p0[0] + 3*mt*mt*t*p1[0] + 3*mt*t*t*p2[0] +
t*t*t*p3[0];

var y =
mt*mt*mt*p0[1] + 3*mt*mt*t*p1[1] + 3*mt*t*t*p2[1] +
t*t*t*p3[1];

return [x,y];
}

// 距離
function distance(a,b){

var dx = b[0]-a[0];
var dy = b[1]-a[1];

return Math.sqrt(dx*dx+dy*dy);
}

// 距離位置取得
function getPointAtDistance(samples, d){

for (var i=1; i<samples.length; i++){

if (samples[i].dist >= d){

var prev = samples[i-1];
var curr = samples[i];

var span =
curr.dist - prev.dist;

var ratio =
(d - prev.dist) / span;

var x =
prev.pt[0] + (curr.pt[0]-prev.pt[0])*ratio;

var y =
prev.pt[1] + (curr.pt[1]-prev.pt[1])*ratio;

return [x,y];
}
}

return samples[samples.length-1].pt;
}

// 新規スムーズパス生成
function createSmoothPath(doc, pts, closed){

var p = doc.pathItems.add();

p.setEntirePath(pts);

p.closed = closed;

p.stroked = true;
p.filled = false;

var pp = p.pathPoints;

for (var i=0; i<pp.length; i++){

pp[i].pointType = PointType.SMOOTH;

var prev =
pts[(i-1+pts.length)%pts.length];

var next =
pts[(i+1)%pts.length];

var dx = (next[0]-prev[0]) * 0.18;
var dy = (next[1]-prev[1]) * 0.18;

pp[i].leftDirection = [
pts[i][0]-dx,
pts[i][1]-dy
];

pp[i].rightDirection = [
pts[i][0]+dx,
pts[i][1]+dy
];
}
}
調整したいオブジェクトを選択して実行すると・・・

アンカーポイント数を指定します。デフォルトは30です。

問題なく処理が完了すると・・・
オリジナルの上にポイントが追加され、オリジナルを選択している状態なので削除またはロックします。
ポイントを60にした結果。
 










ちなみに極端に少ないポイント数を指定すると形状は崩れます。