4

Help me rotation new object by normal a face other object. I have code that works half. The axes at the selected polygon and the new object must be the same

code - http://pasteall.org/632513/python

scene - http://pasteall.org/blend/index.php?id=48131

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

code - http://pasteall.org/632513/python

scene - http://pasteall.org/blend/index.php?id=48131

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • The MathViz addon is very useful to visualize matrices, quats and vecs. Is the normal axis in image above in local or global coordinates? – batFINGER Oct 25 '17 at 04:14
  • I do not understand ... The manipulator in normal mode https://i.imgur.com/PnOisQi.png I need the local axis of the new object to look the same as this normal – Влад Киндюшов Oct 25 '17 at 08:51
  • 1
    if you encounter some issue with 2.8 replace every "*" with "@" https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API – Fox Apr 24 '19 at 00:17

3 Answers3

7

Align to vector using Vector.rotation_difference(v)


Altered the script from this answer to find the quaternion rotation difference between the local z axis of the edit mesh and the normal of its selected (active) face.

Run script with target object in edit mode, one face selected and active. An object in scene named "Cube.001" will be located at face centre and oriented by face normal.

import bpy
from mathutils import Matrix, Vector
import bmesh
context = bpy.context
obj = context.edit_object
mw = obj.matrix_world.copy()
bm = bmesh.from_edit_mesh(obj.data)
# for this example jmake a face active
face = bm.select_history.active
o = face.calc_center_median()
# calculate the axis dif in local coords

axis_src = face.normal
# local z-axis
axis_dst = Vector((0, 0, 1))

matrix_rotate = mw.to_3x3()
matrix_rotate = matrix_rotate * axis_src.rotation_difference(axis_dst).to_matrix()
matrix_translation = Matrix.Translation(mw * o) # 

obj2 = context.scene.objects.get("Cube.001")
obj2.matrix_world = matrix_translation * matrix_rotate.to_4x4()

enter image description here Result of script. Moved cube in edit mode, showing local coords.

batFINGER
  • 84,216
  • 10
  • 108
  • 233
6

Alexander Nedovizin solved this problem

import bpy
from mathutils import Matrix, Vector
import bmesh
context = bpy.context
obj = context.edit_object
mw = obj.matrix_world.copy()
bm = bmesh.from_edit_mesh(obj.data)
face = bm.select_history.active
o = face.calc_center_median()

axis_src = face.normal
axis_src2 = face.calc_tangent_edge()
axis_dst = Vector((0, 0, 1))
axis_dst2 = Vector((0, 1, 0))

vec2 = axis_src * obj.matrix_world.inverted()
matrix_rotate = axis_dst.rotation_difference(vec2).to_matrix().to_4x4()

vec1 = axis_src2 * obj.matrix_world.inverted()
axis_dst2 = axis_dst2*matrix_rotate.inverted()
mat_tmp = axis_dst2.rotation_difference(vec1).to_matrix().to_4x4()
matrix_rotate = mat_tmp*matrix_rotate
matrix_translation = Matrix.Translation(mw * o) #

obj2 = context.scene.objects.get("Cube.001")
obj2.matrix_world = matrix_translation * matrix_rotate.to_4x4()
  • From 2.8+ version you have to change replace "mw * o" with "mw @ o". But it still does not work for Blender 3.2. – Lala_Ghost Aug 10 '22 at 07:56
  • Lala_Ghost you need to replace all * with @. This solution is the only one that worked for me. – APEC Mar 13 '23 at 10:45
2

I had to modify Alexander's solution a bit to make it work:

import bpy
import mathutils
C = bpy.context
cursor = C.scene.cursor
obj = C.active_object
p = obj.data.polygons

#get the normal and center of the selected face normal = obj.data.polygons[p.active].normal center = obj.data.polygons[p.active].center

#generate a quaternion rotation from the normal vector up = normal.to_track_quat('X', 'Y')

print(up)

#set the cursor to the center location and rotate it to match the up vector cursor.location = center cursor.rotation_quaternion = up cursor.rotation_quaternion.to_euler() #get the object world matrix mat = obj.matrix_world

print(mat) #generate the location and rotation rot = cursor.rotation_quaternion.to_euler() print(rot) loc = mat @ cursor.location.copy()

obj2 = C.scene.objects.get("Cube.001") obj2.location = loc obj2.rotation_euler = rot

#bpy.ops.object.mode_set(mode='OBJECT') #bpy.ops.mesh.primitive_cube_add(location = loc, size = 0.1, rotation =rot) #bpy.ops.object.empty_add(type='ARROWS',location = loc, rotation =rot)

Robert Gützkow
  • 25,622
  • 3
  • 47
  • 78