Turn an object according the normal of a selected face from second mesh I'm trying to find a way to place an object on the selected face from another object according the normal face.

I'd like the local orientation of my first object correspond to the normals of the selected face from my second object.

This is my code.

import bpy
import bmesh
from mathutils import Vector


class TestPanel(bpy.types.Panel):
    bl_idname = "object.test_panel"
    bl_label = "Test Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'


    def draw(self, context):
        layout = self.layout
        layout.operator("object.clone_object", text="Add Suzanne")



class AddCloneObject(bpy.types.Operator):
    bl_idname = "object.clone_object"
    bl_label = "Clone object"

    def execute(self, context):

        if context.object.mode == 'EDIT':
           bpy.ops.object.mode_set(mode='OBJECT')
           obj = bpy.context.active_object

           bm = bmesh.new()
           bm.from_mesh(obj.data)

           selected_faces = [f for f in bm.faces if f.select]

           for face in selected_faces:

               face_location = face.calc_center_median() 
               loc_world_space = obj.matrix_world * Vector(face_location) 
               z = Vector((0,0,1))

               angle = face.normal.angle(z)
               axis = z.cross(face.normal)
               bpy.context.scene.cursor_location = loc_world_space

               bpy.ops.mesh.primitive_monkey_add(radius=0.5) 

               bpy.ops.transform.rotate(value=angle, axis=axis)


               print("face location = ", loc_world_space)
               print("angle = ", angle)
               print("axis = ", axis)
               print("Suzanne = ", context.active_object.matrix_world)

           bm.to_mesh(obj.data)
           bm.free()

           return {'FINISHED'}

       else:
           self.report({'INFO'}, "Need to be in edit_mode")

           return {'FINISHED'}

def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()


The X and Y axis are good, but the Z axis seems not to undergo rotation.
Made a couple of changes to this script, the main one is using the Vector.rotation_difference method instead of the angle method. This one calculates rotation in all 3 axes.

import bpy
import bmesh
from mathutils import Vector
from math import degrees # Added for more readable printing of rotation angles

class TestPanel(bpy.types.Panel):
    bl_idname = "object.test_panel"
    bl_label = "Test Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'

    def draw(self, context):
        layout = self.layout
        layout.operator("object.clone_object", text="Add Suzanne")


class AddCloneObject(bpy.types.Operator):
    bl_idname = "object.clone_object"
    bl_label = "Clone object"

    def execute(self, context):

        if context.object.mode == 'EDIT':
           bpy.ops.object.mode_set(mode='OBJECT')
           obj = bpy.context.active_object

           bm = bmesh.new()
           bm.from_mesh(obj.data)

           selected_faces = [f for f in bm.faces if f.select]

           for face in selected_faces:

               face_location = face.calc_center_median() 
               loc_world_space = obj.matrix_world * Vector(face_location) 
               z = Vector((0,0,1))

               # Changes start here
               rot = z.rotation_difference( face.normal ).to_euler()
               bpy.context.scene.cursor_location = loc_world_space

               bpy.ops.mesh.primitive_monkey_add(radius=0.5) 

               bpy.context.object.rotatation_euler = rot

               print("face location = ", loc_world_space)
               print("rotation = ", [degrees(a) for a in rot] )
               print("Suzanne = ", context.active_object.matrix_world)
               # And End here

           bm.to_mesh(obj.data)
           bm.free()

           return {'FINISHED'}

       else:
           self.report({'INFO'}, "Need to be in edit_mode")

           return {'FINISHED'}

def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()