Welcome to mirror list, hosted at ThFree Co, Russian Federation.

BPyMesh.py « bpymodules « scripts « release - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: da251944bbe4eb2d069c692e09d256a4b41a01f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
import Blender
from BPyMesh_redux import redux # seperated because of its size.

	
def meshWeight2Dict(me):
	''' Takes a mesh and return its group names and a list of dicts, one dict per vertex.
	using the group as a key and a float value for the weight.
	These 2 lists can be modified and then used with dict2MeshWeight to apply the changes.
	'''
	
	vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist.
	
	# Clear the vert group.
	groupNames= me.getVertGroupNames()
	
	for group in groupNames:
		for index, weight in me.getVertsFromGroup(group, 1): # (i,w)  tuples.
			vWeightDict[index][group]= weight
	
	# removed this because me may be copying teh vertex groups.
	#for group in groupNames:
	#	me.removeVertGroup(group)
	
	return groupNames, vWeightDict


def dict2MeshWeight(me, groupNames, vWeightDict):
	''' Takes a list of groups and a list of vertex Weight dicts as created by meshWeight2Dict
	and applys it to the mesh.'''
	
	if len(vWeightDict) != len(me.verts):
		raise 'Error, Lists Differ in size, do not modify your mesh.verts before updating the weights'
	
	# Clear the vert group.
	currentGroupNames= me.getVertGroupNames()
	for group in currentGroupNames:
		if group not in groupNames:
			me.removeVertGroup(group) # messes up the active group.
		else:
			me.removeVertsFromGroup(group)
	
	# Add clean unused vert groupNames back
	currentGroupNames= me.getVertGroupNames()
	for group in groupNames:
		if group not in currentGroupNames:
			me.addVertGroup(group)
	
	add_ = Blender.Mesh.AssignModes.ADD
	
	vertList= [None]
	for i, v in enumerate(me.verts):
		vertList[0]= i
		for group, weight in vWeightDict[i].iteritems():
			try:
				me.assignVertsToGroup(group, vertList, min(1, max(0, weight)), add_)
			except:
				pass # vert group is not used anymore.
	
	me.update()

def dictWeightMerge(dict_weights):
	'''
	Takes dict weight list and merges into 1 weight dict item and returns it
	'''
	
	if not dict_weights:
		return {}
	
	keys= []
	for weight in dict_weights:
		keys.extend([ (k, 0.0) for k in weight.iterkeys() ])
	
	new_wdict = dict(keys)
	
	len_dict_weights= len(dict_weights)
	
	for weight in dict_weights:
		for group, value in weight.iteritems():
			new_wdict[group] += value/len_dict_weights
	
	return new_wdict


FLIPNAMES=[\
('Left','Right'),\
('_L','_R'),\
('-L','-R'),\
('.L','.R'),\
]

def dictWeightFlipGroups(dict_weight, groupNames, createNewGroups):
	'''
	Returns a weight with flip names
	dict_weight - 1 vert weight.
	groupNames - because we may need to add new group names.
	dict_weight - Weather to make new groups where needed.
	'''
	
	def flipName(name):
		for n1,n2 in FLIPNAMES:
			for nA, nB in ( (n1,n2), (n1.lower(),n2.lower()), (n1.upper(),n2.upper()) ):
				if createNewGroups:
					newName= name.replace(nA,nB)
					if newName!=name:
						if newName not in groupNames:
							groupNames.append(newName)
						return newName
					
					newName= name.replace(nB,nA)
					if newName!=name:
						if newName not in groupNames:
							groupNames.append(newName)
						return newName
				
				else:
					newName= name.replace(nA,nB)
					if newName!=name and newName in groupNames:
						return newName
					
					newName= name.replace(nB,nA)
					if newName!=name and newName in groupNames:
						return newName
		
		return name
		
	if not dict_weight:
		return dict_weight, groupNames
	
	
	new_wdict = {}
	for group, weight in dict_weight.iteritems():
		flipname= flipName(group)
		new_wdict[flipname]= weight
	
	return new_wdict, groupNames
	

def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None):
	'''
	ob - the object that you want to get the mesh from
	container_mesh - a Blender.Mesh type mesh that is reused to avoid a new datablock per call to getMeshFromObject
	apply_modifiers - if enabled, subsurf bones etc. will be applied to the returned mesh. disable to get a copy of the mesh.
	vgroup - For mesh objects only, apply the vgroup to the the copied mesh. (slower)
	scn - Scene type. avoids getting the current scene each time getMeshFromObject is called.
	
	Returns Mesh or None
	'''
	
	if not scn:
		scn= Blender.Scene.GetCurrent()
	if not container_mesh:
		mesh = Blender.Mesh.New()	
	else:
		mesh= container_mesh
		mesh.verts= None
	
	
	type = ob.getType()
	dataname = ob.getData(1)
	tempob= None
	if apply_modifiers or type != 'Mesh':
		try:
			mesh.getFromObject(ob.name)
		except:
			return None
	
	else:
		'''
		Dont apply modifiers, copy the mesh. 
		So we can transform the data. its easiest just to get a copy of the mesh. 
		'''
		tempob= Blender.Object.New('Mesh')
		tempob.shareFrom(ob)
		scn.link(tempob)
		mesh.getFromObject(tempob.name)
		scn.unlink(tempob)
	
	if type == 'Mesh':
		if vgroups:
			if tempob==None:
				tempob= Blender.Object.New('Mesh')
			tempob.link(mesh)
			try:
				# Copy the influences if possible.
				groupNames, vWeightDict= meshWeight2Dict(tempMe)
				dict2MeshWeight(mesh, groupNames, vWeightDict)
			except:
				# if the modifier changes the vert count then it messes it up for us.
				pass
	
	return mesh


def faceRayIntersect(f, orig, dir):
	'''
	Returns face, side
	Side is the side of a quad we intersect.
		side 0 == 0,1,2
		side 1 == 0,2,3
	'''
	f_v= f.v
	isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[1].co, f_v[2].co, dir, orig, 1) # 1==clip
	
	if isect:
		return isect, 0
	
	if len(f_v)==4:
		isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[2].co, f_v[3].co, dir, orig, 1) # 1==clip
		if isect:
			return isect, 1
	return False, 0


def pickMeshRayFace(me, orig, dir):
	best_dist= 1000000
	best_isect= best_side= best_face= None
	for f in me.faces:
		isect, side= faceRayIntersect(f, orig, dir)
		if isect:
			dist= (isect-orig).length
			if dist<best_dist:
				best_dist= dist
				best_face= f
				best_side= side
				best_isect= isect
	f= best_face
	isect= best_isect
	side= best_side
	
	if f==None:
		return None, None, None, None, None
	
	f_v= [v.co for v in f.v]
	if side==1: # we can leave side 0 without changes.
		f_v = f_v[0], f_v[2], f_v[3]
	
	l0= (f_v[0]-isect).length
	l1= (f_v[1]-isect).length
	l2= (f_v[2]-isect).length
	
	w0 = (l1+l2)
	w1 = (l0+l2)
	w2 = (l1+l2)
	
	totw= w0 + w1 + w2
	w0=w0/totw
	w1=w1/totw
	w2=w2/totw
	
	return f, side, w0, w1, w2



def pickMeshGroupWeight(me, act_group, orig, dir):
	f, side, w0, w1, w2= pickMeshRayFace(me, orig, dir)
	
	f_v= f.v
	if side==0:
		f_vi= (f_v[0].index, f_v[1].index, f_v[2].index)
	else:
		f_vi= (f_v[0].index, f_v[2].index, f_v[3].index)
	
	vws= [0.0,0.0,0.0]
	for i in xrange(3):
		try:		vws[i]= me.getVertsFromGroup(act_group, 1, [f_vi[i],])[0][1]
		except:	pass
	
	return w0*vws[0] + w1*vws[1]  + w2*vws[2]

def pickMeshGroupVCol(me, orig, dir):
	Vector= Blender.Mathutils.Vector
	f, side, w0, w1, w2= pickMeshRayFace(me, orig, dir)
	
	def col2vec(c):
		return Vector(c.r, c.g, c.b)
	
	if side==0:
		idxs= 0,1,2
	else:
		idxs= 0,2,3
	f_c= f.col
	f_colvecs= [col2vec(f_c[i]) for i in idxs]
	return f_colvecs[0]*w0 +  f_colvecs[1]*w1 + f_colvecs[2]*w2

# reuse me more.
def sorted_edge_indicies(ed):
	i1= ed.v1.index
	i2= ed.v2.index
	if i1>i2:
		i1,i2= i2,i1
	return i1, i2

def edge_face_users(me):
	''' 
	Takes a mesh and returns a list aligned with the meshes edges.
	Each item is a list of the faces that use the edge
	would be the equiv for having ed.face_users as a property
	'''
	
	face_edges_dict= dict([(sorted_edge_indicies(ed), (ed.index, [])) for ed in me.edges])
	for f in me.faces:
		fvi= [v.index for v in f.v]# face vert idx's
		for i in xrange(len(f)):
			i1= fvi[i]
			i2= fvi[i-1]
			
			if i1>i2:
				i1,i2= i2,i1
			
			face_edges_dict[i1,i2][1].append(f)
	
	face_edges= [None] * len(me.edges)
	for ed_index, ed_faces in face_edges_dict.itervalues():
		face_edges[ed_index]= ed_faces
	
	return face_edges
		
		
def face_edges(me):
	'''
	Returns a list alligned to the meshes faces.
	each item is a list of lists: that is 
	face_edges -> face indicies
	face_edges[i] -> list referencs local faces v indicies 1,2,3 &| 4
	face_edges[i][j] -> list of faces that this edge uses.
	crap this is tricky to explain :/
	'''
	face_edges= [ [None] * len(f) for f in me.faces ]
	
	face_edges_dict= dict([(sorted_edge_indicies(ed), []) for ed in me.edges])
	for fidx, f in enumerate(me.faces):
		fvi= [v.index for v in f.v]# face vert idx's
		for i in xrange(len(f)):
			i1= fvi[i]
			i2= fvi[i-1]
			
			if i1>i2:
				i1,i2= i2,i1
			
			edge_face_users= face_edges_dict[i1,i2]
			edge_face_users.append(f)
			
			face_edges[fidx][i]= edge_face_users
			
	return face_edges
	

def facesPlanerIslands(me):
	DotVecs= Blender.Mathutils.DotVecs
	
	def roundvec(v):
		return round(v[0], 4), round(v[1], 4), round(v[2], 4)
	
	face_props= [(cent, no, roundvec(no), DotVecs(cent, no)) for f in me.faces    for no, cent in ((f.no, f.cent),)]
	
	face_edge_users= face_edges(me)
	islands= []
	
	used_faces= [0] * len(me.faces)
	while True:
		new_island= False
		for i, used_val in enumerate(used_faces):
			if used_val==0:
				island= [i]
				new_island= True
				used_faces[i]= 1
				break
		
		if not new_island:
			break
		
		island_growing= True
		while island_growing:
			island_growing= False
			for fidx1 in island[:]:
				if used_faces[fidx1]==1:
					used_faces[fidx1]= 2
					face_prop1= face_props[fidx1]
					for ed in face_edge_users[fidx1]:
						for f2 in ed:
							fidx2= f2.index
							if fidx1 != fidx2 and used_faces[fidx2]==0:
								island_growing= True
								face_prop2= face_props[fidx2]
								# normals are the same?
								if face_prop1[2]==face_prop2[2]:
									if abs(face_prop1[3] - DotVecs(face_prop1[1], face_prop2[0])) < 0.000001:
										used_faces[fidx2]= 1
										island.append(fidx2)
		islands.append([me.faces[i] for i in island])
	return islands



def facesUvIslands(me, PREF_IMAGE_DELIMIT=True):
	DotVecs= Blender.Mathutils.DotVecs
	def roundvec(v):
		return round(v[0], 4), round(v[1], 4)
	
	if not me.faceUV:
		return [ list(me.faces), ]
	
	# make a list of uv dicts
	face_uvs= [ [roundvec(uv) for uv in f.uv] for f in me.faces]
	
	# key - face uv || value - list of face idxs
	uv_connect_dict= dict([ (uv, [] ) for f_uvs in face_uvs for uv in f_uvs])
	
	for i, f_uvs in enumerate(face_uvs):
		for uv in f_uvs: # loops through rounded uv values
			uv_connect_dict[uv].append(i)
	islands= []
	
	used_faces= [0] * len(me.faces)
	while True:
		new_island= False
		for i, used_val in enumerate(used_faces):
			if used_val==0:
				island= [i]
				new_island= True
				used_faces[i]= 1
				break
		
		if not new_island:
			break
		
		island_growing= True
		while island_growing:
			island_growing= False
			for fidx1 in island[:]:
				if used_faces[fidx1]==1:
					used_faces[fidx1]= 2
					for uv in face_uvs[fidx1]:
						for fidx2 in uv_connect_dict[uv]:
							if fidx1 != fidx2 and used_faces[fidx2]==0:
								if not PREF_IMAGE_DELIMIT or me.faces[fidx1].image==me.faces[fidx2].image:
									island_growing= True
									used_faces[fidx2]= 1
									island.append(fidx2)
		
		islands.append([me.faces[i] for i in island])
	return islands

#def faceUvBounds(me, faces= None):
	

def facesUvRotate(me, deg, faces= None, pivot= (0,0)):
	'''
	Faces can be None an all faces will be used
	pivot is just the x/y well rotated about
	
	positive deg value for clockwise rotation
	'''
	if faces==None: faces= me.faces
	pivot= Blender.Mathutils.Vector(pivot)
	
	rotmat= Blender.Mathutils.RotationMatrix(-deg, 2)
	
	for f in faces:
		f.uv= [((uv-pivot)*rotmat)+pivot for uv in f.uv]

def facesUvScale(me, sca, faces= None, pivot= (0,0)):
	'''
	Faces can be None an all faces will be used
	pivot is just the x/y well rotated about
	sca can be wither an int/float or a vector if you want to
	  scale x/y seperately.
	  a sca or (1.0, 1.0) will do nothing.
	'''
	def vecmulti(v1,v2):
		'''V2 is unchanged'''
		v1[:]= (v1.x*v2.x, v1.y*v2.y)
		return v1
	
	sca= Blender.Mathutils.Vector(sca)
	if faces==None: faces= me.faces
	pivot= Blender.Mathutils.Vector(pivot)
	
	for f in faces:
		f.uv= [vecmulti(uv-pivot, sca)+pivot for uv in f.uv]

	
def facesUvTranslate(me, tra, faces= None, pivot= (0,0)):
	'''
	Faces can be None an all faces will be used
	pivot is just the x/y well rotated about
	'''
	if faces==None: faces= me.faces
	tra= Blender.Mathutils.Vector(tra)
	
	for f in faces:
		f.uv= [uv+tra for uv in f.uv]

	

def edgeFaceUserCount(me, faces= None):
	'''
	Return an edge aligned list with the count for all the faces that use that edge. -
	can spesify a subset of the faces, so only those will be counted.
	'''
	if faces==None:
		faces= me.faces
		max_vert= len(me.verts)
	else:
		# find the lighest vert index
		pass
	
	edge_users= [0] * len(me.edges)
	
	edges_idx_dict= dict([(sorted_edge_indicies(ed), ed.index) for ed in me.edges])

	for f in faces:
		fvi= [v.index for v in f.v]# face vert idx's
		for i in xrange(len(f)):
			i1= fvi[i]
			i2= fvi[i-1]
			
			if i1>i2:
				i1,i2= i2,i1
			
			edge_users[edges_idx_dict[i1,i2]] += 1 
			
	return edge_users


#============================================================================#
# Takes a face, and a pixel x/y on the image and returns a worldspace x/y/z  #
# will return none if the pixel is not inside the faces UV                   #
#============================================================================#
def getUvPixelLoc(face, pxLoc, img_size = None, uvArea = None):
	TriangleArea= Blender.Mathutils.TriangleArea
	Vector= Blender.Mathutils.Vector
	
	if not img_size:
		w,h = face.image.size
	else:
		w,h= img_size
	
	scaled_uvs= [Vector(uv.x*w, uv.y*h) for uv in f.uv]
	
	if len(scaled_uvs)==3:
		indicies= ((0,1,2),)
	else:
		indicies= ((0,1,2), (0,2,3))
	
	for fidxs in indicies:
		for i1,i2,i3 in fidxs:
			# IS a point inside our triangle?
			# UVArea could be cached?
			uv_area = TriangleArea(scaled_uvs[i1], scaled_uvs[i2], scaled_uvs[i3])
			area0 = TriangleArea(pxLoc, scaled_uvs[i2], scaled_uvs[i3])
			area1 = TriangleArea(pxLoc, scaled_uvs[i1],	scaled_uvs[i3])
			area2 = TriangleArea(pxLoc, scaled_uvs[i1], scaled_uvs[i2])
			if area0 + area1 + area2 > uv_area + 1: # 1 px bleed/error margin.
				pass # if were a quad the other side may contain the pixel so keep looking.
			else:
				# We know the point is in the tri
				area0 /= uv_area
				area1 /= uv_area
				area2 /= uv_area
				
				# New location
				return Vector(\
					face.v[i1].co[0]*area0 + face.v[i2].co[0]*area1 + face.v[i3].co[0]*area2,\
					face.v[i1].co[1]*area0 + face.v[i2].co[1]*area1 + face.v[i3].co[1]*area2,\
					face.v[i1].co[2]*area0 + face.v[i2].co[2]*area1 + face.v[i3].co[2]*area2\
				)
				
	return None


type_tuple= type( (0,) )
type_list= type( [] )


# Used for debugging ngon
"""
def draw_loops(loops):
	
	me= Blender.Mesh.New()
	for l in loops:
		#~ me= Blender.Mesh.New()
		
		
		i= len(me.verts)
		me.verts.extend([v[0] for v in l])
		try:
			me.verts[0].sel= 1
		except:
			pass
		me.edges.extend([ (j-1, j) for j in xrange(i+1, len(me.verts)) ])
		# Close the edge?
		me.edges.extend((i, len(me.verts)-1))
		
		
		#~ ob= Blender.Object.New('Mesh')
		#~ ob.link(me)
		#~ scn= Blender.Scene.GetCurrent()
		#~ scn.link(ob)
		#~ ob.Layers= scn.Layers
		#~ ob.sel= 1
		
		
		
	# Fill
	#fill= Blender.Mathutils.PolyFill(loops)
	#me.faces.extend(fill)
		
	
	ob= Blender.Object.New('Mesh')
	ob.link(me)
	scn= Blender.Scene.GetCurrent()
	scn.link(ob)
	ob.Layers= scn.Layers
	ob.sel= 1
	Blender.Window.RedrawAll()
"""

def ngon(from_data, indices, PREF_FIX_LOOPS= True):
	'''
	Takes a polyline of indices (fgon)
	and returns a list of face indicie lists.
	Designed to be used for importers that need indices for an fgon to create from existing verts.
	
	from_data: either a mesh, or a list/tuple of vectors.
	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.
	PREF_FIX_LOOPS: If this is enabled polylines that use loops to make multiple polylines are delt with correctly.
	'''
	Vector= Blender.Mathutils.Vector
	if not indices:
		return []
	
	#	return []
	def rvec(co): return round(co.x, 6), round(co.y, 6), round(co.z, 6)
	def mlen(co): return abs(co[0])+abs(co[1])+abs(co[2]) # manhatten length of a vector, faster then length
	
	def vert_treplet(v, i):
		return v, rvec(v), i, mlen(v)
	
	def ed_key_mlen(v1, v2):
		if v1[3] > v2[3]:
			return v2[1], v1[1]
		else:
			return v1[1], v2[1]
	
	
	if not PREF_FIX_LOOPS:
		'''
		Normal single concave loop filling
		'''
		if type(from_data) in (type_tuple, type_list):
			verts= [Vector(from_data[i]) for ii, i in enumerate(indices)]
		else:
			verts= [from_data.verts[i].co for ii, i in enumerate(indices)]
		
		for i in reversed(xrange(1, len(verts))):
			if verts[i][1]==verts[i-1][0]:
				verts.pop(i-1)
		
		fill= Blender.Mathutils.PolyFill([verts])
		
	else:
		'''
		Seperate this loop into multiple loops be finding edges that are used twice
		This is used by lightwave LWO files a lot
		'''
		
		if type(from_data) in (type_tuple, type_list):
			verts= [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)]
		else:
			verts= [vert_treplet(from_data.verts[i].co, ii) for ii, i in enumerate(indices)]
			
		edges= [(i, i-1) for i in xrange(len(verts))]
		if edges:
			edges[0]= (0,len(verts)-1)
		
		if not verts:
			return []
		
		
		edge_used_count= {}
		del_edges= {}
		# We need to check if any edges are used twice location based.
		for ed in edges:
			edkey= ed_key_mlen(verts[ed[0]], verts[ed[1]])
			try:
				del_edges[edkey]= edge_used_count[edkey]
			except:
				edge_used_count[edkey]= True
		
		# Store a list of unconnected loop segments split by double edges.
		# will join later
		loop_segments= [] 
		
		v_prev= verts[0]
		context_loop= [v_prev]
		loop_segments= [context_loop]
		
		for v in verts:
			if v!=v_prev:
				# Arze we crossing an edge we removed?
				#if del_edges.has_key(  ):
				try:	eddata= del_edges[ed_key_mlen(v, v_prev)]
				except:	eddata= None
				
				if eddata:
					context_loop= [v]
					loop_segments.append(context_loop)
				else:
					if context_loop and context_loop[-1][1]==v[1]:
						#raise "as"
						pass
					else:
						context_loop.append(v)
				
				v_prev= v
		# Now join loop segments
		
		def join_seg(s1,s2):
			if s2[-1][1]==s1[0][1]: # 
				s1,s2= s2,s1
			elif s1[-1][1]==s2[0][1]:
				pass
			else:
				return False
			
			# If were stuill here s1 and s2 are 2 segments in the same polyline
			s1.pop() # remove the last vert from s1
			s1.extend(s2) # add segment 2 to segment 1
			
			if s1[0][1]==s1[-1][1]: # remove endpoints double
				s1.pop()
			
			s2[:]= [] # Empty this segment s2 so we dont use it again.
			return True
		
		joining_segments= True
		while joining_segments:
			joining_segments= False
			segcount= len(loop_segments)
			
			for j in reversed(xrange(segcount)):
				seg_j= loop_segments[j]
				if seg_j:
					for k in reversed(xrange(j)):
						if not seg_j:
							break
						seg_k= loop_segments[k]
						
						if seg_k and join_seg(seg_j, seg_k):
							joining_segments= True
		
		loop_list= loop_segments
		
		for verts in loop_list:
			while verts and verts[0][1]==verts[-1][1]:
				verts.pop()
		# DONE DEALING WITH LOOP FIXING
		
		
		# vert mapping
		vert_map= [None]*len(indices)
		ii=0
		for verts in loop_list:
			if len(verts)>2:
				for i, vert in enumerate(verts):
					vert_map[i+ii]= vert[2]
				ii+=len(verts)
		
		fill= Blender.Mathutils.PolyFill([ [v[0] for v in loop] for loop in loop_list if len(loop) > 2 ])
		#draw_loops(loop_list)
		#raise 'done loop'
		# map to original indicies
		fill= [[vert_map[i] for i in reversed(f)] for f in fill]
	
	
	if not fill:
		print 'Warning Cannot scanfill, fallback on a triangle fan.'
		fill= [ [0, i-1, i] for i in xrange(2, len(indices)) ]
	else:
		# Use real scanfill.
		# See if its flipped the wrong way.
		flip= None
		for fi in fill:
			if flip != None:
				break
			for i, vi in enumerate(fi):
				if vi==0 and fi[i-1]==1:
					flip= False
					break
				elif vi==1 and fi[i-1]==0:
					flip= True
					break
		
		if not flip:
			for i, fi in enumerate(fill):
				fill[i]= tuple([ii for ii in reversed(fi)])
		
		
		
	
	return fill
	


# EG
'''
scn= Scene.GetCurrent()
me = scn.getActiveObject().getData(mesh=1)
ind= [v.index for v in me.verts if v.sel] # Get indices

indices = ngon(me, ind) # fill the ngon.

# Extand the faces to show what the scanfill looked like.
print len(indices)
me.faces.extend([[me.verts[ii] for ii in i] for i in indices])
'''

def meshCalcNormals(me, vertNormals=None):
	'''
	takes a mesh and returns very high quality normals 1 normal per vertex.
	The normals should be correct, indipendant of topology
	
	vertNormals - a list of vectors at least as long as the number of verts in the mesh
	'''
	Ang= Blender.Mathutils.AngleBetweenVecs
	Vector= Blender.Mathutils.Vector
	SMALL_NUM=0.000001
	# Weight the edge normals by total angle difference
	# EDGE METHOD
	
	if not vertNormals:
		vertNormals= [ Vector() for v in xrange(len(me.verts)) ]
	else:
		for v in vertNormals:
			v.zero()
		
	edges={}
	for f in me.faces:
		for i in xrange(len(f)):
			i1, i2= f.v[i].index, f.v[i-1].index
			if i1<i2:
				i1,i2= i2,i1
				
			try:
				edges[i1, i2].append(f.no)
			except:
				edges[i1, i2]= [f.no]
				
	# Weight the edge normals by total angle difference
	for fnos in edges.itervalues():
		
		len_fnos= len(fnos)
		if len_fnos>1:
			totAngDiff=0
			for j in reversed(xrange(len_fnos)):
				for k in reversed(xrange(j)):
					#print j,k
					try:
						totAngDiff+= (Ang(fnos[j], fnos[k])) # /180 isnt needed, just to keeop the vert small.
					except:
						pass # Zero length face
			
			# print totAngDiff
			if totAngDiff > SMALL_NUM:
				'''
				average_no= Vector()
				for no in fnos:
					average_no+=no
				'''
				average_no= reduce(lambda a,b: a+b, fnos, Vector())
				fnos.append(average_no*totAngDiff) # average no * total angle diff
			#else:
			#	fnos[0]
		else:
			fnos.append(fnos[0])
	
	for ed, v in edges.iteritems():
		vertNormals[ed[0]]+= v[-1]
		vertNormals[ed[1]]+= v[-1]
	for i, v in enumerate(me.verts):
		v.no= vertNormals[i]




def pointInsideMesh(ob, pt):
	Intersect = Blender.Mathutils.Intersect # 2 less dict lookups.
	Vector = Blender.Mathutils.Vector
	
	def ptInFaceXYBounds(f, pt):
			
		co= f.v[0].co
		xmax= xmin= co.x
		ymax= ymin= co.y
		
		co= f.v[1].co
		xmax= max(xmax, co.x)
		xmin= min(xmin, co.x)
		ymax= max(ymax, co.y)
		ymin= min(ymin, co.y)
		
		co= f.v[2].co
		xmax= max(xmax, co.x)
		xmin= min(xmin, co.x)
		ymax= max(ymax, co.y)
		ymin= min(ymin, co.y)
		
		if len(f)==4: 
			co= f.v[3].co
			xmax= max(xmax, co.x)
			xmin= min(xmin, co.x)
			ymax= max(ymax, co.y)
			ymin= min(ymin, co.y)
		
		# Now we have the bounds, see if the point is in it.
		if\
		pt.x < xmin or\
		pt.y < ymin or\
		pt.x > xmax or\
		pt.y > ymax:
			return False # point is outside face bounds
		else:
			return True # point inside.
		#return xmax, ymax, xmin, ymin
	
	def faceIntersect(f):
		isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, ray, obSpacePt, 1) # Clipped.
		if not isect and len(f) == 4:
			isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, ray, obSpacePt, 1) # Clipped.
				
		if isect and isect.z > obSpacePt.z: # This is so the ray only counts if its above the point. 
			return True
		else:
			return False
	
	
	obImvMat = Blender.Mathutils.Matrix(ob.matrixWorld)
	obImvMat.invert()
	pt.resize4D()
	obSpacePt = pt* obImvMat
	pt.resize3D()
	obSpacePt.resize3D()
	ray = Vector(0,0,-1)
	me= ob.getData(mesh=1)
	
	# Here we find the number on intersecting faces, return true if an odd number (inside), false (outside) if its true.
	return len([None for f in me.faces if ptInFaceXYBounds(f, obSpacePt) if faceIntersect(f)]) % 2


# NMesh wrapper
Vector= Blender.Mathutils.Vector
class NMesh(object):
	__slots__= 'verts', 'faces', 'edges', 'faceUV', 'materials', 'realmesh'
	def __init__(self, mesh):
		'''
		This is an NMesh wrapper that
		mesh is an Mesh as returned by Blender.Mesh.New()
		This class wraps NMesh like access into Mesh
		
		Running NMesh.update() - with this wrapper,
		Will update the realmesh.
		'''
		self.verts= []
		self.faces= []
		self.edges= []
		self.faceUV= False
		self.materials= []
		self.realmesh= mesh
	
	def addFace(self, nmf):
		self.faces.append(nmf)
	
	def Face(self, v=[]):
		return NMFace(v)
	def Vert(self, x,y,z):
		return NMVert(x,y,z)
	
	def hasFaceUV(self, flag):
		if flag:
			self.faceUV= True
		else:
			self.faceUV= False
	
	def addMaterial(self, mat):
		self.materials.append(mat)
	
	def update(self, recalc_normals=False): # recalc_normals is dummy
		mesh= self.realmesh
		mesh.verts= None # Clears the 
		
		# Add in any verts from faces we may have not added.
		for nmf in self.faces:
			for nmv in nmf.v:
				if nmv.index==-1:
					nmv.index= len(self.verts)
					self.verts.append(nmv)
					
		
		mesh.verts.extend([nmv.co for nmv in self.verts])
		for i, nmv in enumerate(self.verts):
			nmv.index= i
			mv= mesh.verts[i]
			mv.sel= nmv.sel
		
		good_faces= [nmf for nmf in self.faces if len(nmf.v) in (3,4)]
		#print len(good_faces), 'AAA'
		
		
		#mesh.faces.extend([nmf.v for nmf in self.faces])
		mesh.faces.extend([[mesh.verts[nmv.index] for nmv in nmf.v] for nmf in good_faces])
		if len(mesh.faces):
			if self.faceUV:
				mesh.faceUV= 1
			
			#for i, nmf in enumerate(self.faces):
			for i, nmf in enumerate(good_faces):
				mf= mesh.faces[i]
				if self.faceUV:
					if len(nmf.uv) == len(mf.v):
						mf.uv= [Vector(uv[0], uv[1]) for uv in nmf.uv]
					if len(nmf.col) == len(mf.v):
						for c, i in enumerate(mf.col):
							c.r, c.g, c.b= nmf.col[i].r, nmf.col[i].g, nmf.col[i].b
					if nmf.image:
						mf.image= nmf.image
		
		mesh.materials= self.materials[:16]

class NMVert(object):
	__slots__= 'co', 'index', 'no', 'sel', 'uvco'
	def __init__(self, x,y,z):
		self.co= Vector(x,y,z)
		self.index= None # set on appending.
		self.no= Vector(0,0,1) # dummy
		self.sel= 0
		self.uvco= None
class NMFace(object):
	__slots__= 'col', 'flag', 'hide', 'image', 'mat', 'materialIndex', 'mode', 'normal',\
	'sel', 'smooth', 'transp', 'uv', 'v'
	
	def __init__(self, v=[]):
		self.col= []
		self.flag= 0
		self.hide= 0
		self.image= None
		self.mat= 0 # materialIndex needs support too.
		self.mode= 0
		self.normal= Vector(0,0,1)
		self.uv= []
		self.sel= 0
		self.smooth= 0
		self.transp= 0
		self.uv= []
		self.v= [] # a list of nmverts.
	
class NMCol(object):
	__slots__ = 'r', 'g', 'b', 'a'
	def __init__(self):
		self.r= 255
		self.g= 255
		self.b= 255
		self.a= 255


'''
# 
verts_split= [dict() for i in xrange(len(me.verts))]

tot_verts= 0
for f in me.faces:
	f_uv= f.uv
	for i, v in enumerate(f.v):
		vert_index= v.index # mesh index
		vert_dict= verts_split[vert_index] # get the dict for this vert
		
		uv= f_uv[i]
		# now we have the vert and the face uv well make a unique dict.
		
		vert_key= v.x, v.y, v.x, uv.x, uv.y # ADD IMAGE NAME HETR IF YOU WANT TO SPLIT BY THAT TOO
		value= vert_index, tot_verts # ADD WEIGHT HERE IF YOU NEED.
		try:
			vert_dict[vert_key] # if this is missing it will fail.
		except:
			# this stores a mapping between the split and orig vert indicies
			vert_dict[vert_key]= value 
			tot_verts+= 1

# a flat list of split verts - can add custom weight data here too if you need
split_verts= [None]*tot_verts

for vert_split_dict in verts_split:
	for key, value in vert_split_dict.iteritems():
		local_index, split_index= value
		split_verts[split_index]= key

# split_verts - Now you have a list of verts split by their UV.
'''