cartesian algorithm

H’mm i’m looking for the fastest python algorithm for finding a vert in a potentially loooong list of verts that is closest to some other point- could be origin or another vert. It seems that the main problem that I run into is that coordinates are represented by a set of numbers which means you cant use a boolean type search to locate the closest points; when then leaves the only alternative searching through the entire list!! any way to convert cartesian coords to a single number?

Here are some ideas. First of all here is the quickest way to see if a particular point is in a list. But instead of a list, use a dictionary. A dictionary uses a hash code to look up an index. Here is some psuedo code.


points = {}
for vert in mesh:
  x,y,z = vert.loc
  points[(x,y,z)] = "" # we dont care what we associate the tuple with

# Do stuff
x = 1.0
y = 2.5
z = .005

# This is much faster than using a list, especially with alot of data
if (x,y,z) in points:
  # :) Point in list
else:
  # :( Point not in list

This is something to keep in mind since it is faster than a list.
However, this just tells you if the point is IN the dictionary. It does not tell you what the closest point in the list is to the point. This is something I’m not experienced at. I have heard of a concept called a quad tree. It is used alot in 2D(maybe 3D) to check collisions when there is alot to check.

I suppose, you will take data from a mesh, so using

    lst = []
    for i in range(len(me.verts)):
        v = me.verts[i].co
        lst.append([v.x,v.y,v.z])
    lst.sort()

you will strip values from verts coords into a list which is sorted at the end. This sorts the list on X,Y and Z so potentially your list will look like this:


[0.10132741928100586, 2.0, 0.0]
[0.21674680709838867, 1.5, 0.0]
[0.25343704223632813, 2.5, 0.0]
[0.32291173934936523, 6.0, 0.0]
[0.5, 1.2818553447723389, 0.0]
[0.5, 1.5, 0.0]
[0.5, 2.0, 0.0]
[0.5, 2.5, 0.0]
[0.5, 2.9661068916320801, 0.0]
[0.5, 5.8457174301147461, 0.0]
[0.5, 6.0, 0.0]
[0.5, 6.2464179992675781, 0.0]
[0.54273796081542969, 3.0, 0.0]
[0.68223714828491211, 6.5, 0.0]
[0.86597919464111328, 1.0, 0.0]
[1.0, 0.89678502082824707, 0.0]
[1.0, 1.0, 0.0]
[1.0, 1.5, 0.0]
[1.0, 2.0, 0.0]
[1.0, 2.5, 0.0]
[1.0, 3.0, 0.0]
[1.0, 3.36263108253479, 0.0]
[1.0, 5.5857415199279785, 0.0]
[1.0, 6.0, 0.0]
[1.0, 6.5, 0.0]
[1.0, 6.9421653747558594, 0.0]
[1.0415630340576172, 7.0, 0.0]
[1.1649026870727539, 5.5, 0.0]
[1.1732163429260254, 3.5, 0.0]
[1.3508620262145996, 1.0, 0.0]
[1.4972610473632813, 4.0, 0.0]
[1.5, 1.4223089218139648, 0.0]

Then using the algorithm for binary search in a sorted array, you’d find your value fairly quickly. In fact, that is the quickest search algorithm!!! :spin: Please note that you need to apply the search three time with a decreasing range - on X,Y and Z :wink:

Regards,

is there a way to do a B -tree search for a list ?

Thanks

the quad tree concept is the key, i am develop an script to search a point nearstest to another point, but i didnt kow that is called like that, i have done some experiment with the origen only and it is fast! in a few days i will be finish and i will upload for sharing. salutation!

Thanks for the input - i’m not sure what you mean by stripping the val’s from the coords -like take the x,y,z cords and append them as separate items in the list or simply append them as a single list so they’re not in the vector format? Could you show example code of how to search the resulting list? I’ve tried the binary search but the real problem becomes the fact that the items in the list - like -[1,3,5] and [2,-4,1] are sorted only based on the x coord because it’s the first within the sub list meaning that the closest x is found in a binary but not necessarily the closest x and y- in my case i’m not using the z.

can’t wait to see the quad tree code!

Just thought i’d let you know. There is a BpyMesh_octree that you can import from blender. The script is located in \Blender.blender\scripts\bpymodules. You might take a look at it. I dont know if it does what you are needing.

Thanks! i’ll check it out.

This octree code is probably the fastest method i’ve seen yet- basicall it finds the origin of your mesh and devides it into eight 3D quadrants or volumes then if there are more than 128 verts in a quadrant it devides it further into eight subquadrants , essentially creating a tree structure. Testing it revealed that it could find the closest vert to any given point in roughly 0.003 seconds on a dual core!!

where can we find this algo for sorting fast?

and is there a sample test file we can play with?

seems interesting for other things too!

Thanks

i’m taking about this algo

BpyMesh_octree

where cani find this

and if there is a sample small file showing it’s use

Thanks

It can be found in your \Blender.blender\scripts\bpymodules directory - i’ll try to upload my modified version soon - it basically allows you to easily test it on any selected mesh all you need is a simple function that supplies the octree with the mesh data of the selected mesh.

Sorry for the delay - here’s the code!!! NOTE the post didn’t quite fit so the rest of the code is in the next post simply place it below this code to execute it.

import Blender
from Blender import*
import bpy
from bpy import*
#import BPyMesh_octree
#from BPyMesh_octree import*

from Blender.Mathutils import*
#from bpy.datascene import*
DotVecs= Mathutils.DotVecs
#========================================================

SPACIAL TREE - Seperate Class - use if you want to

USed for getting vert is a proximity

LEAF_SIZE = 128
class octreeNode:
def init(self, verts, parent,mtx):

	# Assunme we are a leaf node, until split is run.
	self.verts = verts 
	self.children = []
	
	if parent == None: # ROOT NODE, else set bounds when making children,
		# BOUNDS
		try:
			v= verts[0].cent*mtx
			#print v
		except:
			v=verts[0].co*mtx
		maxx,maxy,maxz= v
		minx,miny,minz= maxx,maxy,maxz
		
		for v in verts:
			try:
				v=v.cent*mtx
			except:
				v=v.co*mtx
			x,y,z= v
			if x>maxx: maxx= x
			if y>maxy: maxy= y
			if z>maxz: maxz= z
			
			if x<minx: minx= x
			if y<miny: miny= y
			if z<minz: minz= z
		
		self.minx= minx
		self.miny= miny
		self.minz= minz
		
		self.maxx= maxx
		self.maxy= maxy
		self.maxz= maxz
		
		# We have no parent to split us so split ourselves.
		#self.setCornerPoints()
		self.splitNode()
		
def splitNode(self):
	if len(self.verts) > LEAF_SIZE:
		self.makeChildren() # 8 new children,
		self.verts = None
	# Alredy assumed a leaf not so dont do anything here.
	
def makeChildren(self):
	verts= self.verts
	# Devide into 8 children.
	axisDividedVerts = [[],[],[],[],[],[],[],[]] # Verts Only
	
	
	divx = (self.maxx + self.minx) / 2
	divy = (self.maxy + self.miny) / 2
	divz = (self.maxz + self.minz) / 2
	# Sort into 8
	for v in verts:
		try:
			x,y,z=v.cent*mtx
		except:
			x,y,z = v.co*mtx
		
		if x > divx:
			if y > divy:
				if z > divz:
					axisDividedVerts[0].append(v)
				else:
					axisDividedVerts[1].append(v)
			else:
				if z > divz:
					axisDividedVerts[2].append(v)
				else:
					axisDividedVerts[3].append(v)
		else:
			if y > divy:
				if z > divz:
					axisDividedVerts[4].append(v)
				else:
					axisDividedVerts[5].append(v)
			else:
				if z > divz:
					axisDividedVerts[6].append(v)
				else:
					axisDividedVerts[7].append(v)
				
	# populate self.children
	for i in xrange(8):
		octNode = octreeNode(axisDividedVerts[i], self,mtx)
		# Set bounds manually
		if i == 0:
			octNode.minx = divx
			octNode.maxx = self.maxx
			octNode.miny = divy
			octNode.maxy = self.maxy
			octNode.minz = divz
			octNode.maxz = self.maxz
		elif i == 1:
			octNode.minx = divx
			octNode.maxx = self.maxx
			octNode.miny = divy
			octNode.maxy = self.maxy
			octNode.minz = self.minz #
			octNode.maxz = divz #
		elif i == 2:
			octNode.minx = divx
			octNode.maxx = self.maxx
			octNode.miny = self.miny  # 
			octNode.maxy = divy #
			octNode.minz = divz
			octNode.maxz = self.maxz
		elif i == 3:
			octNode.minx = divx
			octNode.maxx = self.maxx
			octNode.miny = self.miny #
			octNode.maxy = divy #
			octNode.minz = self.minz #
			octNode.maxz = divz #
		elif i == 4:
			octNode.minx = self.minx #
			octNode.maxx = divx #
			octNode.miny = divy
			octNode.maxy = self.maxy
			octNode.minz = divz
			octNode.maxz = self.maxz
		elif i == 5:
			octNode.minx = self.minx #
			octNode.maxx = divx #
			octNode.miny = divy
			octNode.maxy = self.maxy
			octNode.minz = self.minz #
			octNode.maxz = divz #
		elif i == 6:
			octNode.minx = self.minx #
			octNode.maxx = divx #
			octNode.miny = self.miny  # 
			octNode.maxy = divy #
			octNode.minz = divz
			octNode.maxz = self.maxz
		elif i == 7:
			octNode.minx = self.minx #
			octNode.maxx = divx #
			octNode.miny = self.miny  # 
			octNode.maxy = divy #
			octNode.minz = self.minz #
			octNode.maxz = divz #
		#octNode.setCornerPoints()
		octNode.splitNode() # Splits the node if it can.
		self.children.append(octNode)

GETS VERTS IN A Distance RANGE-

def getVertsInRange(self, loc, normal, range_val, vertList,D):
	#loc= Mathutils.Vector(loc)			# MUST BE VECTORS
	#normal= Mathutils.Vector(normal)	

	'''
	loc: Vector of the location to search from
	normal: None or Vector - if a vector- will only get verts on this side of the vector
	range_val: maximum distance. A negative value will fill the list with teh closest vert only.
	vertList: starts as an empty list
	list that this function fills with verts that match
	'''
	xloc,yloc,zloc= loc
	try :
		self.verts[0]
		try:
			self.verts[0].co
		except:
			print 'must be called on a vert octree not face'
			return
	except:
		d=' do nothing'
	
	if range_val<0:
		range_val= -range_val
		FIND_CLOSEST= True

	else:
		FIND_CLOSEST= False

	if self.children:
		# Check if the bounds are in range_val,
		if FIND_CLOSEST and D=='3D':
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx  <= xloc and\
				childNode.maxx  >= xloc and\
				childNode.miny  <= yloc and\
				childNode.maxy  >= yloc and\
				childNode.minz  <= zloc and\
				childNode.maxz  >= zloc:
					# Recurse down or get virts.
					childNode.getVertsInRange(loc, normal, -range_val, vertList,D)
				else:
					print 'false'
		elif FIND_CLOSEST and D=='2D':
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx  <= xloc and\
				childNode.maxx  >= xloc and\
				childNode.miny  <= yloc and\
				childNode.maxy  >= yloc :
					# Recurse down or get virts.
					childNode.getVertsInRange(loc, normal, -range_val, vertList,D)	
		elif not FIND_CLOSEST and D=='3D':
			#print 'notp closest'
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx - range_val < xloc and\
				childNode.maxx + range_val > xloc and\
				childNode.miny - range_val < yloc and\
				childNode.maxy + range_val > yloc and\
				childNode.minz - range_val < zloc and\
				childNode.maxz + range_val > zloc:
					# Recurse down or get virts.
					childNode.getVertsInRange(loc, normal, range_val, vertList,D)
		elif not FIND_CLOSEST and D=='2D':
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx  <= xloc and\
				childNode.maxx  >= xloc and\
				childNode.miny  <= yloc and\
				childNode.maxy  >= yloc :
					# Recurse down or get virts.
					childNode.getVertsInRange(loc, normal, range_val, vertList,D)		
	
	else: # we are a leaf node. Test vert locations.
		#print self.verts[0]
		if D=='2D':
			loc=Vector(loc.x,loc.y,0)
		if not normal:
			# Length only check
			best=None
			for v in self.verts:
				co=v.co*mtx
				if D=='2D':
					co=Vector(co.x,co.y,0)
					
				length = (loc - co).length
				if length < range_val:
					#print range_val
					if FIND_CLOSEST:
						# Just update the 1 vert
						best= (length,co)
						#print length
						range_val= length # Shrink the length so we only get verts from their.
					
					else:
						vertList.append((length,co))
			
			if best and FIND_CLOSEST== True:

co=best[1].co*mtx

				length,coords=best
				vertList+=[length,coords]
				#print best
			
		else:
			for v in self.verts:
				co=v.co*mtx
				if D=='2D':
					co=Vector(co.x,co.y,0)
				length = (loc - co).length
				if length < range_val:
					# Check if the points in front
					dot= DotVecs(normal, loc) - DotVecs(normal,co)
					if dot<0:
						vertList.append((length,co))
def findclstintsct(self,loc,range,intersectlist,D):
	''' this was added
	loc:   Vector of the location to search from
	range:   maximum distance to find any faces
	intersectlist:   an empty list which will contain the intersection of the closest face after the function runs
	D:   the space to operate in either '2D' or '3D' 2d finds the closest face based only on x,y 3d finds closest x,y,z
	'''
	try :
		self.verts[0]
		try:
			self.verts[0].verts
		except:
			print 'must be called with a face octree not vert'
			return
	except:
		d='do nothing'
		
	if D =='2D':
		loc=Vector(loc.x,loc.y,0)
	xloc,yloc,zloc=loc
	if self.children:
		#print self.children
		# Check if the bounds are in range_val,
		if  D=='3D':
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx  <= xloc and\
				childNode.maxx  >= xloc and\
				childNode.miny  <= yloc and\
				childNode.maxy  >= yloc and\
				childNode.minz  <= zloc and\
				childNode.maxz  >= zloc:
					# Recurse down or get virts.
					childNode.findclstintsct(loc,range,intersectlist,D)
				else:
					print 'false'
		elif  D=='2D':
			for childNode in self.children:
				# First test if we are surrounding the point.
				if\
				childNode.minx  <= xloc and\
				childNode.maxx  >= xloc and\
				childNode.miny  <= yloc and\
				childNode.maxy  >= yloc :
					# Recurse down or get virts.
					childNode.findclstintsct(loc,range,intersectlist,D)
	else: # we are a leaf node. Test vert locations.

print self.verts[0]

		best=None
		for f in self.verts:
			if D=='2D':
				cent=Vector(f.cent.x,f.cent.y,0)*mtx
			elif D=='3D':
				cent=f.cent*mtx
			length = (loc - cent).length
			if length < range:
				# Just update the 1 vert
				best= (length,f)
				#print length
				range= length # Shink the length so we only get verts from their.
				
		if best:
			f=best[1].verts
			v1=f[0].co*mtx
			v2=f[1].co*mtx
			v3=f[2].co*mtx
			point=Intersect(v1,v2,v3,Vector(0,0,-100),Vector(loc.x,loc.y,100),0)
			intersectlist+=[best[0],point]

END TREE

THIS SECTION WILL CALL THE OCTREE ON A SELECTED MESH AND FIND THE CLOSEST VERT TO A GIVEN POINT

#clverts=[]
#sc=data.scenes.active
#ob=sc.objects
#me=ob.active.getData(mesh=1)
#verts=me.verts
#faces=me.faces
#mtx=ob.active.mat
#meoctree=octreeNode(faces,None,mtx)
#def exe():

#print len(curve)

#curve=ob[0].data[0]

for p in [Vector(2,1.4,-0.94),Vector(-0.3,2,-0.32)]:

x,y,z=p

#print x,y,z

#meoctree.getVertsInRange(Vector(x,y,z),None,-10,clverts,‘2D’)

meoctree.findclstintsct(Vector(x,y,z),3,clverts,‘2D’)

clverts.sort()

print clverts[:]#[0][0].co#.co,clverts[1]

#def main():

Window.WaitCursor(1)

t = sys.time()

print

print ‘start’

print

exe()

# Timing the script is a good way to be aware on any speed hits when scripting

print ‘My Script finished in %.4f seconds’ % (sys.time()-t)

Window.WaitCursor(0)

#main()

I thought cartesian coordinates were a pretty inventive plot element in the CUBE movie. :smiley:

For future reference (or if you want to fix that post) if you enclose the sections of code in CODE tags it will keep the formatting.