fix for error in line 164
[blender-addons-contrib.git] / mesh_extra_tools / face_inset_fillet.py
1 # -*- coding: utf-8 -*-
2
3 # ***** BEGIN GPL LICENSE BLOCK *****
4 #
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #
20 # ***** END GPL LICENCE BLOCK *****
21
22 # ------ ------
23 bl_info = {
24     'name': 'face_inset_fillet',
25     'author': '',
26     'version': (0, 1, 7),
27     'blender': (2, 6, 3),
28     'api': 46962,
29     'location': 'View3D > Tool Shelf',
30     'description': '',
31     'warning': '',
32     'wiki_url': '',
33     'tracker_url': '',
34     'category': 'Mesh' }
35
36 # ------ ------
37 import bpy
38 import bmesh
39 from bpy.props import FloatProperty, IntProperty, BoolProperty, EnumProperty
40 from math import tan, cos, degrees, radians, sin
41 from mathutils import Matrix
42
43 # ------ ------
44 def edit_mode_out():
45     bpy.ops.object.mode_set(mode = 'OBJECT')
46
47 def edit_mode_in():
48     bpy.ops.object.mode_set(mode = 'EDIT')
49
50 def a_rot(ang, rp, axis, q):
51     return (Matrix.Rotation(ang, 3, axis) * (q - rp)) + rp
52
53 # ------ ------
54 def f_(bme, list_0, opp, adj1, n_, out, radius, en0, kp):
55
56     list_del = []
57     for fi in list_0:
58         f = bme.faces[fi]
59         f.select_set(0)
60         list_del.append(f)
61         f.normal_update()
62         list_2 = [v.index for v in f.verts]
63         dict_0 = {}
64         list_1 = []
65         n = len(list_2)
66         for i in range(n):
67             dict_0[i] = []
68             p = (bme.verts[ list_2[i] ].co).copy()
69             p1 = (bme.verts[ list_2[(i - 1) % n] ].co).copy()
70             p2 = (bme.verts[ list_2[(i + 1) % n] ].co).copy()
71             dict_0[i].append(bme.verts[list_2[i]])
72             vec1 = p - p1
73             vec2 = p - p2
74             ang = vec1.angle(vec2)
75             adj = opp / tan(ang * 0.5)
76             h = (adj ** 2 + opp ** 2) ** 0.5
77             if round(degrees(ang)) == 180 or round(degrees(ang)) == 0.0:
78                 p6 = a_rot(radians(90), p, vec1, p + ((f.normal).normalized() * opp) if out == True else p - ((f.normal).normalized() * opp))
79                 list_1.append(p6)
80             else:
81                 p6 = a_rot(-radians(90), p, ((p - (vec1.normalized() * adj)) - (p - (vec2.normalized() * adj))), p + ((f.normal).normalized() * h) if out == True else p - ((f.normal).normalized() * h))
82                 list_1.append(p6)
83
84         list_2 = []
85         n1_ = len(list_1)
86         for j in range(n1_):
87             q = list_1[j]
88             q1 = list_1[(j - 1) % n1_]
89             q2 = list_1[(j + 1) % n1_]
90             vec1_ = q - q1
91             vec2_ = q - q2
92             ang_ = vec1_.angle(vec2_)
93             if round(degrees(ang_)) == 180 or round(degrees(ang_)) == 0.0:
94                 bme.verts.new(q)
95                 bme.verts.index_update()
96                 list_2.append(bme.verts[-1])
97                 dict_0[j].append(bme.verts[-1])
98             else:
99                 opp_ = adj1
100                 if radius == False:
101                     h_ = adj1 * (1 / cos(ang_ * 0.5))
102                     d = adj1
103                 elif radius == True:
104                     h_ = opp_ / sin(ang_ * 0.5)
105                     d = opp_ / tan(ang_ * 0.5)
106
107                 q3 = q - (vec1_.normalized() * d)
108                 q4 = q - (vec2_.normalized() * d)
109                 rp_ = q - ((q - ((q3 + q4) * 0.5)).normalized() * h_)
110                 axis_ = vec1_.cross(vec2_)
111                 vec3_ = rp_ - q3
112                 vec4_ = rp_ - q4
113                 rot_ang = vec3_.angle(vec4_)
114                 list_3 = []
115                 
116                 for o in range(n_ + 1):
117                     q5 = a_rot((rot_ang * o / n_), rp_, axis_, q4)
118                     bme.verts.new(q5)
119                     bme.verts.index_update()
120                     dict_0[j].append(bme.verts[-1])
121                     list_3.append(bme.verts[-1])
122                 list_3.reverse()
123                 list_2.extend(list_3)
124
125         if out == False:
126             bme.faces.new(list_2)
127             bme.faces.index_update()
128             bme.faces[-1].select_set(1)
129         elif out == True and kp == True:
130             bme.faces.new(list_2)
131             bme.faces.index_update()
132             bme.faces[-1].select_set(1)
133
134         n2_ = len(dict_0)
135         for o in range(n2_):
136             list_a = dict_0[o]
137             list_b = dict_0[(o + 1) % n2_]
138             bme.faces.new( [ list_a[0], list_b[0], list_b[-1], list_a[1] ] )
139             bme.faces.index_update()
140
141         if en0 == 'opt0':
142             for k in dict_0:
143                 if len(dict_0[k]) > 2:
144                     bme.faces.new(dict_0[k])
145                     bme.faces.index_update()
146         if en0 == 'opt1':
147             for k_ in dict_0:
148                 q_ = dict_0[k_][0]
149                 dict_0[k_].pop(0)
150                 n3_ = len(dict_0[k_])
151                 for kk in range(n3_ - 1):
152                     bme.faces.new( [ dict_0[k_][kk], dict_0[k_][(kk + 1) % n3_], q_ ] )
153                     bme.faces.index_update()
154
155     del_ = [bme.faces.remove(f) for f in list_del]
156     del del_
157
158
159
160 # ------ operator 0 ------
161 class fif_op0(bpy.types.Operator):
162     bl_idname = 'fif.op0_id'
163     bl_label = 'Face Inset Fillet'
164     bl_description = 'inset selected faces'
165     bl_options = {'REGISTER', 'UNDO'}
166
167     opp = FloatProperty( name = '', default = 0.04, min = 0, max = 100.0, step = 1, precision = 3 )      # inset amount
168     n_ = IntProperty( name = '', default = 4, min = 1, max = 100, step = 1 )      # number of sides
169     adj1 = FloatProperty( name = '', default = 0.04, min = 0.00001, max = 100.0, step = 1, precision = 3 )
170     out = BoolProperty( name = 'Out', default = False )
171     radius = BoolProperty( name = 'Radius', default = False )
172     en0 = EnumProperty( items =( ('opt0', 'Type 1', ''), ('opt1', 'Type 2', '') ), name = '', default = 'opt0' )
173     kp = BoolProperty( name = 'Keep face', default = False )
174     
175     def draw(self, context):
176         layout = self.layout
177         box = layout.box()
178         box.prop(self, 'en0', text = 'Corner type')
179         row0 = box.row(align = True)
180         row0.prop(self, 'out')
181         if self.out == True:
182             row0.prop(self, 'kp')
183         row = box.split(0.40, align = True)
184         row.label('Inset amount:')
185         row.prop(self, 'opp')
186         row1 = box.split(0.60, align = True)
187         row1.label('Number of sides:')
188         row1.prop(self, 'n_', slider = True)
189         box.prop(self, 'radius')
190         row2 = box.split(0.40, align = True)
191         if self.radius == True:
192             row2.label('Radius:')
193         else:
194             row2.label('Distance:')
195         row2.prop(self, 'adj1')
196
197     def execute(self, context):
198         opp = self.opp
199         n_ = self.n_
200         adj1 = self.adj1
201         out = self.out
202         radius = self.radius
203         en0 = self.en0
204         kp = self.kp
205
206         edit_mode_out()
207         ob_act = context.active_object
208         bme = bmesh.new()
209         bme.from_mesh(ob_act.data)
210         
211         list_0 = [ f.index for f in bme.faces if f.select and f.is_valid ]
212
213         if len(list_0) == 0:
214             self.report({'INFO'}, 'No faces selected unable to continue.')
215             edit_mode_in()
216             return {'CANCELLED'}
217         elif len(list_0) != 0:
218             f_(bme, list_0, opp, adj1, n_, out, radius, en0, kp)
219
220         bme.to_mesh(ob_act.data)
221         edit_mode_in()
222         return {'FINISHED'}
223
224 class inset_help(bpy.types.Operator):
225         bl_idname = 'help.face_inset'
226         bl_label = ''
227
228         def draw(self, context):
229                 layout = self.layout
230                 layout.label('To use:')
231                 layout.label('Select A face or Faces & inset.')
232                 layout.label('Inset on a per face basis')
233                 layout.label('Inset square, circle or outside.')
234         
235         def execute(self, context):
236                 return {'FINISHED'}
237
238         def invoke(self, context, event):
239                 return context.window_manager.invoke_popup(self, width = 300)
240 '''
241 # ------ ------
242 class_list = [ fif_op0, fif_p0 ]
243
244 # ------ register ------
245 def register():
246     for c in class_list:
247         bpy.utils.register_class(c)
248
249 # ------ unregister ------
250 def unregister():
251     for c in class_list:
252         bpy.utils.unregister_class(c)
253
254 # ------ ------
255 if __name__ == "__main__":
256     register()
257 '''