~ubuntu-branches/ubuntu/trusty/blender/trusty

« back to all changes in this revision

Viewing changes to release/scripts/addons/mesh_f2.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2013-03-06 12:08:47 UTC
  • mfrom: (1.5.1) (14.1.8 experimental)
  • Revision ID: package-import@ubuntu.com-20130306120847-frjfaryb2zrotwcg
Tags: 2.66a-1ubuntu1
* Resynchronize with Debian (LP: #1076930, #1089256, #1052743, #999024,
  #1122888, #1147084)
* debian/control:
  - Lower build-depends on libavcodec-dev since we're not
    doing the libav9 transition in Ubuntu yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
bl_info = {
 
22
    'name': "F2",
 
23
    'author': "Bart Crouch",
 
24
    'version': (1, 4, 0),
 
25
    'blender': (2, 65, 9),
 
26
    'location': "Editmode > F",
 
27
    'warning': "",
 
28
    'description': "Extends the 'Make Edge/Face' functionality",
 
29
    'wiki_url': "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
 
30
        "Scripts/Modeling/F2",
 
31
    'tracker_url': "http://projects.blender.org/tracker/index.php?"\
 
32
        "func=detail&aid=33979",
 
33
    'category': 'Mesh'}
 
34
 
 
35
 
 
36
import bmesh
 
37
import bpy
 
38
import itertools
 
39
import mathutils
 
40
from bpy_extras import view3d_utils
 
41
 
 
42
 
 
43
# create a face from a single selected edge
 
44
def quad_from_edge(bm, edge_sel, context, event):
 
45
    ob = context.active_object
 
46
    region = context.region
 
47
    region_3d = context.space_data.region_3d
 
48
 
 
49
    # find linked edges that are open (<2 faces connected) and not part of
 
50
    # the face the selected edge belongs to
 
51
    all_edges = [[edge for edge in edge_sel.verts[i].link_edges if \
 
52
        len(edge.link_faces) < 2 and edge != edge_sel and \
 
53
        sum([face in edge_sel.link_faces for face in edge.link_faces]) == 0] \
 
54
        for i in range(2)]
 
55
    if not all_edges[0] or not all_edges[1]:
 
56
        return
 
57
 
 
58
    # determine which edges to use, based on mouse cursor position
 
59
    mouse_pos = mathutils.Vector([event.mouse_region_x, event.mouse_region_y])
 
60
    optimal_edges = []
 
61
    for edges in all_edges:
 
62
        min_dist = False
 
63
        for edge in edges:
 
64
            vert = [vert for vert in edge.verts if not vert.select][0]
 
65
            world_pos = ob.matrix_world * vert.co.copy()
 
66
            screen_pos = view3d_utils.location_3d_to_region_2d(region,
 
67
                region_3d, world_pos)
 
68
            dist = (mouse_pos - screen_pos).length
 
69
            if not min_dist or dist < min_dist[0]:
 
70
                min_dist = (dist, edge, vert)
 
71
        optimal_edges.append(min_dist)
 
72
 
 
73
    # determine the vertices, which make up the quad
 
74
    v1 = edge_sel.verts[0]
 
75
    v2 = edge_sel.verts[1]
 
76
    edge_1 = optimal_edges[0][1]
 
77
    edge_2 = optimal_edges[1][1]
 
78
    v3 = optimal_edges[0][2]
 
79
    v4 = optimal_edges[1][2]
 
80
 
 
81
    # normal detection
 
82
    flip_align = True
 
83
    normal_edge = edge_1
 
84
    if not normal_edge.link_faces:
 
85
        normal_edge = edge_2
 
86
        if not normal_edge.link_faces:
 
87
            normal_edge = edge_sel
 
88
            if not normal_edge.link_faces:
 
89
                # no connected faces, so no need to flip the face normal
 
90
                flip_align = False
 
91
    if flip_align: # there is a face to which the normal can be aligned
 
92
        ref_verts = [v for v in normal_edge.link_faces[0].verts]
 
93
        if v3 in ref_verts:
 
94
            va_1 = v3
 
95
            va_2 = v1
 
96
        elif normal_edge == edge_sel:
 
97
            va_1 = v1
 
98
            va_2 = v2
 
99
        else:
 
100
            va_1 = v2
 
101
            va_2 = v4
 
102
        if (va_1 == ref_verts[0] and va_2 == ref_verts[-1]) or \
 
103
        (va_2 == ref_verts[0] and va_1 == ref_verts[-1]):
 
104
            # reference verts are at start and end of the list -> shift list
 
105
            ref_verts = ref_verts[1:] + [ref_verts[0]]
 
106
        if ref_verts.index(va_1) > ref_verts.index(va_2):
 
107
            # connected face has same normal direction, so don't flip
 
108
            flip_align = False
 
109
 
 
110
    # material index detection
 
111
    ref_faces = edge_sel.link_faces
 
112
    if not ref_faces:
 
113
        ref_faces = edge_sel.verts[0].link_faces
 
114
    if not ref_faces:
 
115
        ref_faces = edge_sel.verts[1].link_faces
 
116
    if not ref_faces:
 
117
        mat_index = False
 
118
        smooth = False
 
119
    else:
 
120
        mat_index = ref_faces[0].material_index
 
121
        smooth = ref_faces[0].smooth
 
122
 
 
123
    # create quad
 
124
    try:
 
125
        verts = [v3, v1, v2, v4]
 
126
        if flip_align:
 
127
            verts.reverse()
 
128
        face = bm.faces.new(verts)
 
129
        if mat_index:
 
130
            face.material_index = mat_index
 
131
        face.smooth = smooth
 
132
    except:
 
133
        # face already exists
 
134
        return
 
135
 
 
136
    # change selection
 
137
    edge_sel.select = False
 
138
    for vert in edge_sel.verts:
 
139
        vert.select = False
 
140
    for edge in face.edges:
 
141
        if edge.index < 0:
 
142
            edge.select = True
 
143
    v3.select = True
 
144
    v4.select = True
 
145
 
 
146
    # toggle mode, to force correct drawing
 
147
    bpy.ops.object.mode_set(mode='OBJECT')
 
148
    bpy.ops.object.mode_set(mode='EDIT')
 
149
 
 
150
 
 
151
# create a face from a single selected vertex, if it is an open vertex
 
152
def quad_from_vertex(bm, vert_sel, context, event):
 
153
    ob = context.active_object
 
154
    region = context.region
 
155
    region_3d = context.space_data.region_3d
 
156
 
 
157
    # find linked edges that are open (<2 faces connected)
 
158
    edges = [edge for edge in vert_sel.link_edges if len(edge.link_faces) < 2]
 
159
    if len(edges) < 2:
 
160
        return
 
161
 
 
162
    # determine which edges to use, based on mouse cursor position
 
163
    min_dist = False
 
164
    mouse_pos = mathutils.Vector([event.mouse_region_x, event.mouse_region_y])
 
165
    for a, b in itertools.combinations(edges, 2):
 
166
        other_verts = [vert for edge in [a, b] for vert in edge.verts \
 
167
            if not vert.select]
 
168
        mid_other = (other_verts[0].co.copy() + other_verts[1].co.copy()) \
 
169
            / 2
 
170
        new_pos = 2 * (mid_other - vert_sel.co.copy()) + vert_sel.co.copy()
 
171
        world_pos = ob.matrix_world * new_pos
 
172
        screen_pos = view3d_utils.location_3d_to_region_2d(region, region_3d,
 
173
            world_pos)
 
174
        dist = (mouse_pos - screen_pos).length
 
175
        if not min_dist or dist < min_dist[0]:
 
176
            min_dist = (dist, (a, b), other_verts, new_pos)
 
177
 
 
178
    # create vertex at location mirrored in the line, connecting the open edges
 
179
    edges = min_dist[1]
 
180
    other_verts = min_dist[2]
 
181
    new_pos = min_dist[3]
 
182
    vert_new = bm.verts.new(new_pos)
 
183
 
 
184
    # normal detection
 
185
    flip_align = True
 
186
    normal_edge = edges[0]
 
187
    if not normal_edge.link_faces:
 
188
        normal_edge = edges[1]
 
189
        if not normal_edge.link_faces:
 
190
            # no connected faces, so no need to flip the face normal
 
191
                flip_align = False
 
192
    if flip_align: # there is a face to which the normal can be aligned
 
193
        ref_verts = [v for v in normal_edge.link_faces[0].verts]
 
194
        if other_verts[0] in ref_verts:
 
195
            va_1 = other_verts[0]
 
196
            va_2 = vert_sel
 
197
        else:
 
198
            va_1 = vert_sel
 
199
            va_2 = other_verts[1]
 
200
        if (va_1 == ref_verts[0] and va_2 == ref_verts[-1]) or \
 
201
        (va_2 == ref_verts[0] and va_1 == ref_verts[-1]):
 
202
            # reference verts are at start and end of the list -> shift list
 
203
            ref_verts = ref_verts[1:] + [ref_verts[0]]
 
204
        if ref_verts.index(va_1) > ref_verts.index(va_2):
 
205
            # connected face has same normal direction, so don't flip
 
206
            flip_align = False
 
207
 
 
208
    # material index detection
 
209
    ref_faces = vert_sel.link_faces
 
210
    if not ref_faces:
 
211
        mat_index = False
 
212
        smooth = False
 
213
    else:
 
214
        mat_index = ref_faces[0].material_index
 
215
        smooth = ref_faces[0].smooth
 
216
 
 
217
    # create face between all 4 vertices involved
 
218
    verts = [other_verts[0], vert_sel, other_verts[1], vert_new]
 
219
    if flip_align:
 
220
        verts.reverse()
 
221
    face = bm.faces.new(verts)
 
222
    if mat_index:
 
223
        face.material_index = mat_index
 
224
    face.smooth = smooth
 
225
 
 
226
    # change selection
 
227
    vert_new.select = True
 
228
    vert_sel.select = False
 
229
 
 
230
    # toggle mode, to force correct drawing
 
231
    bpy.ops.object.mode_set(mode='OBJECT')
 
232
    bpy.ops.object.mode_set(mode='EDIT')
 
233
 
 
234
 
 
235
class MeshF2(bpy.types.Operator):
 
236
    """Tooltip"""
 
237
    bl_idname = "mesh.f2"
 
238
    bl_label = "Make Edge/Face"
 
239
    bl_description = "Extends the 'Make Edge/Face' functionality"
 
240
    bl_options = {'REGISTER', 'UNDO'}
 
241
 
 
242
    @classmethod
 
243
    def poll(cls, context):
 
244
        # check we are in mesh editmode
 
245
        ob = context.active_object
 
246
        return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
 
247
 
 
248
    def invoke(self, context, event):
 
249
        bm = bmesh.from_edit_mesh(context.active_object.data)
 
250
        sel = [v for v in bm.verts if v.select]
 
251
        if len(sel) > 2:
 
252
            # original 'Make Edge/Face' behaviour
 
253
            bpy.ops.mesh.edge_face_add()
 
254
        elif len(sel) == 1:
 
255
            # single vertex selected -> mirror vertex and create new face
 
256
            quad_from_vertex(bm, sel[0], context, event)
 
257
        elif len(sel) == 2:
 
258
            edges_sel = [ed for ed in bm.edges if ed.select]
 
259
            if len(edges_sel) != 1:
 
260
                # 2 vertices selected, but not on the same edge
 
261
                bpy.ops.mesh.edge_face_add()
 
262
            else:
 
263
                # single edge selected -> new face from linked open edges
 
264
                quad_from_edge(bm, edges_sel[0], context, event)
 
265
 
 
266
        return {'FINISHED'}
 
267
 
 
268
 
 
269
# registration
 
270
classes = [MeshF2]
 
271
addon_keymaps = []
 
272
 
 
273
 
 
274
def register():
 
275
    # add operator
 
276
    for c in classes:
 
277
        bpy.utils.register_class(c)
 
278
 
 
279
    # add keymap entry
 
280
    km = bpy.context.window_manager.keyconfigs.addon.keymaps.new(\
 
281
        name='Mesh', space_type='EMPTY')
 
282
    kmi = km.keymap_items.new("mesh.f2", 'F', 'PRESS')
 
283
    addon_keymaps.append(km)
 
284
 
 
285
 
 
286
def unregister():
 
287
    # remove operator
 
288
    for c in classes:
 
289
        bpy.utils.unregister_class(c)
 
290
 
 
291
    # remove keymap entry
 
292
    for km in addon_keymaps:
 
293
        bpy.context.window_manager.keyconfigs.addon.keymaps.remove(km)
 
294
    addon_keymaps.clear()
 
295
 
 
296
 
 
297
if __name__ == "__main__":
 
298
    register()
 
 
b'\\ No newline at end of file'