# Align empty to normal with object matrix world rotation?

I want to align an empty (Arrows) to a selected face normal with the empty's Z axis aligning to the normal direction (and location which I have working). When the target objects rotation is 0 it's fine... but if I rotate the target object and then select a face I can't work out how to add the object's matrix_world rotation to the empty's matrix_world rotation (I'm thinking this should be easy but can't solve it atm)... here's the code so far. (Add an Arrows Empty named 'Empty' and select one face on an object). any help greatly appreciated.

import mathutils
import bmesh

obj = bpy.context.edit_object
mw = obj.matrix_world.copy()
loc, rot, scale = obj.matrix_world.decompose()

bm = bmesh.from_edit_mesh(obj.data)

# select one face - testing
for f in bm.faces:
if f.select:

f_location = mw @ f.calc_center_median()
print("location: ", f_location) #correct

DirectionVector = f.normal

bpy.data.objects['Empty'].rotation_mode = 'QUATERNION'
bpy.data.objects['Empty'].rotation_quaternion = DirectionVector.to_track_quat('Z','Y')
bpy.data.objects['Empty'].location = f_location

#bpy.data.objects['Empty'].matrix_world.to_euler = rot


import bpy
from bpy import context
from mathutils import Matrix, Vector
import bmesh

obj=context.object
mw = obj.matrix_world
me=obj.data
m = mw.copy()
mi = m.inverted()

def create_rotation_matrix_from_face(mx, face):
normal = (mx.to_3x3() @ face.normal)
tangent = (mx.to_3x3() @ face.calc_tangent_edge_pair())
binormal = normal.cross(tangent)
rot = Matrix()
rot.xyz = binormal
rot.xyz = normal
rot.xyz = tangent
return rot.transposed()

#apply all transforms
me.transform(mw) #Transform mesh vertices by a matrix
mw = Matrix()

bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data)
for face in bm.faces:
if face.select:
mat_rot=create_rotation_matrix_from_face(mw, face)
loc=mw @ face.calc_center_median() # > vector
mat_trans = Matrix.Translation(loc)
break

mat=mat_trans @ mat_rot
bpy.ops.object.mode_set(mode='OBJECT')

scale_empty=context.object.scale.copy() #scale will change after the matrix modification (next line)
context.object.matrix_world @= mat
#empty size
context.object.scale=scale_empty #original size
#or
#context.object.scale=obj.scale #size of the object x,y,z

#apply all modif back
me.transform(mi) #all zero loc rot scale
mw = m #back to start matrix
me.update()

• Thanks for this answer... but the final rotation of the empty doesn't match a selected face rotation if the object is scaled non-uniformly. The object scale is applied to the empty, but the empty's orientation is already aligned with the face so it effects it incorrectly... perhaps applying the object scale to the empty before matching the rotation would work? – Dan Feb 5 at 10:49
• @Dan I finally modified it to correct this problem – Uneconscience UneSource Mar 15 at 17:34
• you can choose an empty proportional to the object scale uncommenting one line – Uneconscience UneSource Mar 15 at 17:41
• finally I inverted axis order again to be sure to have right alignement of empty with object axis. z and y were inverted... – Uneconscience UneSource Mar 15 at 18:48