Adobe Illustrator CC Merge touching Anchor points

I have a vector file containing scribbles, where the lines don’t consist of several anchor points but every segment is an own line by itself.

the blue line in the center of the image is an own element by its own

I need to connect these separate little lines to form single lines containing multiple anchor points as it usually is. The problem is, that there are far too many lines in the file to connect them manually and the “join” function always also connects lines which should not be connected.

Is there a script which only connects lines/anchor points that are at the exact same place in Illustrator?

Answer

JOIN PATHS WITH OVERLAPPING POINTS

This script has been updated Feb 07, 2017 with some new features.

Features:

  1. Stray points are removed.
  2. Split points within paths are merged.
  3. Open paths with overlapping points are joined.

Only selected paths are affected.
Groups are supported, compound paths are not.
For overlapping paths an overlap tolerance applies.

How to use:

  • Copy the code below to a text editor and save it as a .js file (for example join_paths_with_overlapping_points.js).
  • In illustrator, select the paths (or groups) you want to clean up.
  • Select File/Scripts/Other Script, and open the script.
  • Follow the intructions.

// 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.";

})());

Attribution
Source : Link , Question Author : Martin , Answer Author : Community

Leave a Comment