Moho Scripting for Beginners

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

Post Reply
User avatar
neilhammers
Posts: 15
Joined: Tue Oct 17, 2023 1:00 pm

Moho Scripting for Beginners

Post by neilhammers »

Hey,
Could anyone suggest documentation/tutorial to kickstart the scripting for the person that already has (really) basic Lua understanding?
I am looking forward to write a script that could manipulate existing curves. Yet after poking around for a week plus, I still find it overwhelming to do even a basic things, as even I found out bits and ends about WHAT data is stored, HOW to store it myself, address and iterate, then do rebuilding is beyond my reach.
Even seeing linked examples on mohoscripts.com doesn't help much, as those are complex and I am missing the context.
Thank you,
Neil
User avatar
hayasidist
Posts: 3526
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Moho Scripting for Beginners

Post by hayasidist »

Have you read the introduction and script structure pages on https://mohoscripting.com/ ??

there's also a "build an outline script" that is linked from the "Tools" button: https://mohoscripting.com/new_script

Then make a start - get stuck - ask questions here -- plenty of old hands generally very happy to chime in...
User avatar
neilhammers
Posts: 15
Joined: Tue Oct 17, 2023 1:00 pm

Re: Moho Scripting for Beginners

Post by neilhammers »

Hey hayasidist,
Thanks for your quick reply, even I wasn't around.
Yes, I've had read it earlier, yet the structure of the "introduction" seemed rather like junction of crossroads, that got me confused; did not land on first read. I did it again and it made a bit more sense, thanks.

Still, more of your advice would be of great value:

Is there any specific code editor you would suggest for quick code iteration?
Currently, I am with VSCode, however, even it comprehends general Lua, I find it of little help when in context of Moho modules. Thus, to learn the addressing the data cells I go for Moho's Lua Console outputs, to see what values am I working with, and there's a lot of userdata type which I cannot wrap my head around how to handle. In addition, Moho itself feels quite introverted as its Lua Console error messages often are truncated/nonsensical, e. g.:

Code: Select all

...Resources\Support\Scripts\Menu\Draw\NH_Testscript01.lua:101: attempt to call a table value (for iterator 'for iterator
or

Code: Select all

...Resources\Support\Scripts\Menu\Draw\NH_Testscript01.lua:101: attempt to concatenate a table value (local 'curv'
.. which leaves me guessing what and where went wrong.
User avatar
hayasidist
Posts: 3526
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Moho Scripting for Beginners

Post by hayasidist »

I actually use notepad (which gives me most of what I want) even thought it's by no means the best -- it's a very basic text editor with no language support at all. I've never even bothered to get notepad++. I'll defer to others who will have their own recommendations.

I'm concerned that you're working in an inappropriate directory (looks as though you have the Lua files in the factory content folders). This is not recommended. What I have is a working directory in my user account folders
(e.g. C:\Users\Hayasidist\Documents\TestScripts) and this is where I store and edit scripts (including past versions plus various derivations). The target for test gets copied to my custom content folders (set up in Moho: Edit / Preferences / Custom Content Folder) and run inside moho.

Userdata: yeah - moho has lots of it! https://www.mediafire.com/file/x6oqtl6x ... a.lua/file might help a bit with diagnostics - (it doesn't do all the userdata types - just a few that make some sense to be printed).


Without seeing the code that produced the error, I can only guess ... but it looks as though you're using the "curve" userdata table (https://mohoscripting.com/classes/M_Curve) instead of a curve number e.g.

Code: Select all

	local curve
	local ct = mesh:CountCurves()

	for i = 0, ct-1 do
		curve = mesh:Curve(i) -- gives you an M_Curve object 
		if curve:IsSelected() then ...

...
User avatar
SimplSam
Posts: 1048
Joined: Thu Mar 13, 2014 5:09 pm
Location: London, UK
Contact:

Re: Moho Scripting for Beginners

Post by SimplSam »

I am a VSCode'r (edit) just updated to: Sumneko Lua Language Server: (https://marketplace.visualstudio.com/it ... umneko.lua). Formerly with the Tencent LuaHelper extension: (https://marketplace.visualstudio.com/it ... .luahelper)

I suppose there should be a way to make a Moho snippet extension, as there are a couple for Love 2D & Roblox (for example), but I don't know how that all works.
Last edited by SimplSam on Tue Nov 07, 2023 5:11 am, edited 2 times in total.
Moho 14.1 » Win 11 Pro 64GB » NVIDIA GTX 1080ti 11GB
Moho 14.1 » Mac mini 2012 8GB » macOS 10.15 Catalina
Tube: SimplSam


Sam
User avatar
neilhammers
Posts: 15
Joined: Tue Oct 17, 2023 1:00 pm

Re: Moho Scripting for Beginners

Post by neilhammers »

Hey hayasidist,

For experienced programmer Notepad is fine of course, yet we've got a case where someone new is trying to grasp both general Lua and specifics of Moho together and those language/modules hints could have been of great help. Not that I wouldn't be ready to spend years on mastering both, its just me trying to asap evaluate if it is for me or not.

:D I know I am working inside Moho system directory, and yet, I am working with just my own script not touching anything else, inshallah no harm will happen. :D Just another lazy approach on script installation, which I probably overlooked somewhere within overwhelming scripting docs, nm.

Yes, you are right, I am also counting curves, yet general intent is to collect all the raw curve data belonging to selected point/s.

Code: Select all

local mesh = moho:Mesh()
	local crvData = {{},{}} -- Once it works for points and curvatures, to be expanded for curve weights and offsets
	for i = 0, mesh:CountCurves()-1 do
		local curve = mesh:Curve(i)
		for j = 0, curve:CountPoints()-1 do
			local pt = curve:Point(j)
			if pt.fSelected then
				table.insert(crvData[1], mesh:PointID(pt))
				table.insert(crvData[2], curve:GetCurvature(mesh:PointID(pt), 0))
				-- more to be added
			end
		end
	end
	if #crvData then
		print('point IDs\n')
		print(table.concat(crvData[1], " ") )
		print('curvatures\n')
		print(table.concat(crvData[2], " "))
		print('eof')
		for id, curv in crvData do -- Can we somehow print out collected data?
			print(id .. "\t" .. curv .. "\n") -- Outputs  0.30000001192093  0.30000001192093
		end
	else print('empty') end
Before printing data, it also warns, twice:

Code: Select all

M_Curve:GetCurvature - out of range
Which lines cause it and why? Also, when counting curves/pts, why the i and j cycles start with 0, if general Lua table cell numbering paradigm starts with 1? Some connection?
User avatar
hayasidist
Posts: 3526
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Moho Scripting for Beginners

Post by hayasidist »

as with all code, there are many ways to tackle a problem. Here's your original with my suggestion for how to get it working (and gives scope for expansion by adding extra cXXX fields to the base table) [It's based on an approach I use "all the time" when I have an entity that has lots of attributes.]

I'll point out that this (and the original) just lists all the points without separating them by curve... If that's not what you wanted then we have some extra info (e.g. curveId) to consider!

Code: Select all

--
-- ** Data Capture
--
	local mesh = moho:Mesh()
	local i, j, sId, sCurv -- NEW 
--	local crvData = {{},{}} -- Once it works for points and curvatures, to be expanded for curve weights and offsets 
	local crvData = {cId=0, cCurv=0} -- NEW -- replaces original table definition
	for i = 0, mesh:CountCurves()-1 do
		local curve = mesh:Curve(i)
		for j = 0, curve:CountPoints()-1 do
			local pt = curve:Point(j)
			if pt.fSelected then
--vvvv replaces original ---
				table.insert (crvData, {})
				crvData[#crvData].cId = mesh:PointID(pt) -- store the mesh point number
				crvData[#crvData].cCurv = curve:GetCurvature(j, 0) -- curvature of the point in curve (not mesh)
--				table.insert(crvData[1], mesh:PointID(pt))
--				table.insert(crvData[2], curve:GetCurvature(mesh:PointID(pt), 0))  <<<<< THIS LINE causes the "out of range error" because it's getting a mesh point number not a "point in curve" number
				-- more to be added

--^^^^^
			end
		end
	end

--
-- **	Data display
--

	if #crvData then
--vvvvvvv Original plus suggestion for printing 
--[=[ Based on original code...
		print('point IDs\n')
		print(table.concat(crvData[1], " ") )
		print('curvatures\n')
		print(table.concat(crvData[2], " "))
		print('eof')

		for id, curv in crvData do -- Can we somehow print out collected data?
			print(id .. "\t" .. curv .. "\n") -- Outputs  0.30000001192093  0.30000001192093
		end
-- This works with the table of tables ...
		for i = 1, #crvData[1] do
			sId = tostring(crvData[1][i])
			sCurv = tostring(crvData[2][i])

			print (sId .. " " .. sCurv)
		end
]=]
--^^^^^^^^^^^^^^^^^


-- vvvvv -- this with the named variables
		for i = 1, #crvData do
			sId = tostring(crvData[i].cId)
			sCurv = tostring(crvData[i].cCurv)

			print (sId .. " " .. sCurv)
		end
--^^^^^^^^^^^^^^^^^^^^^^^

	else
		print('empty')
	end
Hope that helps. Any questions, just shout ...
User avatar
SimplSam
Posts: 1048
Joined: Thu Mar 13, 2014 5:09 pm
Location: London, UK
Contact:

Re: Moho Scripting for Beginners

Post by SimplSam »

neilhammers wrote: Mon Nov 06, 2023 4:51 pm ... Also, when counting curves/pts, why the i and j cycles start with 0, if general Lua table cell numbering paradigm starts with 1? ...
The reason is because those :fn(xxx) functions like mesh:Curve(xxx) access 'C/C++' native 0 based arrays - which deal with the indexer as an offset. Whilst linear Lua tables (arrays) :tbl[yyy] use 1 based indexing. I think the real question is why is Lua 1 based, when it is built on C, and I believe this is originally to do with non-techy human readability. i.e. 1 is 'The first element' etc ...
Moho 14.1 » Win 11 Pro 64GB » NVIDIA GTX 1080ti 11GB
Moho 14.1 » Mac mini 2012 8GB » macOS 10.15 Catalina
Tube: SimplSam


Sam
User avatar
neilhammers
Posts: 15
Joined: Tue Oct 17, 2023 1:00 pm

Re: Moho Scripting for Beginners

Post by neilhammers »

@SimplSam : Hi and thanks.
Making VSCode extension would be a great idea :idea:, however it also would imply knowledge and human hours of simply doing the typing. As Moho is already v14 and there's no such thing (or was it before?), that likely means that it isn't in the LM's priority list, as even Moho's manual points exclusively to MohoScripting.com, which states being unofficial docs. Meanwhile, wannabe Moho scriptwriters, me including, whean and struggle. :D
I imagine, in the ideal business world the incentive should have come from the LM's Marketing Dept(ding!), yet, if there's no money, then all there is to rely on is magic, 2nd coming of Christ and community. Most likely the ones, who would make it, could be guys who take care of MohoScripting site, as all the numbers are already there. Or, if there were some sort of housekeeping hackathon party, I would participate with my £1.
Your thoughts on indexing origins make sense, thanks. However, could you confirm if it impacts just scripting for Moho, where I assume data is accessible via tables, or is it general Lua paradigm to iterate table indices from 0 to #arr-1? Loop examples in www.lua.org begin with 1, so I am confused again.

@hayasidist :
Thank you for the code snippet and suggestions, and it works. Although, I have several questions:
1. As I understand, then crvData table is inserted with new table for each point it iterates through, right? To store the rest of the curve data, should I edit crvData definition to {cId=0, cCurv=0, cWght=0, cOffs=0}?
2. Should i and j also be defined as local? For some reason VSCode doesn't "see" those being used.
3. In mesh:PointID(pt) we get point ID, don't we? If so, then, for curve:GetCurvature(j, 0),why do we use j instead of that ID, where M_Curve:GetCurvature(ptID, frame)? I've read that curve point IDs might be unordered, thus I would address them by IDs, not the order on curve, while j, apparently, is [0, #pointcount-1] sequential(?).
User avatar
hayasidist
Posts: 3526
Joined: Wed Feb 16, 2011 8:12 pm
Location: Kent, England

Re: Moho Scripting for Beginners

Post by hayasidist »

neilhammers wrote: Tue Nov 07, 2023 2:09 pm @hayasidist :
Thank you for the code snippet and suggestions, and it works. Although, I have several questions:
1. As I understand, then crvData table is inserted with new table for each point it iterates through, right? To store the rest of the curve data, should I edit crvData definition to {cId=0, cCurv=0, cWght=0, cOffs=0}?
2. Should i and j also be defined as local? For some reason VSCode doesn't "see" those being used.
3. In mesh:PointID(pt) we get point ID, don't we? If so, then, for curve:GetCurvature(j, 0),why do we use j instead of that ID, where M_Curve:GetCurvature(ptID, frame)? I've read that curve point IDs might be unordered, thus I would address them by IDs, not the order on curve, while j, apparently, is [0, #pointcount-1] sequential(?).
1: yep. I used the "longhand" to set the entries really to illustrate how it can work especially if you don't have all the fields at the time you want to create an entry. This is another way to set things:

Code: Select all

	local tab = {k="", n=0}
	table.insert (tab, {k="k1", n=1})
pedantically you don't actually need to add the field names to the table definition. I do because it helps me remember the names and not make silly mistakes such as setting a field Xx and (trying and failing) reading a field xX.
But yes. IMO "good practice" exactly as you've suggested. Naming "convention" is whatever you want -- I used the cId (a number) to distinguish from sId (a string) - but you could just as easily call the point id meshPoint=0 or whatever ...

An observation on the suggested fields you have - and it's up to you how you choose to manage this (e.g. array within an array or separate fields) but just be aware that a point that is NOT an endpoint will have a two weight and offset values (i.e. one for each of the bezier handles.)

2: again that's my preference to make them an explicit definition. I'm fairly sure that Lua defines those i, j etc as local to the scope of the iteration (IOW my explicit variables never get used), but it's another one of my "stopping me making silly mistakes" belt and braces approach.

3: In the trivial case where you create (say) two separate closed curves of four points one after the other you'll get mesh points 0,1,2,3 in curve 0 and mesh points 4,5,6,7 in curve 1. But both curves have curve:point 0,1,2,3. [Which BTW was why you were getting point out of range - you were trying to access curve point (e.g.) 6 in a curve with only 4 points -- you were using the mesh point number, not the "sequence in curve" point number.]

It can get more complicated if a mesh point appears in more than one curve (curvature is associated with the curve not the mesh); or appears in one curve more than once. Have a look at https://mohoscripting.com/methods/782 . That explains the fundamental difference between curve:point() and mesh:point().
Post Reply