Simulate Click with Brush

Anyone, especially newbies, asking for help with Photoshop Scripting and Photoshop Automation - as opposed to those contributing to discussion about an aspect of Photoshop Scripting

Moderators: Tom, Kukurykus

Grrr1337
Posts: 8
Joined: Fri Nov 01, 2019 3:40 pm

Simulate Click with Brush

Post by Grrr1337 »

Hey guys,
The web showed me no clues, while
I was trying to figure out how to simulate a brush click - via function that takes a 'Point' as argument:

Code: Select all

// SimulateBrushClick([200, 200])

function SimulateBrushClick (Pt)
{
  var docRef = app.activeDocument;
  docRef.pathItems.removeAll()
  var arrPathPt = new Array()
  var arrSubPathPt = new Array()

  arrPathPt[0] = new PathPointInfo
  arrPathPt[0].kind = PointKind.CORNERPOINT
  arrPathPt[0].anchor =  Pt
  arrPathPt[0].leftDirection = arrPathPt[0].anchor
  arrPathPt[0].rightDirection = arrPathPt[0].anchor
  
  arrSubPathPt[0] = new SubPathInfo()
  arrSubPathPt[0].operation = ShapeOperation.SHAPEXOR
  arrSubPathPt[0].closed = false
  arrSubPathPt[0].entireSubPath = arrPathPt

  var myPathItem = docRef.pathItems.add("PathItemName", arrSubPathPt);
  myPathItem.strokePath(ToolType.PENCIL) // BRUSH
  
}// function SimulateBrushClick
Then I modified the above function to take an argument of points-array, i.e.: [[10, 15],[20, 20],[40, 10]]
Problem is that if I construct such points array of say 1000 items inside - [x, y] subarrays
The SimulateBrushClicks function becomes too slow, at evaluating

Code: Select all

docRef.pathItems.removeAll() // in order to remove the previous 1000 points
and 
docRef.pathItems.add("PathItemName" + i, arrSubPathPt); // in order to add 1000 named paths.(it even breaks the evaluation on the 999th element)
Problems are:
• the construction of the 1000+ items points array is slow (2-3 minutes), [via PtArr.push(Array(x, y));] // ASIDE PROBLEM
• then the iteration over it and placing the brush clicks (2-3 minutes) - since I literally watch how the script places them one by one, and not instantly placing them.

So my questions are:
• are there any alternatives/possibilites to my aproach - in order to place/click with the brush at given coordinates?
• is this the best method to use? - if so whats the reason and the advantages of adding a named path to the pathitems collection?

BTW, Heres a sample on how I would generate the points array -

Code: Select all

// SimulateBrushClicks(PointGrid(50, 50));
function PointGrid (incW, incH)
{
  var PtArr = new Array();
  var dw = (parseInt(app.activeDocument.width) / incW);
  var dh = (parseInt(app.activeDocument.height) / incH);
  var dstW = 0;
  var dstH = 0;
  
  for (i = 0; i < (1 + incW); i++)
  {
    dstH = 0;
    for (j = 0; j < (1 + incH); j++)
    {
      PtArr.push(Array(dstW, dstH));
      dstH = dstH + dh;
    }// for 
    dstW = dstW + dw;
  }// for 
  return PtArr;
}// function PointGrid
User avatar
Kukurykus
Posts: 528
Joined: Mon Jul 25, 2016 12:36 pm

Re: Simulate Click with Brush

Post by Kukurykus »

Forget of slow Document Object Model and learn fast Action Manager to do what you want in 2 - 3 seconds.
Grrr1337
Posts: 8
Joined: Fri Nov 01, 2019 3:40 pm

Re: Simulate Click with Brush

Post by Grrr1337 »

Kukurykus wrote: Tue Dec 29, 2020 5:54 pm Forget of slow Document Object Model and learn fast Action Manager to do what you want in 2 - 3 seconds.
Thanks, Kukurykus -
Until now I wasn't aware why so many people use it, since I thought that it was more logical to use DOM because of the better readability of the code.
Unfortunately my problem remains, since I wasn't able to find out how to translate the syntax from the SimulateBrushClick function into a fast Action Manager .jsx script, and as I've mentioned the internet showed no clues on how to do that (not alternatives found).
Then I tried recording an action with 'Allow tool recording' option on and just clicked with the brush somewhere.
I've exported it to an .atn file and tried with both 'ActionToJavascript' and 'ActionFileToJavascript' (xtools) to convert it to .jsx code - but the next problem was that the conversion process caused an endless loop and my Photoshop crashed - hence was unable to convert it to .jsx.

This 'Click with Brush' task seemed so basic to me, and was expecting to find many asnwers and ways on the internet on how to do it,
but it turns out either I'm bad at googling either I'm inventing the wheel.
Attachments
BrushClickAction.jpg
BrushClickAction.jpg (12.9 KiB) Viewed 4563 times
Last edited by Grrr1337 on Thu Dec 31, 2020 10:45 am, edited 1 time in total.
User avatar
Kukurykus
Posts: 528
Joined: Mon Jul 25, 2016 12:36 pm

Re: Simulate Click with Brush

Post by Kukurykus »

You won't find it on internet. I looked lately too, and even if you understood AM you had to think up workaround to imitate simulate brush click.

What you need it for?
Grrr1337
Posts: 8
Joined: Fri Nov 01, 2019 3:40 pm

Re: Simulate Click with Brush

Post by Grrr1337 »

Kukurykus wrote: Thu Dec 31, 2020 11:31 am You won't find it on internet. I looked lately too, and even if you understood AM you had to think up workaround to imitate simulate brush click.
Considering your much greater experience with PS development than mine, I see now that this is not that simple to do.
Kukurykus wrote: Thu Dec 31, 2020 11:31 am What you need it for?
I'm using it as a part of a much larger routine -
basically I have some underlay drawing (a .pdf file, created with AutoCAD)
and an array/list datastructure, which contains coordinates and string-definitions.
My intention is for the .jsx script to read this data, which is generated via LISP
and to be instructed at which PS coordinate what brush to be placed with the respected size and rotation.
Simply said its a conditional image insertion ontop of a larger underlay image.

Speaking from my user perspective:
For example in the attached urban plan I'm exporting all the linework from ACAD and then perform some postproduction with PS.
But for instance I don't like the graphical presentation of the trees -
I'd like to replace them with something more detailed: say a picture of a tree in top view or maybe a bush or a variation of images of landscape elements in top-view.

My initial plan was to try with loading separate image files as layers,
then duplicate them each N-times, scale, rotate, translate(from center) to match each coordinate - and in the end to merge them in one layer.
But then I came up with the assumption that this algorithm would be slow and inefficient, so I firstly desided to try out with this brush-clicking way.
Although I'm not sure if its possible to define such brush in PS that uses highly-detailed image.

I assume that people rarely use PS's coordinate system for drafting purposes, so my question is rather unpopular.
Considering to try out my initial plan - the layers way...
Attachments
img1.jpg
img1.jpg (1.21 MiB) Viewed 4552 times
img2.jpg
img2.jpg (790.56 KiB) Viewed 4552 times
User avatar
Kukurykus
Posts: 528
Joined: Mon Jul 25, 2016 12:36 pm

Re: Simulate Click with Brush

Post by Kukurykus »

It's possible to do by 2 methods you described, and it wouldn't be so slow when you know how to do it.

I can't match second image fragment to any part of bigger image, so probably it comes from different map.

Anyway send to me private message with your E-Mail address. Attach few examplary pixels coordinates array of original image, then share that image and another one where manually you put for ex. those trees to be placed instead of current ones. Do it both by first method and second too, so brush clicks. Also it would be good you prepared such tree brush the way you can learn from some tutorial as I guess there is some ;)
Grrr1337
Posts: 8
Joined: Fri Nov 01, 2019 3:40 pm

Re: Simulate Click with Brush

Post by Grrr1337 »

Kukurykus wrote: Thu Dec 31, 2020 10:25 pm It's possible to do by 2 methods you described,
I did initial research on finding the different components, which were required for both of my planned algorithms/methods to work,
so I know it should be possible to execute them.

Kukurykus wrote: Thu Dec 31, 2020 10:25 pm ...and it wouldn't be so slow when you know how to do it.
Agreed - although I have some good programming experience, I'm relatively new/unexperienced with PS JS scripting,
Not even a 'heavy' PhotoShop user as you can see. :roll:

Kukurykus wrote: Thu Dec 31, 2020 10:25 pm I can't match second image fragment to any part of bigger image, so probably it comes from different map.
Correct, they are different maps -
The bigger image is my student project from the last year,
and the fragment is the project I'm working on in my current semester,
(where I just came up with the idea I mentioned in this thread).

Kukurykus wrote: Thu Dec 31, 2020 10:25 pm Anyway send to me private message with your E-Mail address. Attach few examplary pixels coordinates array of original image, then share that image and another one where manually you put for ex. those trees to be placed instead of current ones. Do it both by first method and second too, so brush clicks. Also it would be good you prepared such tree brush the way you can learn from some tutorial as I guess there is some ;)
PM sent with sample files. ;)
User avatar
Kukurykus
Posts: 528
Joined: Mon Jul 25, 2016 12:36 pm

Re: Simulate Click with Brush

Post by Kukurykus »

Try this script in place of your previous. It's going to open .psd, read .txt coordinates, and put trees from bottom subfolder on their map spots:

Code: Select all

try{
	sTT = stringIDToTypeID
	pth = File($.fileName).path;
	(dat = File(pth + '/Data.txt'))
	.open('r'), r = dat.read(), dat.close(), arr = eval(r)
	aD = open(File(pth + '/Simulate Click Brush.psd'))
	runMenuItem(sTT('screenModeFullScreen'))
	runMenuItem(sTT('fitOnScreen'))

	zW = -(aD.width / 2), zH = -(aD.height / 2), fctr1 = 72 / 300 * 100 * .65, fctr2 = 180 / Math.PI; for(i = 0; i < arr.length; i++) {
		(dsc1 = new ActionDescriptor()).putPath(sTT('null'), File(pth + '/Resources/Original/' + (cell = arr[i])[0] + '.png'))
		dsc1.putEnumerated(sTT('freeTransformCenterState'), sTT('quadCenterState'), sTT('QCSAverage'));
		(dsc2 = new ActionDescriptor()), object = {horizontal: zW + cell[1][0], vertical: zH + cell[1][1]}
		for(j in object) dsc2.putUnitDouble(sTT(j), sTT('pixelsUnit'), object[j]), dsc1.putObject
		(sTT('offset'), sTT('offset'), dsc2); for(j = 0; j < (wh = ['width', 'height']).length;)
			dsc1.putUnitDouble(sTT(wh[j++]), sTT('percentUnit'), fctr1 * cell[2])
		dsc1.putUnitDouble(sTT('angle'), sTT('angleUnit'), fctr2 * cell[3])
		executeAction(sTT('placeEvent'), dsc1)
	}
}
catch(err){alert('Error in line: ' + err.line)}
Grrr1337
Posts: 8
Joined: Fri Nov 01, 2019 3:40 pm

Re: Simulate Click with Brush

Post by Grrr1337 »

Kukurykus wrote: Sat Jan 02, 2021 4:29 pm Try this script in place of your previous. It's going to open .psd, read .txt coordinates, and put trees from bottom subfolder on their map spots:
My god, that ran atleast 3 times faster!
...and I've got just a sketchy idea on what happens inside of your fast Action Manager script,
Few questions appeared though:
• unlike mine, is that a smart layer you are creating and are there any speed benefits when transforming it?
• is it possible to relink each of the generated layers to the corresponding .png file,
so in a scenario where I replace or modify a file from the resource, all the layer references will update the changes?

BTW, talking about brushes here -
Does such event exists that would read at which point user clicked with the mouse in order to place a copy of such layer?
Attachments
layer.jpg
layer.jpg (3.8 KiB) Viewed 4488 times
relink.jpg
relink.jpg (20.15 KiB) Viewed 4493 times
Last edited by Grrr1337 on Sun Jan 03, 2021 10:48 am, edited 2 times in total.
User avatar
Kukurykus
Posts: 528
Joined: Mon Jul 25, 2016 12:36 pm

Re: Simulate Click with Brush

Post by Kukurykus »

That has nothing to do with Smart Object, you simply have not unchecked 'Always Create Smart Objects When Placing' in General Preferences.

You can keep Smart Objects though if later you want to relink them to other .pngs, or simply replace embedded content.

All smart objects will refresh at once only when first they all were linked to same file, so are duplicates of each other.

There is event for brush clicking, but it's hidden as it generates too many lines of code.

There is workaround for it though, but better use placing method ;)