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()
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()
Thanks for the help!