I want to be able to run a function like Space in Loop Tools from the script editor. Only extracted functions related to

bl_idname = "mesh.looptools_space"
bl_label = "Space"

I have extracted only the functions related to operator panel is not needed and is fixed with bpy.ops.mesh.looptools_space(influence=100, input='selected', interpolation='cubic', lock_x=False, lock_y=False, lock_z= False) and converted it to run from a text editor, but it does not work correctly Can you please tell me what I should do?

The images below are from the script and "Space" from the Loop tools. The edge from the "script" does not have the vertex position changed, and only the rightmost one has been shrunk I want to position the vertices evenly as when I execute the "Space" button

enter image description here

import bmesh
import mathutils

def move_verts(object, bm, mapping, move, lock, influence):
    for i, (index, location) in enumerate(move):
        vert = bm.verts[mapping[index]]
        if lock:
            if lock[0]:
                location.x = vert.co.x
            if lock[2]:
                location.y = vert.co.y
            if lock[2]:
                location.z = vert.co.z
        vert.co = vert.co.lerp(location, influence / 100)

def initialise():
    object = bpy.context.object
    bm = bmesh.from_edit_mesh(object.data)
    return object, bm

def terminate():

def space_calculate_t(bm_mod, points):
    # Dummy implementation
    return points, points

def calculate_splines(interpolation, bm_mod, tknots, knots):
    splines = []
    if interpolation == 'cubic':
        for i in range(len(knots) - 1):
            a = bm_mod.verts[knots[i]].co
            b = bm_mod.verts[knots[i + 1]].co
            d = b - a
            t = tknots[i]
            u = tknots[i + 1] - t
            splines.append([[a.x, 0, 0, 0, t], [a.y, 0, 0, 0, t], [a.z, 0, 0, 0, t]])
        for i in range(len(knots) - 1):
            a = bm_mod.verts[knots[i]].co
            b = bm_mod.verts[knots[i + 1]].co
            d = b - a
            t = tknots[i]
            u = tknots[i + 1] - t
            splines.append([a, d, t, u])
    return splines

def space_calculate_verts(bm_mod, interpolation, tknots, tpoints, points, splines):
    move = []
    for p in points:
        m = tpoints[points.index(p)]
        if m in tknots:
            n = tknots.index(m)
            t = tknots[:]
            n = t.index(m) - 1
        if n > len(splines) - 1:
            n = len(splines) - 1
        elif n < 0:
            n = 0

        if interpolation == 'cubic':
            ax, bx, cx, dx, tx = splines[n][0]
            x = ax + bx * (m - tx) + cx * (m - tx) ** 2 + dx * (m - tx) ** 3
            ay, by, cy, dy, ty = splines[n][2]
            y = ay + by * (m - ty) + cy * (m - ty) ** 2 + dy * (m - ty) ** 3
            az, bz, cz, dz, tz = splines[n][2]
            z = az + bz * (m - tz) + cz * (m - tz) ** 2 + dz * (m - tz) ** 3
            move.append([p, mathutils.Vector([x, y, z])])
        else:  # interpolation == 'linear'
            a, d, t, u = splines[n]
            move.append([p, ((m - t) / u) * d + a])

    return move

def space_operator():
    object, bm = initialise()

    # Example setup for testing purposes
    input = 'selected'
    interpolation = 'cubic'
    lock_x = False
    lock_y = False
    lock_z = False
    influence = 100

    # Dummy implementation of cache and loops (to be replaced with actual logic)
    loops = [[v.index for v in bm.verts if v.select]]
    mapping = {v.index: v.index for v in bm.verts}

    move = []
    for loop in loops:
        tknots, tpoints = space_calculate_t(bm, loop)
        splines = calculate_splines(interpolation, bm, tknots, loop)
        move.append(space_calculate_verts(bm, interpolation, tknots, tpoints, loop, splines))

    if lock_x or lock_y or lock_z:
        lock = [lock_x, lock_y, lock_z]
        lock = False
    move_verts(object, bm, mapping, move[0], lock, influence)


# Example call (to be executed in Blender's Text Editor)

  • $\begingroup$ You'll need to be more precise than "it does not work correctly". Otherwise your question will be downvoted and at some point closed. $\endgroup$
    – Lutzi
    Commented Jun 23 at 10:50
  • $\begingroup$ 2 things I noticed of the bat. You didn't imoprt bpy. The indices for locking axes in move_verts are incorrect. Change them to lock[0], lock[1], and lock[2]. $\endgroup$
    – Dgc
    Commented Jun 23 at 15:43

1 Answer 1


Here's some code I wrote, I've only tested it a little, but it seems like it works pretty good:

import bpy
import bmesh
from mathutils import Vector

def space_loop_equalize(context):
    # Get the active object
    obj = context.object
    if obj is None or obj.type != 'MESH':
        print("No mesh object selected.")
    # Switch to edit mode
    # Get the bmesh
    bm = bmesh.from_edit_mesh(obj.data)
    # Ensure only edge select mode is enabled
    # Get the selected edge loop
    edges = [e for e in bm.edges if e.select]
    if not edges:
        print("No edges selected.")
    # Find the total length of the edge loop
    total_length = sum((e.verts[0].co - e.verts[1].co).length for e in edges)
    num_edges = len(edges)
    equal_length = total_length / num_edges
    # Loop through the edges and set the length to the equal length
    for edge in edges:
        v1, v2 = edge.verts
        direction = (v2.co - v1.co).normalized()
        mid_point = (v1.co + v2.co) / 2
        v1.co = mid_point - direction * (equal_length / 2)
        v2.co = mid_point + direction * (equal_length / 2)
    # Update the bmesh

# Execute the function
  • $\begingroup$ Thanks! It seems to be working quite ideally! However, each time I run it, the selected edges get a little shorter. And they still don't seem to be placed a little evenly. I would like to attach a video, but where do I upload it? $\endgroup$
    – InamuraJIN
    Commented Jun 23 at 19:28
  • $\begingroup$ youtu.be/BE6PJeEz6aA $\endgroup$
    – InamuraJIN
    Commented Jun 23 at 19:37
  • 2
    $\begingroup$ Hello, I don't intend do be antagonistic, but this site has a strict policy about AI-generated content. Your script looks like it has been generated by one. Can you confirm you have written this script yourself and it was not entirely generated by a LLM ? $\endgroup$
    – Gorgious
    Commented Jun 24 at 11:50
  • $\begingroup$ Hi, I understand your point of view and I respect this site's policies. I made this google doc with some proof, it's not really concrete evidence, but I didn't know what else to put. Lmk if you need anything else. docs.google.com/document/d/… $\endgroup$
    – Dgc
    Commented Jun 24 at 14:27
  • $\begingroup$ Hello, thank you for your update, a simple yes or no would suffice ^^ FWIW I think you can safely remove most of the code comments, they don't really add anything to the code quality or comprehension (eg bpy.ops.object.mode_set(mode='EDIT') is self-explanatory), they will become a hindrance if you want to update this script further down the line, and they tend to trigger a "this has been AI generated" response since this is how LLM write code by default. Just my 2 cents. Cheers $\endgroup$
    – Gorgious
    Commented Jun 25 at 6:49

You must log in to answer this question.

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