2
$\begingroup$

I know the result of the ray_cast call is (result, location, normal, index) as stated in the documentation. But what is the ray_cast result in case the ray exactly hits a vertex? It seems that in my tests, just one random linked face is given in the index result. How can I get all linked faces then?

$\endgroup$

1 Answer 1

5
$\begingroup$

Using bmesh

The result gives you the face index. Can look directly at the mesh data, however I'm going to use bmesh since the linked geometry is worked out for us.

In this example I'm using scene raycast. It's much the same beast.

  • Cast the ray into scene from global (10, 10, 10) in the direction (-1, -1, -1). This will hit the default cube at the vertex at (1, 1, 1)
  • From the resultant face, get the face and how far it is from the location of the hit. The hit is put into local object space by multiplying it by the inverted returned matrix world of the obect. Get the distance from the hit to the face center.
  • For each edge in face, calculate the distance from edge middle to hit.
  • Lastly for each vert the distance from vert coordinate to hit.
  • Order the above by distance from hit.
  • If the hit is closest to a vertex or an edge show the linked faces.

Script.

import bpy
import bmesh
from mathutils import Vector

context = bpy.context
vl = context.view_layer

scene = context.scene

ob = context.object
me = ob.data
bm = bmesh.new()

o = (10, 10, 10) # ray origin
d = (-1, -1, -1) # ray direction

hit, loc, norm, idx, obj, mw = scene.ray_cast(vl, o, d)

if hit:
    result = {}
    loc = mw.inverted() @ loc
    bm.from_mesh(me)
    bm.faces.ensure_lookup_table()
    f = bm.faces[idx]
    result[f] = (loc - f.calc_center_median()).length
    for e in f.edges:
        mp = (e.verts[1].co + e.verts[0].co) / 2
        result[e] = (loc - mp).length
    for v in f.verts:
        result[v] = (v.co - loc).length
    print("-" * 33)
    for e, d in sorted(result.items(), key=lambda e: e[1]):
        print(type(e).__name__, e.index, d)
        if hasattr(e, "link_faces"):
            print([f.index for f in e.link_faces])

Result on default cube.

---------------------------------
BMVert 4 1.7691638564653194e-06
[5, 2, 1]
BMEdge 9 0.9999988079071045
[5, 1]
BMEdge 2 0.9999990463261543
[5, 2]
BMFace 5 1.4142120450860174
BMVert 0 1.9999990463258834
[2, 0, 5]
BMVert 7 1.999999046325911
[5, 1, 4]
BMEdge 7 2.2360664847631893
[5, 4]
BMEdge 1 2.2360666180432514
[5, 0]
BMVert 3 2.828425523165797
[5, 0, 4]

Related

What is the space used by Scene.ray_cast()?

Strange object.ray_cast() behavior

Finding neighboring mesh faces

$\endgroup$
2
  • $\begingroup$ scene.ray_cast never returns a hit although obj.ray_cast returns many hits if I use specific objects. Are there any requirements, e.g. before running this code, select all objects that should be tested or anything else? $\endgroup$ Commented Apr 27, 2020 at 6:25
  • $\begingroup$ The example above shows a hit from scene raycast onto the default cube in default scene. Run the script on default scene and should get result as shown. Transform the cube, or change the ray source and direction to hit other geometry. Scene raycasts in a global direction. Added links to explain the difference in raycast arguments, and example of finding neighbouring mesh faces. $\endgroup$
    – batFINGER
    Commented Apr 27, 2020 at 9:03

You must log in to answer this question.

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