How to draw a path?

Hi there,

I am using PS 22.3, and do not see how we can create a path (brush, pencil, path) using batchPlay method. I see there are some models coming from the PS, but there are no information about coordinates, so I cannot just send command and create a path, I cannot find any example of how to do it.

I found near topic and the repo: photoshop-types/Path.d.ts at main · simonhenke/photoshop-types · GitHub

But it does not work either, I created model by these typings, and PS returns the error like “You should specify “descriptor” field” (I’ll update this topic later with exact text).

How to send a command to create path using brush/pencil/path tool?
Can somebody show real example of the command, please?

What I am receiving:

Event toolModalStateChanged {
“level”: 1,
“state”: {
“_enum”: “state”,
“_value”: “enter”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 0,
“state”: {
“_enum”: “state”,
“_value”: “exit”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event historyStateChanged {
“documentID”: 219,
“ID”: 231,
“name”: “New Work Path”,
“hasEnglish”: true,
“itemIndex”: 3,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 1,
“state”: {
“_enum”: “state”,
“_value”: “enter”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 0,
“state”: {
“_enum”: “state”,
“_value”: “exit”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event historyStateChanged {
“documentID”: 219,
“ID”: 232,
“name”: “New Anchor Point”,
“hasEnglish”: true,
“itemIndex”: 4,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 1,
“state”: {
“_enum”: “state”,
“_value”: “enter”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 0,
“state”: {
“_enum”: “state”,
“_value”: “exit”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event historyStateChanged {
“documentID”: 219,
“ID”: 233,
“name”: “New Anchor Point”,
“hasEnglish”: true,
“itemIndex”: 5,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 1,
“state”: {
“_enum”: “state”,
“_value”: “enter”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 0,
“state”: {
“_enum”: “state”,
“_value”: “exit”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event historyStateChanged {
“documentID”: 219,
“ID”: 235,
“name”: “New Anchor Point”,
“hasEnglish”: true,
“itemIndex”: 6,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 1,
“state”: {
“_enum”: “state”,
“_value”: “enter”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event toolModalStateChanged {
“level”: 0,
“state”: {
“_enum”: “state”,
“_value”: “exit”
},
“tool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“selectedTool”: {
“_obj”: “tool”,
“ID”: “penT”,
“title”: “Инструмент “Перо””
},
“kind”: {
“_enum”: “kind”,
“_value”: “mouse”
},
“kcanDispatchWhileModal”: true,
“_isCommand”: false
}
/ge.adobe.ps.assets.…0421f804445456.js:2 Event historyStateChanged {
“documentID”: 219,
“ID”: 236,
“name”: “Close Path”,
“hasEnglish”: true,
“itemIndex”: 7,
“_isCommand”: false
}

Then I tried command:
{
ID: 10,
count: 20,
itemIndex: 30,
kind: {
_enum: ‘pathKind’,
_value: ‘normalPath’
},
pathContents: {
_obj: ‘pathClass’,
pathComponents: [
{
_obj: ‘pathComponent’,
shapeOperation: {
_enum: ‘shapeOperation’,
_value: ‘add’
},
subpathListKey: [
{
_obj: ‘subpathsList’,
closedSubpath: false,
points: [
{
_obj: ‘pathPoint’,
anchor: {
_obj: ‘point’,
horizontal: 10,
vertical: 10
},
backward: {
_obj: ‘point’,
horizontal: 100,
vertical: 100
},
forward: {
_obj: ‘point’,
horizontal: 200,
vertical: 300
},
smooth: false
}
]
}
],
windingFill: false
}
]
},
pathName: ‘Test’,
targetPath: false
}

And response is:
“error Error: Invalid command Property ‘descriptor’ is missing”

Maybe options are wrong, because I have no idea what should be in some properties using only TS interface, so can somebody send an example of the command how to draw path (brush/pencil/path)?

@simonhenke

Hi there,
I’ve the same question: I would like to draw a path by defining its points using UXP or batchPlay.
Unfortunately the Alchemist plugin (which is brilliant!) does not record something if I draw a patch.
Does someone have a idea how to do this? The code from Simon looks promising that this might be possible.

A workaround for others with the same question: If you want to create a “closed” path, you can first create a selection. Then execute “make work path from selection” to convert the selection into a path.
Both actions can be recorded with Alchemist and played with batchPlay.
Unfortunately this does not work for me, because I need a “open” path, and selections can’t be open…

Kind Regards
Jens

You can simply assemble the path programmatically and set it via

function __setWorkPath(components: PathComponentDescriptor[]): ActionDescriptor {
  return {
    _obj: 'set',
    _target: [
      {
        _ref: 'path',
        _property: 'workPath',
      },
    ],
    to: components
  }
}

Hi Simon,
thanks for your quick reply!
Basically I’m struggling with how to assemble the path programmatically.
Unfortunately I can’t find the way to do that in the UXP API and the Alchemist plugin.
Do you know how to create a path e.g. between point1 (X 100px | Y 100px) and point2 (X 200px | Y 200px) using batchPlay (or UXP)?

Kind Regards
Jens

I believe Path.ts holds all the info you need. Just follow the types. In Simons example components is an array of PathComponentDescriptors

@Karmalakas is right :+1:

You can make your life easier by writing some helper functions to construct the individual parts, such as

function _pathPoint(
  anchor: Point,
  forward?: Point,
  backward?: Point
): PathPointDescriptor {
  return {
    _obj: 'pathPoint',
    anchor: _pointDesc(anchor),
    ...(forward ? { forward: _pointDesc(forward) } : {}),
    ...(backward ? { backward: _pointDesc(backward) } : {}),
  }
}

function _pointDesc(point: Point): UVPointDescriptor {
  return {
    _obj: 'paint',
    horizontal: {
      _unit: 'pixelsUnit',
      _value: point.x,
    },
    vertical: {
      _unit: 'pixelsUnit',
      _value: point.y,
    },
  }
}

and so on, so that you can start with a data structure of a simple point like

interface Point {
  x: number
  y: number
}
1 Like

Thanks guys!
Since I only needed to draw a path between two points I’ve now figured out that the “Line Tool” can be recorded with Alchemist and returns what I was searching for:

  {
      "_obj": "set",
      "_target": [
         {
            "_ref": "path",
            "_property": "workPath"
         }
      ],
      "to": {
         "_obj": "lineClass",
         "saturation": {
            "_obj": "paint",
            "horizontal": {
               "_unit": "pixelsUnit",
               "_value": x_point1
            },
            "vertical": {
               "_unit": "pixelsUnit",
               "_value": y_point1
            }
         },
         "end": {
            "_obj": "paint",
            "horizontal": {
               "_unit": "pixelsUnit",
               "_value": x_point2
            },
            "vertical": {
               "_unit": "pixelsUnit",
               "_value": y_point2
            }
         },

Wonder if using start instead of saturation would do any difference. I suspect it’s the same as with green and grain :slightly_smiling_face:

You could verify that by comparing its TypeIDs if they both have the same number then it doesn’t matter.