2
$\begingroup$

I have two surfaces, representing the inner and outer surface of a skull. To calculate the thickness, I thought about calculating the length of each inner surface's vertex normal for the vertex until it crosses the outer surface. I'm trying to use these fantastic functions: https://docs.blender.org/api/blender_python_api_current/mathutils.geometry.html

You can take a look at the surfaces here:

Any help will be most welcome! Thanks, Noam

***** Edit ******

I tried to use ray_cast as @batFINGER suggested. I might do something wrong, but it gives me strange results:

inner_skull = bpy.data.objects['inner_skull']
vert = inner_skull.data.vertices[0]
hit, loc, norm, face = inner_skull.ray_cast(vert.co, vert.normal)

I ran this code after making sure all the normals are pointing outside. That gave me the same vertice pos as the hit location. So I tried to add the normal to the starting point:

hit, loc, norm, face = inner_skull.ray_cast(vert.co + vert.normal, vert.normal)

That gave me no hit at all. (I check and vert.co + vert.normal doesn't cross the outer skull)

Any suggestions?

***** Edit 2 *****

I ended up using the following code, which is really not optimized, but does work:

def check_vert_intersections(vert, surf_obj):
    for face_ind, face in enumerate(surf_obj.data.polygons):
        face_verts = [surf_obj.data.vertices[vert].co for vert in face.vertices]
        intersection_point = mathutils.geometry.intersect_ray_tri(
            face_verts[0], face_verts[1], face_verts[2], vert.normal, vert.co, True)
        if intersection_point is not None:
            return intersection_point, face_ind
    return None, -1

***** Edit 3 *****

I used @batFINGER solution and it's great! I've just changed the loop to go over the vertices and not the faces, because I needed the thickness to be calculated for each vertice. You can see a video of the solution here: https://www.dropbox.com/s/xik3ucmnc94qaba/skull_points_thickness.mp4?dl=0

$\endgroup$
2
  • 2
    $\begingroup$ think object.ray_cast is probably your best bet. Use the vert.co and -vert.normal as ray origin and direction respectively, from inner to outer or vice versa. $\endgroup$
    – batFINGER
    Oct 4, 2017 at 20:14
  • $\begingroup$ Your check_vert_intersections will "work" as long as each objects matrix_world is identity (and maybe same) otherwise need to make compensation of local coordinate space of one to other. Your attempt at ray_cast was from inner_skull out from a vertex normal to inner_skull. Hence the lack of a result. $\endgroup$
    – batFINGER
    Oct 5, 2017 at 12:03

1 Answer 1

2
$\begingroup$

Raycasting from inner to outer

Code to shoot a ray from each inner object face centre along the normal, to the outer object. Object.ray_cast(...) the parameters need to be in the local space of the object we are casting on.

Some notes. The code below gives expected result for a cube within a cube, or sphere within a sphere, as the face normal is the same direction (with origin at centre of geom) as face.centre - origin. For your object, might want to have some test where normal isn't in the direction of closest point on outer. For example on the slope of bumps. (hope that's not too confusing)

import bpy
context = bpy.context
scene = context.scene
show_hit = True # add empty at hit.

def draw_empty_arrow(loc, dir, draw_size=1):
    R = (-dir).to_track_quat('Z', 'X').to_matrix().to_4x4()
    mt = bpy.data.objects.new("mt", None)
    R.translation = loc + dir
    #mt.show_name = True 
    mt.matrix_world = R
    mt.empty_draw_type = 'SINGLE_ARROW'
    mt.empty_draw_size = dir.length
    scene.objects.link(mt)

# check thickness by raycasting from inner object out.
# select inner and outer obj, make inner active
inner_obj = context.active_object
outer_obj = context.selected_objects[1]
omwi = outer_obj.matrix_world.inverted()

imw = inner_obj.matrix_world
omw = outer_obj.matrix_world
mat = omwi * imw
hits = [] # vectors from inner to outer
for face in inner_obj.data.polygons:
    print("face", face.index)
    o = mat * face.center
    n = mat * (face.center + face.normal) - o
    hit, loc, norm, index = outer_obj.ray_cast(o, n)
    if hit:
        print("hit outer on face %d" % index)
        hits.append((o, loc))

# test on cube, cylinder ok.
# print((outer_obj.dimensions.x - inner_obj.dimensions.x) / 2)
if hits:        
    avge_thickness = sum((omw * hit - omw * o).length for o, hit in hits) / len(hits)
    print(avge_thickness)  
    if show_hit:
        for hit, o in hits:
            draw_empty_arrow(omw * o, omw * hit - omw * o, draw_size=1)

enter image description here Result of running on sphere within a sphere (the outer sphere is wireframe) Empties drawn from inner face to outer hit.

$\endgroup$
1
  • $\begingroup$ Thanks so much! I've just changed the main loop to go over the vertices because I need the thickness to be per vertice. The arrows are amazing! Finally I can check if the solution makes sense or not... $\endgroup$
    – Noam Peled
    Oct 5, 2017 at 16:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .