Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBart Crouch <bartius.crouch@gmail.com>2013-01-30 01:42:55 +0400
committerBart Crouch <bartius.crouch@gmail.com>2013-01-30 01:42:55 +0400
commit62c114e2db2dbbb036411a8e577c0298dcdbb247 (patch)
tree75e97a97d63f3ba894203f596d0b66705f3f059b /mesh_f2.py
parentd2c18e87de18837fb10ed47ce0ec6737633ee0be (diff)
Moving F2 from contrib to trunk. Add-on has been approved by a code review.
[[Split portion of a mixed commit.]]
Diffstat (limited to 'mesh_f2.py')
-rw-r--r--mesh_f2.py298
1 files changed, 298 insertions, 0 deletions
diff --git a/mesh_f2.py b/mesh_f2.py
new file mode 100644
index 00000000..b06bb19f
--- /dev/null
+++ b/mesh_f2.py
@@ -0,0 +1,298 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+bl_info = {
+ 'name': "F2",
+ 'author': "Bart Crouch",
+ 'version': (1, 4, 0),
+ 'blender': (2, 65, 9),
+ 'location': "Editmode > F",
+ 'warning': "",
+ 'description': "Extends the 'Make Edge/Face' functionality",
+ 'wiki_url': "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+ "Scripts/Modeling/F2",
+ 'tracker_url': "http://projects.blender.org/tracker/index.php?"\
+ "func=detail&aid=33979",
+ 'category': 'Mesh'}
+
+
+import bmesh
+import bpy
+import itertools
+import mathutils
+from bpy_extras import view3d_utils
+
+
+# create a face from a single selected edge
+def quad_from_edge(bm, edge_sel, context, event):
+ ob = context.active_object
+ region = context.region
+ region_3d = context.space_data.region_3d
+
+ # find linked edges that are open (<2 faces connected) and not part of
+ # the face the selected edge belongs to
+ all_edges = [[edge for edge in edge_sel.verts[i].link_edges if \
+ len(edge.link_faces) < 2 and edge != edge_sel and \
+ sum([face in edge_sel.link_faces for face in edge.link_faces]) == 0] \
+ for i in range(2)]
+ if not all_edges[0] or not all_edges[1]:
+ return
+
+ # determine which edges to use, based on mouse cursor position
+ mouse_pos = mathutils.Vector([event.mouse_region_x, event.mouse_region_y])
+ optimal_edges = []
+ for edges in all_edges:
+ min_dist = False
+ for edge in edges:
+ vert = [vert for vert in edge.verts if not vert.select][0]
+ world_pos = ob.matrix_world * vert.co.copy()
+ screen_pos = view3d_utils.location_3d_to_region_2d(region,
+ region_3d, world_pos)
+ dist = (mouse_pos - screen_pos).length
+ if not min_dist or dist < min_dist[0]:
+ min_dist = (dist, edge, vert)
+ optimal_edges.append(min_dist)
+
+ # determine the vertices, which make up the quad
+ v1 = edge_sel.verts[0]
+ v2 = edge_sel.verts[1]
+ edge_1 = optimal_edges[0][1]
+ edge_2 = optimal_edges[1][1]
+ v3 = optimal_edges[0][2]
+ v4 = optimal_edges[1][2]
+
+ # normal detection
+ flip_align = True
+ normal_edge = edge_1
+ if not normal_edge.link_faces:
+ normal_edge = edge_2
+ if not normal_edge.link_faces:
+ normal_edge = edge_sel
+ if not normal_edge.link_faces:
+ # no connected faces, so no need to flip the face normal
+ flip_align = False
+ if flip_align: # there is a face to which the normal can be aligned
+ ref_verts = [v for v in normal_edge.link_faces[0].verts]
+ if v3 in ref_verts:
+ va_1 = v3
+ va_2 = v1
+ elif normal_edge == edge_sel:
+ va_1 = v1
+ va_2 = v2
+ else:
+ va_1 = v2
+ va_2 = v4
+ if (va_1 == ref_verts[0] and va_2 == ref_verts[-1]) or \
+ (va_2 == ref_verts[0] and va_1 == ref_verts[-1]):
+ # reference verts are at start and end of the list -> shift list
+ ref_verts = ref_verts[1:] + [ref_verts[0]]
+ if ref_verts.index(va_1) > ref_verts.index(va_2):
+ # connected face has same normal direction, so don't flip
+ flip_align = False
+
+ # material index detection
+ ref_faces = edge_sel.link_faces
+ if not ref_faces:
+ ref_faces = edge_sel.verts[0].link_faces
+ if not ref_faces:
+ ref_faces = edge_sel.verts[1].link_faces
+ if not ref_faces:
+ mat_index = False
+ smooth = False
+ else:
+ mat_index = ref_faces[0].material_index
+ smooth = ref_faces[0].smooth
+
+ # create quad
+ try:
+ verts = [v3, v1, v2, v4]
+ if flip_align:
+ verts.reverse()
+ face = bm.faces.new(verts)
+ if mat_index:
+ face.material_index = mat_index
+ face.smooth = smooth
+ except:
+ # face already exists
+ return
+
+ # change selection
+ edge_sel.select = False
+ for vert in edge_sel.verts:
+ vert.select = False
+ for edge in face.edges:
+ if edge.index < 0:
+ edge.select = True
+ v3.select = True
+ v4.select = True
+
+ # toggle mode, to force correct drawing
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.mode_set(mode='EDIT')
+
+
+# create a face from a single selected vertex, if it is an open vertex
+def quad_from_vertex(bm, vert_sel, context, event):
+ ob = context.active_object
+ region = context.region
+ region_3d = context.space_data.region_3d
+
+ # find linked edges that are open (<2 faces connected)
+ edges = [edge for edge in vert_sel.link_edges if len(edge.link_faces) < 2]
+ if len(edges) < 2:
+ return
+
+ # determine which edges to use, based on mouse cursor position
+ min_dist = False
+ mouse_pos = mathutils.Vector([event.mouse_region_x, event.mouse_region_y])
+ for a, b in itertools.combinations(edges, 2):
+ other_verts = [vert for edge in [a, b] for vert in edge.verts \
+ if not vert.select]
+ mid_other = (other_verts[0].co.copy() + other_verts[1].co.copy()) \
+ / 2
+ new_pos = 2 * (mid_other - vert_sel.co.copy()) + vert_sel.co.copy()
+ world_pos = ob.matrix_world * new_pos
+ screen_pos = view3d_utils.location_3d_to_region_2d(region, region_3d,
+ world_pos)
+ dist = (mouse_pos - screen_pos).length
+ if not min_dist or dist < min_dist[0]:
+ min_dist = (dist, (a, b), other_verts, new_pos)
+
+ # create vertex at location mirrored in the line, connecting the open edges
+ edges = min_dist[1]
+ other_verts = min_dist[2]
+ new_pos = min_dist[3]
+ vert_new = bm.verts.new(new_pos)
+
+ # normal detection
+ flip_align = True
+ normal_edge = edges[0]
+ if not normal_edge.link_faces:
+ normal_edge = edges[1]
+ if not normal_edge.link_faces:
+ # no connected faces, so no need to flip the face normal
+ flip_align = False
+ if flip_align: # there is a face to which the normal can be aligned
+ ref_verts = [v for v in normal_edge.link_faces[0].verts]
+ if other_verts[0] in ref_verts:
+ va_1 = other_verts[0]
+ va_2 = vert_sel
+ else:
+ va_1 = vert_sel
+ va_2 = other_verts[1]
+ if (va_1 == ref_verts[0] and va_2 == ref_verts[-1]) or \
+ (va_2 == ref_verts[0] and va_1 == ref_verts[-1]):
+ # reference verts are at start and end of the list -> shift list
+ ref_verts = ref_verts[1:] + [ref_verts[0]]
+ if ref_verts.index(va_1) > ref_verts.index(va_2):
+ # connected face has same normal direction, so don't flip
+ flip_align = False
+
+ # material index detection
+ ref_faces = vert_sel.link_faces
+ if not ref_faces:
+ mat_index = False
+ smooth = False
+ else:
+ mat_index = ref_faces[0].material_index
+ smooth = ref_faces[0].smooth
+
+ # create face between all 4 vertices involved
+ verts = [other_verts[0], vert_sel, other_verts[1], vert_new]
+ if flip_align:
+ verts.reverse()
+ face = bm.faces.new(verts)
+ if mat_index:
+ face.material_index = mat_index
+ face.smooth = smooth
+
+ # change selection
+ vert_new.select = True
+ vert_sel.select = False
+
+ # toggle mode, to force correct drawing
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.mode_set(mode='EDIT')
+
+
+class MeshF2(bpy.types.Operator):
+ """Tooltip"""
+ bl_idname = "mesh.f2"
+ bl_label = "Make Edge/Face"
+ bl_description = "Extends the 'Make Edge/Face' functionality"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ # check we are in mesh editmode
+ ob = context.active_object
+ return(ob and ob.type == 'MESH' and context.mode == 'EDIT_MESH')
+
+ def invoke(self, context, event):
+ bm = bmesh.from_edit_mesh(context.active_object.data)
+ sel = [v for v in bm.verts if v.select]
+ if len(sel) > 2:
+ # original 'Make Edge/Face' behaviour
+ bpy.ops.mesh.edge_face_add()
+ elif len(sel) == 1:
+ # single vertex selected -> mirror vertex and create new face
+ quad_from_vertex(bm, sel[0], context, event)
+ elif len(sel) == 2:
+ edges_sel = [ed for ed in bm.edges if ed.select]
+ if len(edges_sel) != 1:
+ # 2 vertices selected, but not on the same edge
+ bpy.ops.mesh.edge_face_add()
+ else:
+ # single edge selected -> new face from linked open edges
+ quad_from_edge(bm, edges_sel[0], context, event)
+
+ return {'FINISHED'}
+
+
+# registration
+classes = [MeshF2]
+addon_keymaps = []
+
+
+def register():
+ # add operator
+ for c in classes:
+ bpy.utils.register_class(c)
+
+ # add keymap entry
+ km = bpy.context.window_manager.keyconfigs.addon.keymaps.new(\
+ name='Mesh', space_type='EMPTY')
+ kmi = km.keymap_items.new("mesh.f2", 'F', 'PRESS')
+ addon_keymaps.append(km)
+
+
+def unregister():
+ # remove operator
+ for c in classes:
+ bpy.utils.unregister_class(c)
+
+ # remove keymap entry
+ for km in addon_keymaps:
+ bpy.context.window_manager.keyconfigs.addon.keymaps.remove(km)
+ addon_keymaps.clear()
+
+
+if __name__ == "__main__":
+ register() \ No newline at end of file