illustrator插件--常用功能开发--智能填充插件--js脚本开发--AI插件
AI智能填充是一款Illustrator智能填充插件。利用此脚本可以快速把一组对象智能填充到你预置的容器中,填充的对象会根据你的容器形状智能缩放大小以及调整位置。灵活的运用此脚本可以制作出很多各种充满创意的图形。本文根据此需求,展示如何制作这款插件的流程。
·
AI智能填充是一款Illustrator智能填充插件。利用此脚本可以快速把一组对象智能填充到你预置的容器中,填充的对象会根据你的容器形状智能缩放大小以及调整位置。灵活的运用此脚本可以制作出很多各种充满创意的图形。本文根据此需求,展示如何制作这款插件的流程。
1.插件界面
插件最终效果如下:
var win = new Window("dialog", "智能填充");
win.orientation = "column";
win.alignChildren = ["fill", "fill"];
var globalGroup = win.add("group");
globalGroup.orientation = "column";
globalGroup.alignChildren = ["fill", "fill"];
var sizePanel = globalGroup.add("panel", undefined, "对象占总尺寸的比例");
var sizeGroup = sizePanel.add("group");
sizePanel.alignChildren = "fill";
sizeGroup.orientation = "row";
sizeGroup.alignChildren = ["fill", "fill"];
var maxValueGroup = sizeGroup.add("group");
maxValueGroup.orientation = "row";
maxValueGroup.alignChildren = "fill";
var maxValueLabel = maxValueGroup.add("statictext", undefined, "最大值:");
var maxValue = maxValueGroup.add("edittext", [0, 0, 50, 25], "10");
maxValueLabel.justify = "center";
var minValueGroup = sizeGroup.add("group");
minValueGroup.orientation = "row";
minValueGroup.alignChildren = "fill";
var minValueLabel = minValueGroup.add("statictext", undefined, "最小值:");
var minValue = minValueGroup.add("edittext", [0, 0, 50, 25], "4");
minValueLabel.justify = "center";
function checkMnMaxSize(val, item, min, max) {
if (item === minValue) {
if (val > parseFloat(maxValue.text)) {
maxValue.text = val >= max ? max : val;
}
} else {
if (item === maxValue) {
if (val < parseFloat(minValue.text)) {
minValue.text = val < min ? min : val;
}
}
}
}
maxValue.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.keyName === "Right") {
minValue.text = this.text
} else {
inputNumberEvents(e, maxValue, 1, Infinity, checkMnMaxSize)
}
});
minValue.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.keyName === "Left") {
maxValue.text = this.text
} else {
inputNumberEvents(e, minValue, 1, Infinity, checkMnMaxSize)
}
});
var guttersResizeGroup = sizePanel.add("group");
guttersResizeGroup.orientation = "row";
guttersResizeGroup.alignChildren = ["fill", "fill"];
var guttersGroup = guttersResizeGroup.add("group");
guttersGroup.orientation = "column";
guttersGroup.alignChildren = "fill";
var guttersLabel = guttersGroup.add("statictext", undefined, "最小间距");
guttersValue = guttersGroup.add("edittext", undefined, "0");
guttersValue.addEventListener("keydown", function (e) {
inputNumberEvents(e, this, 0, Infinity);
});
var resizeGroup = guttersResizeGroup.add("group");
resizeGroup.orientation = "column";
resizeGroup.alignChildren = "fill";
var resizeLabel = resizeGroup.add("statictext", undefined, "调整大小");
resizeValue = resizeGroup.add("edittext", undefined, "70");
resizeValue.addEventListener("keydown", function (e) {
inputNumberEvents(e, this, 10, Infinity);
});
var rotatePositionGroup = globalGroup.add("group");
rotatePositionGroup.orientation = "row";
rotatePositionGroup.alignChildren = ["fill", "fill"];
var rotatePanel = rotatePositionGroup.add("panel", undefined, "旋转对象:");
rotatePanel.alignChildren = "fill";
var randomRadioGroup = rotatePanel.add("group");
randomRadioGroup.orientation = "column";
var randomRotate = randomRadioGroup.add("radiobutton", undefined, "随机");
var rotateByValue = randomRadioGroup.add("radiobutton", undefined, "度数");
var rotateValue = rotatePanel.add("edittext", undefined, "0");
randomRotate.value = true;
rotateValue.enabled = false;
randomRotate.onClick = function () {
rotateValue.enabled = false;
};
rotateByValue.onClick = function () {
rotateValue.enabled = true;
};
var objectPositionPanel = rotatePositionGroup.add("panel", undefined, "选择填充对象为:");
var objectPositionGroup = objectPositionPanel.add("group");
objectPositionGroup.orientation = "column";
objectPositionGroup.alignChildren = ["fill", "fill"];
var objectPosTop = objectPositionGroup.add("radiobutton", undefined, "填充顶层");
var objectPosBottom = objectPositionGroup.add("radiobutton", undefined, "填充底层");
var objectPosLayers = objectPositionGroup.add("checkbox", undefined, "反转");
objectPosTop.value = true;
var groupResult = globalGroup.add("checkbox", undefined, "执行完成后群组对象");
var randomItems = globalGroup.add("checkbox", undefined, "随机分布(群组对象)");
var removeTopElement = globalGroup.add("checkbox", undefined, "执行后去除填充对象");
var winButtons = globalGroup.add("group");
winButtons.orientation = "row";
winButtons.alignChildren = ["fill", "fill"];
winButtons.margins = 0;
var cancel = winButtons.add("button", undefined, "取消");
cancel.helpTip = "Press Esc to Close";
cancel.onClick = function () {
win.close();
};
var ok = winButtons.add("button", undefined, "OK");
ok.helpTip = "Press Enter to Run";
ok.onClick = function (e) {
startAction();
win.close();
};
ok.active = true;
var progressBar = win.add("progressbar");
var progressBarCounter = 100;
progressBar.value = 0;
progressBar.minvalue = 0;
progressBar.maxvalue = progressBarCounter;
progressBar.maximumSize = [1000, 5];
win.center();
win.show();
2.算法过程
利用二维平面几何,点和多边形的关系来实现,完整的源代码如下:
if (app.documents.length && app.selection.length < 2) {
alert("请选择两个以上对象");
} else {
var scriptName = "Fillinger";
var settingFile = {
name: scriptName + "__setting.json",
folder: Folder.myDocuments + "/LA_AI_Scripts/"
};
function inputNumberEvents(ev, _input, min, max, callback) {
var _dir = ev.keyName.toLowerCase().slice(0, 1);
var _value = parseFloat(_input.text);
var units = ",px,pt,mm,cm,in,".indexOf(_input.text.length > 2 ? "," + _input.text.replace(/ /g, "").slice(-2) + "," : ",!,") > -1 ? _input.text.replace(/ /g, "").slice(-2) : "";
min = min === undefined ? 0 : min;
max = max === undefined ? Infinity : max;
step = ev.shiftKey ? 10 : (ev.ctrlKey ? 0.1 : 1);
if (isNaN(_value)) {
_input.text = min;
} else {
_value = _dir === "u" || _dir === "r" ? _value + step : (_dir === "d" || _dir === "l" ? _value - step : false);
if (_value !== false) {
_value = _value <= min ? min : (_value >= max ? max : _value);
_input.text = _value;
if (callback instanceof Function) {
callback(_value, _input, min, max, units)
}
}
}
}
var win = new Window("dialog", "智能填充");
win.orientation = "column";
win.alignChildren = ["fill", "fill"];
var globalGroup = win.add("group");
globalGroup.orientation = "column";
globalGroup.alignChildren = ["fill", "fill"];
var sizePanel = globalGroup.add("panel", undefined, "对象占总尺寸的比例");
var sizeGroup = sizePanel.add("group");
sizePanel.alignChildren = "fill";
sizeGroup.orientation = "row";
sizeGroup.alignChildren = ["fill", "fill"];
var maxValueGroup = sizeGroup.add("group");
maxValueGroup.orientation = "row";
maxValueGroup.alignChildren = "fill";
var maxValueLabel = maxValueGroup.add("statictext", undefined, "最大值:");
var maxValue = maxValueGroup.add("edittext", [0, 0, 50, 25], "10");
maxValueLabel.justify = "center";
var minValueGroup = sizeGroup.add("group");
minValueGroup.orientation = "row";
minValueGroup.alignChildren = "fill";
var minValueLabel = minValueGroup.add("statictext", undefined, "最小值:");
var minValue = minValueGroup.add("edittext", [0, 0, 50, 25], "4");
minValueLabel.justify = "center";
function checkMnMaxSize(val, item, min, max) {
if (item === minValue) {
if (val > parseFloat(maxValue.text)) {
maxValue.text = val >= max ? max : val;
}
} else {
if (item === maxValue) {
if (val < parseFloat(minValue.text)) {
minValue.text = val < min ? min : val;
}
}
}
}
maxValue.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.keyName === "Right") {
minValue.text = this.text
} else {
inputNumberEvents(e, maxValue, 1, Infinity, checkMnMaxSize)
}
});
minValue.addEventListener("keydown", function (e) {
if (e.ctrlKey && e.keyName === "Left") {
maxValue.text = this.text
} else {
inputNumberEvents(e, minValue, 1, Infinity, checkMnMaxSize)
}
});
var guttersResizeGroup = sizePanel.add("group");
guttersResizeGroup.orientation = "row";
guttersResizeGroup.alignChildren = ["fill", "fill"];
var guttersGroup = guttersResizeGroup.add("group");
guttersGroup.orientation = "column";
guttersGroup.alignChildren = "fill";
var guttersLabel = guttersGroup.add("statictext", undefined, "最小间距");
guttersValue = guttersGroup.add("edittext", undefined, "0");
guttersValue.addEventListener("keydown", function (e) {
inputNumberEvents(e, this, 0, Infinity);
});
var resizeGroup = guttersResizeGroup.add("group");
resizeGroup.orientation = "column";
resizeGroup.alignChildren = "fill";
var resizeLabel = resizeGroup.add("statictext", undefined, "调整大小");
resizeValue = resizeGroup.add("edittext", undefined, "70");
resizeValue.addEventListener("keydown", function (e) {
inputNumberEvents(e, this, 10, Infinity);
});
var rotatePositionGroup = globalGroup.add("group");
rotatePositionGroup.orientation = "row";
rotatePositionGroup.alignChildren = ["fill", "fill"];
var rotatePanel = rotatePositionGroup.add("panel", undefined, "旋转对象:");
rotatePanel.alignChildren = "fill";
var randomRadioGroup = rotatePanel.add("group");
randomRadioGroup.orientation = "column";
var randomRotate = randomRadioGroup.add("radiobutton", undefined, "随机");
var rotateByValue = randomRadioGroup.add("radiobutton", undefined, "度数");
var rotateValue = rotatePanel.add("edittext", undefined, "0");
randomRotate.value = true;
rotateValue.enabled = false;
randomRotate.onClick = function () {
rotateValue.enabled = false;
};
rotateByValue.onClick = function () {
rotateValue.enabled = true;
};
var objectPositionPanel = rotatePositionGroup.add("panel", undefined, "选择填充对象为:");
var objectPositionGroup = objectPositionPanel.add("group");
objectPositionGroup.orientation = "column";
objectPositionGroup.alignChildren = ["fill", "fill"];
var objectPosTop = objectPositionGroup.add("radiobutton", undefined, "填充顶层");
var objectPosBottom = objectPositionGroup.add("radiobutton", undefined, "填充底层");
var objectPosLayers = objectPositionGroup.add("checkbox", undefined, "反转");
objectPosTop.value = true;
var groupResult = globalGroup.add("checkbox", undefined, "执行完成后群组对象");
var randomItems = globalGroup.add("checkbox", undefined, "随机分布(群组对象)");
var removeTopElement = globalGroup.add("checkbox", undefined, "执行后去除填充对象");
var winButtons = globalGroup.add("group");
winButtons.orientation = "row";
winButtons.alignChildren = ["fill", "fill"];
winButtons.margins = 0;
var cancel = winButtons.add("button", undefined, "取消");
cancel.helpTip = "Press Esc to Close";
cancel.onClick = function () {
win.close();
};
var ok = winButtons.add("button", undefined, "OK");
ok.helpTip = "Press Enter to Run";
ok.onClick = function (e) {
startAction();
win.close();
};
ok.active = true;
var progressBar = win.add("progressbar");
var progressBarCounter = 100;
progressBar.value = 0;
progressBar.minvalue = 0;
progressBar.maxvalue = progressBarCounter;
progressBar.maximumSize = [1000, 5];
function startAction() {
globalGroup.enabled = false;
var __rotateValue = Number(rotateValue.text);
maxCircleSize = Number(maxValue.text);
if (maxCircleSize < 0.01 || maxCircleSize > 100) {
maxCircleSize = 20;
}
minCircleSize = Number(minValue.text);
if (minCircleSize < 0.01 || minCircleSize > maxCircleSize) {
minCircleSize = maxCircleSize / 2;
}
maxCircleSize /= 100;
minCircleSize /= 100;
maxCircleSize /= 2;
minCircleSize /= 2;
minDistanceToOtherCircles = Number(guttersValue.text);
var items = selection.concat();
if (!objectPosLayers.value) {
items.sort(function (a, b) {
return a.geometricBounds[1] <= b.geometricBounds[1];
})
}
if (objectPosBottom.value) {
items.reverse()
}
var object = items[0];
if (object.typename !== "PathItem" && object.typename !== "CompoundPathItem") {
return alert("填充对象必须是路径或复合路径项! [" + object.typename + "]");
}
items.splice(0, 1);
var placeObject = items.length === 1 ? (items[0].typename === "GroupItem" ? items[0].pageItems : [items[0]]) : (!items.length ? [] : items);
if (!placeObject.length) {
return alert("No items to fill!");
}
var placeObjectResizeValue = isNaN(parseFloat(resizeValue.text)) ? parseFloat(resizeValue.text) : 70;
var innerpaths = [];
var outerPath = null;
groupItems = groupResult.value ? activeDocument.groupItems.add() : false;
if (groupItems) {
groupItems.move(object, ElementPlacement.PLACEBEFORE)
}
function getNode() {
return !randomItems.value ? placeObject[0] : placeObject[Math.floor(Math.random() * placeObject.length)];
}
if (object.constructor.name == "CompoundPathItem") {
for (var p = 0; p < object.pathItems.length; p += 1) {
innerpaths.push(flattenPath(object.pathItems[p]));
}
if (innerpaths.length == 1 && outerPath == null) {
outerPath = innerpaths[0];
innerpaths = [];
} else {
var minx = innerpaths[0][0][0];
var outer = 0;
for (var p = 0; p < innerpaths.length; p += 1) {
for (var q = 0; q < innerpaths[p].length; q += 1) {
if (innerpaths[p][q][0] < minx) {
minx = innerpaths[p][q][0];
outer = p;
}
}
}
outerPath = innerpaths[outer];
innerpaths.splice(outer, 1);
}
} else {
outerPath = flattenPath(object);
}
if (outerPath == null) {
alert("Got a bad path. What's going on?");
} else {
minx = object.geometricBounds[0];
miny = object.geometricBounds[1];
maxx = object.geometricBounds[2];
maxy = object.geometricBounds[3];
if (minx > maxx) {
x = minx;
minx = maxx;
maxx = x;
}
if (miny > maxy) {
y = miny;
miny = maxy;
maxy = y;
}
maxwide = maxx - minx;
maxhigh = maxy - miny;
totalArea = Math.abs(object.area);
filledArea = 0;
Math_Epsilon = 0.0001;
joinedPath = outerPath;
triangleIndexList = Triangulate(joinedPath, innerpaths);
triangleList = [];
for (var p = 0; p < triangleIndexList.length; p += 3) {
triangleList.push([joinedPath[triangleIndexList[p]], joinedPath[triangleIndexList[p + 1]], joinedPath[triangleIndexList[p + 2]]]);
}
edgeList = [
[outerPath[outerPath.length - 1], outerPath[0]]
];
for (var i = 0; i < outerPath.length - 1; i += 1) {
edgeList.push([outerPath[i], outerPath[i + 1]]);
}
for (var i = 0; i < innerpaths.length; i += 1) {
edgeList.push([innerpaths[i][innerpaths.length - 1], innerpaths[i][0]]);
for (var i2 = 0; i2 < innerpaths[i].length - 1; i2 += 1) {
edgeList.push([innerpaths[i][i2], innerpaths[i][i2 + 1]]);
}
}
areaList = [];
triArea = 0;
for (var p = 0; p < triangleList.length; p += 1) {
triArea += Math.abs(Area([triangleList[p][0], triangleList[p][1], triangleList[p][1], triangleList[p][2], triangleList[p][2], triangleList[p][0]]));
areaList.push(triArea);
}
pointList = [];
circleList = [];
radiiList = [];
maxsize = Math.sqrt(maxwide * maxhigh);
size = maxCircleSize;
while (true) {
radiiList.push(size * maxsize);
size *= 0.667;
if (size < minCircleSize) {
break;
}
}
progressBarCounter = (progressBar.maxvalue * 0.25) / radiiList.length;
for (var rad = 0; rad < radiiList.length; rad += 1) {
for (var p = 0; p < 1000; p += 1) {
a_rnd = Math.random() * triArea;
for (var q = 0; q < triangleList.length; q += 1) {
if (areaList[q] > a_rnd) {
break;
}
}
pt = getRandomPoint(triangleList[q]);
d = distanceToClosestEdge(pt, edgeList);
if (d >= radiiList[rad]) {
for (var c = 0; c < pointList.length; c += 1) {
xd = Math.abs(pt[0] - pointList[c][0]);
yd = Math.abs(pt[1] - pointList[c][1]);
if (xd <= (radiiList[rad] + circleList[c] + minDistanceToOtherCircles) && yd <= (radiiList[rad] + circleList[c] + minDistanceToOtherCircles)) {
d = distanceFromPointToPoint(pt, pointList[c]) - minDistanceToOtherCircles;
if (d < (radiiList[rad] + circleList[c])) {
break;
}
}
}
if (c == pointList.length) {
nrad = radiiList[rad];
pointList.push(pt);
circleList.push(nrad);
}
}
}
progressBar.value += progressBarCounter;
win.update();
}
progressBarCounter = (progressBar.maxvalue * 0.75) / pointList.length;
for (var p = 0; p < pointList.length; p += 1) {
pt = pointList[p];
nrad = distanceToClosestEdge(pt, edgeList);
for (var c = 0; c < pointList.length; c += 1) {
if (c == p) {
continue;
}
xd = Math.abs(pt[0] - pointList[c][0]);
yd = Math.abs(pt[1] - pointList[c][1]);
if (xd <= (nrad + circleList[c] + minDistanceToOtherCircles) && yd <= (nrad + circleList[c] + minDistanceToOtherCircles)) {
nd = (distanceFromPointToPoint(pt, pointList[c]) - circleList[c]) - minDistanceToOtherCircles;
if (nd < nrad) {
nrad = nd
}
}
}
circleList[p] = nrad;
var __placeObject = getNode().duplicate();
var placeObjectSize = __placeObject.width >= __placeObject.height ? "width" : "height";
var placeObjectSizeReverse = placeObjectSize === "width" ? "height" : "width";
var __radius = 2 * nrad;
var __size = __radius * (placeObjectResizeValue / 100);
__ratio = ((__size * 100) / __placeObject[placeObjectSize]) / 100;
__placeObject.move(groupResult.value ? groupItems : object, ElementPlacement[groupResult.value ? "INSIDE" : "PLACEBEFORE"]);
__placeObject[placeObjectSize] = __size;
__placeObject[placeObjectSizeReverse] *= __ratio;
__placeObject.position = [(pt[0] - nrad) + ((__radius - __placeObject.width) / 2), (pt[1] + nrad) - ((__radius - __placeObject.height) / 2)];
if (randomRotate.value) {
__placeObject.rotate(Math.floor(Math.random() * 360))
} else {
if (rotateByValue.value && __rotateValue) {
__placeObject.rotate(__rotateValue)
}
}
progressBar.value += progressBarCounter;
win.update();
}
}
if (removeTopElement.value) {
object.remove()
}
}
function drawLine(a, b) {
var p = app.activeDocument.pathItems.add();
try {
p.setEntirePath([a, b]);
p.strokeWidth = 0.1;
} catch (e) {
alert("Bad line:\ra=" + a + "\rb=" + b);
}
return p;
}
function distanceFromPointToPoint(A, B) {
return Math.sqrt(((A[0] - B[0]) * (A[0] - B[0])) + ((A[1] - B[1]) * (A[1] - B[1])));
}
function flattenPath(obj) {
var newpath = new Array();
var isFlattened = false;
if (!obj.hasOwnProperty("pathPoints")) {
return null;
}
for (var pt = 0; pt < obj.pathPoints.length; pt += 1) {
nextpt = pt + 1;
if (nextpt == obj.pathPoints.length) {
nextpt = 0;
}
if (obj.pathPoints[pt].anchor[0] == obj.pathPoints[pt].rightDirection[0] && obj.pathPoints[pt].anchor[1] == obj.pathPoints[pt].rightDirection[1] && obj.pathPoints[nextpt].anchor[0] == obj.pathPoints[nextpt].leftDirection[0] && obj.pathPoints[nextpt].anchor[1] == obj.pathPoints[nextpt].leftDirection[1]) {
newpath.push(obj.pathPoints[pt].anchor);
} else {
isFlattened = true;
curveList = curve4(obj.pathPoints[pt].anchor[0], obj.pathPoints[pt].anchor[1], obj.pathPoints[pt].rightDirection[0], obj.pathPoints[pt].rightDirection[1], obj.pathPoints[nextpt].leftDirection[0], obj.pathPoints[nextpt].leftDirection[1], obj.pathPoints[nextpt].anchor[0], obj.pathPoints[nextpt].anchor[1], 4);
newpath = newpath.concat(curveList);
}
}
return newpath;
}
function pointInsidePoly(pt, poly) {
var c = false;
var i = -1;
var l = poly.length;
for (var j = l - 1; ++i < l; j = i) {
poly[i][1] <= pt[1] && pt[1] < poly[j][1] || poly[j][1] <= pt[1] && pt[1] < poly[i][1] && pt[0] < ((((poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1])) / (poly[j][1] - poly[i][1])) + poly[i][0]) && c = !c;
}
return c;
}
function getWinding(path) {
var accum = 0;
for (var i = 0; i < path.length - 1; i += 1) {
next = i + 1;
accum += ((path[next][0] * path[i][1]) - (path[i][0] * path[next][1]));
}
next = 0;
accum += ((path[next][0] * path[i][1]) - (path[i][0] * path[next][1]));
return accum / 2;
}
function curve4(x1, y1, x2, y2, x3, y3, x4, y4, nSteps) {
var pointList = new Array();
var dx1 = x2 - x1;
var dy1 = y2 - y1;
var dx2 = x3 - x2;
var dy2 = y3 - y2;
var dx3 = x4 - x3;
var dy3 = y4 - y3;
var subdiv_step = 1 / (nSteps + 1);
var subdiv_step2 = subdiv_step * subdiv_step;
var subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
var pre1 = 3 * subdiv_step;
var pre2 = 3 * subdiv_step2;
var pre4 = 6 * subdiv_step2;
var pre5 = 6 * subdiv_step3;
var tmp1x = (x1 - (x2 * 2)) + x3;
var tmp1y = (y1 - (y2 * 2)) + y3;
var tmp2x = (((x2 - x3) * 3) - x1) + x4;
var tmp2y = (((y2 - y3) * 3) - y1) + y4;
var fx = x1;
var fy = y1;
var dfx = ((x2 - x1) * pre1) + (tmp1x * pre2) + (tmp2x * subdiv_step3);
var dfy = ((y2 - y1) * pre1) + (tmp1y * pre2) + (tmp2y * subdiv_step3);
var ddfx = (tmp1x * pre4) + (tmp2x * pre5);
var ddfy = (tmp1y * pre4) + (tmp2y * pre5);
var dddfx = tmp2x * pre5;
var dddfy = tmp2y * pre5;
var step = nSteps;
pointList.push([x1, y1]);
while (step--) {
fx += dfx;
fy += dfy;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
pointList.push([fx, fy]);
}
return pointList;
}
function Triangulate(m_points, holes) {
var indices = new Array();
if (holes.length) {
for (var hh = 0; hh < holes.length; hh += 1) {
var h = holes[hh];
var miny = 0;
for (var i = 1; i < h.length; i += 1) {
if (h[i][1] < h[miny][1]) {
miny = i;
}
}
var closestpt = 0;
var closestd = distanceFromPointToPoint(h[miny], m_points[closestpt]);
for (var i = 1; i < m_points.length; i += 1) {
var d = distanceFromPointToPoint(h[miny], m_points[i]);
if (d < closestd) {
closestd = d;
closestpt = i;
}
}
m_points.splice(closestpt, 0, [m_points[closestpt][0], m_points[closestpt][1] + 0.05]);
closestpt++;
h.splice(miny, 0, [h[miny][0], h[miny][1]]);
h[miny][1] += 0.05;
for (var i = miny; i >= 0; i--) {
m_points.splice(closestpt, 0, h[i]);
}
for (var i = h.length - 1; i > miny; i--) {
m_points.splice(closestpt, 0, h[i]);
}
}
}
var n = m_points.length;
if (n < 3) {
return indices;
}
var V = new Array(n);
if (Area(m_points) > 0) {
for (var v = 0; v < n; v += 1) {
V[v] = v
}
} else {
for (var v = 0; v < n; v += 1) {
V[v] = (n - 1) - v;
}
}
var nv = n;
var count = 2 * nv;
var m = 0;
var v = nv - 1;
for (; nv > 2;) {
if (count-- <= 0) {
return indices;
}
var u = v;
if (nv <= u) {
u = 0;
}
v = u + 1;
if (nv <= v) {
v = 0;
}
var w = v + 1;
if (nv <= w) {
w = 0;
}
if (Snip(u, v, w, nv, V, m_points)) {
a = V[u];
b = V[v];
c = V[w];
indices.push(a);
indices.push(b);
indices.push(c);
m++;
for (s = v, t = v + 1; t < nv; s++, t++) {
V[s] = V[t];
}
nv--;
count = 2 * nv;
}
}
indices.reverse();
return indices;
}
function Area(m_points) {
var n = m_points.length;
var A = 0;
var p = n - 1;
var q = 0;
for (; q < n; p = q++) {
var pval = m_points[p];
var qval = m_points[q];
A += ((pval[0] * qval[1]) - (qval[0] * pval[1]));
}
return A * 0.5;
}
function Snip(u, v, w, n, V, m_points) {
var A = m_points[V[u]];
var B = m_points[V[v]];
var C = m_points[V[w]];
if (Math_Epsilon > (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0])))) {
return false;
}
for (var p = 0; p < n; p += 1) {
if (p == u || p == v || p == w) {
continue;
}
var P = m_points[V[p]];
if (InsideTriangle(A, B, C, P)) {
return false;
}
}
return true;
}
function InsideTriangle(A, B, C, P) {
ax = C[0] - B[0];
ay = C[1] - B[1];
bx = A[0] - C[0];
by = A[1] - C[1];
cx = B[0] - A[0];
cy = B[1] - A[1];
apx = P[0] - A[0];
apy = P[1] - A[1];
bpx = P[0] - B[0];
bpy = P[1] - B[1];
cpx = P[0] - C[0];
cpy = P[1] - C[1];
aCROSSbp = (ax * bpy) - (ay * bpx);
cCROSSap = (cx * apy) - (cy * apx);
bCROSScp = (bx * cpy) - (by * cpx);
return aCROSSbp >= 0 && bCROSScp >= 0 && cCROSSap >= 0;
}
function plotRandomPoint(triangle) {
Ax = triangle[0][0];
Ay = triangle[0][1];
Bx = triangle[1][0];
By = triangle[1][1];
Cx = triangle[2][0];
Cy = triangle[2][1];
a = Math.random();
b = Math.random() * (1 - a);
c = (1 - a) - b;
Px = (a * Ax) + (b * Bx) + (c * Cx);
Py = (a * Ay) + (b * By) + (c * Cy);
app.activeDocument.pathItems.ellipse(Py + 1, Px - 1, 2, 2);
}
function getRandomPoint(triangle) {
Ax = triangle[0][0];
Ay = triangle[0][1];
Bx = triangle[1][0];
By = triangle[1][1];
Cx = triangle[2][0];
Cy = triangle[2][1];
do {
a = Math.random();
b = Math.random();
} while ((a + b) >= 1)
c = (1 - a) - b;
Px = (a * Ax) + (b * Bx) + (c * Cx);
Py = (a * Ay) + (b * By) + (c * Cy);
return [Px, Py];
}
function getCircleThru(v1, v2, v3) {
var x1 = v1[0];
var y1 = v1[1];
var x2 = v2[0];
var y2 = v2[1];
var x3 = v3[0];
var y3 = v3[1];
var s = 0.5 * (((x2 - x3) * (x1 - x3)) - ((y2 - y3) * (y3 - y1)));
var sUnder = ((x1 - x2) * (y3 - y1)) - ((y2 - y1) * (x1 - x3));
if (Math.abs(sUnder) < 0.001) {
return null;
}
s /= sUnder;
var xc = (0.5 * (x1 + x2)) + (s * (y2 - y1));
var yc = (0.5 * (y1 + y2)) + (s * (x1 - x2));
var radius = Math.sqrt(((xc - x1) * (xc - x1)) + ((yc - y1) * (yc - y1)));
return [xc, yc, radius];
}
function distanceToClosestEdge(pt, edgelist) {
d = ClosestPointOnLine(pt, [edgelist[0][0], edgelist[0][1]]);
d = d[1];
for (var p = 1; p < edgelist.length; p += 1) {
d2 = ClosestPointOnLine(pt, [edgelist[p][0], edgelist[p][1]]);
if (d2[1] < d) {
d = d2[1];
}
}
return d;
}
function ClosestPointOnLine(pt, line) {
var X1 = line[0][0];
var Y1 = line[0][1];
var X2 = line[1][0];
var Y2 = line[1][1];
var px = pt[0];
var py = pt[1];
var dx = X2 - X1;
var dy = Y2 - Y1;
if (dx == 0 && dy == 0) {
nx = X1;
ny = Y1;
} else {
var t = (((px - X1) * dx) + ((py - Y1) * dy)) / ((dx * dx) + (dy * dy));
if (t <= 0) {
nx = X1;
ny = Y1;
} else if (t >= 1) {
nx = X2;
ny = Y2;
} else {
nx = X1 + (t * dx);
ny = Y1 + (t * dy);
}
}
dx = px - nx;
dy = py - ny;
return [[nx, ny], Math.sqrt((dx * dx) + (dy * dy))];
}
function point(arr) {
this.x = arr[0];
this.y = arr[1];
this.distance = function (pt) {
return Math.sqrt(((this.x - pt.x) * (this.x - pt.x)) + ((this.y - pt.y) * (this.y - pt.y)));
};
}
function saveSettings() {
var $file = new File(settingFile.folder + settingFile.name);
var data = [maxValue.text, minValue.text, guttersValue.text, resizeValue.text, rotateValue.text, randomRotate.value, rotateByValue.value, objectPosTop.value, objectPosBottom.value, objectPosLayers.value, randomItems.value, removeTopElement.value, groupResult.value].toString();
$file.open("w");
$file.write(data);
$file.close();
}
function loadSettings() {
var $file = File(settingFile.folder + settingFile.name);
if ($file.exists) {
try {
$file.open("r");
var data = $file.read().split("\n");
var $main = data[0].split(",");
maxValue.text = $main[0];
minValue.text = $main[1];
guttersValue.text = $main[2];
resizeValue.text = $main[3];
rotateValue.text = $main[4];
randomRotate.value = $main[5] === "true";
rotateByValue.value = $main[6] === "true";
objectPosTop.value = $main[7] === "true";
objectPosBottom.value = $main[8] === "true";
objectPosLayers.value = $main[9] === "true";
randomItems.value = $main[10] === "true";
removeTopElement.value = $main[11] === "true";
groupResult.value = $main[12] === "true";
rotateValue.enabled = !randomRotate.value;
} catch (e) {
}
$file.close();
}
}
win.onClose = function () {
saveSettings();
return true;
};
function checkSettingFolder() {
var $folder = new Folder(settingFile.folder);
if (!$folder.exists) {
$folder.create()
}
}
checkSettingFolder();
loadSettings();
win.center();
win.show();
}
3.源码下载
源码已经上传至CSDN,欢迎下载:https://download.csdn.net/download/m0_67316550/83063370
4.实现效果
需要选择所有轮廓,小的轮廓填充在大的轮廓中。如下图所示:
5.作者寄语
合理的脚本代码可以有效的提高工作效率,减少重复劳动。
文章引用至 作者知了-联系方式1
文章引用至 作者知了-联系方式2
更多推荐
已为社区贡献1条内容
所有评论(0)