11

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.

Enter image description here

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

Enter image description here

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

Enter image description here

Please consider that your solution should work for complex shapes:

Enter image description here

Peter Mortensen
  • 681
  • 4
  • 11

4 Answers4

15

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

As demonstrated in @Leander's answer

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.

enter image description here 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)

batFINGER
  • 84,216
  • 10
  • 108
  • 233
10
  1. Add a shapekey.
  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.

process demo

Leander
  • 26,725
  • 2
  • 44
  • 105
7

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 adjacent = [] 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)

Leander
  • 26,725
  • 2
  • 44
  • 105
5

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.

Gordian Knot

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)

Or, to split a cyclic mesh at a selection:

import bpy

in edit mode, select one vertex

bpy.ops.mesh.split() bpy.ops.object.editmode_toggle() bpy.ops.object.convert(target='CURVE', keep_original= False) bpy.ops.object.convert(target='MESH', keep_original= False)

Then merge the two loose ends.

Thanks to @Paul St George

Robin Betts
  • 76,260
  • 8
  • 77
  • 190
  • 1
    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 09:09
  • @SeyedMortezaKamali thanks! I'll put that in. – Robin Betts Aug 21 '20 at 09:41
  • The outcome of this looks random. Se meshes have vertices in clockwise order and some in counter clockwise order and i wonder if it would be possible to get the same direction or at least determine what meshes that have clockwise order. – user2404987 Sep 02 '22 at 10:21
  • @RobinBetts Great solution ('Without a script'). Please will you tell me what this means: "V split the mesh at the vertex you want to be first, if it's not already."

    The rest I get.

    – Paul St George Mar 20 '24 at 21:20
  • Hi, @PaulStGeorge! The keyboard shortcut V will split the vertex, making the mesh acyclic, so the curve has well-defined start and end points. I guess nowadays, there would be a better solution than this, using Geometry Nodes .. ( Points to Curve ?).. and that should be able to fix user2404987's problem, too, which somehow I missed back then. – Robin Betts Mar 21 '24 at 06:26
  • Ah, @RobinBetts. So is Mesh > Split > Selection (Y) even neater?

    'import bpy

    in edit mode, select one vertex

    bpy.ops.mesh.split() bpy.ops.object.editmode_toggle() bpy.ops.object.convert(target='CURVE', keep_original= False) bpy.ops.object.convert(target='MESH', keep_original= False)'

    Then merge the two loose ends???

    – Paul St George Mar 22 '24 at 07:02
  • @PaulStGeorge .. looks good! (Though I'm not exactly a refined .bpy coder, so can't honestly say whether it's perfect ). Edited in. When you have enough rep. feel free to edit my posts.. I can always stomp on edits I feel sniffy about :D – Robin Betts Mar 22 '24 at 09:07