# How to find if a point is inside a mesh?

I need a python script that would help me determine if a point is inside a cylindrical mesh.

I tried the .pointInside(point, selected_all=False) but it is deprecated in the newer versions of blender and google search yielded a couple of scripts (below) and they don't work for me.

import bpy
import mathutils
def pointInsideMesh(point,ob):
axes = [ mathutils.Vector((1,0,0)) ]
outside = False
for axis in axes:
mat = ob.matrix_world
mat.invert()
orig = mat*point
count = 0
while True:
location,normal,index = ob.ray_cast(orig,orig+axis*10000.0)
if index == -1: break
count += 1
orig = location + axis*0.00001
if count%2 == 0:
outside = True
break
return not outside
print(pointInsideMesh( mathutils.Vector((3,0,0)),bpy.context.active_object))


## normals should be pointing out
import bpy
obj = bpy.context.object
cur = bpy.context.scene.cursor_location.copy()
cur = obj.matrix_world.inverted() * cur
cpom = obj.closest_point_on_mesh(cur)
vec = cur - cpom
dot = cpom.dot(vec)
if dot < 0.0: print(dot, 'inside')
else: print(dot, 'outside')


let's look at the documentation for object.ray_cast(start, end).

The ray_cast function returns 3 values: (location, normal, index):

location, The hit location of this ray cast, float array of 3 items in [-inf, inf]

normal, The face normal at the ray cast hit location, float array of 3 items in [-inf, inf]

index, The face index, -1 when no intersection is found, int in [-inf, inf]

It will return the index of the first face encountered on the path between start and end Vectors.

• If the start vector is outside of the Object, and the face index is -1, you already know the point is not inside the object.
• But if it does return a face index, then you start counting how many consecutive faces it intersects by doing a ray_cast from the Vector of the most recent intersection (plus a small offset towards the destination to push it away from the most recent face) to the end point.
• When at some point the face index returns -1, you know there are no more faces between the checked point and the end point, then you add up the total number of intersections.
• If that number is even, it went in and out, and is currently out.
• If it's odd, it's still inside.

In code that might look something like this:

def is_inside(ray_origin, ray_destination, obj):

# the matrix multiplations and inversions are only needed if you
# have unapplied transforms, else they could be dropped. but it's handy
# to have the algorithm take them into account, for generality.
mat = obj.matrix_local.inverted()
f = obj.ray_cast(mat * ray_origin, mat * ray_destination)
loc, normal, face_idx = f

if face_idx == -1:
return False

max_expected_intersections = 1000
fudge_distance = 0.0001
direction = (ray_destination - loc)
dir_len = direction.length
amount = fudge_distance / dir_len

i = 1
while (face_idx != -1):

loc = loc.lerp(direction, amount)
f = obj.ray_cast(mat * loc, mat * ray_destination)
loc, normal, face_idx = f
print(face_idx)
if face_idx == -1:
break
i += 1
if i > max_expected_intersections:
break

return not ((i % 2) == 0)


Here a test blend using Sverchok Scripted Node with that algorithm.  caveat: The fudge distance is not very nicely calculated, if might help precision to repeat the algorithm from a few randomly picked points around the object, and take the most common return value.

edit: I just realized you can track the indices of intersected faces and adjust the fudge factor of the ray until the ray_cast no longer returns the index of a previously intersected face, letting it progress on..

### Another approach

using obj.closest_point_on_mesh. Offered by Kosvor on sverchok issue tracker:

def is_inside(p, max_dist, obj):
# max_dist = 1.84467e+19
point, normal, face = obj.closest_point_on_mesh(p, max_dist)
p2 = point-p
v = p2.dot(normal)
print(v)
return not(v < 0.0)


this assumes all faces of the object are pointing outwards  A non node version

Here's a bmesh version I've been using for when the data isn't in a Mesh datatype.

from mathutils import Vector
from mathutils.bvhtree import BVHTree

def are_inside(points, bm):
"""
input:
points
- a list of vectors (can also be tuples/lists)
bm
- a manifold bmesh with verts and (edge/faces) for which the
returns:
a list
- a mask lists with True if the point is inside the bmesh, False otherwise
"""

rpoints = []
bvh = BVHTree.FromBMesh(bm, epsilon=0.0001)

# return points on polygons
for point in points:
fco, normal, _, _ = bvh.find_nearest(point)
p2 = fco - Vector(point)
v = p2.dot(normal)

return rpoints Here I show a Vector Grid (points) and a Torus (a bmesh). The red dots are outside (False), and white dots are inside (True).

### warning

• This doesn't produce desired results on low poly meshes.
• subdividing low poly meshes for the sake of inputting them into the algorithm, also doesn't work.

a better solution will come.

• start with known point outside of mesh...ray_cast from outsize known point to test point. At each find, move epsilon....ray_cast again until no intersection. Odd number of intersections = inside, even number = outside. Can test each corner of bounding box to avoid low angle errors "slipping through." I plan to write this algorithm sometime soon :-) Nov 27, 2017 at 2:01

If false-inside positives are important to you, consider the following tweak of the dot-product method.

One issue with the dot-product method is that if the angle between the closest-face normal vector and point-closest-mesh-point vector is close to 90 degrees, rounding errors will result in some points that are outside of the mesh to be classified as inside.

Below method adds a tolerance (in degrees) parameter:

from math import pi, acos

def is_inside(target_pt_global, mesh_obj, tolerance=0.05):

# Convert the point from global space to mesh local space
target_pt_local = mesh_obj.matrix_world.inverted() * target_pt_global

# Find the nearest point on the mesh and the nearest face normal
_, pt_closest, face_normal, _ = mesh_obj.closest_point_on_mesh(target_pt_local)

# Get the target-closest pt vector
target_closest_pt_vec = (pt_closest - target_pt_local).normalized()

# Compute the dot product = |a||b|*cos(angle)
dot_prod = target_closest_pt_vec.dot(face_normal)

# Get the angle between the normal and the target-closest-pt vector (from the dot prod)
angle = acos(min(max(dot_prod, -1), 1)) * 180 / pi

# Allow for some rounding error
inside = angle < 90-tolerance

return inside


The downside, however, is that due to the tolerance parameter, there might be some points that are inside the mesh that will be classified as outside (false negatives). Adjust tolerance based on your sensitivity to false-positives.

In my test with ~25K random points, tolerance of 0.02 eliminated all false-positives without any false-negatives.

• I am trying to figure out if a point is inside an object. It's throwing the following errors - for the first line inside the function ""Element-wise multiplication: not supported between 'Matrix' and 'Vector' types"" Oct 13, 2020 at 16:31
• @Sourav I can't test this right now, but try replacing '*' with '@' in the first line. It looks like a blender 2.8 issue (see blender.stackexchange.com/questions/129473/…) Oct 13, 2020 at 18:47
• Yeah, the * has to be replaced with @. However, the script is producing tons of false positibe results for me. Oct 13, 2020 at 18:49
def insideMesh(x,y,z, mesh):
p = Vector((x,y,z))
max_dist = 1.0e+20
hit, point, normal, face = mesh.closest_point_on_mesh(p, max_dist)
p2 = point-p
v = p2.dot(normal)
return not(v < 0.0)


E.G.

def add_cube(x,y,z,r):
return bpy.context.active_object

#add cube with dimension = 10 on x,y,z
mesh =  bpy.data.objects['Cube']
print('inside: '+str(insideMesh(0,0,-5,mesh)))
#(0,0,-5) results true since mesh{x,y,z}  is bounded by  -r to r


don't forget

import bpy
from mathutils import Vector

• Been watching this one for the improvement.. but closest_point_on_mesh is quite expensive? If you're assuming the mesh is right-way-out and closed, won't shooting a ray down X do? Jan 8, 2020 at 12:01
• you 're right, couldn't get the coordinates to work correctly with the earlier ray cast approach so it had to be this Jan 8, 2020 at 16:00

i numpyfied the version of @zeffi


def numpy_apply_transforms(ob, co):
m = np.array(ob.matrix_world)
mat = m[:3, :3].T
loc = m[:3, 3]
return co @ mat + loc

def volume_boolean_data(vg_name, bool_name, reverse=True):

_t = time.time()

target_object = bpy.context.object
bool_object   = bpy.data.objects[bool_name]

#create mesh data
bm = bmesh.new()
bm.from_mesh(bool_object.data)
bmesh.ops.transform(bm, matrix=bool_object.matrix_world, verts=bm.verts) #local to global coord
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
bvh = BVHTree.FromBMesh(bm, epsilon=0.0001)

_d = datetime.timedelta(seconds=time.time()-_t)
print(f"after bmesh {_d}")

#create array of points
l = len(target_object.data.vertices)
points = np.zeros((l * 3), dtype=np.float, )
target_object.data.vertices.foreach_get('co',points)
points.shape = (l, 3, )
points = numpy_apply_transforms(target_object, points) #local to global coord

_d = datetime.timedelta(seconds=time.time()-_t)
print(f"after points apply transform {_d}")

def check_if_inside(row):
nonlocal bvh, reverse
fco, normal, _, _ = bvh.find_nearest(row)
p2 = fco - Vector(row)
v  = p2.dot(normal)
return v < 0.0

def check_if_inside_reverse(row):
nonlocal bvh, reverse
fco, normal, _, _ = bvh.find_nearest(row)
p2 = fco - Vector(row)
v  = p2.dot(normal)
return not v < 0.0

if reverse: values = np.apply_along_axis(check_if_inside, 1,points)
else: values = np.apply_along_axis(check_if_inside_reverse, 1,points)

_d = datetime.timedelta(seconds=time.time()-_t)
print(f"after along axis in {_d}")

if vg_name not in o.vertex_groups:
vg = o.vertex_groups.new(name=vg_name)
else:  vg = o.vertex_groups[vg_name]

for i, v in enumerate(values):

_d = datetime.timedelta(seconds=time.time()-_t)
print(f"finished in {_d}")

return


• Can you provide an example how to use the volume_boolean_data() function. What is o? For example in my case scenario I would like to check if points of one object are inside (or overlapping with) another. First I randomly put some objects inside another and then I want to check whether the object in the new location is within the boundaries of another or not blender.stackexchange.com/questions/212266/… . Feb 25, 2021 at 13:34
def is_inside(point,ob):
box_size = 0.15
location=point, scale=(box_size,box_size,box_size))
box=bpy.data.objects[bpy.context.active_object.name]
try:
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT'
bpy.context.object.modifiers["Boolean"].object =    ob
bpy.ops.object.modifier_apply(modifier="Boolean")
result = (box.dimensions.x >= box_size) and (box.dimensions.y >= box_size) and (box.dimensions.z >= box_size)
return result
finally:


None of the other answers worked for me with an object that I imported from OBJ files, not sure if that was the cause. The object was plane that was then made into a mesh with solidify. The above code is my hack that works, but is slow.

I use the following code to drive the method: obj_object is the mesh.

The flipping of z with y is because I am targeting Table Top Simulator. delta = 0.5

nb_x = math.floor(obj_object.dimensions.x / delta)
nb_z = math.floor(obj_object.dimensions.z / delta)

half_x = obj_object.dimensions.x / 2
half_z = obj_object.dimensions.z / 2
x = -half_x
while x <= half_x :
z = -half_z
while z <= half_z :
p = mathutils.Vector((x, z, 0))
if is_inside(p, obj_object) :
print("{x=%f, y=%f}," % (x,z))
z = z + delta
x = x + delta