1
# ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17
# ##### END GPL LICENSE BLOCK #####
23
'author': "Bart Crouch",
25
'blender': (2, 65, 9),
26
'location': "Editmode > F",
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",
40
from bpy_extras import view3d_utils
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
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] \
55
if not all_edges[0] or not all_edges[1]:
58
# determine which edges to use, based on mouse cursor position
59
mouse_pos = mathutils.Vector([event.mouse_region_x, event.mouse_region_y])
61
for edges in all_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,
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)
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]
84
if not normal_edge.link_faces:
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
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]
96
elif normal_edge == edge_sel:
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
110
# material index detection
111
ref_faces = edge_sel.link_faces
113
ref_faces = edge_sel.verts[0].link_faces
115
ref_faces = edge_sel.verts[1].link_faces
120
mat_index = ref_faces[0].material_index
121
smooth = ref_faces[0].smooth
125
verts = [v3, v1, v2, v4]
128
face = bm.faces.new(verts)
130
face.material_index = mat_index
133
# face already exists
137
edge_sel.select = False
138
for vert in edge_sel.verts:
140
for edge in face.edges:
146
# toggle mode, to force correct drawing
147
bpy.ops.object.mode_set(mode='OBJECT')
148
bpy.ops.object.mode_set(mode='EDIT')
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
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]
162
# determine which edges to use, based on mouse cursor position
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 \
168
mid_other = (other_verts[0].co.copy() + other_verts[1].co.copy()) \
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,
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)
178
# create vertex at location mirrored in the line, connecting the open edges
180
other_verts = min_dist[2]
181
new_pos = min_dist[3]
182
vert_new = bm.verts.new(new_pos)
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
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]
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
208
# material index detection
209
ref_faces = vert_sel.link_faces
214
mat_index = ref_faces[0].material_index
215
smooth = ref_faces[0].smooth
217
# create face between all 4 vertices involved
218
verts = [other_verts[0], vert_sel, other_verts[1], vert_new]
221
face = bm.faces.new(verts)
223
face.material_index = mat_index
227
vert_new.select = True
228
vert_sel.select = False
230
# toggle mode, to force correct drawing
231
bpy.ops.object.mode_set(mode='OBJECT')
232
bpy.ops.object.mode_set(mode='EDIT')
235
class MeshF2(bpy.types.Operator):
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'}
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')
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]
252
# original 'Make Edge/Face' behaviour
253
bpy.ops.mesh.edge_face_add()
255
# single vertex selected -> mirror vertex and create new face
256
quad_from_vertex(bm, sel[0], context, event)
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()
263
# single edge selected -> new face from linked open edges
264
quad_from_edge(bm, edges_sel[0], context, event)
277
bpy.utils.register_class(c)
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)
289
bpy.utils.unregister_class(c)
291
# remove keymap entry
292
for km in addon_keymaps:
293
bpy.context.window_manager.keyconfigs.addon.keymaps.remove(km)
294
addon_keymaps.clear()
297
if __name__ == "__main__":
b'\\ No newline at end of file'