-
Notifications
You must be signed in to change notification settings - Fork 3
/
space_view3d_align_faces.py
85 lines (67 loc) · 2.42 KB
/
space_view3d_align_faces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import bpy
import math
from mathutils import Vector
from functools import reduce
bl_info = {
"name": "Align by faces",
"author": "Tom Rethaller",
"version": (0,2,2),
"blender": (2, 65, 0),
"description": "Align two objects by their active faces",
"warning": "",
"category": "3D View"}
def get_ortho(a,b,c):
if c != 0.0 and -a != b:
return [-b-c, a,a]
else:
return [c,c,-a-b]
def clamp(v,min,max):
if v < min:
return min
if v > max:
return max
return v
def align_faces(from_obj, to_obj):
fpolys = from_obj.data.polygons
tpolys = to_obj.data.polygons
fpoly = fpolys[fpolys.active]
tpoly = tpolys[tpolys.active]
to_obj.rotation_mode = 'QUATERNION'
tnorm = to_obj.rotation_quaternion * tpoly.normal
fnorm = fpoly.normal
axis = fnorm.cross(tnorm)
dot = fnorm.normalized().dot(tnorm.normalized())
dot = clamp(dot, -1.0, 1.0)
# Parallel faces need a new rotation vactor
if axis.length < 1.0e-8:
axis = Vector(get_ortho(fnorm.x, fnorm.y, fnorm.z))
from_obj.rotation_mode = 'AXIS_ANGLE'
from_obj.rotation_axis_angle = [math.acos(dot) + math.pi, axis[0],axis[1],axis[2]]
bpy.context.scene.update()
# Move from_obj so that faces match
fvertices = [from_obj.data.vertices[i].co for i in fpoly.vertices]
tvertices = [to_obj.data.vertices[i].co for i in tpoly.vertices]
fbary = from_obj.matrix_world * (reduce(Vector.__add__, fvertices) / len(fvertices))
tbary = to_obj.matrix_world * (reduce(Vector.__add__, tvertices) / len(tvertices))
from_obj.location = tbary - (fbary - from_obj.location)
class OBJECT_OT_AlignByFaces(bpy.types.Operator):
bl_label = "Align by faces"
bl_description= "Align two objects by their active faces"
bl_idname = "object.align_by_faces"
@classmethod
def poll(cls, context):
if not len(context.selected_objects) is 2:
return False
for obj in context.selected_objects:
if obj.type != 'MESH':
return False
return True
def execute(self, context):
objs_to_move = [o for o in context.selected_objects if o != context.active_object]
for o in objs_to_move:
align_faces(o, context.active_object)
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)