2

I want to build a blender python loop around all my vertices of one plane which is subdivided and so has many vertices. It should select one vertice (do something else with it which is not the question here) and select the next vertice after each iteration.

It should start with the top left, then go through the first row to the right and then step over to the second row and again go from left to right.:

enter image description here

karlo922
  • 21
  • 3
  • Is it always the same plane? Otherwise the script, in order to determine which vertex is fist (which is top-left) would have to read viewport camera properties. – Markus von Broady Apr 04 '21 at 19:07
  • It will be used for one plane only. But this plane could be different when I create a new blender file. But it would be okay to have some manual changes in the script to make it work. It's not necessary to cover every possibility. – karlo922 Apr 04 '21 at 19:20

2 Answers2

5

Operator solution

Since you're asking specifically to select a vertex:

import bpy

data = bpy.context.view_layer.objects.active.data

bpy.ops.object.mode_set(mode = 'OBJECT') verts = sorted([(v.co.z, v.co.x, v.index) for v in data.vertices], reverse=True) bpy.ops.object.mode_set(mode = 'EDIT')

for z, x, v_index in verts: bpy.ops.mesh.select_mode(type="VERT") bpy.ops.mesh.select_all(action = 'DESELECT') bpy.ops.object.mode_set(mode = 'OBJECT') data.vertices[v_index].select = True # modify manually here, e.g.: # data.vertices[v_index].co.y += x bpy.ops.object.mode_set(mode = 'EDIT') # use bpy.ops here, e.g.: # if x > 0: bpy.ops.mesh.hide()

Bmesh solution

import bpy, bmesh

mesh = bpy.context.edit_object.data bm = bmesh.from_edit_mesh(mesh) verts = sorted([(v.co.z, v.co.x, v) for v in bm.verts], reverse=True) for z, x, v in verts: v.co.y = z+x # example

bmesh.update_edit_mesh(mesh)

Changing for different planes (views)

I assumed the Back Orthographic view and XZ plane: . For other planes you need to sort differently. Perhaps remove reverse argument and use minus signs to swap the sign of the coordinate inside the tuple used for sorting. So the following line:

verts = sorted([(v.co.z, v.co.x, v) for v in bm.verts], reverse=True)

would become: [for Back Orthographic]

verts = sorted([(-v.co.z, -v.co.x, v) for v in bm.verts])

and for Front Orthographic you would change the sign of x for sorting:

verts = sorted([(-v.co.z, v.co.x, v) for v in bm.verts])

Likewise you may want to turn (v.co.z, v.co.x, v) to (v.co.x, v.co.z, v) if you rotate the camera by 90 degrees or want to iterate by columns instead of rows.

Interactive Testing

Here's a script allowing to test if the order is correct, by pressing Ctrl+Shift+F12

bl_info = {
    "name": "Vertex iterator",
    "category": "Mesh",
}

import bpy

addon_keymaps = [] current_vertex = 0

class WorkMacro(bpy.types.Operator): """Work Macro""" bl_idname = "object.work_macro" bl_label = "Work Macro" bl_options = {"REGISTER"}

def execute(self, context):    
    global current_vertex    
    data = bpy.context.view_layer.objects.active.data
    bpy.ops.object.mode_set(mode = 'OBJECT')
    verts = sorted([(-v.co.z, -v.co.x, v.index) for v in data.vertices])
    bpy.ops.object.mode_set(mode = 'EDIT')
    z, x, v_index = verts[current_vertex]
    bpy.ops.mesh.select_mode(type="VERT")
    bpy.ops.mesh.select_all(action = 'DESELECT')
    bpy.ops.object.mode_set(mode = 'OBJECT')
    data.vertices[v_index].select = True
    bpy.ops.object.mode_set(mode = 'EDIT')
    current_vertex = (current_vertex + 1) % len(verts) 
    return {'FINISHED'}


def register(): bpy.utils.register_class(WorkMacro) wm = bpy.context.window_manager km = wm.keyconfigs.addon.keymaps.new(name='3D View Generic', space_type='VIEW_3D') kmi = km.keymap_items.new(WorkMacro.bl_idname, 'F12', 'PRESS', ctrl=True, shift=True) addon_keymaps.append(km)

def unregister(): bpy.utils.unregister_class(WorkMacro) wm = bpy.context.window_manager for km in addon_keymaps: wm.keyconfigs.addon.keymaps.remove(km) del addon_keymaps[:]

if name == "main": register()

brockmann
  • 12,613
  • 4
  • 50
  • 93
Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
  • Thank you very much but unfortunately it does not fully work as expected. It somehow gets disrupted somewhere in the middle of the plane. I can reproduce this everytime. I create a plane, go to Edit mode, rotate by x axis with 90°, make it bigger and to very many subdivisions. I have wirtten a script which tehn assigns a vertex group so teach single vertex with a weight of "1". In weight paint mode if you step through all vertices its then easy to see where it did not count correctly (as the weigth paint directly shows this). Its somewhere in the middle of the plane – karlo922 Apr 05 '21 at 06:26
  • It seems that it only correctly works using bmesh. With that I now got it working – karlo922 Apr 05 '21 at 08:13
  • @karlo922 I tested both in Back Orthographic view (the same as in your question), without any transformations on the object containing the plane mesh. I added a script so you can more easily test your case and see if indeed the selection is a problem, or maybe something else. – Markus von Broady Apr 05 '21 at 08:28
  • Hi, the selection is the issue and stays the issue even with bmesh. It works for many vertices but somewhere in the plane it jumps. It jumps after finishing a row not to X near 0 but somewhere in the middle, and then jumps to the x = 0 later. I do not understand what is it doing – karlo922 Apr 05 '21 at 14:21
  • <Vector (0.5935, 0.0000, 3.8381)> <Vector (0.6755, 0.0000, 3.8381)> <Vector (0.7576, 0.0000, 3.8381)> <Vector (0.0190, 0.0000, 3.8381)>'

    This is the sorted list, you see that the last element is in wrong order

    – karlo922 Apr 05 '21 at 14:29
  • found it - if you look deep into the later parts of the numbers, its not all in the same plane- but I do not know why it is like that. – karlo922 Apr 05 '21 at 14:36
  • @karlo922 perhaps if you record the process of creating the plane, or if you share your .blend file, I will be able to find the problem. Perhaps the first 3 vectors are on Z:3.838105, and the last is on Z:3.838106, but they all round up to the same value in printed version. Try displaying the components separately. Also, you may want to replace e.g. (-v.co.z, -v.co.x, v) with (-round(v.co.z, 2), -round(v.co.x, 2), v) to limit precision (only for the sake of sorting) to 2 decimal places. – Markus von Broady Apr 05 '21 at 15:12
2

Default order of things.

If we add a primitive grid, the vertices are ordered left to right ($X$), bottom to top ($Y$).

enter image description here

With all geometry selected, Scaling by -1 in local $Y$ axis, SYY -1 will change index order to left to right, top to bottom, but will flip the normals.

Use AltN to flip them back.

Sorting and Saving

The answer here contains links and shows how to re-sort a meshes indices using bmesh.

So similarly to method of @MarkusvonBroady can sort by geo (in this case $XY$ plane) and set the verts in this order.

import bpy
import bmesh
context = bpy.context
me = context.edit_object.data

bm = bmesh.from_edit_mesh(me) for i, v in enumerate( sorted( bm.verts, key=lambda v: (-v.co.y, v.co.x) ) ): v.index = i

bm.verts.sort() bmesh.update_edit_mesh(me)

Iterate over indices ==> the order we want.

Now simply iterating thru the verts will give the desired order, eg for 4x4 grid as shown in gif,

for v in mesh.vertices:
    row = v.index // 4
    col = v.index % 4 

Note, counting from zero, not one.

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • Nice, I didn't know you can change the vertex index, especially inside a loop. And the links don't break, because each vertex object holds a reference to a link object and only in a file links use indices? – Markus von Broady Apr 05 '21 at 15:19
  • bit confused by your terminology. Imagine this is much like ensure lookup table, get all the valid verts and re-index them. In this case set the order with index property and call bm.verts.sort() to re index faces / edges / loops etc. Would make for an interesting effect in conjunction with vertex parenting. – batFINGER Apr 05 '21 at 15:48