Update
# -*- coding: utf-8 -*-
# ***** 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 LICENCE BLOCK *****
# ------ ------
bl_info = {
'name': 'fillet',
'author': '',
'version': (0, 0, 4),
'blender': (2, 5, 8),
'api': 38887,
'location': 'View3D > Tool Shelf',
'description': '',
'warning': '',
'wiki_url': '',
'tracker_url': '',
'category': 'Mesh' }
# ------ ------
import bpy
from bpy.props import FloatProperty, IntProperty, BoolProperty
from mathutils import Vector, Matrix
from math import cos, pi
from mathutils.geometry import intersect_line_line
# ------ ------
def edit_mode_out():
bpy.ops.object.mode_set(mode = 'OBJECT')
def edit_mode_in():
bpy.ops.object.mode_set(mode = 'EDIT')
def f_1(frst, list_):
fi = frst
tmp = [frst]
while list_ != []:
for i in list_:
if i[0] == fi:
tmp.append(i[1])
fi = i[1]
list_.remove(i)
elif i[1] == fi:
tmp.append(i[0])
fi = i[0]
list_.remove(i)
return tmp
# ------ ------
class f_buf():
me_data = []
msg_type = ''
msg = ''
def f_(me, list_tmp, n, adj, out, flip, ce):
list_0 = list_tmp[:]
frst = [i for i in list_0[0] if i not in list_0[1]][0]
list_1 = f_1(frst, list_0)
list_2 = []
list_3 = []
i = 1
p = (me.vertices[list_1[i]].co).copy()
p1 = (me.vertices[list_1[(i - 1) % n]].co).copy()
p2 = (me.vertices[list_1[(i + 1) % n]].co).copy()
vec1 = p - p1
vec2 = p - p2
ang = vec1.angle(vec2)
h = adj * (1 / cos(ang * 0.5))
p3 = p - (vec1.normalized() * adj)
p4 = p - (vec2.normalized() * adj)
rp = p - ((p - ((p3 + p4) * 0.5)).normalized() * h)
vec3 = rp - p3
vec4 = rp - p4
axis = vec1.cross(vec2)
if out == False:
if flip == False:
rot_ang = vec3.angle(vec4)
for j in range(n + 1):
new_angle = rot_ang * j / n
mtrx = Matrix.Rotation(new_angle, 3, axis)
tmp = p4 - rp
tmp1 = tmp * mtrx
tmp2 = tmp1 + rp
list_2.append(tmp2)
elif flip == True:
rot_ang = vec1.angle(vec2)
for j in range(n + 1):
new_angle = rot_ang * j / n
mtrx = Matrix.Rotation(new_angle, 3, axis)
tmp = p3 - p
tmp1 = tmp * mtrx
tmp2 = tmp1 + p
list_2.append(tmp2)
elif out == True:
rot_ang = (2 * pi) - vec1.angle(vec2)
for j in range(n + 1):
new_angle = rot_ang * j / n
mtrx = Matrix.Rotation(new_angle, 3, axis)
tmp = p4 - p
tmp1 = tmp * mtrx
tmp2 = tmp1 + p
list_2.append(tmp2)
# -- -- -- --
n1 = len(list_2)
for j in range(n1):
me.vertices.add(1)
me.vertices[-1].co = list_2[j]
me.vertices[-1].select = False
list_3.append(me.vertices[-1].index)
# -- -- -- --
for k in range(n1 - 1):
a = list_3[k]
b = list_3[(k + 1) % n1]
me.edges.add(1)
me.edges[-1].vertices = [a, b]
me.vertices[list_1[0]].select = False
me.vertices[list_1[2]].select = False
if flip == True:
me.edges.add(2)
me.edges[-1].vertices = [list_3[-1], list_1[2]]
me.edges[-2].vertices = [list_3[0], list_1[0]]
else:
if ce == False:
me.edges.add(2)
me.edges[-1].vertices = [list_3[-1], list_1[0]]
me.edges[-2].vertices = [list_3[0], list_1[2]]
elif ce == True:
me.edges.add(2)
me.edges[-1].vertices = [list_3[-1], list_1[2]]
me.edges[-2].vertices = [list_3[0], list_1[0]]
me.update(calc_edges = True)
# ------ ------
class f_msg_popup(bpy.types.Operator, f_buf):
bl_idname = 'f.msg_id'
bl_label = ''
def draw(self, context):
if self.msg_type == 'Error :':
self.t = 'ERROR'
else:
self.t = 'NONE'
layout = self.layout
row = layout.split(0.20)
row.label(self.msg_type, icon = self.t)
row.label(self.msg)
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_popup(self)
# ------ panel 0 ------
class f_p0(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
#bl_idname = 'f_p0_id'
bl_label = 'Fillet'
bl_context = 'mesh_edit'
def draw(self, context):
layout = self.layout
layout.operator('f.op0_id', text = 'Fillet')
# ------ operator 0 ------
class f_op0(bpy.types.Operator):
bl_idname = 'f.op0_id'
bl_label = 'fillet'
bl_options = {'REGISTER', 'UNDO'}
adj = FloatProperty( default = 0.08, min = 0.00001, max = 100.0, step = 1, precision = 3 )
n = IntProperty( name = '', default = 4, min = 3, max = 100, step = 1 )
out = BoolProperty( name = 'Outside',default = False )
flip = BoolProperty( name = 'Flip',default = False )
ce = BoolProperty( name = 'Correct edges', default = False )
def draw(self, context):
layout = self.layout
layout.label('Distance:')
layout.prop(self, 'adj')
layout.label('Number of sides:')
layout.prop(self, 'n')
layout.prop(self, 'out')
if self.out == False:
layout.prop(self, 'flip')
layout.prop(self, 'ce')
def execute(self, context):
n = self.n
adj = self.adj
out = self.out
flip = self.flip
ce = self.ce
edit_mode_out()
ob_act = context.active_object
me = ob_act.data
# -- -- -- --
list_tmp = [ [e.vertices[0], e.vertices[1]] for e in me.edges if e.select ]
e_mode = [i for i in bpy.context.tool_settings.mesh_select_mode]
if len(list_tmp) != 2:
f_buf.msg_type = 'Error :'
f_buf.msg = 'Only two adjacent edges must be selected.'
bpy.ops.f.msg_id('INVOKE_DEFAULT')
edit_mode_in()
return 'CANCELLED'
elif e_mode[1] == True:
f_buf.msg_type = 'Error :'
f_buf.msg = 'Switch to vertex select mode.'
bpy.ops.f.msg_id('INVOKE_DEFAULT')
edit_mode_in()
return 'CANCELLED'
else:
f_(me, list_tmp, n, adj, out, flip, ce)
del list_tmp
# -- -- -- --
edit_mode_in()
bpy.ops.mesh.delete(type = 'VERT')
return {'FINISHED'}
# ------ ------
class_list = [ f_p0,
f_op0,
f_msg_popup ]
# ------ register ------
def register():
for c in class_list:
bpy.utils.register_class(c)
# ------ unregister ------
def unregister():
for c in class_list:
bpy.utils.unregister_class(c)
# ------ ------
if __name__ == "__main__":
register()
To use:
Go to edit mode.
Go vertex select mode.
Select two adjacent edges.
Click Fillet button and play with the options.
I have decided to remove toggle buttons to make gui less confusing.
I have added popup error dialog, and got rid of most of the bugs.
There is two bugs that for now i do not know how to fix.
Addon can not be used in edge select mode because Blender instead of deleting just one corner vertex deletes edges connected to that vertex and in one special case when two adjacent vectors do not have the same direction edges are not being created properly if that happens just click correct edges.
If anybody wants to try to fix those bugs be my guest.
For now i am very happy with what the script does.