• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

How to add anchor points to paths with js script

Community Beginner ,
Apr 22, 2023 Apr 22, 2023

Copy link to clipboard

Copied

Add a new anchor point to the left or right of the selected anchor point, the new anchor point is 30px away from the original anchor point, can it be achieved with jsx? Because I often need to do this.
Thank you all

2023-04-22_171727.png

TOPICS
Scripting

Views

4.2K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Guide , May 04, 2023 May 04, 2023

This should accommodate end points and closed paths.

/*
 * "Add an anchor point to a non-curved path" by Femke Blanco
 * 04/05/2023
 * Instructions: 
 * - select ONE point with the white arrow tool (A);
 * - to add a point after the selected point, enter a (+) number;
 * - to add a point before the selected point, enter a (-) number.
 */

var input = prompt("", "30");
var before = [];
var after = [];
var swap, i, addedPoint, array;

try {
    var path1 = app.activeDocument.selection[0];
    var p
...

Votes

Translate

Translate
Engaged ,
Apr 22, 2023 Apr 22, 2023

Copy link to clipboard

Copied

Yes, it's possible. The only catch is that if you want to add a point between already existing points on the path you essentially have to recreate the path via your script up until the new point. If you just append a point to your path like described in the API docs (see here), it will be added to the end of the path.

 

Below is some EXPERIMENTAL code I was playing with a while back for splicing paths. PLEASE NOTE, if your path has any curves you would need to calculate updated handle points (left/right direction) which is doable using an algorithm like De Casteljau's algorithm.

 

Hopefully, you can get what you need from this. Cheers!

 

function PathPointPlus(obj) {
  this.self = obj;
  for (prop in obj) {
    try {
      this[prop] = obj[prop];
    } catch (e) {
      this[prop] = e;
    }
  }
  // allow user to specify only an anchor point when creating custom points
  if (!this.hasOwnProperty("leftDirection")) this.leftDirection = this.anchor;
  if (!this.hasOwnProperty("rightDirection")) this.rightDirection = this.anchor;
  if (!this.hasOwnProperty("pointType")) this.pointType = PointType.CORNER;
}

PathPointPlus.prototype = {
  addToPath: function (parentPath, idx) {
    // add to end of path if index not specified
    idx = idx !== undefined ? idx : parentPath.pathPoints.length;
    // prompt for index for example purposes
    // idx = prompt("What index?", idx);
    // validate provided index
    if (idx < 0 || idx > parentPath.pathPoints.length) {
      alert(
        "Invalid insertion index (`idx`).\n0 <= idx <= " + parentPath.pathPoints.length
      );
      return;
    }
    // remove all points after idx
    var removedPoints = [];
    if (idx < parentPath.pathPoints.length) {
      for (var i = parentPath.pathPoints.length - 1; i >= idx; i--) {
        removedPoints.push(new PathPointPlus(parentPath.pathPoints[i]));
        if (i == 0) {
          parentPath.pathPoints[i].anchor = this.anchor;
          parentPath.pathPoints[i].leftDirection = this.rightDirection;
          parentPath.pathPoints[i].rightDirection = this.leftDirection;
          parentPath.pathPoints[i].pointType = this.pointType;
        } else {
          parentPath.pathPoints[i].remove();
        }
      }
    }
    // add the points back
    if (idx > 0) removedPoints.push(this);
    // add the new point plus the removed points
    var newPoint;
    for (var i = removedPoints.length - 1; i >= 0; i--) {
      newPoint = parentPath.pathPoints.add();
      newPoint.anchor = removedPoints[i].anchor;
      if (removedPoints[i].hasOwnProperty("leftDirection"))
        newPoint.leftDirection = removedPoints[i].rightDirection;
      if (removedPoints[i].hasOwnProperty("rightDirection"))
        newPoint.rightDirection = removedPoints[i].leftDirection;
      if (removedPoints[i].hasOwnProperty("pointType"))
        newPoint.pointType = removedPoints[i].pointType;
    }
  },
};

function PathItemPlus(obj) {
  this.self = obj;
  for (prop in obj) {
    try {
      this[prop] = obj[prop];
    } catch (e) {
      this[prop] = e;
    }
  }
}

PathItemPlus.prototype = {
  /**
   * The splice() method changes the PathPoints of a PathItem by removing
   * or replacing existing PathPoints and/or adding new PathPoints in place.
   *
   * splice(start, deleteCount, item1, item2, itemN)
   *
   * @Param   {Number}    start       Zero-based index at which to start replacing or removing PathPoints.
   * @Param   {Number}    deleteCount An integer indicating the number of PathPoints in the PathItem to remove from start.
   * @Param   {PathPoint} item...     The PathPoints elements to add to the PathItem, beginning from start.
   * @Returns {Array}                 An array containing the deleted PathPoints.
   */
  splice: function (start, deleteCount) {
    var arr = [];
    var resultArr = [];

    start = start !== undefined ? Number(start) : 0;

    // delete entire path if start = 0
    if (start === 0 && deleteCount === undefined) {
      for (var i = 0; i < this.pathPoints.length; i++)
        resultArr.push(new PathPointPlus(this.pathPoints[i]));
      this.self.remove();
      return resultArr;
    }

    // append items to end if start if > pathPoints
    if (start > this.pathPoints.length) {
      start = this.pathPoints.length;
      end = this.pathPoints.length;
      deleteCount = 0;
    }

    // if start is a negative number
    if (start < -this.pathPoints.length) {
      start = 0;
    } else if (start < 0) {
      start = start + this.pathPoints.length;
    }

    deleteCount = deleteCount !== undefined ? Number(deleteCount) : 0;
    if (deleteCount >= this.pathPoints.length - start) {
      deleteCount = this.pathPoints.length - start;
    } else if (deleteCount === undefined) {
      deleteCount = 0;
    }

    // grab all items to splice (if any)
    var items = [];
    for (var i = 2; i < arguments.length; i++) items.push(arguments[i]);

    var stop = this.pathPoints.length;

    // if deleteCount is defined
    if (deleteCount) {
      // we add it to start. This gives us the index we will stop in the data array.
      stop = start + deleteCount;
      // if deleteCount is negative, we splice no array
      if (deleteCount < 0) stop = 0;
    }

    // cut out the deleted points
    for (var i = start; i < stop; i++) {
      resultArr.push(new PathPointPlus(this.pathPoints[i]));
    }

    // if deleteCount is defined but items is empty
    if (deleteCount && items.length <= 0) {
      for (var i = 0; i < this.pathPoints.length; i++) {
        if (i === start) {
          i = stop;
        } else {
          arr.push(new PathPointPlus(this.pathPoints[i]));
        }
      }
    }

    // if there is something in the items array
    if (items.length > 0) {
      for (var i = 0; i < this.pathPoints.length; i++) {
        if (i === start && arr.length < start + items.length) {
          arr = arr.concat(items);
          i = deleteCount > 0 ? stop : i - 1;
        } else {
          arr.push(new PathPointPlus(this.pathPoints[i]));
        }
      }
    }

    // actually delete the points from the path
    if (deleteCount > 0) {
      for (var i = 0; i < resultArr.length; i++) {
        if (resultArr[i].self.hasOwnProperty("remove")) resultArr[i].self.remove();
      }
    }

    // remove all points (except first)
    for (var i = 1; i < this.pathPoints.length; i++) {
      this.pathPoints[i].remove();
    }

    // add the updated points
    if (arr) {
      for (var i = start; i < arr.length; i++) {
        if (i === 0) {
          this.pathPoints[i].anchor = arr[i].anchor;
          if (arr[i].hasOwnProperty("leftDirection"))
            this.pathPoints[i].leftDirection = arr[i].leftDirection;
          if (arr[i].hasOwnProperty("rightDirection"))
            this.pathPoints[i].rightDirection = arr[i].rightDirection;
          if (arr[i].hasOwnProperty("pointType"))
            this.pathPoints[i].pointType = arr[i].pointType;
        } else {
          arr[i].addToPath(this, this.pathPoints.length);
        }
      }
    }

    return resultArr;
  },
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Apr 22, 2023 Apr 22, 2023

Copy link to clipboard

Copied

This is a quick attempt intended for non-curved lines. To Use: (1) select one point (with the white arrow tool); (2) to add a point after, use a positive number; to add a point before, use a negative number. (The script will fail if you try to add before the first point or after the last point.)

var input = prompt("", "30");
var before = [];
var after = [];
var swap = false;
var j;

var path1 = app.activeDocument.selection[0];
var points = path1.pathPoints;
for (var i = 0; i < points.length; i++) {
    if (points[i].selected == PathPointSelection.ANCHORPOINT) {
        swap = true;
        j = i;
        continue;
    }
    if (swap) {
        after.push([points[i].anchor[0], points[i].anchor[1]]);
    } else {
        before.push([points[i].anchor[0], points[i].anchor[1]]);
    }
}

var addedPoint, array;
if (input > 0) {
    addedPoint = getAddedPoint(points[j], points[j + 1], input);
    array = before.concat(
        [[points[j].anchor[0], points[j].anchor[1]], addedPoint])
} else {
    addedPoint = getAddedPoint(points[j], points[j - 1], -input);
    array = before.concat(
        [addedPoint, [points[j].anchor[0], points[j].anchor[1]]])
}
array = array.concat(after);

var path2 = app.activeDocument.pathItems.add();
path2.setEntirePath(array);
path2.selected = true;
path1.remove();

function getAddedPoint(p1, p2, h) {
    var x1 = p1.anchor[0];
    var y1 = p1.anchor[1];
    var x2 = p2.anchor[0];
    var y2 = p2.anchor[1];
    var dx = x2 - x1;
    var dy = y2 - y1;
    var a = Math.atan2(dy, dx)
    dx = Math.cos(a) * h;
    dy = Math.sin(a) * h;
    return [x1 + dx, y1 + dy];
}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 04, 2023 May 04, 2023

Copy link to clipboard

Copied

After running the code, the last node is broken

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
May 04, 2023 May 04, 2023

Copy link to clipboard

Copied

This was written with an open path in mind. I'll amend it later to accommodate closed paths.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
May 04, 2023 May 04, 2023

Copy link to clipboard

Copied

This should accommodate end points and closed paths.

/*
 * "Add an anchor point to a non-curved path" by Femke Blanco
 * 04/05/2023
 * Instructions: 
 * - select ONE point with the white arrow tool (A);
 * - to add a point after the selected point, enter a (+) number;
 * - to add a point before the selected point, enter a (-) number.
 */

var input = prompt("", "30");
var before = [];
var after = [];
var swap, i, addedPoint, array;

try {
    var path1 = app.activeDocument.selection[0];
    var points = path1.pathPoints;
    for (var j = 0; j < points.length; j++) {
        if (points[j].selected == PathPointSelection.ANCHORPOINT) {
            swap = true;
            i = j;
            continue;
        }
        if (!swap) {
            before.push([points[j].anchor[0], points[j].anchor[1]]);
        } else {
            after.push([points[j].anchor[0], points[j].anchor[1]]);
        }
    }

    var j;
    if (input > 0) {
        if (i != points.length - 1) {
            j = i + 1;
        } else {
            if (!path1.closed) j = i - 1, input *= -1;
            else j = 0;
        }
        addedPoint = getAddedPoint(points[i], points[j], input);
        array = before.concat(
            [[points[i].anchor[0], points[i].anchor[1]], addedPoint]
        );
    } else {
        if (i != 0) {
            j = i - 1;
        } else {
            if (!path1.closed) j = i + 1, input *= -1;
            else j = points.length - 1;
        }
        addedPoint = getAddedPoint(points[i], points[j], -input);
        array = before.concat(
            [addedPoint, [points[i].anchor[0], points[i].anchor[1]]]
        );
    }
    array = array.concat(after);

    var path2 = app.activeDocument.pathItems.add();
    path2.setEntirePath(array);
    if (path1.closed) path2.closed = true;
    path2.selected = true;
    path1.remove();
} catch (e) {
    alert(e);
}

function getAddedPoint(p1, p2, h/*hypotenuse*/) {
    var x1 = p1.anchor[0];
    var y1 = p1.anchor[1];
    var x2 = p2.anchor[0];
    var y2 = p2.anchor[1];
    var dx = x2 - x1;
    var dy = y2 - y1;
    var a = Math.atan2(dy, dx)
    dx = Math.cos(a) * h;
    dy = Math.sin(a) * h;
    return [x1 + dx, y1 + dy];
}

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
May 04, 2023 May 04, 2023

Copy link to clipboard

Copied

LATEST

Although only one node can be selected for use at a time, thanks!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
May 04, 2023 May 04, 2023

Copy link to clipboard

Copied

@Bryce29110882cdze, this most certainly will not work and smells a bit like it was ChaptGPT generated (same as your other posts here and here). The Illustrator API doesn't allow you to access anchor points like `sel.anchor[0]`, and using `sel.pathPoints.add()` appends a point to the end of the path, it doesn't insert it along the path as I mentioned above. You must recreate the original path with the new point as @femkeblanco does in his code.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines