627 lines
22 KiB
Python
627 lines
22 KiB
Python
# Updated for 2.8 jan 5 2019
|
|
|
|
# ##### 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, Alexander Nedovizin, Paul Kotelevets "
|
|
"(concept design), Adrian Rutkowski",
|
|
"version": (1, 8, 4),
|
|
"blender": (2, 80, 0),
|
|
"location": "Editmode > F",
|
|
"warning": "",
|
|
"description": "Extends the 'Make Edge/Face' functionality",
|
|
"doc_url": "{BLENDER_MANUAL_URL}/addons/mesh/f2.html",
|
|
"category": "Mesh",
|
|
}
|
|
|
|
# ref: https://github.com/Cfyzzz/Other-scripts/blob/master/f2.py
|
|
|
|
import bmesh
|
|
import bpy
|
|
import itertools
|
|
import mathutils
|
|
import math
|
|
from mathutils import Vector
|
|
from bpy_extras import view3d_utils
|
|
|
|
|
|
# returns a custom data layer of the UV map, or None
|
|
def get_uv_layer(ob, bm, mat_index):
|
|
uv = None
|
|
uv_layer = None
|
|
if ob.material_slots:
|
|
me = ob.data
|
|
if me.uv_layers:
|
|
uv = me.uv_layers.active.name
|
|
# 'material_slots' is deprecated (Blender Internal)
|
|
# else:
|
|
# mat = ob.material_slots[mat_index].material
|
|
# if mat is not None:
|
|
# slot = mat.texture_slots[mat.active_texture_index]
|
|
# if slot and slot.uv_layer:
|
|
# uv = slot.uv_layer
|
|
# else:
|
|
# for tex_slot in mat.texture_slots:
|
|
# if tex_slot and tex_slot.uv_layer:
|
|
# uv = tex_slot.uv_layer
|
|
# break
|
|
if uv:
|
|
uv_layer = bm.loops.layers.uv.get(uv)
|
|
|
|
return (uv_layer)
|
|
|
|
|
|
# create a face from a single selected edge
|
|
def quad_from_edge(bm, edge_sel, context, event):
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
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 and v1 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
|
|
|
|
if addon_prefs.quad_from_e_mat:
|
|
mat_index = bpy.context.object.active_material_index
|
|
|
|
# create quad
|
|
try:
|
|
if v3 == v4:
|
|
# triangle (usually at end of quad-strip
|
|
verts = [v3, v1, v2]
|
|
else:
|
|
# normal face creation
|
|
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
|
|
|
|
# adjust uv-map
|
|
if __name__ != '__main__':
|
|
if addon_prefs.adjustuv:
|
|
uv_layer = get_uv_layer(ob, bm, mat_index)
|
|
if uv_layer:
|
|
uv_ori = {}
|
|
for vert in [v1, v2, v3, v4]:
|
|
for loop in vert.link_loops:
|
|
if loop.face.index > -1:
|
|
uv_ori[loop.vert.index] = loop[uv_layer].uv
|
|
if len(uv_ori) == 4 or len(uv_ori) == 3:
|
|
for loop in face.loops:
|
|
if loop.vert.index in uv_ori:
|
|
loop[uv_layer].uv = uv_ori[loop.vert.index]
|
|
|
|
# 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):
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
ob = context.active_object
|
|
me = ob.data
|
|
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
|
|
|
|
if addon_prefs.quad_from_v_mat:
|
|
mat_index = bpy.context.object.active_material_index
|
|
|
|
# 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
|
|
|
|
# adjust uv-map
|
|
if __name__ != '__main__':
|
|
if addon_prefs.adjustuv:
|
|
uv_layer = get_uv_layer(ob, bm, mat_index)
|
|
if uv_layer:
|
|
uv_others = {}
|
|
uv_sel = None
|
|
uv_new = None
|
|
# get original uv coordinates
|
|
for i in range(2):
|
|
for loop in other_verts[i].link_loops:
|
|
if loop.face.index > -1:
|
|
uv_others[loop.vert.index] = loop[uv_layer].uv
|
|
break
|
|
if len(uv_others) == 2:
|
|
mid_other = (list(uv_others.values())[0] +
|
|
list(uv_others.values())[1]) / 2
|
|
for loop in vert_sel.link_loops:
|
|
if loop.face.index > -1:
|
|
uv_sel = loop[uv_layer].uv
|
|
break
|
|
if uv_sel:
|
|
uv_new = 2 * (mid_other - uv_sel) + uv_sel
|
|
|
|
# set uv coordinates for new loops
|
|
if uv_new:
|
|
for loop in face.loops:
|
|
if loop.vert.index == -1:
|
|
x, y = uv_new
|
|
elif loop.vert.index in uv_others:
|
|
x, y = uv_others[loop.vert.index]
|
|
else:
|
|
x, y = uv_sel
|
|
loop[uv_layer].uv = (x, y)
|
|
|
|
# toggle mode, to force correct drawing
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
|
|
def expand_vert(self, context, event):
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
ob = context.active_object
|
|
obj = bpy.context.object
|
|
me = obj.data
|
|
bm = bmesh.from_edit_mesh(me)
|
|
region = context.region
|
|
region_3d = context.space_data.region_3d
|
|
rv3d = context.space_data.region_3d
|
|
|
|
for v in bm.verts:
|
|
if v.select:
|
|
v_active = v
|
|
|
|
try:
|
|
depth_location = v_active.co
|
|
except:
|
|
return {'CANCELLED'}
|
|
# create vert in mouse cursor location
|
|
|
|
mouse_pos = Vector((event.mouse_region_x, event.mouse_region_y))
|
|
location_3d = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, depth_location)
|
|
|
|
c_verts = []
|
|
# find and select linked edges that are open (<2 faces connected) add those edge verts to c_verts list
|
|
linked = v_active.link_edges
|
|
for edges in linked:
|
|
if len(edges.link_faces) < 2:
|
|
edges.select = True
|
|
for v in edges.verts:
|
|
if v is not v_active:
|
|
c_verts.append(v)
|
|
|
|
# Compare distance in 2d between mouse and edges middle points
|
|
screen_pos_va = view3d_utils.location_3d_to_region_2d(region, region_3d,
|
|
ob.matrix_world @ v_active.co)
|
|
screen_pos_v1 = view3d_utils.location_3d_to_region_2d(region, region_3d,
|
|
ob.matrix_world @ c_verts[0].co)
|
|
screen_pos_v2 = view3d_utils.location_3d_to_region_2d(region, region_3d,
|
|
ob.matrix_world @ c_verts[1].co)
|
|
|
|
mid_pos_v1 = Vector(((screen_pos_va[0] + screen_pos_v1[0]) / 2, (screen_pos_va[1] + screen_pos_v1[1]) / 2))
|
|
mid_pos_V2 = Vector(((screen_pos_va[0] + screen_pos_v2[0]) / 2, (screen_pos_va[1] + screen_pos_v2[1]) / 2))
|
|
|
|
dist1 = math.log10(pow((mid_pos_v1[0] - mouse_pos[0]), 2) + pow((mid_pos_v1[1] - mouse_pos[1]), 2))
|
|
dist2 = math.log10(pow((mid_pos_V2[0] - mouse_pos[0]), 2) + pow((mid_pos_V2[1] - mouse_pos[1]), 2))
|
|
|
|
bm.normal_update()
|
|
bm.verts.ensure_lookup_table()
|
|
|
|
# Deselect not needed point and create new face
|
|
if dist1 < dist2:
|
|
c_verts[1].select = False
|
|
lleft = c_verts[0].link_faces
|
|
|
|
else:
|
|
c_verts[0].select = False
|
|
lleft = c_verts[1].link_faces
|
|
|
|
lactive = v_active.link_faces
|
|
# lverts = lactive[0].verts
|
|
|
|
mat_index = lactive[0].material_index
|
|
smooth = lactive[0].smooth
|
|
|
|
for faces in lactive:
|
|
if faces in lleft:
|
|
cface = faces
|
|
if len(faces.verts) == 3:
|
|
bm.normal_update()
|
|
bmesh.update_edit_mesh(obj.data)
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
v_active.select = True
|
|
bpy.ops.mesh.rip_edge_move('INVOKE_DEFAULT')
|
|
return {'FINISHED'}
|
|
|
|
lverts = cface.verts
|
|
|
|
# create triangle with correct normal orientation
|
|
# if You looking at that part - yeah... I know. I still dont get how blender calculates normals...
|
|
|
|
# from L to R
|
|
if dist1 < dist2:
|
|
if (lverts[0] == v_active and lverts[3] == c_verts[0]) \
|
|
or (lverts[2] == v_active and lverts[1] == c_verts[0]) \
|
|
or (lverts[1] == v_active and lverts[0] == c_verts[0]) \
|
|
or (lverts[3] == v_active and lverts[2] == c_verts[0]):
|
|
v_new = bm.verts.new(v_active.co)
|
|
face_new = bm.faces.new((c_verts[0], v_new, v_active))
|
|
|
|
elif (lverts[1] == v_active and lverts[2] == c_verts[0]) \
|
|
or (lverts[0] == v_active and lverts[1] == c_verts[0]) \
|
|
or (lverts[3] == v_active and lverts[0] == c_verts[0]) \
|
|
or (lverts[2] == v_active and lverts[3] == c_verts[0]):
|
|
v_new = bm.verts.new(v_active.co)
|
|
face_new = bm.faces.new((v_active, v_new, c_verts[0]))
|
|
|
|
else:
|
|
pass
|
|
# from R to L
|
|
else:
|
|
if (lverts[2] == v_active and lverts[3] == c_verts[1]) \
|
|
or (lverts[0] == v_active and lverts[1] == c_verts[1]) \
|
|
or (lverts[1] == v_active and lverts[2] == c_verts[1]) \
|
|
or (lverts[3] == v_active and lverts[0] == c_verts[1]):
|
|
v_new = bm.verts.new(v_active.co)
|
|
face_new = bm.faces.new((v_active, v_new, c_verts[1]))
|
|
|
|
elif (lverts[0] == v_active and lverts[3] == c_verts[1]) \
|
|
or (lverts[2] == v_active and lverts[1] == c_verts[1]) \
|
|
or (lverts[1] == v_active and lverts[0] == c_verts[1]) \
|
|
or (lverts[3] == v_active and lverts[2] == c_verts[1]):
|
|
v_new = bm.verts.new(v_active.co)
|
|
face_new = bm.faces.new((c_verts[1], v_new, v_active))
|
|
|
|
else:
|
|
pass
|
|
|
|
# set smooth and mat based on starting face
|
|
if addon_prefs.tris_from_v_mat:
|
|
face_new.material_index = bpy.context.object.active_material_index
|
|
else:
|
|
face_new.material_index = mat_index
|
|
face_new.smooth = smooth
|
|
|
|
# update normals
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
v_new.select = True
|
|
bm.select_history.add(v_new)
|
|
|
|
bm.normal_update()
|
|
bmesh.update_edit_mesh(obj.data)
|
|
bpy.ops.transform.translate('INVOKE_DEFAULT')
|
|
|
|
|
|
def checkforconnected(conection):
|
|
obj = bpy.context.object
|
|
me = obj.data
|
|
bm = bmesh.from_edit_mesh(me)
|
|
|
|
# Checks for number of edes or faces connected to selected vertex
|
|
for v in bm.verts:
|
|
if v.select:
|
|
v_active = v
|
|
if conection == 'faces':
|
|
linked = v_active.link_faces
|
|
elif conection == 'edges':
|
|
linked = v_active.link_edges
|
|
|
|
bmesh.update_edit_mesh(obj.data)
|
|
return len(linked)
|
|
|
|
|
|
# autograb preference in addons panel
|
|
class F2AddonPreferences(bpy.types.AddonPreferences):
|
|
bl_idname = __name__
|
|
adjustuv : bpy.props.BoolProperty(
|
|
name="Adjust UV",
|
|
description="Automatically update UV unwrapping",
|
|
default=False)
|
|
autograb : bpy.props.BoolProperty(
|
|
name="Auto Grab",
|
|
description="Automatically puts a newly created vertex in grab mode",
|
|
default=True)
|
|
extendvert : bpy.props.BoolProperty(
|
|
name="Enable Extend Vert",
|
|
description="Enables a way to build tris and quads by adding verts",
|
|
default=False)
|
|
quad_from_e_mat : bpy.props.BoolProperty(
|
|
name="Quad From Edge",
|
|
description="Use active material for created face instead of close one",
|
|
default=True)
|
|
quad_from_v_mat : bpy.props.BoolProperty(
|
|
name="Quad From Vert",
|
|
description="Use active material for created face instead of close one",
|
|
default=True)
|
|
tris_from_v_mat : bpy.props.BoolProperty(
|
|
name="Tris From Vert",
|
|
description="Use active material for created face instead of close one",
|
|
default=True)
|
|
ngons_v_mat : bpy.props.BoolProperty(
|
|
name="Ngons",
|
|
description="Use active material for created face instead of close one",
|
|
default=True)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
col = layout.column()
|
|
col.label(text="behaviours:")
|
|
col.prop(self, "autograb")
|
|
col.prop(self, "adjustuv")
|
|
col.prop(self, "extendvert")
|
|
|
|
col = layout.column()
|
|
col.label(text="use active material when creating:")
|
|
col.prop(self, "quad_from_e_mat")
|
|
col.prop(self, "quad_from_v_mat")
|
|
col.prop(self, "tris_from_v_mat")
|
|
col.prop(self, "ngons_v_mat")
|
|
|
|
|
|
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 usequad(self, bm, sel, context, event):
|
|
quad_from_vertex(bm, sel, context, event)
|
|
if __name__ != '__main__':
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
if addon_prefs.autograb:
|
|
bpy.ops.transform.translate('INVOKE_DEFAULT')
|
|
|
|
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
|
|
try:
|
|
bpy.ops.mesh.edge_face_add('INVOKE_DEFAULT')
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
if addon_prefs.ngons_v_mat:
|
|
bpy.ops.object.material_slot_assign()
|
|
except:
|
|
return {'CANCELLED'}
|
|
elif len(sel) == 1:
|
|
# single vertex selected -> mirror vertex and create new face
|
|
addon_prefs = context.preferences.addons[__name__].preferences
|
|
if addon_prefs.extendvert:
|
|
if checkforconnected('faces') in [2]:
|
|
if checkforconnected('edges') in [3]:
|
|
expand_vert(self, context, event)
|
|
else:
|
|
self.usequad(bm, sel[0], context, event)
|
|
|
|
elif checkforconnected('faces') in [1]:
|
|
if checkforconnected('edges') in [2]:
|
|
expand_vert(self, context, event)
|
|
else:
|
|
self.usequad(bm, sel[0], context, event)
|
|
else:
|
|
self.usequad(bm, sel[0], context, event)
|
|
else:
|
|
self.usequad(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, F2AddonPreferences]
|
|
addon_keymaps = []
|
|
|
|
|
|
def register():
|
|
# add operator
|
|
for c in classes:
|
|
bpy.utils.register_class(c)
|
|
|
|
# add keymap entry
|
|
kcfg = bpy.context.window_manager.keyconfigs.addon
|
|
if kcfg:
|
|
km = kcfg.keymaps.new(name='Mesh', space_type='EMPTY')
|
|
kmi = km.keymap_items.new("mesh.f2", 'F', 'PRESS')
|
|
addon_keymaps.append((km, kmi.idname))
|
|
|
|
|
|
def unregister():
|
|
# remove keymap entry
|
|
for km, kmi_idname in addon_keymaps:
|
|
for kmi in km.keymap_items:
|
|
if kmi.idname == kmi_idname:
|
|
km.keymap_items.remove(kmi)
|
|
addon_keymaps.clear()
|
|
|
|
# remove operator and preferences
|
|
for c in reversed(classes):
|
|
bpy.utils.unregister_class(c)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|