ScriptSpot

Forum topic · Scripts Wanted

Selected vertex attracts it's nearest neighbor to weld into it.

By tofoaso · 2013-06-08

Description

Hello guys,

I am in need of a script:

Selected vertex attracts it's nearest neighbor to weld into it.

If more than one vertex is selected, each selected vertex should attract to it the nearest neighbor. Only one vertex to be welded to the original vertex.

Would appreciate if anybody could help on this :)

Comments (28)

Select nearest object based on selected object

Sharath pawar · 2019-03-24

Hi can anyone help me i need to write a maxscript for this
I need to give concrete texture to a floor-object mentioned in the image attached to this comment
based on the availability of the railing-object which sits exactly on top of the floor-object as u can see
but almost every floor-object has same name except their last number in order
so i need to give concrete texture everytime to one particular floor-object only as shown

any help ?

Attachments

Seletive weld (target weld script)

rvidal · 2019-01-20

Hello
I need to complete my script and this topic is related to my problem, so I hope somebody help me out.

The problem:
I need to weld some verteces that I already know one by one.
Exemple:
$.EditablePoly.SetSelection #Vertex #{4326}
$.EditablePoly.SetSelection #Vertex #{4322}

to the closest vertex located in a selected border at the same object (see the attached pictures)

Attachments

.

miauu · 2019-01-21

Try this:


(
	global rol_weldVerts
	try(destroyDialog rol_weldVerts)catch()
	rollout rol_weldVerts "miauu"
	(
		local curO = undefined
		local vertsToWeldBA = #{}
		
		button btn_getVerts "Get Verts" width:140
		button btn_weld "WELD" width:140
		
		on btn_getVerts pressed do
		(
			curO = modPanel.getCurrentObject()
			if classOf curO == Editable_Poly do
			(
				vertsToWeldBA = polyop.getVertSelection curO
			)
		)
		
		on btn_weld pressed do
		(
			if vertsToWeldBA.isEmpty == false do
			(
				selEdgesBA = polyop.getEdgeSelection curO
				selEdgesVertsBA = polyop.getVertsUsingEdge curO selEdgesBA
				closestVertsArr = #()
				for v in vertsToWeldBA do
				(
					vPos = polyop.getVert curO v
					minDist = 1e9
					tmpArr = #(v, vv, undefined)
					for vv in selEdgesVertsBA do
					(
						vvPos = polyop.getVert curO vv
						if (dist = distance vPos vvPos) < minDist do
						(
							minDist = dist
							tmpArr[2] = vv
							tmpArr[3] = vvPos
						)
					)
					append closestVertsArr tmpArr
				)
				if closestVertsArr.count != 0 do with undo "Weld verts" on
				(
					vertsIdxToWeldBA = #{}
					for arr in closestVertsArr do
					(
						polyop.setVert curO arr[1] arr[3]
						vertsIdxToWeldBA += #{arr[1], arr[2]}
					)					
					curO.weldThreshold = 0.0001
					polyop.weldVertsByThreshold curO vertsIdxToWeldBA
				)
			)
		)
	)
	createdialog rol_weldVerts
)

Select the verts that you want to weld.
Select the edges.
Press the [Get Verts] button.
Press the [Weld] button.

Great. Thank you Miauu. I

rvidal · 2019-01-23

OMG... Thank you Miauu. I will look the code and try to learn. I think it will solve my problem

...#2 distance comparation method

barigazy · 2013-06-12


(
if selection.count == 1 and isKindOf (obj = selection[1]) Editable_poly then
(
selVertsBA = obj.selectedverts as bitarray
if selVertsBA.isEmpty then (messagebox "Select some vertices" title:"Invalid Selection" beep:off) else
(
local vertsToWeldBA = #{}, vertsToSelBA = #{}, vertsPos = #()
local getVuE = polyop.getVertsUsingEdge
local getEuV = polyop.getEdgesUsingVert
local weldThresh = obj.weldThreshold = 0.001
local weldVerts = polyop.weldVertsByThreshold
for v in selVertsBA do
(
vPos = obj.verts[v].pos
neighborVertsBA = (getVuE obj (getEuV obj v)) - #{v}
minDist = 1e9 ; nearestVert = undefined
for nv in neighborVertsBA do (if (dist = distance (obj.verts[nv].pos) vPos) < minDist do (minDist = dist ; nearestVert = nv))
join vertsToWeldBA #{v, nearestVert}
append vertsPos (obj.verts[nearestVert].pos = vPos)
)
weldVerts obj vertsToWeldBA
for v in 1 to obj.numverts do
(
for p in vertsPos where distance obj.verts[v].pos p <= weldThresh do append vertsToSelBA v
)
obj.selectedverts = vertsToSelBA
)
) else (messagebox "Select only one editable poly object" title:"Invalid Selection" beep:off)
)

barigazy · 2013-06-12

So the point here is to reduce usage of polyOp struct to minimum.
Hope you agree with me Kostadin. I have not had time to to compare speed and memory.

miauu · 2013-06-12

I think that for object with small amount of verts the speed using polyop and not using polyop will be too small. But when I have time I will compare both methods.
The most important I think is to stop the last for loop when all verts, that have to be selected, are found. So we have to use something like this:


cnt = 0
			for v in 1 to obj.numverts while cnt  < selVertsBA.numberset do
			(
				for p in vertsPos where distance obj.verts[v].pos p <= weldThresh do
				(
					append vertsToSelBA v
					cnt += 1
				)
			)

barigazy · 2013-06-12

Nice one i like it. Maybe there is a better finding method because what if we used few last verts that need to be selected after welding.

miauu · 2013-06-12

Not work. :)

...#1 string comparation method

barigazy · 2013-06-12

Sorry for double post and untested version.
I have a new aproch without using getset Vert Data.

(
if selection.count == 1 and isKindOf (obj = selection[1]) Editable_poly then
(
selVertsBA = obj.selectedverts as bitarray
if selVertsBA.isEmpty then (messagebox "Select some vertices" title:"Invalid Selection" beep:off) else
(
local vertsToWeldBA = #{}, vertsPos = #()
local getVuE = polyop.getVertsUsingEdge
local getEuV = polyop.getEdgesUsingVert
local weldVerts = polyop.weldVertsByThreshold
for v in selVertsBA do
(
vPos = obj.verts[v].pos
neighborVertsBA = (getVuE obj (getEuV obj v)) - #{v}
minDist = 1e9 ; nearestVert = undefined
for nv in neighborVertsBA do (if (dist = distance (obj.verts[nv].pos) vPos) < minDist do (minDist = dist ; nearestVert = nv))
join vertsToWeldBA #{v, nearestVert}
append vertsPos ((obj.verts[nearestVert].pos = vPos) as string)
)
weldVerts obj vertsToWeldBA
obj.selectedverts = (for v in 1 to obj.numverts where findItem vertsPos (obj.verts[v].pos as string) != 0 collect v)
)
) else (messagebox "Select only one editable poly object" title:"Invalid Selection" beep:off)
)

tofoaso · 2013-06-11

Thanks, it works although again not for all vertices.. would be nice if there is a way to tweak it so it works for all.
If not doable I am still happy with what it does now though. Thanks :)

barigazy · 2013-06-11

This can be done using get/set VDataValue polyop methods

tofoaso · 2013-06-11

Thanks Branko for the suggestion! :)

miauu · 2013-06-11

Can you send me a scene where the script not works for some of the verts? You can select the verts, so when I open the scene I can see which verts hav e to attract iss neighbors and then I will see whic verts are not selected.

And here is the script.

miauu · 2013-06-11

Don't forget to thanks to Branko.




(
	if selection.count == 1 and classof (curO = selection[1]) == Editable_poly then
	(
		selVertsBA = polyop.getVertSelection curO
		if selVertsBA.numberset != 0 then
		(
			channelIdx = 12
			polyop.setVDataChannelSupport curO channelIdx true
			vertsToWeldArr = #()
			for v in selVertsBA do
			(
				vPos = polyop.getVert curO v
				neighborVertsBA = (polyop.getVertsUsingEdge curO (polyop.getEdgesUsingVert curO v)) - #{v}
				minDist = 1e9
				nearestVert = undefined
				for nv in neighborVertsBA do
				(
					dist = distance (polyop.getVert curO nv) vPos
					if dist < minDist do
					(
						minDist = dist
						nearestVert = nv						
					)
				)
				append vertsToWeldArr #(v, nearestVert)
			)
			if vertsToWeldArr.count != 0 do
			(
				vertsArr = #()
				for i = 1 to vertsToWeldArr.count do
				(
					polyop.setVDataValue curO channelIdx vertsToWeldArr[i] 0.12
					polyop.setVert curO vertsToWeldArr[i][2] (polyop.getVert curO vertsToWeldArr[i][1])
					append vertsArr vertsToWeldArr[i][1]
					append vertsArr vertsToWeldArr[i][2]
				)
				polyop.weldVertsByThreshold curO vertsArr
				--
				vertsToSelBA = #{}
				curOvertsBA = #{1..(polyop.getNumVerts curO)}
				for v in curOvertsBA where ((polyop.getVDataValue curO channelIdx v) == 0.12) do
				(
					append vertsToSelBA v
				)
				polyop.setVertSelection curO vertsToSelBA
				polyop.freeVData curO channelIdx
			)			
		)
		else
			messagebox "Select some vertices" title:"Invalid Selection"
	)
	else
		messagebox "Select only one editable poly object" title:"Invalid Selection"
)

Hey Kostadin

barigazy · 2013-06-11

Why me? I didn't do anything.
All credits goes to you. You did exelent job. :)
Anyway how is your son, do you feel better?

miauu · 2013-06-11

Because you shows me the right direction. :)
My son is fill better, but still have temperature(tonsillitis).

tofoaso · 2013-06-11

Works great! Thanks for the script, its greatly appreciated! :)

A slightly optimized version

miauu · 2013-06-11


(
	if selection.count == 1 and classof (curO = selection[1]) == Editable_poly then
	(
		selVertsBA = polyop.getVertSelection curO
		if selVertsBA.numberset != 0 then
		(
			channelIdx = 12
			polyop.setVDataChannelSupport curO channelIdx true
			vertsToWeldBA = #{}
			for v in selVertsBA do
			(
				vPos = polyop.getVert curO v
				neighborVertsBA = (polyop.getVertsUsingEdge curO (polyop.getEdgesUsingVert curO v)) - #{v}
				minDist = 1e9
				nearestVert = undefined
				for nv in neighborVertsBA do
				(
					dist = distance (polyop.getVert curO nv) vPos
					if dist < minDist do
					(
						minDist = dist
						nearestVert = nv						
					)
				)
				append vertsToWeldBA v
				append vertsToWeldBA nearestVert
				polyop.setVDataValue curO channelIdx v 0.12
				polyop.setVDataValue curO channelIdx nearestVert 0.12
				polyop.setVert curO nearestVert vPos
			)
			polyop.weldVertsByThreshold curO vertsToWeldBA
			vertsToSelBA = #{}
			curOvertsBA = #{1..(polyop.getNumVerts curO)}
			for v in curOvertsBA where ((polyop.getVDataValue curO channelIdx v) == 0.12) do
			(
				append vertsToSelBA v
			)
			polyop.setVertSelection curO vertsToSelBA
			polyop.freeVData curO channelIdx
		)
		else
			messagebox "Select some vertices" title:"Invalid Selection"
	)
	else
		messagebox "Select only one editable poly object" title:"Invalid Selection"
)

...more then sligthly optimized version :)

barigazy · 2013-06-11

You know that the code will be optimized more if you use polyOp outside for-loop :)
Perhaps in this case there is no need for that. Can you try this:(not tested)

(
if selection.count == 1 and isKindOf (curO = selection[1]) Editable_poly then
(
selVertsBA = curO.selectedverts as bitarray
if selVertsBA.isEmpty then (messagebox "Select some vertices" title:"Invalid Selection" beep:off) else
(
local channelIdx = 12, vertsToWeldBA = #{}, vertsToSelBA = #{}
local getVuE = polyop.getVertsUsingEdge
local getEuV = polyop.getEdgesUsingVert
local setVData = polyop.setVDataValue
local getVData = polyop.getVDataValue
local weldVerts = polyop.weldVerts --polyop.weldVertsByThreshold
polyop.setVDataChannelSupport curO channelIdx true
for v in selVertsBA do
(
vPos = curO.verts[v].pos
neighborVertsBA = (getVuE curO (getEuV curO v)) - #{v}
minDist = 1e9 ; nearestVert = undefined
for nv in neighborVertsBA do (if dist = (distance (curO.verts[nv].pos) vPos) < minDist do (minDist = dist ; nearestVert = nv))
vertsToWeldBA + #{v}+#{nearestVert}
setVData curO channelIdx #{v,nearestVert} 0.12
--curO.verts[nearestVert].pos = vPos
weldVerts cur0 v nearestVert vPos
)
--weldVerts curO vertsToWeldBA
for v in 1 to obj.numverts where ((getVData curO channelIdx v) == 0.12) do vertsToSelBA + #{v}
curO.selectedverts = vertsToSelBA
polyop.freeVData curO channelIdx
)
) else (messagebox "Select only one editable poly object" title:"Invalid Selection" beep:off)
)

...more then sligthly optimized version :)

barigazy · 2013-06-11

You know that the code will be optimized more if you use polyOp outside for-loop :)
Perhaps in this case there is no need for that. Can you try this:(not tested)

(
if selection.count == 1 and isKindOf (curO = selection[1]) Editable_poly then
(
selVertsBA = curO.selectedverts as bitarray
if selVertsBA.isEmpty then (messagebox "Select some vertices" title:"Invalid Selection" beep:off) else
(
local channelIdx = 12, vertsToWeldBA = #{}, vertsToSelBA = #{}
local getVuE = polyop.getVertsUsingEdge
local getEuV = polyop.getEdgesUsingVert
local setVData = polyop.setVDataValue
local getVData = polyop.getVDataValue
local weldVerts = polyop.weldVerts --polyop.weldVertsByThreshold
polyop.setVDataChannelSupport curO channelIdx true
for v in selVertsBA do
(
vPos = curO.verts[v].pos
neighborVertsBA = (getVuE curO (getEuV curO v)) - #{v}
minDist = 1e9 ; nearestVert = undefined
for nv in neighborVertsBA do (if dist = (distance (curO.verts[nv].pos) vPos) < minDist do (minDist = dist ; nearestVert = nv))
vertsToWeldBA + #{v}+#{nearestVert}
setVData curO channelIdx #{v,nearestVert} 0.12
--curO.verts[nearestVert].pos = vPos
weldVerts cur0 v nearestVert vPos
)
--weldVerts curO vertsToWeldBA
for v in 1 to obj.numverts where ((getVData curO channelIdx v) == 0.12) do vertsToSelBA + #{v}
curO.selectedverts = vertsToSelBA
polyop.freeVData curO channelIdx
)
) else (messagebox "Select only one editable poly object" title:"Invalid Selection" beep:off)
)

...more then sligthly optimized version :)

barigazy · 2013-06-11

You know that the code will be optimized more if you use polyOp outside for-loop :)
Perhaps in this case there is no need for that. Can you try this:(not tested)

(
if selection.count == 1 and isKindOf (curO = selection[1]) Editable_poly then
(
selVertsBA = curO.selectedverts as bitarray
if selVertsBA.isEmpty then (messagebox "Select some vertices" title:"Invalid Selection" beep:off) else
(
local channelIdx = 12, vertsToWeldBA = #{}, vertsToSelBA = #{}
local getVuE = polyop.getVertsUsingEdge
local getEuV = polyop.getEdgesUsingVert
local setVData = polyop.setVDataValue
local getVData = polyop.getVDataValue
local weldVerts = polyop.weldVerts --polyop.weldVertsByThreshold
polyop.setVDataChannelSupport curO channelIdx true
for v in selVertsBA do
(
vPos = curO.verts[v].pos
neighborVertsBA = (getVuE curO (getEuV curO v)) - #{v}
minDist = 1e9 ; nearestVert = undefined
for nv in neighborVertsBA do (if dist = (distance (curO.verts[nv].pos) vPos) < minDist do (minDist = dist ; nearestVert = nv))
vertsToWeldBA + #{v,nearestVert}
setVData curO channelIdx #{v,nearestVert} 0.12
--curO.verts[nearestVert].pos = vPos
weldVerts cur0 v nearestVert vPos
)
--weldVerts curO vertsToWeldBA
for v in 1 to obj.numverts where ((getVData curO channelIdx v) == 0.12) do vertsToSelBA + #{v}
curO.selectedverts = vertsToSelBA
polyop.freeVData curO channelIdx
)
) else (messagebox "Select only one editable poly object" title:"Invalid Selection" beep:off)
)

miauu · 2013-06-09

The problem is that when Max weld vertices the index of the new vert is changed. If you select only one vert and run the script then is easy to get the resulting vert index - it is always the smaller index(I hope) of the vertices that are welded. So this will works only when you weld 2 verts(one is selected, the script find other one)


(
	if selection.count == 1 and classof (curO = selection[1]) == Editable_poly then
	(
		newVertsBA = #{}
		selVertsBA = polyop.getVertSelection curO
		if selVertsBA.numberset != 0 then
		(
			vertsToWeldArr = #()
			for v in selVertsBA do
			(
				vPos = polyop.getVert curO v
				neighborVertsBA = (polyop.getVertsUsingEdge curO (polyop.getEdgesUsingVert curO v)) - #{v}
				minDist = 1e9
				nearestVert = undefined
				for nv in neighborVertsBA do
				(
					dist = distance (polyop.getVert curO nv) vPos
					if dist < minDist do
					(
						minDist = dist
						nearestVert = nv						
					)
				)
				append vertsToWeldArr #(v, nearestVert)
			)
			if vertsToWeldArr.count != 0 do
			(
				vertsArr = #()
				for i = 1 to vertsToWeldArr.count do
				(
					if vertsToWeldArr[i][2] < vertsToWeldArr[i][1] then 
						append newVertsBA vertsToWeldArr[i][2]
					else
						append newVertsBA vertsToWeldArr[i][1]
					--
					polyop.setVert curO vertsToWeldArr[i][2] (polyop.getVert curO vertsToWeldArr[i][1])
					append vertsArr vertsToWeldArr[i][1]
					append vertsArr vertsToWeldArr[i][2]
				)
				polyop.weldVertsByThreshold curO vertsArr
			)
			polyop.setVertSelection curO newVertsBA
		)
		else
			messagebox "Select some vertices" title:"Invalid Selection"
	)
	else
		messagebox "Select only one editable poly object" title:"Invalid Selection"
)

The method above can't be used when you want to weld 2 or more verts. Welding the first pair of verts will change their indexes, so the next pair will have new indexes and the rule with the smaller index can't work. This is why the I not weld verts pair by pair - after the first weld process the cript will loose track of the next verts that have to be welded. So I use weld by threshold, which welds all verts at once and there is no problem with verts indexes. The code below will select the resulting vertices when you weld 1,2 or more pairs.


(
	if selection.count == 1 and classof (curO = selection[1]) == Editable_poly then
	(
		selVertsBA = polyop.getVertSelection curO
		selVertsFacesArr = #()
		if selVertsBA.numberset != 0 then
		(
			vertsToWeldArr = #()
			for v in selVertsBA do
			(
				vPos = polyop.getVert curO v
				append selVertsFacesArr #(vPos,(polyop.getFacesUsingVert curO v))
				neighborVertsBA = (polyop.getVertsUsingEdge curO (polyop.getEdgesUsingVert curO v)) - #{v}
				minDist = 1e9
				nearestVert = undefined
				for nv in neighborVertsBA do
				(
					dist = distance (polyop.getVert curO nv) vPos
					if dist < minDist do
					(
						minDist = dist
						nearestVert = nv						
					)
				)
				append vertsToWeldArr #(v, nearestVert)
			)
			if vertsToWeldArr.count != 0 do
			(
				vertsArr = #()
				for i = 1 to vertsToWeldArr.count do
				(
					polyop.setVert curO vertsToWeldArr[i][2] (polyop.getVert curO vertsToWeldArr[i][1])
					append vertsArr vertsToWeldArr[i][1]
					append vertsArr vertsToWeldArr[i][2]
				)
				polyop.weldVertsByThreshold curO vertsArr
				--	
				vertsToSelBA = #{}
				for idx in  selVertsFacesArr do
				(
					vertsBA = polyop.getVertsUsingFace curO idx[2]
					for v in vertsBA do
					(
						if distance idx[1] (polyop.getVert curO v) < 0.0001 do
							append vertsToSelBA v
					)
				)
				polyop.setVertSelection curO vertsToSelBA
			)			
		)
		else
			messagebox "Select some vertices" title:"Invalid Selection"
	)
	else
		messagebox "Select only one editable poly object" title:"Invalid Selection"
)

tofoaso · 2013-06-09

Small addition if possible..
Script to keep the original vertices selected after the welding process?

At the end I have added:
(
Selection = SelVertsBA
)

It does work only in certain areas, and sometimes not at all.

I also tried the other way around with keeping the selection with the numbers of the vertices that were to be welded but it does not work as well.

I am very new to maxscript as you can guess :)

tofoaso · 2013-06-09

Thanks for the quick help!
The script does its job in no time :)

miauu · 2013-06-08

It is easy to write a script that do what you want.


(
	if selection.count == 1 and classof (curO = selection[1]) == Editable_poly then
	(
		selVertsBA = polyop.getVertSelection curO
		if selVertsBA.numberset != 0 then
		(
			vertsToWeldArr = #()
			for v in selVertsBA do
			(
				vPos = polyop.getVert curO v
				neighborVertsBA = (polyop.getVertsUsingEdge curO (polyop.getEdgesUsingVert curO v)) - #{v}
				minDist = 1e9
				nearestVert = undefined
				for nv in neighborVertsBA do
				(
					dist = distance (polyop.getVert curO nv) vPos
					if dist < minDist do
					(
						minDist = dist
						nearestVert = nv						
					)
				)
				append vertsToWeldArr #(v, nearestVert)
			)
			if vertsToWeldArr.count != 0 do
			(
				vertsArr = #()
				for i = 1 to vertsToWeldArr.count do
				(
					polyop.setVert curO vertsToWeldArr[i][2] (polyop.getVert curO vertsToWeldArr[i][1])
					append vertsArr vertsToWeldArr[i][1]
					append vertsArr vertsToWeldArr[i][2]
				)
				polyop.weldVertsByThreshold curO vertsArr
			)
		)
		else
			messagebox "Select some vertices" title:"Invalid Selection"
	)
	else
		messagebox "Select only one editable poly object" title:"Invalid Selection"
)

Another option..

tofoaso · 2013-06-08

If that's tricky another option that would also work is to convert that selection (one vertex, or few vertices placed in different places on the mesh) to edges, but only the shortest connected edge to each vertex. Then I could add collapse to the script :)