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

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐