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

