How do I get the normal vector of a selected vertex?(BMESH) I'm trying to Orient my gizmo according to the normals, but I can't do it exactly. Sometimes perfect, sometimes not. Can you tell me what's wrong?




ob = position_matrix()
orig_loc, orig_rot, orig_scale = ob.decompose()

uniques = bpy.context.objects_in_mode_unique_data

# Selected Object(EDIT_MODE)  
bms = {}
for obj in uniques:
    bms[obj] = bmesh.from_edit_mesh(obj.data)

# Selected Vertex
verts = []
for obj in bms:
    verts.extend([v for v in bms[obj].verts if v.select]) 

# Get Faces Normal
faces_normal = []
for vert in verts:
    faces_normal.extend([f.normal for f in vert.link_faces]) 

# Delete Duplicate Vector
def del_duplicate(list):
    newList = []
    for i in list:
        if i not in newList:
            newList.append(i)
    return newList


# Tangent 
faces_tang = []
for vert in verts:
    faces_tang.extend([f.calc_tangent_edge() for f in vert.link_faces])  


faces_normal_Sort = del_duplicate(faces_normal)
faces_tang_Sort = del_duplicate(faces_tang)

normal_vector = Vector(sum(faces_normal_Sort, Vector())).normalized()
tangent_vector = Vector(sum(faces_tang_Sort, Vector())).normalized()

axis_dst = Vector((0, 0, 1)) 
axis_dst2 = Vector((0, -1, 0))

vec1 = normal_vector @ ob.inverted()
matrix_normal = axis_dst.rotation_difference(vec1).to_matrix().to_4x4() #@ 
ob.inverted()



vec2 = tangent_vector @ ob.inverted()
matrix_tang = axis_dst2 @ matrix_normal.inverted()
mat_tmp = matrix_tang.rotation_difference(vec2).to_matrix().to_4x4()

matrix_rotate = mat_tmp @ matrix_normal

matrix_rotate = matrix_rotate.to_4x4()
Track to Quaternion

To align to vertex normal.

The normal orientation with respect to vertices appear to be a track to quaternion, tracking Z in direction of normal, and using -Y as up.  Since the only options for up in Vector.to_track_quat(to, up) are 'X', 'Y', 'Z' track with Y up and inverting the scale in X and Y.



Simple example. Run in object mode, adds an empty displayed as arrows, at each vertex aligned to normal. 

import bpy
from mathutils import Matrix

context = bpy.context

ob = context.object
mw = ob.matrix_world
me = ob.data
Sx = Matrix.Scale(-1, 4, (1, 0, 0))
Sy = Matrix.Scale(-1, 4, (0, 1, 0))

S = Sx @ Sy

for v in me.vertices:
    R = v.normal.to_track_quat('Z', 'Y').to_matrix().to_4x4()
    R.translation = mw @ v.co

    bpy.ops.object.empty_add()
    mt = context.object
    mt.empty_display_type = 'ARROWS'
    mt.matrix_world = R @ S


Related 

Align to face normal vector