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
  }
}
1 Like

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
}
2 Likes

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:

1 Like

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

@Jarda would you mind elaborating on the TypeIDs that you refer to here?

It is low-level stuff. In javascript you do use strings for the “properties” names. E.g. “paint” or “point” and that is fine. But Photoshop is in C++ and it uses numbers for properties names instead of strings. Usually this is an internal thing and in UXP you don’t have to worry about it at all. Therefore each property is translated into number and both “paint” and “point” are translated into same number meaning they have same value and meaning in PS action manager code.

1 Like