Skip to main content
3 of 3
removed `print` statement and unused `import`
Markus von Broady
  • 42.4k
  • 3
  • 37
  • 107

I see I'm late, but here's my take on this problem:

import bpy, bmesh
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):
    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:

Markus von Broady
  • 42.4k
  • 3
  • 37
  • 107