37c7b64c12a2d91b82c617437179bace430e2f62
[blender.git] / release / scripts / bevel_center.py
1 #!BPY
2
3 """ Registration info for Blender menus
4 Name: 'Bevel Center'
5 Blender: 233
6 Group: 'Mesh'
7 Tip: 'Bevel selected vertices.'
8 """
9
10 # $Id$
11 #
12 ######################################################################
13 # Bevel Center v1 for Blender
14 #
15 # This script lets you bevel the selected vertices and control the
16 # thickness of the bevel
17 #
18 # (c) 2004 Loïc Berthe (loic.berthe@lilotux.net)
19 # released under Blender Artistic License
20 #
21 ######################################################################
22
23 import Blender
24 from Blender import NMesh, Window
25 from Blender.Draw import *
26 from Blender.BGL import *
27
28 from math import pi, sin, sqrt
29
30 ######################################################################
31 # Functions to handle the global structures of the script NV, NE and NC
32 # which contain informations about the vertices, faces and corners to be
33 # created
34
35 class Dir:
36         def __init__(self, co):
37                 self.co = co
38
39 def add_to_NV(old,co,new):
40         dir = Dir(co)
41         #
42         if old in NV.keys():
43                 NV[old][dir] = new
44         else:
45                 NV[old] = {dir:new}
46
47 def is_in_NV(old,co):
48         if old in NV.keys():
49                 for dir in NV[old]:
50                         if dir.co == co : return NV[old][dir]
51         #
52         return False
53
54 def add_to_NE(old, new):
55         ind1 = old[0].index
56         ind2 = old[1].index
57         if ind1 > ind2:
58                 new.reverse()
59                 ind1,ind2 = ind2,ind1
60         id = str(ind1)+"_"+str(ind2)
61         if id in NE.keys():
62                 [NE[id].append(v) for v in new]
63         else:
64                 NE[id] = new
65
66 def add_to_NC(old,edge):
67         if old in NC.keys():
68                 NC[old].append(edge)
69         else:
70                 NC[old] = [edge]
71                 
72 ######################################################################
73 # Geometric functions
74                 
75 def norm(vec):
76         n = sqrt(vec[0]**2+vec[1]**2+vec[2]**2)
77         return [vec[0]/n,vec[1]/n,vec[2]/n]
78
79 def parall_coord(old, dir):
80         co = old.co
81         vec = [0.0,0.0,0.0]
82         nco = [0.0,0.0,0.0]
83         #
84         if len(dir) == 1:
85                 for i in range(3): vec[i] = dir[0].co[i] - co[i] 
86                 vec = norm(vec)
87         #
88         elif len(dir) == 2:
89                 vec1 = [0.0,0.0,0.0]
90                 vec2 = [0.0,0.0,0.0]
91                 for i in range(3):
92                         vec1[i] = dir[0].co[i] - co[i] 
93                         vec2[i] = dir[1].co[i] - co[i] 
94                 vec1 = norm(vec1)
95                 vec2 = norm(vec2)
96                 for i in range(3) : vec[i] = vec1[i]+vec2[i]
97         #
98         for i in range(3): nco[i] = co[i] + dist.val*vec[i]
99         return (nco,vec)
100
101 def get_vert(old, dir):
102         """ Look in NV if a vertice corresponding to the vertex old and the
103         direction dir already exists, and create one otherwise""" 
104         (nco, vec) = parall_coord(old, dir)
105         v = is_in_NV(old,vec)
106         if v: return v
107         #
108         v = NMesh.Vert(nco[0],nco[1],nco[2])
109         v.sel = 1
110         me.verts.append(v)
111         add_to_NV(old,vec,v)
112         return v
113                         
114 ######################################################################
115 # Functions to create the differents faces
116         
117 def make_NF():
118         """ Analyse the mesh, sort the faces containing selected vertices and
119         create a liste NF : NF = [[flag, vertlist, old_face]]. Flag describes the
120         topology of the face.""" 
121         #
122         for f in me.faces:
123                 V = f.v
124                 v_sel = [x.sel for x in V]
125                 nb_sel = sum(v_sel)
126                 if nb_sel == 0 :
127                         pass
128                 else:
129                         nb_v = len(V)
130                         #
131                         if nb_v == 4:
132                                 #
133                                 if nb_sel == 4:
134                                         NF.append([1,V,f])
135                                 #
136                                 elif nb_sel == 3:
137                                         if v_sel == [0,1,1,1]: V = [V[1],V[2],V[3],V[0]]
138                                         elif v_sel == [1,0,1,1]: V = [V[2],V[3],V[0],V[1]]
139                                         elif v_sel == [1,1,0,1]: V = [V[3],V[0],V[1],V[2]]
140                                         NF.append([2,V,f])
141                                 #               
142                                 elif nb_sel == 2:
143                                         if v_sel == [1,0,1,0] or v_sel == [0,1,0,1]:
144                                                 if      v_sel == [0,1,0,1]: V = [V[1],V[2],V[3],V[0]]
145                                                 NF.append([5,[V[0],V[1],V[3]],f])
146                                                 NF.append([5,[V[2],V[1],V[3]]])
147                                         else:
148                                                 if v_sel == [0,1,1,0]: V = [V[1],V[2],V[3],V[0]]
149                                                 elif v_sel == [0,0,1,1]: V = [V[2],V[3],V[0],V[1]]
150                                                 elif v_sel == [1,0,0,1]: V = [V[3],V[0],V[1],V[2]]
151                                                 NF.append([3,V,f])
152                                 #
153                                 else:
154                                         if v_sel == [0,1,0,0]: V = [V[1],V[2],V[3],V[0]]
155                                         elif v_sel == [0,0,1,0]: V = [V[2],V[3],V[0],V[1]]
156                                         elif v_sel == [0,0,0,1]: V = [V[3],V[0],V[1],V[2]]
157                                         NF.append([4,V,f])
158                         #
159                         elif nb_v == 3:
160                                 #
161                                 if nb_sel == 3:
162                                         NF.append([6,V,f])
163                                 #
164                                 elif nb_sel == 2:
165                                         if v_sel == [0,1,1]: V = [V[1],V[2],V[0]]
166                                         elif v_sel == [1,0,1]: V = [V[2],V[0],V[1]]
167                                         NF.append([7,V,f])
168                                 #
169                                 else:
170                                         if v_sel == [0,1,0]: V = [V[1],V[2],V[0]]
171                                         elif v_sel == [0,0,1]: V = [V[2],V[0],V[1]]
172                                         NF.append([5,V,f])
173
174 def make_faces():
175         """ Make the new faces according to NF """
176         #
177         for N in NF:
178                 cas = N[0]
179                 V = N[1]
180                 #
181                 if cas < 6:
182                         new_v = [0,0,0,0]
183                         if cas == 1:                            # v_sel = [1,1,1,1]
184                                 for i in range(-1,3):
185                                         new_v[i] = get_vert(V[i],[V[i-1],V[i+1]])
186                                 new_f = NMesh.Face(new_v)
187                                 me.faces.append(new_f)
188                                 for i in range(-1,3):
189                                         add_to_NE([V[i],V[i+1]],[new_v[i],new_v[i+1]])
190                         #
191                         elif cas == 2:                          # v_sel = [1,1,1,0]
192                                 new_v[0] = get_vert(V[0],[V[3]])
193                                 new_v[1] = get_vert(V[1],[V[0],V[2]])
194                                 new_v[2] = get_vert(V[2],[V[3]])
195                                 new_v[3] = V[3]
196                                 #
197                                 new_f = NMesh.Face(new_v)
198                                 me.faces.append(new_f)
199                                 #
200                                 add_to_NE([V[0],V[1]],[new_v[0],new_v[1]])
201                                 add_to_NE([V[1],V[2]],[new_v[1],new_v[2]])
202                         #               
203                         elif cas == 3:                          # v_sel = [1,1,0,0]
204                                 new_v[0] = get_vert(V[0],[V[3]])
205                                 new_v[1] = get_vert(V[1],[V[2]])
206                                 new_v[2] = V[2]
207                                 new_v[3] = V[3]
208                                 #
209                                 new_f = NMesh.Face(new_v)
210                                 me.faces.append(new_f)
211                                 #
212                                 add_to_NE([V[0],V[1]],[new_v[0],new_v[1]])
213                         #
214                         elif cas == 4:                          # v_sel = [1,0,0,0]
215                                 new_v[0] = get_vert(V[0],[V[3]])
216                                 new_v[1] = get_vert(V[0],[V[1]])
217                                 new_v[2] = V[1]
218                                 new_v[3] = V[3]
219                                 #
220                                 new_f = NMesh.Face(new_v)
221                                 me.faces.append(new_f)
222                                 #
223                                 add_to_NC(V[0], new_v[0:2])
224                                 #
225                                 new_v[0] = V[1]
226                                 new_v[1] = V[2]
227                                 new_v[2] = V[3]
228                                 #
229                                 new_f = NMesh.Face(new_v[:3])
230                                 me.faces.append(new_f)
231                         #
232                         else:                           # v_sel = [1,0,0]
233                                 new_v[0] = get_vert(V[0],[V[2]])
234                                 new_v[1] = get_vert(V[0],[V[1]])
235                                 new_v[2] = V[1]
236                                 new_v[3] = V[2]
237                                 #
238                                 new_f = NMesh.Face(new_v)
239                                 me.faces.append(new_f)
240                                 #
241                                 add_to_NC(V[0], new_v[0:2])
242                 #
243                 else:
244                         new_v = [0,0,0]
245                         #
246                         if cas == 6:                            # v_sel = [1,1,1]
247                                 for i in range(-1,2):
248                                         new_v[i] = get_vert(V[i],[V[i-1],V[i+1]])
249                                 new_f = NMesh.Face(new_v)
250                                 me.faces.append(new_f)
251                                 for i in range(-1,2):
252                                         add_to_NE([V[i],V[i+1]],[new_v[i],new_v[i+1]])
253                         #
254                         elif cas == 7:                          # v_sel = [1,1,0]
255                                 new_v[0] = get_vert(V[0],[V[2]])
256                                 new_v[1] = get_vert(V[1],[V[2]])
257                                 new_v[2] = V[2]
258                                 #
259                                 new_f = NMesh.Face(new_v)
260                                 me.faces.append(new_f)
261                                 add_to_NE([V[0],V[1]],[new_v[0],new_v[1]])
262
263 def make_edges():
264         """ Make the faces corresponding to selected edges """
265         #
266         for l in NE.values():
267                 if len(l) == 4:
268                         f = NMesh.Face([l[0],l[1],l[3],l[2]])
269                         me.faces.append(f)
270
271 def make_corners():
272         """ Make the faces corresponding to selected corners """
273         #
274         for v in NV.keys():
275                 V = NV[v].values()
276                 nb_v = len(V)
277                 #
278                 if nb_v < 3:
279                         pass
280                 #
281                 elif nb_v == 3:
282                         new_f = NMesh.Face(V)
283                         me.faces.append(new_f)
284                 #
285                 else:
286                         # We need to know which are the edges around the corner.
287                         # First, we look for the quads surrounding the corner.
288                         q = [NE[id] for id in NE.keys() if str(v.index) in id.split('_')]
289                         #
290                         # We will put the associated edges in the list eed
291                         is_in_v = lambda x:x in V
292                         eed =  [filter(is_in_v, l) for l in q]
293                         #
294                         # We will add the edges coming from faces where only one vertice is selected.
295                         # They are stocked in NC.
296                         if v in NC.keys():
297                                 eed = eed+NC[v]
298                         b = eed.pop()
299                         # b will contain the sorted list of vertices
300                         #
301                         while  eed:
302                                 for l in eed:
303                                         if l[0] == b[-1]:
304                                                 b.append(l[1])
305                                                 eed.remove(l)
306                                                 break
307                                         elif l[1] == b[-1]:
308                                                 b.append(l[0])
309                                                 eed.remove(l)
310                                                 break
311                         # Now we can create the faces
312                         if nb_v == 4:
313                                 new_f = NMesh.Face(b[:4])
314                                 me.faces.append(new_f)
315                         #
316                         else:
317                                 co = [0.0, 0.0,0.0]
318                                 vec = [0.0, 0.0,0.0]
319                                 for x in V:
320                                         co[0] += x[0]
321                                         co[1] += x[1]
322                                         co[2] += x[2]
323                                 #
324                                 for dir in NV[v]:
325                                         vec[0] += dir.co[0]
326                                         vec[1] += dir.co[1]
327                                         vec[2] += dir.co[2]
328                                 #
329                                 co = [x/nb_v for x in co]
330                                 vec = [x/nb_v for x in vec]
331                                 center = NMesh.Vert(co[0],co[1],co[2])
332                                 center.sel = 1
333                                 me.verts.append(center)
334                                 add_to_NV(v,vec,center)
335                                 #
336                                 for k in range(nb_v):
337                                         new_f = NMesh.Face([center, b[k], b[k+1]])
338                                         me.faces.append(new_f)
339                 #
340
341 def clear_old():
342         """ Erase old faces and vertices """
343         for F in NF:
344                 if len(F) == 3:
345                         me.faces.remove(F[2])
346         #
347         for v in NV.keys():
348                 me.verts.remove(v)
349
350 ######################################################################
351 # Interface
352 #
353 global dist
354 dist = Create(0.2)
355 left = Create(0.0)
356 right = Create(1.0)
357 num = Create(2)
358
359 # Events
360 EVENT_NOEVENT = 1
361 EVENT_BEVEL = 2
362 EVENT_UPDATE = 3
363 EVENT_RECURS = 4
364 EVENT_EXIT = 5
365
366 def draw():
367         global dist, left, right, num
368         global EVENT_NOEVENT, EVENT_BEVEL, EVENT_UPDATE, EVENT_RECURS, EVENT_EXIT
369
370         glClear(GL_COLOR_BUFFER_BIT)
371         Button("Bevel",EVENT_BEVEL,10,100,280,25)
372         left=Number('',  EVENT_NOEVENT,10,70,45, 20,left.val,0,right.val,'Set the minimum of the slider')
373         right = Number("",EVENT_NOEVENT,245,70,45,20,right.val,left.val,200,"Set the maximum of the slider")
374         dist=Slider("Thickness  ",EVENT_UPDATE,60,70,180,20,dist.val,left.val,right.val,0,"Thickness of the bevel, can be changed even after bevelling")
375         glRasterPos2d(8,40)
376         Text('To finish, you can use recursive bevel to smooth it')
377         num=Number('',  EVENT_NOEVENT,10,10,40, 16,num.val,1,100,'Recursion level')
378         Button("Recursive",EVENT_RECURS,55,10,100,16)
379         Button("Exit",EVENT_EXIT,210,10,80,20)
380
381 def event(evt, val):
382         if ((evt == QKEY or evt == ESCKEY) and not val):
383                 Exit()
384
385 def bevent(evt):
386         if evt == EVENT_EXIT :
387                 Exit()
388         #
389         elif evt == EVENT_BEVEL:
390                 bevel()
391         #
392         elif evt == EVENT_UPDATE:
393                 try:
394                         bevel_update()
395                 except NameError:
396                         pass
397         #
398         elif evt == EVENT_RECURS:
399                 recursive()
400
401 Register(draw, event, bevent)
402
403 ######################################################################
404 def bevel():
405         """ The main function, which creates the bevel """
406         global me,NF,NV,NE,NC, old_dist
407         #
408         is_editmode = Window.EditMode()
409         if is_editmode: Window.EditMode(0)
410         objects = Blender.Object.GetSelected() 
411         me = NMesh.GetRaw(objects[0].data.name)
412         #
413         NF = []
414         NV = {}
415         NE = {}
416         NC = {}
417         #
418         make_NF()
419         make_faces()
420         make_edges()
421         make_corners()
422         clear_old()
423         #
424         old_dist = dist.val
425         #
426         me.update(1)
427         if is_editmode: Window.EditMode(1)
428         Blender.Redraw()
429
430 def bevel_update():
431         """ Use NV to update the bevel """
432         global dist, old_dist
433         is_editmode = Window.EditMode()
434         if is_editmode: Window.EditMode(0)
435         fac = dist.val - old_dist
436         old_dist = dist.val
437         #
438         for old_v in NV.keys():
439                 for dir in NV[old_v].keys():
440                         for i in range(3):
441                                 NV[old_v][dir].co[i] += fac*dir.co[i]
442         #
443         me.update(1)
444         if is_editmode: Window.EditMode(1)
445         Blender.Redraw()
446
447 def recursive():
448         """ Make a recursive bevel... still experimental """
449         global dist
450         #
451         if num.val > 1:
452                 a = pi/4
453                 ang = []
454                 for k in range(num.val):
455                         ang.append(a)
456                         a = (pi+2*a)/4
457                 #
458                 l = [2*(1-sin(x))/sin(2*x) for x in ang]
459                 R = dist.val/sum(l)
460                 l = [x*R for x in l]
461                 #
462                 dist.val = l[0]
463                 bevel_update()
464                 #
465                 for x in l[1:]:
466                         dist.val = x
467                         bevel()
468         
469 # vim:set ts=4 sw=4:
470