Trying to swap vertex with uv by script Basically, I want to create a tool that can swap between one vertex coordinate with specialized UV data named 'SwappedUV' for whole vertex in active object (if not exist, it'll create new UV layer instead)

import bpy
import bmesh

def swapOnAxis (axis):
    C = bpy.context
    o = C.active_object.data
    bm = bmesh.new()
    if C.active_object.mode != 'EDIT':
        bm.from_mesh(o)
    else:
        bm.from_mesh_edit(o)

    uv_layer = None
    name = 'SwappedUV'
    if name in bm.loops.layers.uv.keys():
        uv_layer = bm.loops.layers.uv[name]
    else:
        uv_layer = bm.loops.layers.uv.new(name)

    for f in bm.faces:
        for l in f.loops:
            luv = l[uv_layer]
            # apply the location of the vertex as a UV
            luv.uv.x, l.vert.co[axis] = l.vert.co[axis], luv.uv.x
            #print(l.vert.co[axis])

    if C.active_object.mode != 'EDIT':
        bm.to_mesh(o)
    else:
        bm.update_edit_mesh(o)

    bm.free() 
    o.update()

class SwapOnXOperator(bpy.types.Operator):
    """Change X Position of verts with UV data SwappedUV"""
    bl_idname = "myaddon.swaponx_operator"
    bl_label = "SwapUtils Operator"
    bl_options = {'UNDO'}

    def execute(self, context):
        swapOnAxis(0)
        self.report({'INFO'}, "Sucessfully swap UV data")
        return {'FINISHED'}

class SwapOnYOperator(bpy.types.Operator):
    """Change Y Position of verts with UV data SwappedUV"""
    bl_idname = "myaddon.swapony_operator"
    bl_label = "SwapUtils Operator"
    bl_options = {'UNDO'}

    def execute(self, context):
        swapOnAxis(1)
        self.report({'INFO'}, "Sucessfully swap UV data")
        return {'FINISHED'}

class SwapOnZOperator(bpy.types.Operator):
    """Change Z Position of verts with UV data SwappedUV"""
    bl_idname = "myaddon.swaponz_operator"
    bl_label = "SwapUtils Operator"
    bl_options = {'UNDO'}

    def execute(self, context):
        swapOnAxis(2)
        self.report({'INFO'}, "Sucessfully swap UV data")
        return {'FINISHED'}


class SwapUtilsPanel(bpy.types.Panel):
    """Docstring of SwapUtilsPanel"""
    bl_idname = "VIEW3D_PT_Swaputils"
    bl_label = "SwapUtils Panel"

    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_category = 'Tools'

    def draw(self, context):
        layout = self.layout
        row = layout.row(align=True)
        row.alignment = 'EXPAND'
        row.operator(SwapOnXOperator.bl_idname, text = "Swap X", icon = 'COLOR_RED')
        row.operator(SwapOnYOperator.bl_idname, text = "Swap Y", icon = 'COLOR_GREEN')
        row.operator(SwapOnZOperator.bl_idname, text = "Swap Z", icon = 'COLOR_BLUE')

def register():
    bpy.utils.register_class(SwapOnXOperator)
    bpy.utils.register_class(SwapOnYOperator)
    bpy.utils.register_class(SwapOnZOperator)
    bpy.utils.register_class(SwapUtilsPanel)

def unregister():
    bpy.utils.unregister_class(SwapOnXOperator)
    bpy.utils.unregister_class(SwapOnYOperator)
    bpy.utils.unregister_class(SwapOnZOperator)
    bpy.utils.unregister_class(SwapUtilsPanel)

if __name__ == "__main__":
    register()


Actually, this plugin works properly. But why it takes 4 steps to make the selected mesh returning back to it initial state? Is there something I miss?

PS: I'm fairly new to scripting in blender
Look like i'm just understand with the concept of loops in blender.

What i'm not aware is one vert can be referenced on more than one loop (and it's pretty common).

So, the solution for this is make sure that the code do not touch the same vertex index (by making a list):

def swapOnAxis (axis):
    C = bpy.context
    o = C.active_object.data
    bm = bmesh.new()
    if C.active_object.mode != 'EDIT':
        bm.from_mesh(o)
    else:
        bm.from_mesh_edit(o)

    uv_layer = None
    if 'SwappedUV' in bm.loops.layers.uv.keys():
        uv_layer = bm.loops.layers.uv['SwappedUV']
    else:
        uv_layer = bm.loops.layers.uv.new('SwappedUV')

    dict = []
    for f in bm.faces:
        for l in f.loops:
            luv = l[uv_layer]
            v = l.vert
            # make sure that we haven't do it on the same vertex
            if (v.index not in dict):
                luv.uv.x, v.co[axis] = v.co[axis], luv.uv.x
                dict.append(v.index)
            else:
                luv.uv.x = v.co[axis]

    if C.active_object.mode != 'EDIT':
        bm.to_mesh(o)
    else:
        bm.update_edit_mesh(o)

    bm.free() 
    o.update()