move script directories for internal blender scripts.
[blender.git] / release / scripts / startup / bl_operators / mesh.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22
23
24 class MeshSelectInteriorFaces(bpy.types.Operator):
25     '''Select faces where all edges have more then 2 face users.'''
26
27     bl_idname = "mesh.faces_select_interior"
28     bl_label = "Select Interior Faces"
29     bl_options = {'REGISTER', 'UNDO'}
30
31     @classmethod
32     def poll(cls, context):
33         ob = context.active_object
34         return (ob and ob.type == 'MESH')
35
36     def execute(self, context):
37         ob = context.active_object
38         context.tool_settings.mesh_select_mode = False, False, True
39         is_editmode = (ob.mode == 'EDIT')
40         if is_editmode:
41             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
42
43         mesh = ob.data
44
45         face_list = mesh.faces[:]
46         face_edge_keys = [face.edge_keys for face in face_list]
47
48         edge_face_count = mesh.edge_face_count_dict
49
50         def test_interior(index):
51             for key in face_edge_keys[index]:
52                 if edge_face_count[key] < 3:
53                     return False
54             return True
55
56         for index, face in enumerate(face_list):
57             if(test_interior(index)):
58                 face.select = True
59             else:
60                 face.select = False
61
62         if is_editmode:
63             bpy.ops.object.mode_set(mode='EDIT', toggle=False)
64         return {'FINISHED'}
65
66
67 class MeshMirrorUV(bpy.types.Operator):
68     '''Copy mirror UV coordinates on the X axis based on a mirrored mesh'''
69     bl_idname = "mesh.faces_miror_uv"
70     bl_label = "Copy Mirrored UV coords"
71     bl_options = {'REGISTER', 'UNDO'}
72
73     @classmethod
74     def poll(cls, context):
75         ob = context.active_object
76         return (ob and ob.type == 'MESH')
77
78     def execute(self, context):
79         DIR = 1  # TODO, make an option
80
81         from mathutils import Vector
82
83         ob = context.active_object
84         is_editmode = (ob.mode == 'EDIT')
85         if is_editmode:
86             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
87
88         mesh = ob.data
89
90         # mirror lookups
91         mirror_gt = {}
92         mirror_lt = {}
93
94         vcos = [v.co.to_tuple(5) for v in mesh.vertices]
95
96         for i, co in enumerate(vcos):
97             if co[0] > 0.0:
98                 mirror_gt[co] = i
99             elif co[0] < 0.0:
100                 mirror_lt[co] = i
101             else:
102                 mirror_gt[co] = i
103                 mirror_lt[co] = i
104
105         #for i, v in enumerate(mesh.vertices):
106         vmap = {}
107         for mirror_a, mirror_b in (mirror_gt, mirror_lt), (mirror_lt, mirror_gt):
108             for co, i in mirror_a.items():
109                 nco = (-co[0], co[1], co[2])
110                 j = mirror_b.get(nco)
111                 if j is not None:
112                     vmap[i] = j
113
114         active_uv_layer = None
115         for lay in mesh.uv_textures:
116             if lay.active:
117                 active_uv_layer = lay.data
118                 break
119
120         fuvs = [(uv.uv1, uv.uv2, uv.uv3, uv.uv4) for uv in active_uv_layer]
121         fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) for uv in fuvs]
122
123         # as a list
124         faces = mesh.faces[:]
125
126         fuvsel = [(False not in uv.select_uv) for uv in active_uv_layer]
127         fcents = [f.center for f in faces]
128
129         # find mirror faces
130         mirror_fm = {}
131         for i, f in enumerate(faces):
132             verts = list(f.vertices)
133             verts.sort()
134             verts = tuple(verts)
135             mirror_fm[verts] = i
136
137         fmap = {}
138         for i, f in enumerate(faces):
139             verts = [vmap.get(j) for j in f.vertices]
140             if None not in verts:
141                 verts.sort()
142                 j = mirror_fm.get(tuple(verts))
143                 if j is not None:
144                     fmap[i] = j
145
146         done = [False] * len(faces)
147         for i, j in fmap.items():
148
149             if not fuvsel[i] or not fuvsel[j]:
150                 continue
151             elif DIR == 0 and fcents[i][0] < 0.0:
152                 continue
153             elif DIR == 1 and fcents[i][0] > 0.0:
154                 continue
155
156             # copy UVs
157             uv1 = fuvs[i]
158             uv2 = fuvs_cpy[j]
159
160             # get the correct rotation
161             v1 = faces[j].vertices[:]
162             v2 = [vmap[k] for k in faces[i].vertices[:]]
163
164             for k in range(len(uv1)):
165                 k_map = v1.index(v2[k])
166                 uv1[k].x = - (uv2[k_map].x - 0.5) + 0.5
167                 uv1[k].y = uv2[k_map].y
168
169         if is_editmode:
170             bpy.ops.object.mode_set(mode='EDIT', toggle=False)
171
172         return {'FINISHED'}