bl_info cleanup
[blender-addons-contrib.git] / mesh_extra_tools / mesh_filletplus.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": "FilletPlus",
25         "author": "Gert De Roost - original by zmj100",
26         "version": (0, 4, 2),
27         "blender": (2, 6, 1),
28         "api": 43085,
29         "location": "View3D > Tool Shelf",
30         "description": "",
31         "warning": "",
32         "wiki_url": "",
33         "tracker_url": "",
34         "category": "Mesh"}
35
36 # ------ ------
37 import bpy
38 from bpy.props import FloatProperty, IntProperty, BoolProperty
39 import bmesh
40 from mathutils import Matrix
41 from math import cos, pi, degrees, sin, tan
42
43
44 def list_clear_(l):
45         del l[:]
46         return l
47
48 def get_adj_v_(list_):
49                 tmp = {}
50                 for i in list_:
51                                 try:                     tmp[i[0]].append(i[1])
52                                 except KeyError: tmp[i[0]] = [i[1]]
53                                 try:                     tmp[i[1]].append(i[0])
54                                 except KeyError: tmp[i[1]] = [i[0]]
55                 return tmp
56
57 # ------ ------
58 class f_buf():
59         an = 0
60
61 # ------ ------
62 def f_(list_0, startv, vertlist, face, adj, n, out, flip, radius):
63
64         dict_0 = get_adj_v_(list_0)
65         list_1 = [[dict_0[i][0], i, dict_0[i][1]] for i in dict_0 if (len(dict_0[i]) == 2)][0]
66         list_3 = []
67         for elem in list_1:
68                 list_3.append(bm.verts[elem])
69         list_2 = []
70
71         p_ = list_3[1]
72         p = (list_3[1].co).copy()
73         p1 = (list_3[0].co).copy()
74         p2 = (list_3[2].co).copy()
75
76         vec1 = p - p1
77         vec2 = p - p2
78
79         ang = vec1.angle(vec2, any)
80         f_buf.an = round(degrees(ang))
81
82         # -- -- -- --
83         if f_buf.an == 180 or f_buf.an == 0.0:
84                 return
85
86         # -- -- -- --
87         opp = adj
88
89         if radius == False:
90                 h = adj * (1 / cos(ang * 0.5))
91                 adj_ = adj
92         elif radius == True:
93                 h = opp / sin(ang * 0.5)
94                 adj_ = opp / tan(ang * 0.5)
95
96         p3 = p - (vec1.normalized() * adj_)
97         p4 = p - (vec2.normalized() * adj_)
98         rp = p - ((p - ((p3 + p4) * 0.5)).normalized() * h)
99
100         vec3 = rp - p3
101         vec4 = rp - p4
102
103         axis = vec1.cross(vec2)
104
105         if out == False:
106                 if flip == False:
107                         rot_ang = vec3.angle(vec4)
108                 elif flip == True:
109                         rot_ang = vec1.angle(vec2)
110         elif out == True:
111                 rot_ang = (2 * pi) - vec1.angle(vec2)
112
113         for j in range(n + 1):
114                 new_angle = rot_ang * j / n
115                 mtrx = Matrix.Rotation(new_angle, 3, axis)
116                 if out == False:
117                         if flip == False:
118                                 tmp = p4 - rp
119                                 tmp1 = mtrx * tmp
120                                 tmp2 = tmp1 + rp
121                         elif flip == True:
122                                 p3 = p - (vec1.normalized() * opp)
123                                 tmp = p3 - p
124                                 tmp1 = mtrx * tmp
125                                 tmp2 = tmp1 + p
126                 elif out == True:
127                         p4 = p - (vec2.normalized() * opp)
128                         tmp = p4 - p
129                         tmp1 = mtrx * tmp
130                         tmp2 = tmp1 + p
131
132                 v = bm.verts.new(tmp2)
133                 list_2.append(v)
134                 
135         if flip == True:
136                 list_3[1:2] = list_2
137         else:
138                 list_2.reverse()
139                 list_3[1:2] = list_2
140
141         list_clear_(list_2)
142
143         n1 = len(list_3)
144         for t in range(n1 - 1):
145                 bm.edges.new([list_3[t], list_3[(t + 1) % n1]])
146
147                 v = bm.verts.new(p)
148                 bm.edges.new([v, p_])
149         
150         if face != None:
151                 for l in face.loops:
152                         if l.vert == list_3[0]:
153                                 startl = l
154                                 break
155                 vertlist2 = []
156                 if startl.link_loop_next.vert == startv:
157                         l = startl.link_loop_prev
158                         while len(vertlist) > 0:
159                                 vertlist2.insert(0, l.vert)
160                                 vertlist.pop(vertlist.index(l.vert))
161                                 l = l.link_loop_prev
162                 else:
163                         l = startl.link_loop_next
164                         while len(vertlist) > 0:
165                                 vertlist2.insert(0, l.vert)
166                                 vertlist.pop(vertlist.index(l.vert))
167                                 l = l.link_loop_next
168                 for v in list_3:
169                         vertlist2.append(v)
170                 bm.faces.new(vertlist2)
171                 
172         bm.verts.remove(startv)
173         list_3[1].select = 1
174         list_3[-2].select = 1
175         bm.edges.get([list_3[0], list_3[1]]).select = 1
176         bm.edges.get([list_3[-1], list_3[-2]]).select = 1
177         bm.verts.index_update()
178         bm.edges.index_update()
179         bm.faces.index_update()
180         
181         me.update(calc_edges = True, calc_tessface=True)
182         
183         
184
185 def do_filletplus(pair):
186         
187         global inaction
188         global flip
189         
190         
191         list_0 = [list([e.verts[0].index, e.verts[1].index]) for e in pair]
192
193         vertset = set([])
194         vertset.add(bm.verts[list_0[0][0]])
195         vertset.add(bm.verts[list_0[0][1]])
196         vertset.add(bm.verts[list_0[1][0]])
197         vertset.add(bm.verts[list_0[1][1]])
198         
199         v1, v2, v3 = vertset
200
201         if len(list_0) != 2:
202                 self.report({'INFO'}, 'Two adjacent edges must be selected.')
203                 return
204         else:
205                 inaction = 1
206                 vertlist = []
207                 found = 0
208                 for f in v1.link_faces:
209                         if v2 in f.verts and v3 in f.verts:
210                                 found = 1
211                 if not(found):
212                         for v in [v1, v2, v3]:
213                                 if v.index in list_0[0] and v.index in list_0[1]:
214                                         startv = v
215                         face = None
216                 else:
217                         for f in v1.link_faces:
218                                 if v2 in f.verts and v3 in f.verts:
219                                         for v in f.verts:
220                                                 if not(v in vertset):
221                                                         vertlist.append(v)
222                                                 if v in vertset and v.link_loops[0].link_loop_prev.vert in vertset and v.link_loops[0].link_loop_next.vert in vertset:
223                                                         startv = v
224                                         face = f
225                 if out == True:
226                         flip = False
227                 f_(list_0, startv, vertlist, face, adj, n, out, flip, radius)
228                 
229
230 '''
231
232 # ------ panel 0 ------
233 class f_p0(bpy.types.Panel):
234         bl_space_type = 'VIEW_3D'
235         bl_region_type = 'TOOLS'
236         #bl_idname = 'f_p0_id'                                                                                            
237         bl_label = 'Fillet'
238         bl_context = 'mesh_edit'
239
240         def draw(self, context):
241                 layout = self.layout
242                 
243                 row = layout.split(0.80)
244                 row.operator('f.op0_id', text = 'Fillet plus')
245                 row.operator('f.op1_id', text = '?')
246 '''
247 # ------ operator 0 ------
248 class fillet_op0(bpy.types.Operator):
249         bl_idname = 'fillet.op0_id'
250         bl_label = 'Fillet'
251         bl_description = 'Fillet ajoining edges'
252         bl_options = {'REGISTER', 'UNDO'}
253
254         adj = FloatProperty( name = '', default = 0.1, min = 0.00001, max = 100.0, step = 1, precision = 3 )
255         n = IntProperty( name = '', default = 3, min = 1, max = 100, step = 1 )
256         out = BoolProperty( name = 'Outside', default = False )
257         flip = BoolProperty( name = 'Flip', default = False )
258         radius = BoolProperty( name = 'Radius', default = False )
259         
260         
261         @classmethod
262         def poll(cls, context):
263                 obj = context.active_object
264                 return (obj and obj.type == 'MESH' and context.mode == 'EDIT_MESH')
265         
266         def draw(self, context):
267                 layout = self.layout
268
269                 if f_buf.an == 180 or f_buf.an == 0.0:
270                         layout.label('Info:')
271                         layout.label('Angle equal to 0 or 180,')
272                         layout.label('can not fillet.')
273                 else:
274                         layout.prop(self, 'radius')
275                         if self.radius == True:
276                                 layout.label('Radius:')
277                         elif self.radius == False:
278                                 layout.label('Distance:')
279                         layout.prop(self, 'adj')
280                         layout.label('Number of sides:')
281                         layout.prop(self, 'n', slider = True)
282                         if self.n > 1:
283                                 row = layout.row(align = False)
284                                 row.prop(self, 'out')
285                                 if self.out == False:
286                                         row.prop(self, 'flip')
287
288         def execute(self, context):
289
290                 global inaction
291                 global bm, me, adj, n, out, flip, radius
292
293                 adj = self.adj
294                 n = self.n
295                 out = self.out
296                 flip = self.flip
297                 radius = self.radius
298                 
299                 inaction = 0
300
301                 ob_act = context.active_object
302                 me = ob_act.data
303                 bm = bmesh.from_edit_mesh(me)
304 #               e_mode = bpy.context.tool_settings.mesh_select_mode
305                 
306                 done = 1
307                 while done:
308                         tempset = set([])
309                         for v in bm.verts:
310                                 if v.select:
311                                         tempset.add(v)
312                         done = 0                
313                         for v in tempset:
314                                 cnt = 0
315                                 edgeset = set([])
316                                 for e in v.link_edges:
317                                         if e.select:
318                                                 edgeset.add(e)
319                                                 cnt += 1
320                                 if cnt == 2:
321                                         do_filletplus(edgeset)
322                                         done = 1
323                                         break
324                                         #return {'FINISHED'}
325                                 if done:
326                                         break
327                                         
328                 if inaction == 1:
329                         bpy.ops.mesh.select_all(action="DESELECT")
330                         for v in bm.verts:
331                                 if len(v.link_edges) == 0:
332                                         bm.verts.remove(v)
333                         bpy.ops.object.editmode_toggle()
334                         bpy.ops.object.editmode_toggle()
335                         return {'FINISHED'}
336                 else:
337                         return {'CANCELLED'}
338
339 # ------ operator 1 ------
340 class filletedgehelp(bpy.types.Operator):
341         bl_idname = 'help.edge_fillet'
342         bl_label = ''
343
344         def draw(self, context):
345                 layout = self.layout
346                 layout.label('To use:')
347                 layout.label('Select two adjacent edges and press Fillet button.')
348                 layout.label('To Help:')
349                 layout.label('best used on flat plane.')
350         
351         def execute(self, context):
352                 return {'FINISHED'}
353
354         def invoke(self, context, event):
355                 return context.window_manager.invoke_popup(self, width = 350)
356
357 # ------ operator 2 ------
358 class fillet_op2(bpy.types.Operator):
359         bl_idname = 'fillet.op2_id'
360         bl_label = ''
361
362         def execute(self, context):
363                 bpy.ops.f.op1_id('INVOKE_DEFAULT')
364                 return {'FINISHED'}
365 '''
366 # ------ ------
367 class_list = [ f_op0, f_op1, f_op2, f_p0]
368
369 # ------ register ------
370 def register():
371         for c in class_list:
372                 bpy.utils.register_class(c)
373
374 # ------ unregister ------
375 def unregister():
376         for c in class_list:
377                 bpy.utils.unregister_class(c)
378
379 # ------ ------
380 if __name__ == "__main__":
381         register()
382 '''