Use URL icon, add Tip: prefix, increase lower margin
[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, 61, 0),
28     "location": "View3D > Tool Shelf",
29     "description": "",
30     "warning": "",
31     "wiki_url": "",
32     "tracker_url": "",
33     "category": "Mesh"}
34
35 # ------ ------
36 import bpy
37 from bpy.props import FloatProperty, IntProperty, BoolProperty
38 import bmesh
39 from mathutils import Matrix
40 from math import cos, pi, degrees, sin, tan
41
42
43 def list_clear_(l):
44         del l[:]
45         return l
46
47 def get_adj_v_(list_):
48                 tmp = {}
49                 for i in list_:
50                                 try:                     tmp[i[0]].append(i[1])
51                                 except KeyError: tmp[i[0]] = [i[1]]
52                                 try:                     tmp[i[1]].append(i[0])
53                                 except KeyError: tmp[i[1]] = [i[0]]
54                 return tmp
55
56 # ------ ------
57 class f_buf():
58         an = 0
59
60 # ------ ------
61 def f_(list_0, startv, vertlist, face, adj, n, out, flip, radius):
62
63         dict_0 = get_adj_v_(list_0)
64         list_1 = [[dict_0[i][0], i, dict_0[i][1]] for i in dict_0 if (len(dict_0[i]) == 2)][0]
65         list_3 = []
66         for elem in list_1:
67                 list_3.append(bm.verts[elem])
68         list_2 = []
69
70         p_ = list_3[1]
71         p = (list_3[1].co).copy()
72         p1 = (list_3[0].co).copy()
73         p2 = (list_3[2].co).copy()
74
75         vec1 = p - p1
76         vec2 = p - p2
77
78         ang = vec1.angle(vec2, any)
79         f_buf.an = round(degrees(ang))
80
81         # -- -- -- --
82         if f_buf.an == 180 or f_buf.an == 0.0:
83                 return
84
85         # -- -- -- --
86         opp = adj
87
88         if radius == False:
89                 h = adj * (1 / cos(ang * 0.5))
90                 adj_ = adj
91         elif radius == True:
92                 h = opp / sin(ang * 0.5)
93                 adj_ = opp / tan(ang * 0.5)
94
95         p3 = p - (vec1.normalized() * adj_)
96         p4 = p - (vec2.normalized() * adj_)
97         rp = p - ((p - ((p3 + p4) * 0.5)).normalized() * h)
98
99         vec3 = rp - p3
100         vec4 = rp - p4
101
102         axis = vec1.cross(vec2)
103
104         if out == False:
105                 if flip == False:
106                         rot_ang = vec3.angle(vec4)
107                 elif flip == True:
108                         rot_ang = vec1.angle(vec2)
109         elif out == True:
110                 rot_ang = (2 * pi) - vec1.angle(vec2)
111
112         for j in range(n + 1):
113                 new_angle = rot_ang * j / n
114                 mtrx = Matrix.Rotation(new_angle, 3, axis)
115                 if out == False:
116                         if flip == False:
117                                 tmp = p4 - rp
118                                 tmp1 = mtrx * tmp
119                                 tmp2 = tmp1 + rp
120                         elif flip == True:
121                                 p3 = p - (vec1.normalized() * opp)
122                                 tmp = p3 - p
123                                 tmp1 = mtrx * tmp
124                                 tmp2 = tmp1 + p
125                 elif out == True:
126                         p4 = p - (vec2.normalized() * opp)
127                         tmp = p4 - p
128                         tmp1 = mtrx * tmp
129                         tmp2 = tmp1 + p
130
131                 v = bm.verts.new(tmp2)
132                 list_2.append(v)
133
134         if flip == True:
135                 list_3[1:2] = list_2
136         else:
137                 list_2.reverse()
138                 list_3[1:2] = list_2
139
140         list_clear_(list_2)
141
142         n1 = len(list_3)
143         for t in range(n1 - 1):
144                 bm.edges.new([list_3[t], list_3[(t + 1) % n1]])
145
146                 v = bm.verts.new(p)
147                 bm.edges.new([v, p_])
148
149         if face != None:
150                 for l in face.loops:
151                         if l.vert == list_3[0]:
152                                 startl = l
153                                 break
154                 vertlist2 = []
155                 if startl.link_loop_next.vert == startv:
156                         l = startl.link_loop_prev
157                         while len(vertlist) > 0:
158                                 vertlist2.insert(0, l.vert)
159                                 vertlist.pop(vertlist.index(l.vert))
160                                 l = l.link_loop_prev
161                 else:
162                         l = startl.link_loop_next
163                         while len(vertlist) > 0:
164                                 vertlist2.insert(0, l.vert)
165                                 vertlist.pop(vertlist.index(l.vert))
166                                 l = l.link_loop_next
167                 for v in list_3:
168                         vertlist2.append(v)
169                 bm.faces.new(vertlist2)
170
171         bm.verts.remove(startv)
172         list_3[1].select = 1
173         list_3[-2].select = 1
174         bm.edges.get([list_3[0], list_3[1]]).select = 1
175         bm.edges.get([list_3[-1], list_3[-2]]).select = 1
176         bm.verts.index_update()
177         bm.edges.index_update()
178         bm.faces.index_update()
179
180         me.update(calc_edges = True, calc_tessface=True)
181
182
183
184 def do_filletplus(pair):
185
186         global inaction
187         global flip
188
189
190         list_0 = [list([e.verts[0].index, e.verts[1].index]) for e in pair]
191
192         vertset = set([])
193         bm.verts.ensure_lookup_table()  
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 '''