Merge with trunk r37849
[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 from bpy.props import EnumProperty
24
25
26 class MeshSelectInteriorFaces(bpy.types.Operator):
27     '''Select faces where all edges have more then 2 face users.'''
28
29     bl_idname = "mesh.faces_select_interior"
30     bl_label = "Select Interior Faces"
31     bl_options = {'REGISTER', 'UNDO'}
32
33     @classmethod
34     def poll(cls, context):
35         ob = context.active_object
36         return (ob and ob.type == 'MESH')
37
38     def execute(self, context):
39         from bpy_extras import mesh_utils
40         ob = context.active_object
41         context.tool_settings.mesh_select_mode = False, False, True
42         is_editmode = (ob.mode == 'EDIT')
43         if is_editmode:
44             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
45
46         mesh = ob.data
47
48         face_list = mesh.faces[:]
49         face_edge_keys = [face.edge_keys for face in face_list]
50
51         edge_face_count = mesh_utils.edge_face_count_dict(mesh)
52
53         def test_interior(index):
54             for key in face_edge_keys[index]:
55                 if edge_face_count[key] < 3:
56                     return False
57             return True
58
59         for index, face in enumerate(face_list):
60             if(test_interior(index)):
61                 face.select = True
62             else:
63                 face.select = False
64
65         if is_editmode:
66             bpy.ops.object.mode_set(mode='EDIT', toggle=False)
67         return {'FINISHED'}
68
69
70 class MeshMirrorUV(bpy.types.Operator):
71     '''Copy mirror UV coordinates on the X axis based on a mirrored mesh'''
72     bl_idname = "mesh.faces_mirror_uv"
73     bl_label = "Copy Mirrored UV coords"
74     bl_options = {'REGISTER', 'UNDO'}
75
76     direction = EnumProperty(items=(
77                         ('POSITIVE', "Positive", ""),
78                         ('NEGATIVE', "Negative", "")),
79                 name="Axis Direction",
80                 description="")
81
82     @classmethod
83     def poll(cls, context):
84         ob = context.active_object
85         return (ob and ob.type == 'MESH')
86
87     def execute(self, context):
88         DIR = (self.direction == 'NEGATIVE')
89
90         from mathutils import Vector
91
92         ob = context.active_object
93         is_editmode = (ob.mode == 'EDIT')
94         if is_editmode:
95             bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
96
97         mesh = ob.data
98
99         # mirror lookups
100         mirror_gt = {}
101         mirror_lt = {}
102
103         vcos = [v.co.to_tuple(5) for v in mesh.vertices]
104
105         for i, co in enumerate(vcos):
106             if co[0] > 0.0:
107                 mirror_gt[co] = i
108             elif co[0] < 0.0:
109                 mirror_lt[co] = i
110             else:
111                 mirror_gt[co] = i
112                 mirror_lt[co] = i
113
114         #for i, v in enumerate(mesh.vertices):
115         vmap = {}
116         for mirror_a, mirror_b in (mirror_gt, mirror_lt), (mirror_lt, mirror_gt):
117             for co, i in mirror_a.items():
118                 nco = (-co[0], co[1], co[2])
119                 j = mirror_b.get(nco)
120                 if j is not None:
121                     vmap[i] = j
122
123         active_uv_layer = None
124         for lay in mesh.uv_textures:
125             if lay.active:
126                 active_uv_layer = lay.data
127                 break
128
129         fuvs = [(uv.uv1, uv.uv2, uv.uv3, uv.uv4) for uv in active_uv_layer]
130         fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) for uv in fuvs]
131
132         # as a list
133         faces = mesh.faces[:]
134
135         fuvsel = [(False not in uv.select_uv) for uv in active_uv_layer]
136         fcents = [f.center for f in faces]
137
138         # find mirror faces
139         mirror_fm = {}
140         for i, f in enumerate(faces):
141             verts = list(f.vertices)
142             verts.sort()
143             verts = tuple(verts)
144             mirror_fm[verts] = i
145
146         fmap = {}
147         for i, f in enumerate(faces):
148             verts = [vmap.get(j) for j in f.vertices]
149             if None not in verts:
150                 verts.sort()
151                 j = mirror_fm.get(tuple(verts))
152                 if j is not None:
153                     fmap[i] = j
154
155         for i, j in fmap.items():
156
157             if not fuvsel[i] or not fuvsel[j]:
158                 continue
159             elif DIR == 0 and fcents[i][0] < 0.0:
160                 continue
161             elif DIR == 1 and fcents[i][0] > 0.0:
162                 continue
163
164             # copy UVs
165             uv1 = fuvs[i]
166             uv2 = fuvs_cpy[j]
167
168             # get the correct rotation
169             v1 = faces[j].vertices[:]
170             v2 = [vmap[k] for k in faces[i].vertices[:]]
171
172             if len(v1) == len(v2):
173                 for k in range(len(v1)):
174                     k_map = v1.index(v2[k])
175                     uv1[k].xy = - (uv2[k_map].x - 0.5) + 0.5, uv2[k_map].y
176
177         if is_editmode:
178             bpy.ops.object.mode_set(mode='EDIT', toggle=False)
179
180         return {'FINISHED'}