How can i retrieve the list of selected verts/edges/faces after selecting the shortest_path_select() ?
I can see in the viewport that the correct faces are being selected, but from code i cannot see them selected, neither from the bmesh neither from the mesh itself.
As you can see in the image, after the call to bpy.ops.mesh.shortest_path_select() i see the faces selected in the viewport, but from code only the face 655 of the mesh results selected.
After calling the function i DO call bmesh.update_edit_mesh(me) but still it seems not to work as wanted.
the most straightforward way to determine this would be to create two sets and then get the difference between them.
before_selection = {v for v in bm.verts if v.select}
bpy.ops.mesh.shortest_path_select()
after_selection = {v for v in bm.verts if v.select}
shortest_path_verts = after_selection.difference(before_selection)
it’s worth pointing out that this will not give you the path that was selected in the sense that it is sequential, it’s just going to be a chunk of verts in whatever order Blender decided to return them in. If you need a shortest path that is actually in sequential order you’d be better off writing your own pathfinding algorithm, which by the way is not as hard as it sounds. With a python Queue object you can write an A* function in about 20 lines of code.
class MESH_OT_path_test(bpy.types.Operator):
"""test shortest path"""
bl_idname = "mesh.path_test"
bl_label = "path_test"
bl_options = {'REGISTER'}
@classmethod
def poll(cls, context):
active_object = context.active_object
return active_object is not None and active_object.type == 'MESH' or active_object.select_get() and context.area.type == "VIEW_3D"
def execute(self, context):
# Get the active mesh
me = context.object.data
# Get a BMesh representation
bm = bmesh.new() # create an empty BMesh
bm.from_mesh(me) # fill it in from a Mesh
# Modify the BMesh, can do anything here...
if hasattr(bm.verts, "ensure_lookup_table"):
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode="EDIT")
bm.faces[655].select = True
bm.faces[5071].select = True
before_selection = {f for f in bm.faces if f.select}
bpy.ops.mesh.shortest_path_select()
after_selection = {f for f in bm.faces if f.select}
shortest_path_faces = after_selection.difference(before_selection)
self.report({"INFO"}, f"len bef {len(before_selection)}")
self.report({"INFO"}, f"len aft {len(after_selection)}")
self.report({"INFO"}, f"spf {len(shortest_path_faces)}")
for f in shortest_path_faces:
bm.faces[f.index].select = True
me.polygons[f.index].select = True
bpy.ops.object.mode_set(mode="OBJECT")
bm.to_mesh(me)
bm.free()
bpy.ops.object.mode_set(mode="EDIT")
return {'FINISHED'}
and this is the output:
The only faces selected are the ones i do from code.
What am i doing wrong?
The problem is that you’re creating a bmesh from scratch and then giving it data from object mode- which means the select_shortest_path operator’s changes aren’t reflected. If you need to use a bmesh object in this way you’ll need to force the bmesh data to be updated each time you make a change to it.
Far easier to just run the operator from edit mode and then create your bmesh object from the edit mesh.
import bpy, bmesh
obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
#selected_faces = {f for f in bm.faces if f.select}
#print(selected_faces)
before_selection = {f for f in bm.faces if f.select}
bpy.ops.mesh.shortest_path_select()
after_selection = {f for f in bm.faces if f.select}
shortest_path_faces = after_selection.difference(before_selection)
print([f.index for f in shortest_path_faces])