Made BPyMesh's ngon a lot better- handels polylines that loop back on themselves...
[blender.git] / release / scripts / bpymodules / BPyMesh.py
1 import Blender
2 from BPyMesh_redux import redux # seperated because of its size.
3
4         
5 def meshWeight2Dict(me):
6         ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex.
7         using the group as a key and a float value for the weight.
8         These 2 lists can be modified and then used with dict2MeshWeight to apply the changes.
9         '''
10         
11         vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist.
12         
13         # Clear the vert group.
14         groupNames= me.getVertGroupNames()
15         
16         for group in groupNames:
17                 for index, weight in me.getVertsFromGroup(group, 1): # (i,w)  tuples.
18                         vWeightDict[index][group]= weight
19         
20         # removed this because me may be copying teh vertex groups.
21         #for group in groupNames:
22         #       me.removeVertGroup(group)
23         
24         return groupNames, vWeightDict
25
26
27 def dict2MeshWeight(me, groupNames, vWeightDict):
28         ''' Takes a list of groups and a list of vertex Weight dicts as created by meshWeight2Dict
29         and applys it to the mesh.'''
30         
31         if len(vWeightDict) != len(me.verts):
32                 raise 'Error, Lists Differ in size, do not modify your mesh.verts before updating the weights'
33         
34         # Clear the vert group.
35         currentGroupNames= me.getVertGroupNames()
36         for group in currentGroupNames:
37                 if group not in groupNames:
38                         me.removeVertGroup(group) # messes up the active group.
39                 else:
40                         me.removeVertsFromGroup(group)
41         
42         # Add clean unused vert groupNames back
43         currentGroupNames= me.getVertGroupNames()
44         for group in groupNames:
45                 if group not in currentGroupNames:
46                         me.addVertGroup(group)
47         
48         add_ = Blender.Mesh.AssignModes.ADD
49         
50         vertList= [None]
51         for i, v in enumerate(me.verts):
52                 vertList[0]= i
53                 for group, weight in vWeightDict[i].iteritems():
54                         try:
55                                 me.assignVertsToGroup(group, vertList, min(1, max(0, weight)), add_)
56                         except:
57                                 pass # vert group is not used anymore.
58         
59         me.update()
60
61 def dictWeightMerge(dict_weights):
62         '''
63         Takes dict weight list and merges into 1 weight dict item and returns it
64         '''
65         
66         if not dict_weights:
67                 return {}
68         
69         keys= []
70         for weight in dict_weights:
71                 keys.extend([ (k, 0.0) for k in weight.iterkeys() ])
72         
73         new_wdict = dict(keys)
74         
75         len_dict_weights= len(dict_weights)
76         
77         for weight in dict_weights:
78                 for group, value in weight.iteritems():
79                         new_wdict[group] += value/len_dict_weights
80         
81         return new_wdict
82
83
84 FLIPNAMES=[\
85 ('Left','Right'),\
86 ('_L','_R'),\
87 ('-L','-R'),\
88 ('.L','.R'),\
89 ]
90
91 def dictWeightFlipGroups(dict_weight, groupNames, createNewGroups):
92         '''
93         Returns a weight with flip names
94         dict_weight - 1 vert weight.
95         groupNames - because we may need to add new group names.
96         dict_weight - Weather to make new groups where needed.
97         '''
98         
99         def flipName(name):
100                 for n1,n2 in FLIPNAMES:
101                         for nA, nB in ( (n1,n2), (n1.lower(),n2.lower()), (n1.upper(),n2.upper()) ):
102                                 if createNewGroups:
103                                         newName= name.replace(nA,nB)
104                                         if newName!=name:
105                                                 if newName not in groupNames:
106                                                         groupNames.append(newName)
107                                                 return newName
108                                         
109                                         newName= name.replace(nB,nA)
110                                         if newName!=name:
111                                                 if newName not in groupNames:
112                                                         groupNames.append(newName)
113                                                 return newName
114                                 
115                                 else:
116                                         newName= name.replace(nA,nB)
117                                         if newName!=name and newName in groupNames:
118                                                 return newName
119                                         
120                                         newName= name.replace(nB,nA)
121                                         if newName!=name and newName in groupNames:
122                                                 return newName
123                 
124                 return name
125                 
126         if not dict_weight:
127                 return dict_weight, groupNames
128         
129         
130         new_wdict = {}
131         for group, weight in dict_weight.iteritems():
132                 flipname= flipName(group)
133                 new_wdict[flipname]= weight
134         
135         return new_wdict, groupNames
136         
137
138 def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None):
139         '''
140         ob - the object that you want to get the mesh from
141         container_mesh - a Blender.Mesh type mesh that is reused to avoid a new datablock per call to getMeshFromObject
142         apply_modifiers - if enabled, subsurf bones etc. will be applied to the returned mesh. disable to get a copy of the mesh.
143         vgroup - For mesh objects only, apply the vgroup to the the copied mesh. (slower)
144         scn - Scene type. avoids getting the current scene each time getMeshFromObject is called.
145         
146         Returns Mesh or None
147         '''
148         
149         if not scn:
150                 scn= Blender.Scene.GetCurrent()
151         if not container_mesh:
152                 mesh = Blender.Mesh.New()       
153         else:
154                 mesh= container_mesh
155                 mesh.verts= None
156         
157         
158         type = ob.getType()
159         dataname = ob.getData(1)
160         tempob= None
161         if apply_modifiers or type != 'Mesh':
162                 try:
163                         mesh.getFromObject(ob.name)
164                 except:
165                         return None
166         
167         else:
168                 '''
169                 Dont apply modifiers, copy the mesh. 
170                 So we can transform the data. its easiest just to get a copy of the mesh. 
171                 '''
172                 tempob= Blender.Object.New('Mesh')
173                 tempob.shareFrom(ob)
174                 scn.link(tempob)
175                 mesh.getFromObject(tempob.name)
176                 scn.unlink(tempob)
177         
178         if type == 'Mesh':
179                 if vgroups:
180                         if tempob==None:
181                                 tempob= Blender.Object.New('Mesh')
182                         tempob.link(mesh)
183                         try:
184                                 # Copy the influences if possible.
185                                 groupNames, vWeightDict= meshWeight2Dict(tempMe)
186                                 dict2MeshWeight(mesh, groupNames, vWeightDict)
187                         except:
188                                 # if the modifier changes the vert count then it messes it up for us.
189                                 pass
190         
191         return mesh
192
193
194 def faceRayIntersect(f, orig, dir):
195         '''
196         Returns face, side
197         Side is the side of a quad we intersect.
198                 side 0 == 0,1,2
199                 side 1 == 0,2,3
200         '''
201         f_v= f.v
202         isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[1].co, f_v[2].co, dir, orig, 1) # 1==clip
203         
204         if isect:
205                 return isect, 0
206         
207         if len(f_v)==4:
208                 isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[2].co, f_v[3].co, dir, orig, 1) # 1==clip
209                 if isect:
210                         return isect, 1
211         return False, 0
212
213
214 def pickMeshRayFace(me, orig, dir):
215         best_dist= 1000000
216         best_isect= best_side= best_face= None
217         for f in me.faces:
218                 isect, side= faceRayIntersect(f, orig, dir)
219                 if isect:
220                         dist= (isect-orig).length
221                         if dist<best_dist:
222                                 best_dist= dist
223                                 best_face= f
224                                 best_side= side
225                                 best_isect= isect
226         f= best_face
227         isect= best_isect
228         side= best_side
229         
230         if f==None:
231                 return None, None, None, None, None
232         
233         f_v= [v.co for v in f.v]
234         if side==1: # we can leave side 0 without changes.
235                 f_v = f_v[0], f_v[2], f_v[3]
236         
237         l0= (f_v[0]-isect).length
238         l1= (f_v[1]-isect).length
239         l2= (f_v[2]-isect).length
240         
241         w0 = (l1+l2)
242         w1 = (l0+l2)
243         w2 = (l1+l2)
244         
245         totw= w0 + w1 + w2
246         w0=w0/totw
247         w1=w1/totw
248         w2=w2/totw
249         
250         return f, side, w0, w1, w2
251
252
253
254 def pickMeshGroupWeight(me, act_group, orig, dir):
255         f, side, w0, w1, w2= pickMeshRayFace(me, orig, dir)
256         
257         f_v= f.v
258         if side==0:
259                 f_vi= (f_v[0].index, f_v[1].index, f_v[2].index)
260         else:
261                 f_vi= (f_v[0].index, f_v[2].index, f_v[3].index)
262         
263         vws= [0.0,0.0,0.0]
264         for i in xrange(3):
265                 try:            vws[i]= me.getVertsFromGroup(act_group, 1, [f_vi[i],])[0][1]
266                 except: pass
267         
268         return w0*vws[0] + w1*vws[1]  + w2*vws[2]
269
270 def pickMeshGroupVCol(me, orig, dir):
271         Vector= Blender.Mathutils.Vector
272         f, side, w0, w1, w2= pickMeshRayFace(me, orig, dir)
273         
274         def col2vec(c):
275                 return Vector(c.r, c.g, c.b)
276         
277         if side==0:
278                 idxs= 0,1,2
279         else:
280                 idxs= 0,2,3
281         f_c= f.col
282         f_colvecs= [col2vec(f_c[i]) for i in idxs]
283         return f_colvecs[0]*w0 +  f_colvecs[1]*w1 + f_colvecs[2]*w2
284
285 # reuse me more.
286 def sorted_edge_indicies(ed):
287         i1= ed.v1.index
288         i2= ed.v2.index
289         if i1>i2:
290                 i1,i2= i2,i1
291         return i1, i2
292
293 def edge_face_users(me):
294         ''' 
295         Takes a mesh and returns a list aligned with the meshes edges.
296         Each item is a list of the faces that use the edge
297         would be the equiv for having ed.face_users as a property
298         '''
299         
300         face_edges_dict= dict([(sorted_edge_indicies(ed), (ed.index, [])) for ed in me.edges])
301         for f in me.faces:
302                 fvi= [v.index for v in f.v]# face vert idx's
303                 for i in xrange(len(f)):
304                         i1= fvi[i]
305                         i2= fvi[i-1]
306                         
307                         if i1>i2:
308                                 i1,i2= i2,i1
309                         
310                         face_edges_dict[i1,i2][1].append(f)
311         
312         face_edges= [None] * len(me.edges)
313         for ed_index, ed_faces in face_edges_dict.itervalues():
314                 face_edges[ed_index]= ed_faces
315         
316         return face_edges
317                 
318                 
319 def face_edges(me):
320         '''
321         Returns a list alligned to the meshes faces.
322         each item is a list of lists: that is 
323         face_edges -> face indicies
324         face_edges[i] -> list referencs local faces v indicies 1,2,3 &| 4
325         face_edges[i][j] -> list of faces that this edge uses.
326         crap this is tricky to explain :/
327         '''
328         face_edges= [ [None] * len(f) for f in me.faces ]
329         
330         face_edges_dict= dict([(sorted_edge_indicies(ed), []) for ed in me.edges])
331         for fidx, f in enumerate(me.faces):
332                 fvi= [v.index for v in f.v]# face vert idx's
333                 for i in xrange(len(f)):
334                         i1= fvi[i]
335                         i2= fvi[i-1]
336                         
337                         if i1>i2:
338                                 i1,i2= i2,i1
339                         
340                         edge_face_users= face_edges_dict[i1,i2]
341                         edge_face_users.append(f)
342                         
343                         face_edges[fidx][i]= edge_face_users
344                         
345         return face_edges
346         
347
348 def facesPlanerIslands(me):
349         DotVecs= Blender.Mathutils.DotVecs
350         
351         def roundvec(v):
352                 return round(v[0], 4), round(v[1], 4), round(v[2], 4)
353         
354         face_props= [(cent, no, roundvec(no), DotVecs(cent, no)) for f in me.faces    for no, cent in ((f.no, f.cent),)]
355         
356         face_edge_users= face_edges(me)
357         islands= []
358         
359         used_faces= [0] * len(me.faces)
360         while True:
361                 new_island= False
362                 for i, used_val in enumerate(used_faces):
363                         if used_val==0:
364                                 island= [i]
365                                 new_island= True
366                                 used_faces[i]= 1
367                                 break
368                 
369                 if not new_island:
370                         break
371                 
372                 island_growing= True
373                 while island_growing:
374                         island_growing= False
375                         for fidx1 in island[:]:
376                                 if used_faces[fidx1]==1:
377                                         used_faces[fidx1]= 2
378                                         face_prop1= face_props[fidx1]
379                                         for ed in face_edge_users[fidx1]:
380                                                 for f2 in ed:
381                                                         fidx2= f2.index
382                                                         if fidx1 != fidx2 and used_faces[fidx2]==0:
383                                                                 island_growing= True
384                                                                 face_prop2= face_props[fidx2]
385                                                                 # normals are the same?
386                                                                 if face_prop1[2]==face_prop2[2]:
387                                                                         if abs(face_prop1[3] - DotVecs(face_prop1[1], face_prop2[0])) < 0.000001:
388                                                                                 used_faces[fidx2]= 1
389                                                                                 island.append(fidx2)
390                 islands.append([me.faces[i] for i in island])
391         return islands
392
393
394
395 def facesUvIslands(me, PREF_IMAGE_DELIMIT=True):
396         DotVecs= Blender.Mathutils.DotVecs
397         def roundvec(v):
398                 return round(v[0], 4), round(v[1], 4)
399         
400         if not me.faceUV:
401                 return [ list(me.faces), ]
402         
403         # make a list of uv dicts
404         face_uvs= [ [roundvec(uv) for uv in f.uv] for f in me.faces]
405         
406         # key - face uv || value - list of face idxs
407         uv_connect_dict= dict([ (uv, [] ) for f_uvs in face_uvs for uv in f_uvs])
408         
409         for i, f_uvs in enumerate(face_uvs):
410                 for uv in f_uvs: # loops through rounded uv values
411                         uv_connect_dict[uv].append(i)
412         islands= []
413         
414         used_faces= [0] * len(me.faces)
415         while True:
416                 new_island= False
417                 for i, used_val in enumerate(used_faces):
418                         if used_val==0:
419                                 island= [i]
420                                 new_island= True
421                                 used_faces[i]= 1
422                                 break
423                 
424                 if not new_island:
425                         break
426                 
427                 island_growing= True
428                 while island_growing:
429                         island_growing= False
430                         for fidx1 in island[:]:
431                                 if used_faces[fidx1]==1:
432                                         used_faces[fidx1]= 2
433                                         for uv in face_uvs[fidx1]:
434                                                 for fidx2 in uv_connect_dict[uv]:
435                                                         if fidx1 != fidx2 and used_faces[fidx2]==0:
436                                                                 if not PREF_IMAGE_DELIMIT or me.faces[fidx1].image==me.faces[fidx2].image:
437                                                                         island_growing= True
438                                                                         used_faces[fidx2]= 1
439                                                                         island.append(fidx2)
440                 
441                 islands.append([me.faces[i] for i in island])
442         return islands
443
444 #def faceUvBounds(me, faces= None):
445         
446
447 def facesUvRotate(me, deg, faces= None, pivot= (0,0)):
448         '''
449         Faces can be None an all faces will be used
450         pivot is just the x/y well rotated about
451         
452         positive deg value for clockwise rotation
453         '''
454         if faces==None: faces= me.faces
455         pivot= Blender.Mathutils.Vector(pivot)
456         
457         rotmat= Blender.Mathutils.RotationMatrix(-deg, 2)
458         
459         for f in faces:
460                 f.uv= [((uv-pivot)*rotmat)+pivot for uv in f.uv]
461
462 def facesUvScale(me, sca, faces= None, pivot= (0,0)):
463         '''
464         Faces can be None an all faces will be used
465         pivot is just the x/y well rotated about
466         sca can be wither an int/float or a vector if you want to
467           scale x/y seperately.
468           a sca or (1.0, 1.0) will do nothing.
469         '''
470         def vecmulti(v1,v2):
471                 '''V2 is unchanged'''
472                 v1[:]= (v1.x*v2.x, v1.y*v2.y)
473                 return v1
474         
475         sca= Blender.Mathutils.Vector(sca)
476         if faces==None: faces= me.faces
477         pivot= Blender.Mathutils.Vector(pivot)
478         
479         for f in faces:
480                 f.uv= [vecmulti(uv-pivot, sca)+pivot for uv in f.uv]
481
482         
483 def facesUvTranslate(me, tra, faces= None, pivot= (0,0)):
484         '''
485         Faces can be None an all faces will be used
486         pivot is just the x/y well rotated about
487         '''
488         if faces==None: faces= me.faces
489         tra= Blender.Mathutils.Vector(tra)
490         
491         for f in faces:
492                 f.uv= [uv+tra for uv in f.uv]
493
494         
495
496 def edgeFaceUserCount(me, faces= None):
497         '''
498         Return an edge aligned list with the count for all the faces that use that edge. -
499         can spesify a subset of the faces, so only those will be counted.
500         '''
501         if faces==None:
502                 faces= me.faces
503                 max_vert= len(me.verts)
504         else:
505                 # find the lighest vert index
506                 pass
507         
508         edge_users= [0] * len(me.edges)
509         
510         edges_idx_dict= dict([(sorted_edge_indicies(ed), ed.index) for ed in me.edges])
511
512         for f in faces:
513                 fvi= [v.index for v in f.v]# face vert idx's
514                 for i in xrange(len(f)):
515                         i1= fvi[i]
516                         i2= fvi[i-1]
517                         
518                         if i1>i2:
519                                 i1,i2= i2,i1
520                         
521                         edge_users[edges_idx_dict[i1,i2]] += 1 
522                         
523         return edge_users
524
525
526 #============================================================================#
527 # Takes a face, and a pixel x/y on the image and returns a worldspace x/y/z  #
528 # will return none if the pixel is not inside the faces UV                   #
529 #============================================================================#
530 def getUvPixelLoc(face, pxLoc, img_size = None, uvArea = None):
531         TriangleArea= Blender.Mathutils.TriangleArea
532         Vector= Blender.Mathutils.Vector
533         
534         if not img_size:
535                 w,h = face.image.size
536         else:
537                 w,h= img_size
538         
539         scaled_uvs= [Vector(uv.x*w, uv.y*h) for uv in f.uv]
540         
541         if len(scaled_uvs)==3:
542                 indicies= ((0,1,2),)
543         else:
544                 indicies= ((0,1,2), (0,2,3))
545         
546         for fidxs in indicies:
547                 for i1,i2,i3 in fidxs:
548                         # IS a point inside our triangle?
549                         # UVArea could be cached?
550                         uv_area = TriangleArea(scaled_uvs[i1], scaled_uvs[i2], scaled_uvs[i3])
551                         area0 = TriangleArea(pxLoc, scaled_uvs[i2], scaled_uvs[i3])
552                         area1 = TriangleArea(pxLoc, scaled_uvs[i1],     scaled_uvs[i3])
553                         area2 = TriangleArea(pxLoc, scaled_uvs[i1], scaled_uvs[i2])
554                         if area0 + area1 + area2 > uv_area + 1: # 1 px bleed/error margin.
555                                 pass # if were a quad the other side may contain the pixel so keep looking.
556                         else:
557                                 # We know the point is in the tri
558                                 area0 /= uv_area
559                                 area1 /= uv_area
560                                 area2 /= uv_area
561                                 
562                                 # New location
563                                 return Vector(\
564                                         face.v[i1].co[0]*area0 + face.v[i2].co[0]*area1 + face.v[i3].co[0]*area2,\
565                                         face.v[i1].co[1]*area0 + face.v[i2].co[1]*area1 + face.v[i3].co[1]*area2,\
566                                         face.v[i1].co[2]*area0 + face.v[i2].co[2]*area1 + face.v[i3].co[2]*area2\
567                                 )
568                                 
569         return None
570
571
572 type_tuple= type( (0,) )
573 type_list= type( [] )
574 def ngon(from_data, indices, PREF_FIX_LOOPS= True):
575         '''
576         takes a polyline of indices (fgon)
577         and returns a list of face indicie lists.
578         Designed to be used for importers that need indices for an fgon to create from existing verts.
579         
580         from_data: either a mesh, or a list/tuple of vectors.
581         indices: a list of indicies to use this list is the ordered closed polyline to fill, and can be a subset of the data given.
582         PREF_FIX_LOOPS: If this is enabled polylines that use loops to make ultiple polylines are delt with correctly.
583         '''
584         Mesh= Blender.Mesh
585         Window= Blender.Window
586         Scene= Blender.Scene
587         Object= Blender.Object
588         
589         if len(indices) < 4:
590                 return [indices]
591         temp_mesh_name= '~NGON_TEMP~'
592         is_editmode= Window.EditMode()
593         if is_editmode:
594                 Window.EditMode(0)
595         try:
596                 temp_mesh = Mesh.Get(temp_mesh_name)
597                 if temp_mesh.users!=0:
598                         temp_mesh = Mesh.New(temp_mesh_name)
599         except:
600                 temp_mesh = Mesh.New(temp_mesh_name)
601                 
602         if type(from_data) in (type_tuple, type_list):
603                 # From a list/tuple of vectors
604                 temp_mesh.verts.extend( [from_data[i] for i in indices] )
605                 #temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
606                 edges= [(i, i-1) for i in xrange(len(temp_mesh.verts))]
607         else:
608                 # From a mesh
609                 temp_mesh.verts.extend( [from_data.verts[i].co for i in indices] )
610                 #temp_mesh.edges.extend( [(temp_mesh.verts[i], temp_mesh.verts[i-1]) for i in xrange(len(temp_mesh.verts))] )
611                 edges= [(i, i-1) for i in xrange(len(temp_mesh.verts))]
612         if edges:
613                 edges[0]= (0,len(temp_mesh.verts)-1)
614         
615         def rvec(co): return round(co.x, 6), round(co.y, 6), round(co.z, 6)
616         def mlen(co): return co[0]+co[1]+co[2] # manhatten length of a vector
617         
618         if PREF_FIX_LOOPS:
619                 edge_used_count= {}
620                 
621                 rounded_verts= [rvec(v.co) for v in temp_mesh.verts] # rounded verts we can use as dict keys.
622                 
623                 # We need to check if any edges are used twice location based.
624                 for ed_idx, ed in enumerate(edges):
625                         ed_v1= rounded_verts[ed[0]]
626                         ed_v2= rounded_verts[ed[1]]
627                         
628                         if ed_v1==ed_v2: # Same locations, remove the edge.
629                                 edges[ed_idx]= None
630                         else:
631                                 if mlen(ed_v1) < mlen(ed_v2):
632                                         edkey= ed_v1, ed_v2
633                                 else:
634                                         edkey= ed_v2, ed_v1
635                                 
636                                 try:
637                                         edge_user_list= edge_used_count[edkey]
638                                         edge_user_list.append(ed_idx)
639                                         
640                                         # remove edges if there are doubles.
641                                         if len(edge_user_list) > 1:
642                                                 for edidx in edge_user_list:\
643                                                         edges[edidx]= None
644                                 except:
645                                         edge_used_count[edkey]= [ed_idx]
646                 
647                 
648                 # Now remove double verts
649                 vert_doubles= {}
650                 for edidx, ed in enumerate(edges):
651                         if ed != None:
652                                 ed_v1= rounded_verts[ed[0]]
653                                 ed_v2= rounded_verts[ed[1]]
654                                 
655                                 if ed_v1==ed_v2:
656                                         edges[edidx]= None # will clear later, edge is zero length.
657                                         #print 'REMOVING DOUBLES'
658                                         
659                                 else:
660                                         # Try and replace with an existing vert or add teh one we use.
661                                         try:    edges[edidx]= vert_doubles[ed_v1], ed[1]
662                                         except:
663                                                 vert_doubles[ed_v1]= ed[0]
664                                                 #print 'REMOVING DOUBLES'
665                                                 
666                                         try:    edges[edidx]= ed[0], vert_doubles[ed_v2]
667                                         except:
668                                                 vert_doubles[ed_v2]= ed[1]
669                                                 #print 'REMOVING DOUBLES'
670                 
671                 edges= [ed for ed in edges if ed != None] # != None
672                 # Done removing double edges!
673                 
674         # DONE DEALING WITH LOOP FIXING
675         
676         
677         
678         temp_mesh.edges.extend(edges)
679                 
680         # Move verts to middle and normalize.
681         # For a good fill we need to normalize and scale the vert location.
682         
683         xmax=ymax=zmax= -1<<30
684         xmin=ymin=zmin= 1<<30
685         for v in temp_mesh.verts:
686                 co= v.co
687                 x= co.x
688                 y= co.y
689                 z= co.z
690                 if x<xmin: xmin=x
691                 if y<ymin: ymin=y
692                 if z<zmin: zmin=z
693                 if x>xmax: xmax=x
694                 if y>ymax: ymax=y
695                 if z>zmax: zmax=z
696         
697         # get the bounds on the largist axis
698         size= xmax-xmin
699         size= max(size, ymax-ymin)
700         size= max(size, zmax-zmin)
701         
702         xmid= (xmin+xmax)/2
703         ymid= (ymin+ymax)/2
704         zmid= (zmin+zmax)/2
705
706         x=x/len(temp_mesh.verts)
707         y=y/len(temp_mesh.verts)
708         z=z/len(temp_mesh.verts)
709         
710         for v in temp_mesh.verts:
711                 co= v.co
712                 co.x= (co.x-xmid)/size
713                 co.y= (co.y-ymid)/size
714                 co.z= (co.z-zmid)/size
715         # finished resizing the verts.
716         
717         oldmode = Mesh.Mode()
718         Mesh.Mode(Mesh.SelectModes['VERTEX'])
719         temp_mesh.sel= 1 # Select all verst     
720         
721         # Must link to scene
722         scn= Scene.GetCurrent()
723         temp_ob= Object.New('Mesh')
724         temp_ob.link(temp_mesh)
725         scn.link(temp_ob)
726         
727         temp_mesh.fill()
728         scn.unlink(temp_ob)
729         Mesh.Mode(oldmode)
730         
731         new_indices= [ [v.index for v in f.v]  for f in temp_mesh.faces ]
732         
733         if not new_indices: # JUST DO A FAN, Cant Scanfill
734                 print 'Warning Cannot scanfill!- Fallback on a triangle fan.'
735                 new_indices = [ [0, i-1, i] for i in xrange(2, len(indices)) ]
736         else:
737                 # Use real scanfill.
738                 # See if its flipped the wrong way.
739                 flip= None
740                 for fi in new_indices:
741                         if flip != None:
742                                 break
743                         for i, vi in enumerate(fi):
744                                 if vi==0 and fi[i-1]==1:
745                                         flip= False
746                                         break
747                                 elif vi==1 and fi[i-1]==0:
748                                         flip= True
749                                         break
750                 
751                 if not flip:
752                         for fi in new_indices:
753                                 fi.reverse()
754         
755         if is_editmode:
756                 Window.EditMode(1)
757                 
758         # Save some memory and forget about the verts.
759         # since we cant unlink the mesh.
760         temp_mesh.verts= None 
761         
762         return new_indices
763         
764
765
766 # EG
767 '''
768 scn= Scene.GetCurrent()
769 me = scn.getActiveObject().getData(mesh=1)
770 ind= [v.index for v in me.verts if v.sel] # Get indices
771
772 indices = ngon(me, ind) # fill the ngon.
773
774 # Extand the faces to show what the scanfill looked like.
775 print len(indices)
776 me.faces.extend([[me.verts[ii] for ii in i] for i in indices])
777 '''
778
779 def meshCalcNormals(me, vertNormals=None):
780         '''
781         takes a mesh and returns very high quality normals 1 normal per vertex.
782         The normals should be correct, indipendant of topology
783         
784         vertNormals - a list of vectors at least as long as the number of verts in the mesh
785         '''
786         Ang= Blender.Mathutils.AngleBetweenVecs
787         Vector= Blender.Mathutils.Vector
788         SMALL_NUM=0.000001
789         # Weight the edge normals by total angle difference
790         # EDGE METHOD
791         
792         if not vertNormals:
793                 vertNormals= [ Vector() for v in xrange(len(me.verts)) ]
794         else:
795                 for v in vertNormals:
796                         v.zero()
797                 
798         edges={}
799         for f in me.faces:
800                 for i in xrange(len(f)):
801                         i1, i2= f.v[i].index, f.v[i-1].index
802                         if i1<i2:
803                                 i1,i2= i2,i1
804                                 
805                         try:
806                                 edges[i1, i2].append(f.no)
807                         except:
808                                 edges[i1, i2]= [f.no]
809                                 
810         # Weight the edge normals by total angle difference
811         for fnos in edges.itervalues():
812                 
813                 len_fnos= len(fnos)
814                 if len_fnos>1:
815                         totAngDiff=0
816                         for j in reversed(xrange(len_fnos)):
817                                 for k in reversed(xrange(j)):
818                                         #print j,k
819                                         try:
820                                                 totAngDiff+= (Ang(fnos[j], fnos[k])) # /180 isnt needed, just to keeop the vert small.
821                                         except:
822                                                 pass # Zero length face
823                         
824                         # print totAngDiff
825                         if totAngDiff > SMALL_NUM:
826                                 '''
827                                 average_no= Vector()
828                                 for no in fnos:
829                                         average_no+=no
830                                 '''
831                                 average_no= reduce(lambda a,b: a+b, fnos, Vector())
832                                 fnos.append(average_no*totAngDiff) # average no * total angle diff
833                         #else:
834                         #       fnos[0]
835                 else:
836                         fnos.append(fnos[0])
837         
838         for ed, v in edges.iteritems():
839                 vertNormals[ed[0]]+= v[-1]
840                 vertNormals[ed[1]]+= v[-1]
841         for i, v in enumerate(me.verts):
842                 v.no= vertNormals[i]
843
844
845
846
847 def pointInsideMesh(ob, pt):
848         Intersect = Blender.Mathutils.Intersect # 2 less dict lookups.
849         Vector = Blender.Mathutils.Vector
850         
851         def ptInFaceXYBounds(f, pt):
852                         
853                 co= f.v[0].co
854                 xmax= xmin= co.x
855                 ymax= ymin= co.y
856                 
857                 co= f.v[1].co
858                 xmax= max(xmax, co.x)
859                 xmin= min(xmin, co.x)
860                 ymax= max(ymax, co.y)
861                 ymin= min(ymin, co.y)
862                 
863                 co= f.v[2].co
864                 xmax= max(xmax, co.x)
865                 xmin= min(xmin, co.x)
866                 ymax= max(ymax, co.y)
867                 ymin= min(ymin, co.y)
868                 
869                 if len(f)==4: 
870                         co= f.v[3].co
871                         xmax= max(xmax, co.x)
872                         xmin= min(xmin, co.x)
873                         ymax= max(ymax, co.y)
874                         ymin= min(ymin, co.y)
875                 
876                 # Now we have the bounds, see if the point is in it.
877                 if\
878                 pt.x < xmin or\
879                 pt.y < ymin or\
880                 pt.x > xmax or\
881                 pt.y > ymax:
882                         return False # point is outside face bounds
883                 else:
884                         return True # point inside.
885                 #return xmax, ymax, xmin, ymin
886         
887         def faceIntersect(f):
888                 isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, ray, obSpacePt, 1) # Clipped.
889                 if not isect and len(f) == 4:
890                         isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, ray, obSpacePt, 1) # Clipped.
891                                 
892                 if isect and isect.z > obSpacePt.z: # This is so the ray only counts if its above the point. 
893                         return True
894                 else:
895                         return False
896         
897         
898         obImvMat = Blender.Mathutils.Matrix(ob.matrixWorld)
899         obImvMat.invert()
900         pt.resize4D()
901         obSpacePt = pt* obImvMat
902         pt.resize3D()
903         obSpacePt.resize3D()
904         ray = Vector(0,0,-1)
905         me= ob.getData(mesh=1)
906         
907         # Here we find the number on intersecting faces, return true if an odd number (inside), false (outside) if its true.
908         return len([None for f in me.faces if ptInFaceXYBounds(f, obSpacePt) if faceIntersect(f)]) % 2
909
910
911 # NMesh wrapper
912 Vector= Blender.Mathutils.Vector
913 class NMesh(object):
914         __slots__= 'verts', 'faces', 'edges', 'faceUV', 'materials', 'realmesh'
915         def __init__(self, mesh):
916                 '''
917                 This is an NMesh wrapper that
918                 mesh is an Mesh as returned by Blender.Mesh.New()
919                 This class wraps NMesh like access into Mesh
920                 
921                 Running NMesh.update() - with this wrapper,
922                 Will update the realmesh.
923                 '''
924                 self.verts= []
925                 self.faces= []
926                 self.edges= []
927                 self.faceUV= False
928                 self.materials= []
929                 self.realmesh= mesh
930         
931         def addFace(self, nmf):
932                 self.faces.append(nmf)
933         
934         def Face(self, v=[]):
935                 return NMFace(v)
936         def Vert(self, x,y,z):
937                 return NMVert(x,y,z)
938         
939         def hasFaceUV(self, flag):
940                 if flag:
941                         self.faceUV= True
942                 else:
943                         self.faceUV= False
944         
945         def addMaterial(self, mat):
946                 self.materials.append(mat)
947         
948         def update(self, recalc_normals=False): # recalc_normals is dummy
949                 mesh= self.realmesh
950                 mesh.verts= None # Clears the 
951                 
952                 # Add in any verts from faces we may have not added.
953                 for nmf in self.faces:
954                         for nmv in nmf.v:
955                                 if nmv.index==-1:
956                                         nmv.index= len(self.verts)
957                                         self.verts.append(nmv)
958                                         
959                 
960                 mesh.verts.extend([nmv.co for nmv in self.verts])
961                 for i, nmv in enumerate(self.verts):
962                         nmv.index= i
963                         mv= mesh.verts[i]
964                         mv.sel= nmv.sel
965                 
966                 good_faces= [nmf for nmf in self.faces if len(nmf.v) in (3,4)]
967                 #print len(good_faces), 'AAA'
968                 
969                 
970                 #mesh.faces.extend([nmf.v for nmf in self.faces])
971                 mesh.faces.extend([[mesh.verts[nmv.index] for nmv in nmf.v] for nmf in good_faces])
972                 if len(mesh.faces):
973                         if self.faceUV:
974                                 mesh.faceUV= 1
975                         
976                         #for i, nmf in enumerate(self.faces):
977                         for i, nmf in enumerate(good_faces):
978                                 mf= mesh.faces[i]
979                                 if self.faceUV:
980                                         if len(nmf.uv) == len(mf.v):
981                                                 mf.uv= [Vector(uv[0], uv[1]) for uv in nmf.uv]
982                                         if len(nmf.col) == len(mf.v):
983                                                 for c, i in enumerate(mf.col):
984                                                         c.r, c.g, c.b= nmf.col[i].r, nmf.col[i].g, nmf.col[i].b
985                                         if nmf.image:
986                                                 mf.image= nmf.image
987                 
988                 mesh.materials= self.materials[:16]
989
990 class NMVert(object):
991         __slots__= 'co', 'index', 'no', 'sel', 'uvco'
992         def __init__(self, x,y,z):
993                 self.co= Vector(x,y,z)
994                 self.index= None # set on appending.
995                 self.no= Vector(0,0,1) # dummy
996                 self.sel= 0
997                 self.uvco= None
998 class NMFace(object):
999         __slots__= 'col', 'flag', 'hide', 'image', 'mat', 'materialIndex', 'mode', 'normal',\
1000         'sel', 'smooth', 'transp', 'uv', 'v'
1001         
1002         def __init__(self, v=[]):
1003                 self.col= []
1004                 self.flag= 0
1005                 self.hide= 0
1006                 self.image= None
1007                 self.mat= 0 # materialIndex needs support too.
1008                 self.mode= 0
1009                 self.normal= Vector(0,0,1)
1010                 self.uv= []
1011                 self.sel= 0
1012                 self.smooth= 0
1013                 self.transp= 0
1014                 self.uv= []
1015                 self.v= [] # a list of nmverts.
1016         
1017 class NMCol(object):
1018         __slots__ = 'r', 'g', 'b', 'a'
1019         def __init__(self):
1020                 self.r= 255
1021                 self.g= 255
1022                 self.b= 255
1023                 self.a= 255
1024
1025
1026 '''
1027
1028 verts_split= [dict() for i in xrange(len(me.verts))]
1029
1030 tot_verts= 0
1031 for f in me.faces:
1032         f_uv= f.uv
1033         for i, v in enumerate(f.v):
1034                 vert_index= v.index # mesh index
1035                 vert_dict= verts_split[vert_index] # get the dict for this vert
1036                 
1037                 uv= f_uv[i]
1038                 # now we have the vert and the face uv well make a unique dict.
1039                 
1040                 vert_key= v.x, v.y, v.x, uv.x, uv.y # ADD IMAGE NAME HETR IF YOU WANT TO SPLIT BY THAT TOO
1041                 value= vert_index, tot_verts # ADD WEIGHT HERE IF YOU NEED.
1042                 try:
1043                         vert_dict[vert_key] # if this is missing it will fail.
1044                 except:
1045                         # this stores a mapping between the split and orig vert indicies
1046                         vert_dict[vert_key]= value 
1047                         tot_verts+= 1
1048
1049 # a flat list of split verts - can add custom weight data here too if you need
1050 split_verts= [None]*tot_verts
1051
1052 for vert_split_dict in verts_split:
1053         for key, value in vert_split_dict.iteritems():
1054                 local_index, split_index= value
1055                 split_verts[split_index]= key
1056
1057 # split_verts - Now you have a list of verts split by their UV.
1058 '''