Select SImilar Geometry by (bbox, position, vertex count)


#1

Does anyone know a good solution to achieve selection of similar geometry by selecting #all and comparing their bbox, position, vertex count?

I can find only code pieces which I can’t put together — any script-monster I do doesn’t work properly


#2

right now I have something like this:

dobj=#()
ar0=#()
global factorValue = 0.1

function comparesize obj1 obj2 factor = (
	s1 = obj1.max - obj1.min --determine bounding boxes
	s2 = obj2.max - obj2.min
	
	if (s2.x >= (s1.x*(1-factor)) AND s2.x <= (s1.x*(1+factor))) OR (s2.x >= (s1.y*(1-factor)) AND s2.x <= (s1.y*(1+factor))) OR (s2.x >= (s1.z*(1-factor)) AND s2.x <= (s1.z*(1+factor)))THEN
		if (s2.y >= (s1.y*(1-factor)) AND s2.y <= (s1.y*(1+factor))) OR (s2.y >= (s1.x*(1-factor)) AND s2.y <= (s1.x*(1+factor))) OR (s2.y >= (s1.z*(1-factor)) AND s2.y <= (s1.z*(1+factor))) THEN
			if (s2.z >= (s1.z*(1-factor)) AND s2.z <= (s1.z*(1+factor))) OR (s2.z >= (s1.x*(1-factor)) AND s2.z <= (s1.x*(1+factor))) OR (s2.z >= (s1.y*(1-factor)) AND s2.z <= (s1.y*(1+factor))) THEN
			(
				return true
			)
			else return false
		else return false
	else return false			
)

for i in selection do (
	num=getNumVerts i.mesh
	compSize = comparesize ar0 i factorValue
	print compSize

	if findItem ar0 num == 0 then (
		append ar0 num
	)
	
	else if compSize == false then (
		append ar0 num
	)
	else append dobj i
)
select dobj
CompleteRedraw()

it doesn’t work because I don’t know how to loop throught selected array with comparesize function


#3

Check this: http://www.scriptspot.com/3ds-max/scripts/select-similar-objects


#4

miauu, appreciate! but I already took a part of it for my needs which I showed above.

I think I looked through everything, even bought DDO (didn’t get a copy though)


#5

I have to go to sleep now(here is 03:015 after midnight) but you can upload a test scene to show which objects are “similar” for you. I have tested the Select Similar script from scriptspot on 4 boxes(1 main and 3 copies) and when the Size(Bounding box) is on the script selects all 4 boxes as objects with similar sizes. Position and vertex count are easy to be added to the script.


#6

thanks! here’s the scene with:
https://drive.google.com/open?id=17DEGbh5wHWa5aQmnJ6KQYDCsA4i9d3zs

the behavior I’m trying to create:

  1. select some objects
  2. start the script
  3. some process
  4. only duplicates are selected

#7

OK. But can you tell the names of 3-4 objects that are the same and the criteria that you use to “mark” them as “the same”?


#8

it’s Mesh_2453, Mesh_2776, Mesh_2998, Mesh_3157


#9

OK. Those objects have the same verts count, size and overall shape. But why the script to which I gave you a link(and whcih you have tested) not works? Please, watch this video, where the same scirpt is used and it selects similar objects.

https://drive.google.com/open?id=1drAsbjgtl_VAHTnBvTY6ENEkodAJF_tR


#10

thanks for the video!
it is work for single mesh comparison but I want to compare all (or selected) scene objects at once and select all duppies


#11

Try this:

(
	global rol_selectSimilar
	try(destroyDialog rol_selectSimilar)catch()
	rollout rol_selectSimilar "Select Similar"
	(
		checkBox chkBox_selectByBBox "Size(Bounding Box)"
		checkBox chkBtn_vertsFaceCount "Verts/FAces count" 
		button btn_selectSimilar "Select Similar"
		
		function GetShapeMeshPolyCount obj = 
		(
			oldState = obj.render_renderable -- keep rend. state
			obj.render_renderable = true -- set to true for next operation
			res = (getPolygonCount obj) as string -- get Real mesh (v,f) !
			obj.render_renderable = oldState -- restore state
			res -- return result
		)
		
		
		on btn_selectSimilar pressed do
		(
			selObjsArr = selection as array
			if selObjsArr.count != 0 do
			(
				similarObjsArr = #()
				
				for i = 1 to selObjsArr.count where isValidNode selObjsArr[i] do
				(
					iObj = selObjsArr[i]
					iMeshData = #(0,0)
					if chkBtn_vertsFaceCount.checked do
					(
						iMeshData = if isShapeObject iObj then 
							(
								GetShapeMeshPolyCount iObj 
							)
							else
							(
								(getPolygonCount iObj) as string
							)
					)
					for j = selObjsArr.count to i + 1 by -1 where isValidNode selObjsArr[j] do
					(
						if chkBox_selectByBBox.checked do
						(
							if (nodeGetBoundingBox iObj iObj.transform) as string == (nodeGetBoundingBox selObjsArr[j] selObjsArr[j].transform) as string do
							(
								appendIfUnique similarObjsArr selObjsArr[j]
								deleteitem selObjsArr j
							)							
						)
						
						if chkBtn_vertsFaceCount.checked do
						(
							if isShapeObject selObjsArr[j] then 
							(
								if (GetShapeMeshPolyCount selObjsArr[j]) == iMeshData do 
								(
									appendIfUnique similarObjsArr selObjsArr[j]
									deleteitem selObjsArr j
								)								
							)
							else 
							(
								if (getPolygonCount selObjsArr[j]) as string == iMeshData do
								(
									appendIfUnique similarObjsArr selObjsArr[j]
									deleteitem selObjsArr j	
								)
							)
						)
					)
				)
				if similarObjsArr.count != 0 do select similarObjsArr
			)
		)
		
	)
	createdialog rol_selectSimilar 	
)

Select the objects you want to check and use the script. It will select all similar objects.


#12

for some reason I can’t use it. If I tick both options I get:
image

but if I use only bbox option it work!


#13

I have edited the tags in my previous post. Can you try again?


#14

I did it but still no go

I use v2009 if it’s matters


#15

I have tested it in Max 2009. The script works with no errors when the both checkboxes are not turned On at the same time. Please, see this video: https://drive.google.com/open?id=19ZrdOAFSw6TepeBHTVzeThlJPJ2KKpzD


#16

I experienced the same behavior,
it’s impossible to check both at the same time?


#17

This should works with both checkboxes turned ON

(
	global rol_selectSimilar
	try(destroyDialog rol_selectSimilar)catch()
	rollout rol_selectSimilar "Select Similar"
	(
		checkBox chkBox_selectByBBox "Size(Bounding Box)"
		checkBox chkBtn_vertsFaceCount "Verts/FAces count" 
		button btn_selectSimilar "Select Similar"
		
		function GetShapeMeshPolyCount obj = 
		(
			oldState = obj.render_renderable -- keep rend. state
			obj.render_renderable = true -- set to true for next operation
			res = (getPolygonCount obj) as string -- get Real mesh (v,f) !
			obj.render_renderable = oldState -- restore state
			res -- return result
		)
		
		
		on btn_selectSimilar pressed do
		(
			selObjsArr = selection as array
			if selObjsArr.count != 0 do
			(
				similarObjsArr = #()
				
				for i = 1 to selObjsArr.count where isValidNode selObjsArr[i] do
				(
					iObj = selObjsArr[i]
					iMeshData = #(0,0)
					if chkBtn_vertsFaceCount.checked do
					(
						iMeshData = if isShapeObject iObj then 
							(
								GetShapeMeshPolyCount iObj 
							)
							else
							(
								(getPolygonCount iObj) as string
							)
					)
					for j = selObjsArr.count to i + 1 by -1 where isValidNode selObjsArr[j] do
					(
						if chkBox_selectByBBox.checked do
						(
							if (nodeGetBoundingBox iObj iObj.transform) as string == (nodeGetBoundingBox selObjsArr[j] selObjsArr[j].transform) as string do
							(
								appendIfUnique similarObjsArr selObjsArr[j]
								deleteitem selObjsArr j
							)							
						)
						
						if chkBtn_vertsFaceCount.checked and isValidNode selObjsArr[j] do
						(
							if isShapeObject selObjsArr[j] then 
							(
								if (GetShapeMeshPolyCount selObjsArr[j]) == iMeshData do 
								(
									appendIfUnique similarObjsArr selObjsArr[j]
									deleteitem selObjsArr j
								)								
							)
							else 
							(
								if (getPolygonCount selObjsArr[j]) as string == iMeshData do
								(
									appendIfUnique similarObjsArr selObjsArr[j]
									deleteitem selObjsArr j	
								)
							)
					)
					)
				)
				if similarObjsArr.count != 0 do select similarObjsArr
			)
		)
		
	)
	createdialog rol_selectSimilar 	
)

#18

here is how i would search geometrically same objects (i would use mesh vertex positions hash)…
as you can see transform of objects doesn’t matter in this case:

fn roundFloat d pre:0.001 =
(
	d = (d as float)/pre
	v = if (d - (v1 = floor d)) > ((v2 = ceil d) - d) then v2 else v1 
	v*pre
)

fn roundPoint3 p pre:0.001 =
(
	p.x = roundFloat p.x pre:pre
	p.y = roundFloat p.y pre:pre
	p.z = roundFloat p.z pre:pre

	p
)

fn getMeshHash obj round:0 = 
(
	hash = 0
	mesh = obj.mesh 
	for k=1 to mesh.numverts do 
	(
		p = getvert mesh k
		if round > 0 do roundPoint3 p pre:round
		hash = gethashvalue p hash
	)
	hash
)

/*
delete objects
for k=1 to 100 do 
(
	teapot segs:(random 4 6) pos:(random -[100,100,100] [100,100,100]) rotation:(eulerangles 0 0 (random 0 180)) scale:([0.2,0.2,0.2] * (random 1 3))
)

-- find all the same as one selected: 
(
	node = selection[1]
	hash = getMeshHash node
	same = for obj in geometry where (getMeshHash obj) == hash collect obj
)
*/

you can use “round” to have less accuracy but adding some tolerance


#19

you can use the hashing method to find similar (same) bounding boxes as well.
it needs only one point (size) or four points (transform) for hashing


#20

here is a snippet to play with:

try(destroydialog FindSameRol) catch()
rollout FindSameRol "Find Same with denisT" width:191
(
	fn roundFloat d pre:0.001 =
	(
		d = (d as float)/pre
		v = if (d - (v1 = floor d)) > ((v2 = ceil d) - d) then v2 else v1 
		v*pre
	)

	fn roundPoint3 p pre:0.001 =
	(
		p.x = roundFloat p.x pre:pre
		p.y = roundFloat p.y pre:pre
		p.z = roundFloat p.z pre:pre

		p
	)

	fn getBBoxHash obj round:0 hash:0 = 
	(
		--bb = nodeLocalBoundingBox obj
		tm = translate (rotate (scalematrix [1,1,1]) obj.transform.rotation) obj.transform.position
		bb = nodeGetBoundingBox obj tm
		size = bb[2] - bb[1]
		if round > 0 do roundPoint3 size pre:round
		gethashvalue size hash
	)
	fn getAnimHash obj round:0 hash:0 = 
	(
		tm = obj.transform
		for k=1 to 3 do 
		(
			p = tm[k]
			if round > 0 do roundPoint3 p pre:round
			hash = gethashvalue p hash
		)
		hash
	)
	fn getMeshHash obj round:0 hash:0 = 
	(
		mesh = obj.mesh 
		for k=1 to mesh.numverts do 
		(
			p = getvert mesh k
			if round > 0 do roundPoint3 p pre:round
			hash = gethashvalue p hash
		)
		hash
	)

	group "Search: "
	(
		checkbox check_bbox_ch "Check Bounding Box" checked:off offset:[0,0]
		checkbox check_anim_ch "Check Orientation" checked:off offset:[0,0]
		checkbox check_mesh_ch "Check Geometry" checked:on
		
		button find_same_bt "Find Same Objects" width:172 align:#left offset:[-4,4]
		progressbar pb width:172 height:8 color:orange align:#left offset:[-4,-2]
		label info_lb ">> " align:#left offset:[0,0]
	)
	group "Debug: "
	(
		button make_scene_bt "Make Test Scene" width:172 align:#left offset:[-4,0]
	)
	
	fn runCompare source targets compare_fn color: = 
	(
		pb.value = 0.0
		pb.color = color
		hash = compare_fn source

		found = #()
		for k=1 to targets.count do
		(
			pb.value = 100.0 * k / targets.count
			target = targets[k]
			if (compare_fn target) == hash do append found target
		)
		found
	)

	on find_same_bt pressed do undo "Find Same" on 
	(
		if iskindof (source = selection[1]) GeometryClass do
		(
			pb.value = 0.0
			targets = geometry as array

			if check_bbox_ch.state do
			(
				targets = runCompare source targets getBBoxHash color:green
			)
			if check_anim_ch.state do
			(
				targets = runCompare source targets getAnimHash color:blue
			)
			if check_mesh_ch.state do
			(
				targets = runCompare source targets getMeshHash color:orange
			)
			
			if targets.count > 0 do select targets
			info_lb.text = ">> " + targets.count as string + " objects found" 
		)
	)
	
	on make_scene_bt pressed do undo "Make Test Scene" on
	(
		delete objects
		for k=1 to 100 do 
		(
			teapot segs:(random 4 6) pos:(random -[100,100,100] [100,100,100]) rotation:(eulerangles 0 0 (90 *(random 0 3))) scale:([0.2,0.2,0.2] * (random 1 3))
		)
		select objects[random 1 objects.count]
	)
)
createdialog FindSameRol