added list2MeshWeight and meshWeight2List - faster then the dict equivilents and...
[blender.git] / release / scripts / bpymodules / BPyMesh_redux.py
1 # ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 # (C) Copyright 2006 MetaVR, Inc.
4 # http://www.metavr.com
5 # Written by Campbell Barton
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software Foundation,
19 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 #
21 # ***** END GPL LICENCE BLOCK *****
22 # --------------------------------------------------------------------------
23
24 import Blender
25 Vector= Blender.Mathutils.Vector
26 Ang= Blender.Mathutils.AngleBetweenVecs
27 LineIntersect= Blender.Mathutils.LineIntersect
28 CrossVecs= Blender.Mathutils.CrossVecs
29 import BPyMesh
30
31 # If python version is less than 2.4, try to get set stuff from module
32
33 try:
34         set
35 except:
36         try:
37                 from sets import Set as set
38         except:
39                 set= None
40
41 def uv_key(uv):
42         return round(uv.x, 5), round(uv.y, 5)
43         
44 def uv_key_mix(uv1, uv2, w1, w2):
45         # Weighted mix. w1+w2==1.0
46         return w1*uv1[0]+w2*uv2[0], w1*uv1[1]+w2*uv2[1]
47
48 def col_key(col):
49         return col.r, col.g, col.b
50         
51 def col_key_mix(col1, col2,  w1, w2):
52         # Weighted mix. w1+w2==1.0
53         return int(w1*col1[0] + w2*col2[0]), int(w1*col1[1] + w2*col2[1]), int(w1*col1[2]+col2[2]*w2)
54
55 def ed_key(ed):
56         i1= ed.v1.index
57         i2= ed.v2.index
58         if i1<i2: return i1,i2
59         return i2,i1
60
61 def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, REMOVE_DOUBLES=False, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True, DO_UV=True, DO_VCOL=True, DO_WEIGHTS=True, VGROUP_INF_REDUX= None, VGROUP_INF_WEIGHT=0.5):
62         """
63         BOUNDRY_WEIGHT - 0 is no boundry weighting. 2.0 will make them twice as unlikely to collapse.
64         FACE_AREA_WEIGHT - 0 is no weight. 1 is normal, 2.0 is higher.
65         """
66         
67         if REDUX<0 or REDUX>1.0:
68                 raise 'Error, factor must be between 0 and 1.0'
69         elif not set:
70                 raise 'Error, this function requires Python 2.4 or a full install of Python 2.3'
71         
72         BOUNDRY_WEIGHT= 1+BOUNDRY_WEIGHT
73         
74         """ # DEBUG!
75         if Blender.Get('rt') == 1000:
76                 DEBUG=True
77         else:
78                 DEBUG= False
79         """
80         
81         me= ob.getData(mesh=1)
82         me.hide= False # unhide all data,.
83         if len(me.faces)<5:
84                 return
85         
86         
87         
88         if FACE_TRIANGULATE or REMOVE_DOUBLES:
89                 me.sel= True
90         
91         if FACE_TRIANGULATE:
92                 me.quadToTriangle()
93         
94         if REMOVE_DOUBLES:
95                 me.remDoubles(0.0001)
96         
97         vgroups= me.getVertGroupNames()
98         
99         if not me.getVertGroupNames():
100                 DO_WEIGHTS= False
101         
102         if (VGROUP_INF_REDUX!= None and VGROUP_INF_REDUX not in vgroups) or\
103         VGROUP_INF_WEIGHT==0.0:
104                 VGROUP_INF_REDUX= None
105         
106         try:
107                 VGROUP_INF_REDUX_INDEX= vgroups.index(VGROUP_INF_REDUX)
108         except:
109                 VGROUP_INF_REDUX_INDEX= -1
110         
111         # del vgroups
112         len_vgroups= len(vgroups)
113         
114         
115         
116         OLD_MESH_MODE= Blender.Mesh.Mode()
117         Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
118         
119         if (DO_UV or DO_VCOL) and not me.faceUV:
120                 DO_VCOL= DO_UV= False
121                 
122         current_face_count= len(me.faces)
123         target_face_count= int(current_face_count * REDUX)
124         # % of the collapseable faces to collapse per pass.
125         #collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
126         collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
127         
128         """# DEBUG!
129         if DEBUG:
130                 COUNT= [0]
131                 def rd():
132                         if COUNT[0]< 330:
133                                 COUNT[0]+=1
134                                 return
135                         me.update()
136                         Blender.Window.RedrawAll()
137                         print 'Press key for next, count "%s"' % COUNT[0]
138                         try: input()
139                         except KeyboardInterrupt:
140                                 raise "Error"
141                         except:
142                                 pass
143                                 
144                         COUNT[0]+=1
145         """
146         
147         class collapseEdge(object):
148                 __slots__ = 'length', 'key', 'faces', 'collapse_loc', 'v1', 'v2','uv1', 'uv2', 'col1', 'col2', 'collapse_weight'
149                 def __init__(self, ed):
150                         self.init_from_edge(ed) # So we can re-use the classes without using more memory.
151                 
152                 def init_from_edge(self, ed):
153                         self.key= ed_key(ed)
154                         self.length= ed.length
155                         self.faces= []
156                         self.v1= ed.v1
157                         self.v2= ed.v2
158                         if DO_UV or DO_VCOL:
159                                 self.uv1= []
160                                 self.uv2= []
161                                 self.col1= []
162                                 self.col2= []
163                                 
164                         # self.collapse_loc= None # new collapse location.
165                         # Basic weighting.
166                         #self.collapse_weight= self.length *  (1+ ((ed.v1.no-ed.v2.no).length**2))
167                         self.collapse_weight= 1.0
168
169         class collapseFace(object):
170                 __slots__ = 'verts', 'normal', 'area', 'index', 'orig_uv', 'orig_col', 'uv', 'col' # , 'collapse_edge_count'
171                 def __init__(self, f):
172                         self.init_from_face(f)
173                 
174                 def init_from_face(self, f):
175                         self.verts= f.v
176                         self.normal= f.no
177                         self.area= f.area
178                         self.index= f.index
179                         if DO_UV or DO_VCOL:
180                                 self.orig_uv= [uv_key(uv) for uv in f.uv]
181                                 self.uv= f.uv
182                                 self.orig_col= [col_key(col) for col in f.col]
183                                 self.col= f.col
184         
185         collapse_edges= collapse_faces= None
186         
187         # So meshCalcNormals can avoid making a new list all the time.
188         reuse_vertNormals= [ Vector() for v in xrange(len(me.verts)) ]
189         
190         while target_face_count <= len(me.faces):
191                 BPyMesh.meshCalcNormals(me, reuse_vertNormals)
192                 
193                 if DO_WEIGHTS:
194                         #groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me)
195                         groupNames, vWeightList= BPyMesh.meshWeight2List(me)
196                 
197                 # THIS CRASHES? Not anymore.
198                 verts= list(me.verts)
199                 edges= list(me.edges)
200                 faces= list(me.faces)
201                 
202                 # THIS WORKS
203                 #verts= me.verts
204                 #edges= me.edges
205                 #faces= me.faces
206                 
207                 # if DEBUG: DOUBLE_CHECK= [0]*len(verts)
208                 me.sel= False
209                 
210                 if not collapse_faces: # Initialize the list.
211                         collapse_faces= [collapseFace(f) for f in faces]
212                         collapse_edges= [collapseEdge(ed) for ed in edges]
213                 else:
214                         for i, ed in enumerate(edges):
215                                 collapse_edges[i].init_from_edge(ed)
216                         
217                         # Strip the unneeded end off the list
218                         collapse_edges[i+1:]= []
219                                 
220                         for i, f in enumerate(faces):
221                                 collapse_faces[i].init_from_face(f)
222                         
223                         # Strip the unneeded end off the list
224                         collapse_faces[i+1:]= []
225                         
226                         
227                 collapse_edges_dict= dict( [(ced.key, ced) for ced in collapse_edges] )
228                 
229                 # Store verts edges.
230                 vert_ed_users= [[] for i in xrange(len(verts))]
231                 for ced in collapse_edges:
232                         vert_ed_users[ced.key[0]].append(ced)
233                         vert_ed_users[ced.key[1]].append(ced)
234                 
235                 # Store face users
236                 vert_face_users= [[] for i in xrange(len(verts))]
237                 
238                 # Have decieded not to use this. area is better.
239                 #face_perim= [0.0]* len(me.faces)
240                 
241                 for ii, cfa in enumerate(collapse_faces):
242                         for i, v1 in enumerate(cfa.verts):
243                                 vert_face_users[v1.index].append( (i,cfa) )
244                                 
245                                 # add the uv coord to the vert
246                                 v2 = cfa.verts[i-1]
247                                 i1= v1.index
248                                 i2= v2.index
249                                 
250                                 if i1>i2: ced= collapse_edges_dict[i2,i1]
251                                 else: ced= collapse_edges_dict[i1,i2]
252                                 
253                                 ced.faces.append(cfa)
254                                 if DO_UV or DO_VCOL:
255                                         # if the edge is flipped from its order in the face then we need to flip the order indicies.
256                                         if cfa.verts[i]==ced.v1:        i1,i2 = i, i-1
257                                         else:                                           i1,i2 = i-1, i
258                                         
259                                         if DO_UV:
260                                                 ced.uv1.append( cfa.orig_uv[i1] )
261                                                 ced.uv2.append( cfa.orig_uv[i2] )
262                                         
263                                         if DO_VCOL:
264                                                 ced.col1.append( cfa.orig_col[i1] )
265                                                 ced.col2.append( cfa.orig_col[i2] )
266                                         
267                                 
268                                 # PERIMITER
269                                 #face_perim[ii]+= ced.length
270                 
271                 
272                 
273                 # How weight the verts by the area of their faces * the normal difference.
274                 # when the edge collapses, to vert weights are taken into account 
275                 
276                 vert_weights= [0.5] * len(verts)
277                 
278                 for ii, vert_faces in enumerate(vert_face_users):
279                         for f in vert_faces:
280                                 try:
281                                         no_ang= (Ang(verts[ii].no, f[1].normal)/180) * f[1].area
282                                 except:
283                                         no_ang= 1.0
284                                 
285                                 vert_weights[ii] += no_ang
286                 
287                 # Use a vertex group as a weighting.
288                 if VGROUP_INF_REDUX!=None:
289                         
290                         # Get Weights from a vgroup.
291                         """
292                         vert_weights_map= [1.0] * len(verts)
293                         for i, wd in enumerate(vWeightDict):
294                                 try:    vert_weights_map[i]= 1+(wd[VGROUP_INF_REDUX] * VGROUP_INF_WEIGHT)
295                                 except: pass
296                         """
297                         vert_weights_map= [1+(wl[VGROUP_INF_REDUX_INDEX]*VGROUP_INF_WEIGHT) for wl in vWeightList ]
298                         
299                 
300                 # BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
301                 # Now we know how many faces link to an edge. lets get all the boundry verts
302                 if BOUNDRY_WEIGHT > 0:
303                         verts_boundry= [1] * len(verts)
304                         #for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
305                         for ced in collapse_edges:
306                                 if len(ced.faces) < 2:
307                                         for key in ced.key: # only ever 2 key indicies.
308                                                 verts_boundry[key]= 2
309                         
310                         for ced in collapse_edges:
311                                 b1= verts_boundry[ced.key[0]]
312                                 b2= verts_boundry[ced.key[1]]
313                                 if b1 != b2:
314                                         # Edge has 1 boundry and 1 non boundry vert. weight higher
315                                         ced.collapse_weight= BOUNDRY_WEIGHT
316                                 #elif b1==b2==2: # if both are on a seam then weigh half as bad.
317                                 #       ced.collapse_weight= ((BOUNDRY_WEIGHT-1)/2) +1
318                         # weight the verts by their boundry status
319                         del b1
320                         del b2
321                         
322                         for ii, boundry in enumerate(verts_boundry):
323                                 if boundry==2:
324                                         vert_weights[ii] *= BOUNDRY_WEIGHT
325                         
326                         vert_collapsed= verts_boundry
327                         del verts_boundry
328                 else:
329                         vert_collapsed= [1] * len(verts)
330                 
331                 
332                 def ed_set_collapse_loc(ced):
333                         v1co= ced.v1.co
334                         v2co= ced.v2.co
335                         v1no= ced.v1.no
336                         v2no= ced.v2.no
337                         
338                         # Basic operation, works fine but not as good as predicting the best place.
339                         #between= ((v1co*w1) + (v2co*w2))
340                         #ced.collapse_loc= between
341                         
342                         # Use the vertex weights to bias the new location.
343                         w1= vert_weights[ced.key[0]]
344                         w2= vert_weights[ced.key[1]]
345                         
346                         # normalize the weights of each vert - se we can use them as scalers.
347                         wscale= w1+w2
348                         if not wscale: # no scale?
349                                 w1=w2= 0.5
350                         else:
351                                 w1/=wscale
352                                 w2/=wscale
353                         
354                         length= ced.length
355                         between= (v1co+v2co) * 0.5
356                         
357                         # Collapse
358                         # new_location = between # Replace tricky code below. this code predicts the best collapse location.
359                         
360                         # Make lines at right angles to the normals- these 2 lines will intersect and be
361                         # the point of collapsing.
362                         
363                         # Enlarge so we know they intersect:  ced.length*2
364                         cv1= CrossVecs(v1no, CrossVecs(v1no, v1co-v2co))
365                         cv2= CrossVecs(v2no, CrossVecs(v2no, v2co-v1co))
366                         
367                         # Scale to be less then the edge lengths.
368                         cv1.normalize()
369                         cv2.normalize()
370                         cv1 = cv1 * (length* 0.4)
371                         cv2 = cv2 * (length* 0.4)
372                         
373                         smart_offset_loc= between + (cv1 + cv2)
374                         
375                         
376                         if (smart_offset_loc-between).length > length/2:
377                                 # New collapse loc is way out, just use midpoint.
378                                 ced.collapse_loc= between
379                         else:
380                                 # Now we need to blend between smart_offset_loc and w1/w2
381                                 # you see were blending between a vert and the edges midpoint, so we cant use a normal weighted blend.
382                                 if w1 > 0.5: # between v1 and smart_offset_loc
383                                         #ced.collapse_loc= v1co*(w2+0.5) + smart_offset_loc*(w1-0.5)
384                                         w2*=2
385                                         w1= 1-w2
386                                         
387                                         
388                                         ced.collapse_loc= v1co*w1 + smart_offset_loc*w2
389                                 else: # w between v2 and smart_offset_loc
390                                         w1*=2
391                                         w2= 1-w1
392                                         ced.collapse_loc= v2co*w2 + smart_offset_loc*w1
393                                         
394                                 if ced.collapse_loc.x != ced.collapse_loc.x: # NAN LOCATION, revert to between
395                                         ced.collapse_loc= between
396                                 
397                 
398                 # Best method, no quick hacks here, Correction. Should be the best but needs tweaks.
399                 def ed_set_collapse_error(ced):
400                         i1, i2= ced.key
401                         
402                         test_faces= set()
403                         for i in (i1,i2): # faster then LC's
404                                 for f in vert_face_users[i]:
405                                         test_faces.add(f[1].index)
406                         
407                         for f in ced.faces:
408                                 test_faces.remove(f.index)
409                         
410                         # test_faces= tuple(test_faces) # keep order
411                         
412                         v1_orig= Vector(ced.v1.co)
413                         v2_orig= Vector(ced.v2.co)
414                         
415                         ced.v1.co= ced.v2.co= ced.collapse_loc
416                         
417                         new_nos= [faces[i].no for i in test_faces]
418                         
419                         ced.v1.co= v1_orig
420                         ced.v2.co= v2_orig
421                         
422                         # now see how bad the normals are effected
423                         angle_diff= 1.0
424                         
425                         for ii, i in enumerate(test_faces): # local face index, global face index
426                                 cfa= collapse_faces[i] # this collapse face
427                                 try:
428                                         # can use perim, but area looks better.
429                                         if FACE_AREA_WEIGHT:
430                                                 # Psudo code for wrighting
431                                                 # angle_diff= The before and after angle difference between the collapsed and un-collapsed face.
432                                                 # ... devide by 180 so the value will be between 0 and 1.0
433                                                 # ... add 1 so we can use it as a multiplyer and not make the area have no eefect (below)
434                                                 # area_weight= The faces original area * the area weight
435                                                 # ... add 1.0 so a small area face dosent make the angle_diff have no effect.
436                                                 #
437                                                 # Now multiply - (angle_diff * area_weight)
438                                                 # ... The weight will be a minimum of 1.0 - we need to subtract this so more faces done give the collapse an uneven weighting.
439                                                 
440                                                 angle_diff+= ((1+(Ang(cfa.normal, new_nos[ii])/180)) * (1+(cfa.area * FACE_AREA_WEIGHT))) -1 # 4 is how much to influence area
441                                         else:
442                                                 angle_diff+= (Ang(cfa.normal), new_nos[ii])/180
443                                                 
444                                 except:
445                                         pass
446                         
447                         # This is very arbirary, feel free to modify
448                         try:            no_ang= (Ang(ced.v1.no, ced.v2.no)/180) + 1
449                         except:         no_ang= 2.0
450                                 
451                         # do *= because we face the boundry weight to initialize the weight. 1.0 default.
452                         ced.collapse_weight*=  ((no_ang * ced.length) * (1-(1/angle_diff)))# / max(len(test_faces), 1)
453                         
454                         
455                         # are we using a weight map
456                         if VGROUP_INF_REDUX:
457                                 v= vert_weights_map[i1]+vert_weights_map[i2]
458                                 ced.collapse_weight*= v
459                                 
460                 
461                 # We can calculate the weights on __init__ but this is higher qualuity.
462                 for ced in collapse_edges:
463                         if ced.faces: # dont collapse faceless edges.
464                                 ed_set_collapse_loc(ced)
465                                 ed_set_collapse_error(ced)
466                 
467                 # Wont use the function again.
468                 del ed_set_collapse_error
469                 del ed_set_collapse_loc
470                 # END BOUNDRY. Can remove
471                 
472                 # sort by collapse weight
473                 collapse_edges.sort(lambda ced1, ced2: cmp(ced1.collapse_weight, ced2.collapse_weight)) # edges will be used for sorting
474                 
475                 vert_collapsed= [0]*len(verts)
476                 
477                 collapse_edges_to_collapse= []
478                 
479                 # Make a list of the first half edges we can collapse,
480                 # these will better edges to remove.
481                 collapse_count=0
482                 for ced in collapse_edges:
483                         if ced.faces:
484                                 i1, i2= ced.key
485                                 # Use vert selections 
486                                 if vert_collapsed[i1] or vert_collapsed[i2]:
487                                         pass
488                                 else:
489                                         # Now we know the verts havnyt been collapsed.
490                                         vert_collapsed[i2]= vert_collapsed[i1]= 1 # Dont collapse again.
491                                         collapse_count+=1
492                                         collapse_edges_to_collapse.append(ced)
493                 
494                 # Get a subset of the entire list- the first "collapse_per_pass", that are best to collapse.
495                 if collapse_count > 4:
496                         collapse_count = int(collapse_count*collapse_per_pass)
497                 else:
498                         collapse_count = len(collapse_edges)
499                 # We know edge_container_list_collapse can be removed.
500                 for ced in collapse_edges_to_collapse:
501                         """# DEBUG!
502                         if DEBUG:
503                                 if DOUBLE_CHECK[ced.v1.index] or\
504                                 DOUBLE_CHECK[ced.v2.index]:
505                                         raise 'Error'
506                                 else:
507                                         DOUBLE_CHECK[ced.v1.index]=1
508                                         DOUBLE_CHECK[ced.v2.index]=1
509                                 
510                                 tmp= (ced.v1.co+ced.v2.co)*0.5
511                                 Blender.Window.SetCursorPos(tmp.x, tmp.y, tmp.z)
512                                 Blender.Window.RedrawAll()
513                         """
514                         
515                         # Chech if we have collapsed our quota.
516                         collapse_count-=1
517                         if not collapse_count:
518                                 break
519                         
520                         current_face_count -= len(ced.faces)
521                         
522                         # Interpolate the bone weights.
523                         if DO_WEIGHTS:
524                                 i1, i2= ced.key
525                                 w1= vert_weights[i1]
526                                 w2= vert_weights[i2]
527                                 
528                                 # Normalize weights
529                                 wscale= w1+w2
530                                 if not wscale: # no scale?
531                                         w1=w2= 0.5
532                                 else:
533                                         w1/= wscale
534                                         w2/= wscale
535                                 
536                                 
537                                 # add verts vgroups to eachother
538                                 '''
539                                 wd1= vWeightDict[i1] # v1 weight dict
540                                 wd2= vWeightDict[i2] # v2 weight dict
541                                 
542                                 # Make sure vert groups on both verts exist.
543                                 for wd_from, wd_to in ((wd1, wd2), (wd2, wd1)):
544                                         for group_key, weight_value in wd_from.iteritems():
545                                                 try: wd_to[group_key] # We have this weight?
546                                                 except: wd_to[group_key]= 0.0 # Adding a zero weight.
547                                 
548                                 # Mix the weights for vert groups
549                                 for group_key in wd_from.iterkeys():
550                                         wd1[group_key]= wd2[group_key]= (wd1[group_key]*w1) + (wd2[group_key]*w2)
551                                 '''
552                                 
553                                 wl1= vWeightList[i1] # v1 weight dict
554                                 wl2= vWeightList[i2] # v2 weight dict
555                                 for group_index in xrange(len_vgroups):
556                                         wl1[group_index]= wl2[group_index]= (wl1[group_index]*w1) + (wl2[group_index]*w2)
557                                 
558                         
559                         if DO_UV or DO_VCOL:
560                                 # Handel UV's and vert Colors!
561                                 for v, my_weight, other_weight, edge_my_uvs, edge_other_uvs, edge_my_cols, edge_other_cols in (\
562                                 ( ced.v1, vert_weights[ced.key[0]], vert_weights[ced.key[1]], ced.uv1, ced.uv2, ced.col1, ced.col2),\
563                                 ( ced.v2, vert_weights[ced.key[1]], vert_weights[ced.key[0]], ced.uv2, ced.uv1, ced.col2, ced.col1)\
564                                 ):
565                                         
566                                         # Normalize weights
567                                         wscale= my_weight+other_weight
568                                         if not wscale: # no scale?
569                                                 my_weight=other_weight= 0.5
570                                         else:
571                                                 my_weight/= wscale
572                                                 other_weight/= wscale
573                                         
574                                         uvs_mixed=   [ uv_key_mix(edge_my_uvs[iii],   edge_other_uvs[iii],  my_weight, other_weight)  for iii in xrange(len(edge_my_uvs))  ]
575                                         cols_mixed=  [ col_key_mix(edge_my_cols[iii], edge_other_cols[iii], my_weight, other_weight) for iii in xrange(len(edge_my_cols)) ]
576                                         
577                                         for face_vert_index, cfa in vert_face_users[v.index]:
578                                                 if len(cfa.verts)==3 and cfa not in ced.faces: # if the face is apart of this edge then dont bother finding the uvs since the face will be removed anyway.
579                                                 
580                                                         if DO_UV:
581                                                                 # UV COORDS
582                                                                 uvk=  cfa.orig_uv[face_vert_index] 
583                                                                 try:
584                                                                         tex_index= edge_my_uvs.index(uvk)
585                                                                 except:
586                                                                         tex_index= None
587                                                                         """ # DEBUG!
588                                                                         if DEBUG:
589                                                                                 print 'not found', uvk, 'in', edge_my_uvs, 'ed index', ii, '\nwhat about', edge_other_uvs
590                                                                         """
591                                                                 if tex_index != None: # This face uses a uv in the collapsing face. - do a merge
592                                                                         other_uv= edge_other_uvs[tex_index]
593                                                                         uv_vec= cfa.uv[face_vert_index]
594                                                                         uv_vec.x, uv_vec.y= uvs_mixed[tex_index]
595                                                         
596                                                         # TEXFACE COLORS
597                                                         if DO_VCOL:
598                                                                 colk= cfa.orig_col[face_vert_index] 
599                                                                 try:    tex_index= edge_my_cols.index(colk)
600                                                                 except: pass
601                                                                 if tex_index != None:
602                                                                         other_col= edge_other_cols[tex_index]
603                                                                         col_ob= cfa.col[face_vert_index]
604                                                                         col_ob.r, col_ob.g, col_ob.b= cols_mixed[tex_index]
605                                                         
606                                                         # DEBUG! if DEBUG: rd()
607                                 
608                         # Execute the collapse
609                         ced.v1.sel= ced.v2.sel= True # Select so remove doubles removed the edges and faces that use it
610                         ced.v1.co= ced.v2.co=  ced.collapse_loc
611                                 
612                         # DEBUG! if DEBUG: rd()
613                         if current_face_count <= target_face_count:
614                                 break
615                 
616                 # Copy weights back to the mesh before we remove doubles.
617                 if DO_WEIGHTS:
618                         #BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
619                         BPyMesh.list2MeshWeight(me, groupNames, vWeightList)
620                 
621                 doubles= me.remDoubles(0.0001) 
622                 current_face_count= len(me.faces)
623                 
624                 if current_face_count <= target_face_count or not doubles: # not doubles shoule never happen.
625                         break
626         
627         me.update()
628         Blender.Mesh.Mode(OLD_MESH_MODE)
629
630
631 # Example usage
632 def main():
633         Blender.Window.EditMode(0)
634         scn= Blender.Scene.GetCurrent()
635         active_ob= scn.getActiveObject()
636         t= Blender.sys.time()
637         redux(active_ob, 0.5)
638         print '%.4f' % (Blender.sys.time()-t)
639
640 if __name__=='__main__':
641         main()