How can I sort vertex positions sequentially indices in a closed area?

I need to sort the vertices sequentially to make an effect, something like this video. I know I should draw them by hand, but sometimes when I subdivide edges, vertex indexes will change.

The vertices in the below image are correct.

For example, vertices in the below image are sorted randomly. Can you sort them sequentially?

I tried to sort them along the x-axis or z-axis, but it didn't work.

• Do you have the python script of your attempt at sorting x and y axis. (I assume it did work, it's not the result you are after) – batFINGER Aug 20 '20 at 7:37
• @batFINGER I used this script – Seyed Morteza Kamali Aug 20 '20 at 7:40
• The title is incomprehensible near "indices". Can you fix it? – Peter Mortensen Aug 21 '20 at 7:48

Sorting vertex indices.

Answer here https://blender.stackexchange.com/a/36619/15543 shows how to sort vertex indices using bmesh.

Answer here How to order a list of vertices based upon position? goes some way to show how to walk the edges for an order.

To be sure to be sure would walk the edges as shown above until the first vert is re-encountered (since its a loop) and use this for the order. Will leave it up to you to piece together the script from what is here

Shortest Path

Using the script from What is the bmesh equivalent to bpy.ops.mesh.shortest_path_select()? could select edge as link between first and last index vert, temporarily remove it, find the shortest remaining path (the only path) from edges two verts. Reinstate the edge.

Face winding order

Instead for a closed loop like above, contend can fill with an ngon, use the ngon face verts as order (the winding order) re-sort the verts and remove the ngon face created.

import bpy
import bmesh

ob = bpy.context.object
assert ob.type == "MESH"
me = ob.data

bm = bmesh.from_edit_mesh(me)

f = bmesh.ops.contextual_create(
bm,
geom=bm.verts[:] + bm.edges[:],
)["faces"][0]

for i, v in enumerate(f.verts):
v.index = i
bm.verts.sort()
bm.faces.remove(f)

bmesh.update_edit_mesh(me)


Select a first vert and clockwise or counter

To select the zeroth vert, and ensure anticlockwise (from top view) indexing

Run in edit mode, select the vert you wish to have index 0. Assumes the shape lies in the XY plane. If the normal of the created face points down the winding is flipped.

Random order, vert 22 selected, script below run

import bpy
import bmesh
from mathutils import Vector

ob = bpy.context.object
assert ob.type == "MESH"
me = ob.data

bm = bmesh.from_edit_mesh(me)
v0 = bm.select_history.active
f = bmesh.ops.contextual_create(
bm,
geom=bm.verts[:] + bm.edges[:],
)["faces"][0]

fverts = f.verts[:]

if f.normal.dot((0, 0, 1)) < 0:
fverts.reverse()

for i, v in enumerate(fverts):
v.index = i
if v is v0:
iv0 = i

for v in fverts:
v.index = (v.index - iv0) % len(fverts)

bm.verts.sort()

bm.faces.remove(f)

bmesh.update_edit_mesh(me)

• And here I was walking the edges >_> – Leander Aug 20 '20 at 8:17
• @Leander yours is a very clever "outside the box" solution. Are you ripping the selected first vert? – batFINGER Aug 20 '20 at 8:41
• Just deleted the edge to stop the extra edge from blocking the screencast. – Leander Aug 20 '20 at 8:57
• @batFINGER your solution didn't work in complex shapes it just worked for simple shapes – Seyed Morteza Kamali Aug 20 '20 at 10:12
• @batFINGER I tested on several shapes and it works correctly! thank you, man! you're great! – Seyed Morteza Kamali Aug 20 '20 at 12:15
2. In that shapekey, place the vertices which are to be first and last far away from each other on the x axis.
3. Select all other (inbetween vertices).
4. Choose W > Smooth Vertices and set a high iteration count. The vertices will be smoothed.
5. Sort them by x-axis. Mesh > Sort Elements > X Axis
6. Delete the shapekey.

• Oh my God, you are a legend! it's a nice solution without a line of code! I have a lot of shapes to sort can you write a python script? because I used the python tag in my question. – Seyed Morteza Kamali Aug 20 '20 at 7:41
• your solution didn't work for complex shapes it just worked for simple shapes – Seyed Morteza Kamali Aug 20 '20 at 10:34
• no sorry. I tried again and I have the same result. It didn't work for complex shapes.I also tried to move that two-point in infinity position along x axis but it didn't work correctly. – Seyed Morteza Kamali Aug 20 '20 at 11:18
• Upload your blend with the "complex shape", I don't know why it shouldn't work... – Leander Aug 20 '20 at 11:21
• I uploaded the file and I attached It to my question. – Seyed Morteza Kamali Aug 20 '20 at 11:28

Using Python, you can walk the edges and assign them a new index.

import bpy
import bmesh

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

# index of the start vertex
initial = bm.verts[0]

vert = initial
prev = None
for i in range(len(bm.verts)):
print(vert.index, i)
vert.index = i
next = None
for v in [e.other_vert(vert) for e in vert.link_edges]:
if (v != prev and v != initial):
next = v
if next == None: break
prev, vert = vert, next

bm.verts.sort()

bmesh.update_edit_mesh(me)

• nice! it works like a charm! I'm confused about what answer should I accept! @batFINGER answer or your answer!😅 – Seyed Morteza Kamali Aug 20 '20 at 12:09
• @Leander there is also the BMEdge.other_vert(v) method that knowing one vert of edge v returns the other. – batFINGER Aug 20 '20 at 12:14
• Thanks a lot for reading through all these answer and giving helpful suggestions! – Leander Aug 20 '20 at 15:00
• "Python", not "python". – Peter Mortensen Aug 21 '20 at 7:50

Without a script:

1. V split the mesh at the vertex you want to be first, if it's not already.
2. Header > Object Menu > Convert the mesh to a curve
3. Edit mode, right-click menu, switch the direction of the spline, if necessary
4. Object mode, right-click menu, Convert back to a mesh.

I don't know why Convert isn't in the Object mode right-click menu for a mesh, that's kind of annoying.

Barring refinements, as supplied by @Seyed, the script would be:

import bpy
bpy.ops.object.convert(target='CURVE', keep_original= False)
bpy.ops.object.convert(target='MESH', keep_original= False)

• you're genius! nice job! It works with three lines of code!!! import bpy bpy.ops.object.convert(target='CURVE', keep_original= False) bpy.ops.object.convert(target='MESH', keep_original= False) – Seyed Morteza Kamali Aug 21 '20 at 9:09
• @SeyedMortezaKamali thanks! I'll put that in. – Robin Betts Aug 21 '20 at 9:41