Granular Python control over vertex indices (vertex group indices)?

I know vertex indices are a read-only property. However, it’s clearly possible to change them, as withbpy.ops.mesh.sort_elements(). I’d really like to write custom sorting methods (“spiral around my mesh!” “Every other row!” etc.). Hence, I gotta figure out how sort_elements() makes index changes. Any advice?

In my experience, if something is a read-only property, any operator that appears to change it is working on a duplicate copy. In this case, however, I’m not convinced that vertex indices are a read-only property. You can easily modify vertex indices:
https://blender.stackexchange.com/questions/36577/how-are-vertex-indices-determined
So I’m curious where you’ve found that vertex indices are read-only?

When I pull up the Data API in the outliner window, navigate to a vertex and mouse-hover over the ‘Index’ value, the hover tool-tip has a red line: ‘Disabled: This property is for internal use only and can’t be edited’. When I’ve tried to modify them with something like

bpy.data.objects[0].data.vertices[0].index = 3

I get

AttributeError: bpy_struct: attribute "index" from "MeshVertex" is read-only

If I add a dot after the index property, there are no methods that seem to modify it.

Anyway, I’ll take a look at that thread, thanks.

The vertex sorting component of Sort Elements works by storing a pair of vertex index and a scalar based on the view-transformed coordinate and the input axis (x or z), into an intermediate array. The array is sorted based on the scalar and then mapped back into the bmesh
The source can be found here.

Manual vertex ordering is indeed possible.

If you’re dealing with only a few dozen vertices, you can use the selection history to order the indices.
If you want total control of setting indices, i.e select a vertex, set a number, etc. you could store custom indices on a vertex data layer and then sort the vertex indices by that.

This example demonstrates using the selection history. Select some vertices in edit mode and run the script. Make sure vertex indices are displayed in viewport.

import bpy
import bmesh


def main():
    count = 0
    def vsort(vert):
        if vert in ordered:
            return ordered[vert]

        nonlocal count
        value = count
        count += 1
        return value

    ordered = dict()
    me = bpy.context.object.data
    bm = bmesh.from_edit_mesh(me)

    for elem in bm.select_history:
        if isinstance(elem, bmesh.types.BMVert):
            ordered[elem] = count
            count += 1

    bm.verts.sort(key=vsort)
    bmesh.update_edit_mesh(me)


if __name__ == "__main__":
    main()

1 Like

This looks great! I haven’t dug in and tested it out, but would I be able to use this to reorder some verts “in the middle” of my index? For example, suppose I have
50: vert A
54: vert B
51: vert C
52: vert D
53: vert E

I would be able to switch them to (50: A, 51: B, 52: C, 53: D, 54: E)? Sorry to ask without testing for myself, just hoping to save some time if you can say yes or no.

That would require a slightly different approach. The previous example orders the selection from zero and up.

Still doable, though. The script will throw an error if the selection history doesn’t form a sequence.

import bpy
import bmesh


def main():
    def seq_sort(vert):
        if vert in sequence:
            return vmin + sequence.index(vert)
        return vert.index

    sequence = []
    me = bpy.context.object.data
    bm = bmesh.from_edit_mesh(me)

    for elem in bm.select_history:
        if isinstance(elem, bmesh.types.BMVert):
            sequence.append(elem)

    index_getter = lambda v: v.index
    vmin = min(sequence, key=index_getter).index
    vmax = max(sequence, key=index_getter).index

    if vmax - vmin + 1 != len(sequence):
        raise Exception("Selection does not form a sequence")

    bm.verts.sort(key=seq_sort)
    bmesh.update_edit_mesh(me)


if __name__ == "__main__":
    main()
1 Like

Thanks for the help!

1 Like