Adobe Illustrator CC 合并触摸锚点

平面设计 adobe-illustrator 锚点
2022-01-29 15:08:37

我有一个包含涂鸦的矢量文件,其中的线条不包含几个锚点,但每个片段本身都是一条自己的线条。

图像中心的蓝线本身就是一个自己的元素

我需要连接这些单独的小线以形成包含多个锚点的单线,就像通常一样。问题是,文件中有太多行无法手动连接它们,并且“连接”功能也总是连接不应该连接的行。

是否有一个脚本连接 Illustrator 中完全相同位置的线/锚点?

2个回答

加入具有重叠点的路径

此脚本已于 2017 年 2 月 7 日更新,具有一些新功能。

特征:

  1. 杂散点被移除。
  2. 路径内的分割点被合并。
  3. 具有重叠点的开放路径被连接起来。

只有选定的路径会受到影响。
支持组,不支持复合路径。
对于重叠路径,适用重叠容差。

如何使用:

  • 将以下代码复制到文本编辑器并将其保存为 .js 文件(例如join_paths_with_overlapping_points.js)。
  • 在 illustrator 中,选择要清理的路径(或组)。
  • 选择File/Scripts/Other Script,然后打开脚本。
  • 按照说明进行操作。

// JOIN PATHS WITH OVERLAPPING POINTS
// Copyright (c) 2017 Mads Wolff
// This script is distributed under the MIT License.

#target illustrator

alert((function () {

  alert(
    "JOIN PATHS WITH OVERLAPPING POINTS\n" +
    "Copyright (c) 2017 Mads Wolff\n" +
    "This script is distributed under the MIT License.\n" +
    "Use at your own risk.\n\n\n" +
    "FEATURES:\n\n" +
    "1. Stray points are removed.\n" +
    "2. Split points within paths are merged.\n" +
    "3. Open paths with overlapping points are joined.\n\n" +
    "Only selected paths are affected.\n" +
    "Groups are supported, compound paths are not.\n" +
    "For overlapping paths an overlap tolerance applies.\n\n\n" +
    "HOW TO USE:\n\n" +
    "Press OK, select an overlap tolerance (in points),\n" +
    "press OK again (or CANCEL).\n"
  );

  // ---

  var doc = app.activeDocument;

  // ---

  if (doc.selection.length === 0) return "JOIN PATHS WITH OVERLAPPING POINTS\n\nABORT: There is no selection.";
  var tolerance = prompt("JOIN PATHS WITH OVERLAPPING POINTS\n\nEnter overlap tolerance (in points):", 0);
  if (tolerance === null) return "JOIN PATHS WITH OVERLAPPING POINTS\n\nABORT: Script execution cancelled.";
  if (!(tolerance >= 0)) return "JOIN PATHS WITH OVERLAPPING POINTS\n\nABORT: The overlap tolerance must be a number of points equal to or larger than 0.";

  // ---

  var numberOfPoints = 0;
  var numberOfStrayPoints = 0;
  var numberOfOpenPaths = 0;
  var numberOfClosedPaths = 0;
  var numberOfSplitPointsMerged = 0;
  var numberOfOverlappingPathsJoined = 0;
  var numberOfResultingPaths = 0;

  // ---

  function execute(itemList) {

    itemList = Array.prototype.slice.call(itemList);

    var items = [];
    var overlapClusters = [];

    for (var i = 0; i < itemList.length; i++) {
      var item = itemList[i];
      if (item.typename === "GroupItem") {
        execute(item.pageItems);
      } else if (item.typename === "PathItem") {
        if (item.pathPoints.length > 1) {
          var points = Array.prototype.slice.call(item.pathPoints);
          for (var j = 0; j < points.length; j++) {
            var point = points[j];
            var nextPoint = points[j + ((!item.closed || (j + 1 < points.length)) ? 1 : 1 - points.length)];
            if (nextPoint) {
              if (
                point.anchor[0] === nextPoint.anchor[0] &&
                point.anchor[1] === nextPoint.anchor[1] &&
                point.rightDirection[0] === point.anchor[0] &&
                point.rightDirection[1] === point.anchor[1] &&
                nextPoint.leftDirection[0] === point.anchor[0] &&
                nextPoint.leftDirection[1] === point.anchor[1]
              ) {
                nextPoint.leftDirection = point.leftDirection;
                point.remove();
                numberOfSplitPointsMerged++;
              }
            }
            numberOfPoints++;
          }
        }
        if (item.pathPoints.length === 1) {
          item.remove();
          numberOfStrayPoints++;
          numberOfOpenPaths++;
        } else if (!item.closed) {
          items.push(item);
          numberOfOpenPaths++;
        } else {
          numberOfClosedPaths++;
        }
      }
    }

    numberOfOpenPaths += items.length;

    for (var i = 0; i < items.length; i++) {
      var itemA = items[i];
      var pointsA = itemA.pathPoints;
      for (var j = 0; j < pointsA.length; j++) {
        var pointA = pointsA[j];
        for (var k = i + 1; k < items.length; k++) {
          var itemB = items[k];
          var pointsB = itemB.pathPoints;
          for (var l = 0; l < pointsB.length; l++) {
            var pointB = pointsB[l];
            var overlap = tolerance === 0 ?
              (pointA.anchor[0] === pointB.anchor[0] && pointA.anchor[1] === pointB.anchor[1]) :
              (tolerance >= Math.sqrt(Math.pow(pointA.anchor[0] - pointB.anchor[0], 2) + Math.pow(pointA.anchor[1] - pointB.anchor[1], 2)));
            if (overlap) {
              if (tolerance > 0) {
                var d0 = (pointA.anchor[0] - pointB.anchor[0]) / 2;
                var d1 = (pointA.anchor[1] - pointB.anchor[1]) / 2;
                pointA.anchor = [pointA.anchor[0] - d0, pointA.anchor[1] - d1];
                pointA.leftDirection = [pointA.leftDirection[0] - d0, pointA.leftDirection[1] - d1];
                pointA.rightDirection = [pointA.rightDirection[0] - d0, pointA.rightDirection[1] - d1];
                pointB.anchor = [pointB.anchor[0] + d0, pointB.anchor[1] + d1];
                pointB.leftDirection = [pointB.leftDirection[0] + d0, pointB.leftDirection[1] + d1];
                pointB.rightDirection = [pointB.rightDirection[0] + d0, pointB.rightDirection[1] + d1];
              }
              if (itemA.overlapCluster === undefined) {
                if (itemB.overlapCluster === undefined) {
                  itemA.overlapCluster = [];
                  itemB.overlapCluster = itemA.overlapCluster;
                  itemA.overlapCluster.push(itemA);
                  itemA.overlapCluster.push(itemB);
                  overlapClusters.push(itemA.overlapCluster);
                } else {
                  itemA.overlapCluster = itemB.overlapCluster;
                  itemA.overlapCluster.push(itemA);
                }
              } else {
                itemB.overlapCluster = itemA.overlapCluster;
                itemA.overlapCluster.push(itemB);
              }
            }
          }
        }
      }
    }

    for (var i = 0; i < overlapClusters.length; i++) {
      var overlapCluster = overlapClusters[i];
      doc.selection = overlapCluster;
      numberOfOverlappingPathsJoined += doc.selection.length;
      numberOfResultingPaths++;
      app.executeMenuCommand("join");
      var joinedItem = doc.selection[0];
      delete joinedItem.overlapCluster;
    }

  }

  // ---

  execute(doc.selection);

  // ---

  doc.selection = [];

  // ---

  return "JOIN PATHS WITH OVERLAPPING POINTS\n\n" +
    numberOfPoints + " points in " + (numberOfOpenPaths + numberOfClosedPaths) + " paths ("+ numberOfOpenPaths +" open and " + numberOfClosedPaths + " closed) were processed.\n\n" +
    numberOfStrayPoints + " stray points were removed.\n" +
    numberOfSplitPointsMerged + " split points were merged.\n" +
    numberOfOverlappingPathsJoined + " paths with overlapping points were joined to " + numberOfResultingPaths + " paths.";

})());

试试这个脚本,它通过连接从终点到起点的路径来工作,但不适用于具有相同起点和终点的路径。但是,由于连接路径会更改路径计数,因为它将两条路径合并为一条,因此在此“连接点”算法期间,起点/终点可能会发生变化。

#target illustrator
function test(){
  function overlapPointExists(){
    var doc = app.activeDocument, thisPath, firstPt, lastPt, nextPath, firstPt_2, lastPt_2;
    for(var i=0; i < doc.pathItems.length; i++){
      thisPath = doc.pathItems[i];
      if(!thisPath.closed){
        firstPt = thisPath.pathPoints[0];
        lastPt = thisPath.pathPoints[thisPath.pathPoints.length - 1];
        for(var j=0; j < doc.pathItems.length; j++){
          if(i != j){
            nextPath = doc.pathItems[j];
            firstPt_2 = nextPath.pathPoints[0];
            lastPt_2 = nextPath.pathPoints[nextPath.pathPoints.length - 1];
            if(lastPt.anchor[0] == firstPt_2.anchor[0] && lastPt.anchor[1] == firstPt_2.anchor[1]){
              lastPt.selected = PathPointSelection.ANCHORPOINT;
              firstPt_2.selected = PathPointSelection.ANCHORPOINT;
              return true;
            }
          }
        };
      }
    };
    doc.selection = null;
    return false;
  };
  while(overlapPointExists()){
    app.executeMenuCommand("join");
    app.activeDocument.selection = null;
  }
  app.activeDocument.selection = null;
};
test();

在此处输入图像描述

编辑:当文档中不只有几个路径时,脚本太慢了。所以这是一个基于路径选择的工作。它的工作原理是您首先选择单独和未分组的路径,然后将它们分组到一个临时组中(如果项目不是连续顺序,堆叠顺序可能会改变),并且它使用该组作为隔离容器仅执行在其中的路径上。完成后,该组将被取消分组,并且一个警报框会告诉您这一切花了多长时间。当它变得烦人时,注释掉或删除这个警报。

#target illustrator
function test(){
  function overlapPointExists(pathCollection){
    var doc = app.activeDocument, thisPath, firstPt, lastPt, nextPath, firstPt_2, lastPt_2;
    for(var i=0; i < pathCollection.pathItems.length; i++){
      thisPath = pathCollection.pathItems[i];
      if(!thisPath.closed){
        firstPt = thisPath.pathPoints[0];
        lastPt = thisPath.pathPoints[thisPath.pathPoints.length - 1];
        for(var j=0; j < pathCollection.pathItems.length; j++){
          if(i != j){
            nextPath = pathCollection.pathItems[j];
            firstPt_2 = nextPath.pathPoints[0];
            lastPt_2 = nextPath.pathPoints[nextPath.pathPoints.length - 1];
            if(lastPt.anchor[0] == firstPt_2.anchor[0] && lastPt.anchor[1] == firstPt_2.anchor[1]){
              lastPt.selected = PathPointSelection.ANCHORPOINT;
              firstPt_2.selected = PathPointSelection.ANCHORPOINT;
              return true;
            }
          }
        };
      } else {
        return false;
      }
    };
    doc.selection = null;
    return false;
  };
  if(app.activeDocument.selection == null || app.activeDocument.selection.length == 0){
    alert("Please select something first.");
    return;
  }
  var doc = app.activeDocument;
  var start = (new Date().getTime());
  var sel = doc.selection;
  app.executeMenuCommand("group");
  var pathGroup = sel[0].parent;
  pathGroup.selected = false;

  while(overlapPointExists(pathGroup)){
    app.redraw();
    app.executeMenuCommand("join");
    doc.selection = null;
  }
  pathGroup.selected = true;
  app.executeMenuCommand("ungroup");
  doc.selection = null;
  var end = (new Date().getTime());
  alert("Process took " + (end - start) / 1000 + " seconds.");
};
test();