Bmesh selection problem

Hi,

I wrote a script to automatically copy some vertex group weights wihin the same model. It is supposed to cycle through the vertices that are selected and find the closest one from the list of unselected vertices. It works well for the most part, however, some of the weights are changed on vertices that were not selected before.

I’m not very good at Python scripting, so the solution is probably simple…but I tried a few solutions I could think of (f.e. completely deslecting everything within the loop, which obviously made the thing very slow) and nothing works, so if anyone has an idea, I’d be grateful.


import bpy
import math
import bmesh

def VectorDistance(VectorA,VectorB):    
    x = VectorA[0] - VectorB[0]
    y = VectorA[1] - VectorB[1]
    z = VectorA[2] - VectorB[2]
    
    return (x**2 + y**2 + z**2)**0.5

def viewport_update():
    bpy.context.scene.objects.active = bpy.context.scene.objects.active

def main():
    bpy.ops.object.mode_set(mode='EDIT', toggle=False)
    
    me = bpy.context.object.data
    
    bm = bmesh.from_edit_mesh(me)
    
    #save seleced and nonselected verts in lists
    selectedVerts = [v for v in bm.verts if v.select == True and v.hide != True]
    nonSelectedVerts = [a for a in bm.verts if a.select != True  and a.hide != True]
    
    #Deselect everything
    for v in bm.verts:
        v.select_set(False)
        
    viewport_update()

    for v in selectedVerts:
        distance = 0.3
        closestVert = v
        
        #find closest vertex from unselected
        for a in nonSelectedVerts:
            newDistance = VectorDistance(v.co,a.co)
            if newDistance < distance:
                distance = newDistance
                closestVert = a
        
        #Get the vertices in the right selection order and copy the weights (probably where the error is?)
        if(closestVert != v):
            bm.select_history.add(closestVert)
            v.select_set(True)
            closestVert.select_set(True)
            viewport_update()
            bpy.ops.object.vertex_weight_copy()
            v.select_set(False)
            closestVert.select_set(False)
        else:
            print("Warning, no weights were copied")        
    
    bpy.ops.object.mode_set(mode='WEIGHT_PAINT', toggle=False)

main()

Also, is there any pre-made function for viewport updates in bmesh? I found that workaround in a forum, but it strikes me as weird that there is no build in thing for that…

Yup, assigning them directly works better :slight_smile:
Thanks, that saves me a load of work ^^

for other peoples can you show end script so it can be used as a reference thread

thanks
happy bl

Here you go =)

import bpy
import math
import bmesh

def VectorDistance(VectorA,VectorB):    
    x = VectorA[0] - VectorB[0]
    y = VectorA[1] - VectorB[1]
    z = VectorA[2] - VectorB[2]
    
    return (x**2 + y**2 + z**2)**0.5

def main():

    #Entering Object mode to make sure all selected verts are saved in the object information
    bpy.ops.object.mode_set(mode='OBJECT')
    myObject = bpy.context.active_object
    
    #save seleced and nonselected verts in lists
    selectedVerts = [v for v in myObject.data.vertices if v.select == True and v.hide != True]
    nonSelectedVerts = [a for a in myObject.data.vertices if a.select != True  and a.hide != True]

    for v in selectedVerts:
        distance = 1       #use distance to limit which weights will be copied
        closestVert = v
        
        for a in nonSelectedVerts:
            newDistance = VectorDistance(v.undeformed_co,a.undeformed_co)
            if newDistance < distance:
                distance = newDistance
                closestVert = a
        
        if(closestVert != v):
            for group in v.groups:
                group.weight = 0.00
                
            for group in myObject.vertex_groups:
                for vGroup in closestVert.groups:
                    if vGroup.group == group.index:
                        weight = group.weight(closestVert.index)
                        group.add(([v.index]),weight,'REPLACE')
                    
        else:
            print("Warning, no weights were copied")
    
    # Ends in weight paint mode to immediately show results, you can delete this line if you want to stay in object mode
    bpy.ops.object.mode_set(mode='WEIGHT_PAINT', toggle=False)

main()

#How to Use:
#Hide all vertices that you want to be unaffected
#select all vertices that you want to copy weights to
#all unselected vertices will be seen as source to copy from
#Run the script
#Warning: do not use with too many unselected vertices, as this code cycles through them alot (feel free to optimize this)


Btw, I would recommend splitting up the object and using Blenders weight transfer function instead, because thats probably way more efficient. However in this case I did not have the option to make changes to the geometry, thus the in-mesh version.