# Select shortest path with python

I am using Blender 2.78. I have found 2 vertices that I want to automate measuring the shortest path between them on the surface. I have written the following function that I give the object that I found the vertices on and the vertices themselves, it looks like this.

def surf_len(ob, path):
# first select the object in edit mode
# and select the vertex path to measure
ob.select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
for vert in path:
vert.select=True
bpy.context.object.update_from_editmode()

# select the shortest path
r = bpy.ops.mesh.shortest_path_select()
bpy.ops.object.mode_set(mode='OBJECT')
ob.select = False
if 'CANCELLED' in r:
raise ValueError('Cannot build shortest path: {}'.format(r))


However when I run this code I always get back CANCELLED and get the exception thrown even though I think I have selected valid vertices.

I also read somewhere that I might need to use the bmesh module for this so I did modify the code like this

def surf_len(ob, path):
# first select the object in edit mode
# and select the vertex path to measure
ob.select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
me = ob.data
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
for vert in path:
bm.verts[vert.index].select_set(True)

# select the shortest path
r = bpy.ops.mesh.shortest_path_select()
bpy.ops.object.mode_set(mode='OBJECT')
ob.select = False
if 'CANCELLED' in r:
raise ValueError('Cannot build shortest path: {}'.format(r))


But this always raises ValueError: The mesh must be in editmode which is weird because when I run this from the console within blender, rather than as a start up script, I can see it working

I would really appreciate any help.

Cheers

OK sweet I managed to figure this one out myself with a lot of perseverance (and grumbling) ... This is the final code I needed

def surf_len(ob, path):
# first deselect all other objects and select our target object
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
ob.select = True
bpy.context.scene.objects.active = ob

# next select all the vertices in out path
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_mode(type="VERT")
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
for vi in vert_indices:
ob.data.vertices[vi].select = True

# select the shortest path
bpy.ops.object.mode_set(mode='EDIT')
r = bpy.ops.mesh.shortest_path_select()
ob.select = False
bpy.ops.object.mode_set(mode='OBJECT')
if 'CANCELLED' in r:
raise ValueError('Cannot build shortest path for {}: {}'.format(vert_indices, r))


I think the main thing I needed was the vertices objects I was passing in, that I had found earlier, had completely crazy indexes and just were not the same ones I found for some reason, this seemed to happen after I added a bunch of other objects to the scene

So instead I record the vertices index when I found it on the object originally and then use those for the select which seems to work .... I don't know why this happens, all I was doing was saving a list of ob.data.vertices[vert_index] to a list, when I came to use them later the vert_index were ok to be used in the function above, but the actual vertices seemed to be no good ... ie the code did something like this

    vert_list = []
for face in ob.data.polygons:
for vert_index, loop in zip(face.vertices, face.loop_indices):
vert_list.append(ob.data.vertices[vert_index])


If anyone knows why this vert_list is no good after a bit of editing to the scene I would be very interested.

Anyway once I figure out that problem, the rest was just a matter of fiddling about to get the correct modes etc ... I think bpy.context.scene.objects.active = ob helped too

Cheers all.

Ok, I don't expect this answer to be accepted as a full answer, because I think you just want the existing bpy.ops to work with the correct context

1. The shortest path bpy.ops.mesh.shortest_path_select() can only find the path across the surface which uses existing edges and verts. This is not necessarily the shortest path on the surface

2. To find the shortest path on the surface (as if a ball could roll not constrained to the verts and edges) you will need a geodesic distance field, and then "walk" back up the field to measure the distance. This will still most likley be an approximation, but a much more accurate aproximation than if you have to wind your way across edges.

So, if you are ok with the limitations of #1, then solving that problem is easy.

If you are hoping to achieve what I described in #2, then all the information you need is in this blender-artist post including links to hosted code