I see I'm late, but here's my take on this problem:
import bpy, bmesh
from collections import defaultdict
from functools import reduce
from mathutils import Vector
mesh = bpy.context.edit_object.data
bm = bmesh.from_edit_mesh(mesh)
active = bm.select_history.active
found = {} # key: vertex object, value: number of connected vertices, which are selected
def recursive_add(found_vertex):
found[found_vertex] = 0
for edge in found_vertex.link_edges:
for vertex_candidate in edge.verts:
if vertex_candidate is not found_vertex and vertex_candidate.select:
found[found_vertex] += 1
if vertex_candidate not in found:
recursive_add(vertex_candidate)
recursive_add(active)
ends = [vertex for vertex, connections in found.items() if connections == 1]
origin = ends[0].co
offset = ends[1].co - origin
individual_offsets = [0]
current_path_length = 0
def analyze_path(a, b):
global current_path_length
distance = (a.co - b.co).length
individual_offsets.append(current_path_length + distance)
current_path_length += distance
return b
reduce(analyze_path, found)
# Edit: as @batFINGER mentioned, OP asks to keep the original length of the path:
ratio = current_path_length / offset.length
offset *= ratio
for v, individual_offset in zip(found, individual_offsets):
print(origin, individual_offset, offset)
v.co = origin + (individual_offset / current_path_length) * offset
bmesh.update_edit_mesh(mesh)
at least one of selected vertices has to be active, and it only works in vertex selection mode:
Edit: as pointed by batFINGER, this didn't keep the length of the path, so I added a small update: