Align UV script optimizations


here’s what I suggested doing with uv-mesh representation
don’t know why but subsequent execution of this code doing twice as fast than the first run after max start

t1=timestamp();hf = heapfree
max create mode
with redraw off
-- example scene
sp = 
	max create mode
	delete objects
	sp = teapot name:"test_mesh" mapcoords:on isSelected:true
	addmodifier sp (Uvwmap maptype:0)
	sp = converttopoly sp
	for k=1 to 3 do polyop.attach sp (copy sp)
	update sp

fn GetAllMeshElements node =
	local tmesh = snapshotasmesh node
	local faces = #{1..tmesh.numfaces}
	local verts = for v = 1 to tmesh.numverts collect #()
	local undone = #{1..tmesh.numverts}
	local elements = #()
	for j in faces do
		f = getface tmesh j

		append verts[f[1]] j
		append verts[f[2]] j
		append verts[f[3]] j		

	for i in faces do
		element = #(i)
		for j in element where faces[j] do
			faces[j] = false
			f = getface tmesh j
			if undone[f[1]] do (join element verts[f[1]]; undone[f[1]] = false )
			if undone[f[2]] do (join element verts[f[2]]; undone[f[2]] = false )
			if undone[f[3]] do (join element verts[f[3]]; undone[f[3]] = false )
		append elements (element as bitarray)

sel = selection[1]
convertToMesh sel

tri = snapshotasmesh sel
meshUV = mesh mesh:tri
channelInfo.CopyChannel meshUV 3 1
channelInfo.PasteChannel meshUV 1 0
collapseStack meshUV

elems = GetAllMeshElements meshUV

meshop_getVertsUsingFace = meshop.getVertsUsingFace

vts = #()
vts.count = tri.numverts
for el in elems do
	fv = meshop_getVertsUsingFace meshUV el
	shift = [ 1e9, 1e9, 0 ]
	for v in fv do
		vts[v] = vv = getVert meshUV v
		if vv.x < shift.x do shift.x = vv.x
		if vv.y < shift.y do shift.y = vv.y
	for v in fv do setvert meshUV v (vts[v] - shift)

update meshUV

channelInfo.CopyChannel  meshUV 1 0
channelInfo.PasteChannel sel 3 1
delete meshUV
free tri

format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)


Could you post an example of the scene you usually work with? (preferably MAX 2020 version). I need to see the type of data you have. How best to optimize depends on it.


It’s easy not to use a snapshot for a mesh, as well as any conversion to mesh geometry at all. I do this in order to use the methods of meshop mapfaces to elements that are not part of polyop.
I assume that after improving the code and algorithms, we can increase the performance up to 4 times continuing to use only pure maxscript.


Here is an example file.
Katwijk.max (25.9 MB)

You can ignore the roofs, only the walls are relevant.

@Serejah I did a quick test and it seems fast, but I’m not sure how fast. I only tried a small portion of the example file. It was faster than my original script anyway.

I did a test in Blender, which is the source app for the buildings, and it did it in about 4 min 45 sec. I first clicked left, and then bottom. Maybe it could do it twice as fast if there was a button for bottom left align, but there’s not.


I’ve made the test - do all using c++ :
roofs :
CPP SDK elements:22610 time:205

CPP SDK elements:137464 time:4315

the time in msec… :wink: it definitely beats the Blender


not bad for 131391 uv elements :slight_smile:


walls are 137464 elements… what I found


Very nice :slight_smile:
I guess I will have to learn a bit more to progress further :wink:
@denisT What’s your opinion on python in Max?


Most likely your number is more correct. I just copy pasted uvs to mesh and used MNMeshElements to check the count. And maybe it isn’t a valid method at all.


I think that is possible to make pure MXS version which can do the job for about 40-60 sec


Oh wow, that would still be fantastic! So there is still reason for me to dig deeper :+1:


do you remember that MXS algorithm to find all mesh geom elements? You already found it once on this forum…


this was a greatest thread of all times


I posted above my updated version of Jorge’s code. If I remember correctly it was working bit faster, but I didn’t do muchs tests to see if it doesn;t break on some corner case scenarios


Thanks! :+1:
I will try to make this UV shift thing based on fast element search using only MXS… the same I do in my cpp code


I didn’t expect this kind of help, I really appreciate it, both of you :pray:


and finally:

fn shiftPolyMapAllElements node ch:1  =
	local faces = #{1..node.numfaces}
	local numtverts = polyop.getnummapverts node ch
	local verts = for v = 1 to numtverts collect #()
	local undone = #{1..numtverts}
	local elements = #()
	getmapface = polyop.getmapface 
	getmapvert = polyop.getmapvert
	setmapvert = polyop.setmapvert
	for f in faces do
		tvv = getmapface node ch f
		for tv in tvv do append verts[tv] f

	for i in faces do
		element = #(i)
		tverts = #()
		for j in element where faces[j] do
			faces[j] = off
			f = getmapface node ch j
			for k=1 to f.count do
				v = f[k]
				if undone[v] do 
					join element verts[v]
					append tverts v 
					undone[v] = off 
		u = 1e9
		v = 1e9
		pp = for tv in tverts collect
			p = getmapvert node ch tv
			if p.x < u do u = p.x
			if p.y < v do v = p.y
		shift = [u,v,0]
		k = 1
		--format ">> % = %\n" tverts.count tverts
		for tv in tverts do
			setmapvert node ch tv (pp[k] - shift) 
			k += 1

		append elements tverts.count
	update node
undo "Shift POLY Elements" on
	node = $
	converttopoly node -- for undo!!!
	t1 = timestamp()
	m1 = heapfree
	elements = shiftPolyMapAllElements node
	format "MXS POLY elements:% time:% memory:%  >> %\n" (try(elements.count) catch(-1)) (timestamp() - t1) (m1 - heapfree) elements

MXS POLY elements:137464 time:4237 memory:268027116L >> #(5, 4, 5, 4, 5, 4, 5, 4, 13, 12, 11, 12, 7, 4, 4, 8, 4, 19, 4, 4, …)

pure MXS is ~4 sec on my machine (!)

Blender sucks! :stuck_out_tongue_closed_eyes:


Thank you for restoring my faith, oh wise one :pray:
Seriously though, that’s pretty awesome :exploding_head:
I’ll try it out tomorrow!
Thanks for the help! I hope others find it useful too!
If anyone needs to bottom left align 100k+ UV islands, they will find this thread :grin:


Tried it and it works great. Very fast! I can now do this step in Max :slight_smile:
Out of interest, what are the main differences from the first script I made, that result in such an improvement?


do you see any sign of an unwrap in the code?
the rest is just a very clever algorithm to lookup for uv elements
moving verts is simple, knowing which ones to move is not :wink: