4636866ab6e3da0604305e3dab2e1f3461f29539
[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         del vgroups
106         
107         OLD_MESH_MODE= Blender.Mesh.Mode()
108         Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
109         
110         if (DO_UV or DO_VCOL) and not me.faceUV:
111                 DO_VCOL= DO_UV= False
112                 
113         current_face_count= len(me.faces)
114         target_face_count= int(current_face_count * REDUX)
115         # % of the collapseable faces to collapse per pass.
116         #collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
117         collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
118         
119         """# DEBUG!
120         if DEBUG:
121                 COUNT= [0]
122                 def rd():
123                         if COUNT[0]< 330:
124                                 COUNT[0]+=1
125                                 return
126                         me.update()
127                         Blender.Window.RedrawAll()
128                         print 'Press key for next, count "%s"' % COUNT[0]
129                         try: input()
130                         except KeyboardInterrupt:
131                                 raise "Error"
132                         except:
133                                 pass
134                                 
135                         COUNT[0]+=1
136         """
137         
138         class collapseEdge(object):
139                 __slots__ = 'length', 'key', 'faces', 'collapse_loc', 'v1', 'v2','uv1', 'uv2', 'col1', 'col2', 'collapse_weight'
140                 def __init__(self, ed):
141                         self.init_from_edge(ed) # So we can re-use the classes without using more memory.
142                 
143                 def init_from_edge(self, ed):
144                         self.key= ed_key(ed)
145                         self.length= ed.length
146                         self.faces= []
147                         self.v1= ed.v1
148                         self.v2= ed.v2
149                         if DO_UV or DO_VCOL:
150                                 self.uv1= []
151                                 self.uv2= []
152                                 self.col1= []
153                                 self.col2= []
154                                 
155                         # self.collapse_loc= None # new collapse location.
156                         # Basic weighting.
157                         #self.collapse_weight= self.length *  (1+ ((ed.v1.no-ed.v2.no).length**2))
158                         self.collapse_weight= 1.0
159
160         class collapseFace(object):
161                 __slots__ = 'verts', 'normal', 'area', 'index', 'orig_uv', 'orig_col', 'uv', 'col' # , 'collapse_edge_count'
162                 def __init__(self, f):
163                         self.init_from_face(f)
164                 
165                 def init_from_face(self, f):
166                         self.verts= f.v
167                         self.normal= f.no
168                         self.area= f.area
169                         self.index= f.index
170                         if DO_UV or DO_VCOL:
171                                 self.orig_uv= [uv_key(uv) for uv in f.uv]
172                                 self.uv= f.uv
173                                 self.orig_col= [col_key(col) for col in f.col]
174                                 self.col= f.col
175         
176         collapse_edges= collapse_faces= None
177         
178         # So meshCalcNormals can avoid making a new list all the time.
179         reuse_vertNormals= [ Vector() for v in xrange(len(me.verts)) ]
180         
181         while target_face_count <= len(me.faces):
182                 BPyMesh.meshCalcNormals(me, reuse_vertNormals)
183                 
184                 if DO_WEIGHTS:
185                         groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me)
186                 
187                 # THIS CRASHES? Not anymore.
188                 verts= list(me.verts)
189                 edges= list(me.edges)
190                 faces= list(me.faces)
191                 
192                 # THIS WORKS
193                 #verts= me.verts
194                 #edges= me.edges
195                 #faces= me.faces
196                 
197                 # if DEBUG: DOUBLE_CHECK= [0]*len(verts)
198                 me.sel= False
199                 
200                 if not collapse_faces: # Initialize the list.
201                         collapse_faces= [collapseFace(f) for f in faces]
202                         collapse_edges= [collapseEdge(ed) for ed in edges]
203                 else:
204                         for i, ed in enumerate(edges):
205                                 collapse_edges[i].init_from_edge(ed)
206                         
207                         # Strip the unneeded end off the list
208                         collapse_edges[i+1:]= []
209                                 
210                         for i, f in enumerate(faces):
211                                 collapse_faces[i].init_from_face(f)
212                         
213                         # Strip the unneeded end off the list
214                         collapse_faces[i+1:]= []
215                         
216                         
217                 collapse_edges_dict= dict( [(ced.key, ced) for ced in collapse_edges] )
218                 
219                 # Store verts edges.
220                 vert_ed_users= [[] for i in xrange(len(verts))]
221                 for ced in collapse_edges:
222                         vert_ed_users[ced.key[0]].append(ced)
223                         vert_ed_users[ced.key[1]].append(ced)
224                 
225                 # Store face users
226                 vert_face_users= [[] for i in xrange(len(verts))]
227                 
228                 # Have decieded not to use this. area is better.
229                 #face_perim= [0.0]* len(me.faces)
230                 
231                 for ii, cfa in enumerate(collapse_faces):
232                         for i, v1 in enumerate(cfa.verts):
233                                 vert_face_users[v1.index].append( (i,cfa) )
234                                 
235                                 # add the uv coord to the vert
236                                 v2 = cfa.verts[i-1]
237                                 i1= v1.index
238                                 i2= v2.index
239                                 
240                                 if i1>i2: ced= collapse_edges_dict[i2,i1]
241                                 else: ced= collapse_edges_dict[i1,i2]
242                                 
243                                 ced.faces.append(cfa)
244                                 if DO_UV or DO_VCOL:
245                                         # if the edge is flipped from its order in the face then we need to flip the order indicies.
246                                         if cfa.verts[i]==ced.v1:        i1,i2 = i, i-1
247                                         else:                                           i1,i2 = i-1, i
248                                         
249                                         if DO_UV:
250                                                 ced.uv1.append( cfa.orig_uv[i1] )
251                                                 ced.uv2.append( cfa.orig_uv[i2] )
252                                         
253                                         if DO_VCOL:
254                                                 ced.col1.append( cfa.orig_col[i1] )
255                                                 ced.col2.append( cfa.orig_col[i2] )
256                                         
257                                 
258                                 # PERIMITER
259                                 #face_perim[ii]+= ced.length
260                 
261                 
262                 
263                 # How weight the verts by the area of their faces * the normal difference.
264                 # when the edge collapses, to vert weights are taken into account 
265                 
266                 vert_weights= [0.5] * len(verts)
267                 
268                 for ii, vert_faces in enumerate(vert_face_users):
269                         for f in vert_faces:
270                                 try:
271                                         no_ang= (Ang(verts[ii].no, f[1].normal)/180) * f[1].area
272                                 except:
273                                         no_ang= 1.0
274                                 
275                                 vert_weights[ii] += no_ang
276                 
277                 # Use a vertex group as a weighting.
278                 if VGROUP_INF_REDUX!=None:
279                         vert_weights_map= [1.0] * len(verts)
280                         # Get Weights from a vgroup.
281                         for i, wd in enumerate(vWeightDict):
282                                 try:    vert_weights_map[i]= 1+(wd[VGROUP_INF_REDUX] * VGROUP_INF_WEIGHT)
283                                 except: pass
284                 
285                 # BOUNDRY CHECKING AND WEIGHT EDGES. CAN REMOVE
286                 # Now we know how many faces link to an edge. lets get all the boundry verts
287                 if BOUNDRY_WEIGHT > 0:
288                         verts_boundry= [1] * len(verts)
289                         #for ed_idxs, faces_and_uvs in edge_faces_and_uvs.iteritems():
290                         for ced in collapse_edges:
291                                 if len(ced.faces) < 2:
292                                         for key in ced.key: # only ever 2 key indicies.
293                                                 verts_boundry[key]= 2
294                         
295                         for ced in collapse_edges:
296                                 b1= verts_boundry[ced.key[0]]
297                                 b2= verts_boundry[ced.key[1]]
298                                 if b1 != b2:
299                                         # Edge has 1 boundry and 1 non boundry vert. weight higher
300                                         ced.collapse_weight= BOUNDRY_WEIGHT
301                                 #elif b1==b2==2: # if both are on a seam then weigh half as bad.
302                                 #       ced.collapse_weight= ((BOUNDRY_WEIGHT-1)/2) +1
303                         # weight the verts by their boundry status
304                         del b1
305                         del b2
306                         
307                         for ii, boundry in enumerate(verts_boundry):
308                                 if boundry==2:
309                                         vert_weights[ii] *= BOUNDRY_WEIGHT
310                         
311                         vert_collapsed= verts_boundry
312                         del verts_boundry
313                 else:
314                         vert_collapsed= [1] * len(verts)
315                 
316                 
317                 def ed_set_collapse_loc(ced):
318                         v1co= ced.v1.co
319                         v2co= ced.v2.co
320                         v1no= ced.v1.no
321                         v2no= ced.v2.no
322                         
323                         # Basic operation, works fine but not as good as predicting the best place.
324                         #between= ((v1co*w1) + (v2co*w2))
325                         #ced.collapse_loc= between
326                         
327                         # Use the vertex weights to bias the new location.
328                         w1= vert_weights[ced.key[0]]
329                         w2= vert_weights[ced.key[1]]
330                         
331                         # normalize the weights of each vert - se we can use them as scalers.
332                         wscale= w1+w2
333                         if not wscale: # no scale?
334                                 w1=w2= 0.5
335                         else:
336                                 w1/=wscale
337                                 w2/=wscale
338                         
339                         length= ced.length
340                         between= (v1co+v2co) * 0.5
341                         
342                         # Collapse
343                         # new_location = between # Replace tricky code below. this code predicts the best collapse location.
344                         
345                         # Make lines at right angles to the normals- these 2 lines will intersect and be
346                         # the point of collapsing.
347                         
348                         # Enlarge so we know they intersect:  ced.length*2
349                         cv1= CrossVecs(v1no, CrossVecs(v1no, v1co-v2co))
350                         cv2= CrossVecs(v2no, CrossVecs(v2no, v2co-v1co))
351                         
352                         # Scale to be less then the edge lengths.
353                         cv1.normalize()
354                         cv2.normalize()
355                         cv1 = cv1 * (length* 0.4)
356                         cv2 = cv2 * (length* 0.4)
357                         
358                         smart_offset_loc= between + (cv1 + cv2)
359                         
360                         
361                         if (smart_offset_loc-between).length > length/2:
362                                 # New collapse loc is way out, just use midpoint.
363                                 ced.collapse_loc= between
364                         else:
365                                 # Now we need to blend between smart_offset_loc and w1/w2
366                                 # you see were blending between a vert and the edges midpoint, so we cant use a normal weighted blend.
367                                 if w1 > 0.5: # between v1 and smart_offset_loc
368                                         #ced.collapse_loc= v1co*(w2+0.5) + smart_offset_loc*(w1-0.5)
369                                         w2*=2
370                                         w1= 1-w2
371                                         
372                                         
373                                         ced.collapse_loc= v1co*w1 + smart_offset_loc*w2
374                                 else: # w between v2 and smart_offset_loc
375                                         w1*=2
376                                         w2= 1-w1
377                                         ced.collapse_loc= v2co*w2 + smart_offset_loc*w1
378                                         
379                                 if ced.collapse_loc.x != ced.collapse_loc.x: # NAN LOCATION, revert to between
380                                         ced.collapse_loc= between
381                                 
382                 
383                 # Best method, no quick hacks here, Correction. Should be the best but needs tweaks.
384                 def ed_set_collapse_error(ced):
385                         i1, i2= ced.key
386                         
387                         test_faces= set()
388                         for i in (i1,i2): # faster then LC's
389                                 for f in vert_face_users[i]:
390                                         test_faces.add(f[1].index)
391                         
392                         for f in ced.faces:
393                                 test_faces.remove(f.index)
394                         
395                         # test_faces= tuple(test_faces) # keep order
396                         
397                         v1_orig= Vector(ced.v1.co)
398                         v2_orig= Vector(ced.v2.co)
399                         
400                         ced.v1.co= ced.v2.co= ced.collapse_loc
401                         
402                         new_nos= [faces[i].no for i in test_faces]
403                         
404                         ced.v1.co= v1_orig
405                         ced.v2.co= v2_orig
406                         
407                         # now see how bad the normals are effected
408                         angle_diff= 1.0
409                         
410                         for ii, i in enumerate(test_faces): # local face index, global face index
411                                 cfa= collapse_faces[i] # this collapse face
412                                 try:
413                                         # can use perim, but area looks better.
414                                         if FACE_AREA_WEIGHT:
415                                                 # Psudo code for wrighting
416                                                 # angle_diff= The before and after angle difference between the collapsed and un-collapsed face.
417                                                 # ... devide by 180 so the value will be between 0 and 1.0
418                                                 # ... add 1 so we can use it as a multiplyer and not make the area have no eefect (below)
419                                                 # area_weight= The faces original area * the area weight
420                                                 # ... add 1.0 so a small area face dosent make the angle_diff have no effect.
421                                                 #
422                                                 # Now multiply - (angle_diff * area_weight)
423                                                 # ... The weight will be a minimum of 1.0 - we need to subtract this so more faces done give the collapse an uneven weighting.
424                                                 
425                                                 angle_diff+= ((1+(Ang(cfa.normal, new_nos[ii])/180)) * (1+(cfa.area * FACE_AREA_WEIGHT))) -1 # 4 is how much to influence area
426                                         else:
427                                                 angle_diff+= (Ang(cfa.normal), new_nos[ii])/180
428                                                 
429                                 except:
430                                         pass
431                         
432                         # This is very arbirary, feel free to modify
433                         try:            no_ang= (Ang(ced.v1.no, ced.v2.no)/180) + 1
434                         except:         no_ang= 2.0
435                                 
436                         # do *= because we face the boundry weight to initialize the weight. 1.0 default.
437                         ced.collapse_weight*=  ((no_ang * ced.length) * (1-(1/angle_diff)))# / max(len(test_faces), 1)
438                         
439                         
440                         # are we using a weight map
441                         if VGROUP_INF_REDUX:
442                                 v= vert_weights_map[i1]+vert_weights_map[i2]
443                                 ced.collapse_weight*= v
444                                 
445                 
446                 # We can calculate the weights on __init__ but this is higher qualuity.
447                 for ced in collapse_edges:
448                         if ced.faces: # dont collapse faceless edges.
449                                 ed_set_collapse_loc(ced)
450                                 ed_set_collapse_error(ced)
451                 
452                 # Wont use the function again.
453                 del ed_set_collapse_error
454                 del ed_set_collapse_loc
455                 # END BOUNDRY. Can remove
456                 
457                 # sort by collapse weight
458                 collapse_edges.sort(lambda ced1, ced2: cmp(ced1.collapse_weight, ced2.collapse_weight)) # edges will be used for sorting
459                 
460                 vert_collapsed= [0]*len(verts)
461                 
462                 collapse_edges_to_collapse= []
463                 
464                 # Make a list of the first half edges we can collapse,
465                 # these will better edges to remove.
466                 collapse_count=0
467                 for ced in collapse_edges:
468                         if ced.faces:
469                                 i1, i2= ced.key
470                                 # Use vert selections 
471                                 if vert_collapsed[i1] or vert_collapsed[i2]:
472                                         pass
473                                 else:
474                                         # Now we know the verts havnyt been collapsed.
475                                         vert_collapsed[i2]= vert_collapsed[i1]= 1 # Dont collapse again.
476                                         collapse_count+=1
477                                         collapse_edges_to_collapse.append(ced)
478                 
479                 # Get a subset of the entire list- the first "collapse_per_pass", that are best to collapse.
480                 if collapse_count > 4:
481                         collapse_count = int(collapse_count*collapse_per_pass)
482                 else:
483                         collapse_count = len(collapse_edges)
484                 # We know edge_container_list_collapse can be removed.
485                 for ced in collapse_edges_to_collapse:
486                         """# DEBUG!
487                         if DEBUG:
488                                 if DOUBLE_CHECK[ced.v1.index] or\
489                                 DOUBLE_CHECK[ced.v2.index]:
490                                         raise 'Error'
491                                 else:
492                                         DOUBLE_CHECK[ced.v1.index]=1
493                                         DOUBLE_CHECK[ced.v2.index]=1
494                                 
495                                 tmp= (ced.v1.co+ced.v2.co)*0.5
496                                 Blender.Window.SetCursorPos(tmp.x, tmp.y, tmp.z)
497                                 Blender.Window.RedrawAll()
498                         """
499                         
500                         # Chech if we have collapsed our quota.
501                         collapse_count-=1
502                         if not collapse_count:
503                                 break
504                         
505                         current_face_count -= len(ced.faces)
506                         
507                         # Interpolate the bone weights.
508                         if DO_WEIGHTS:
509                                 i1, i2= ced.key
510                                 w1= vert_weights[i1]
511                                 w2= vert_weights[i2]
512                                 
513                                 # Normalize weights
514                                 wscale= w1+w2
515                                 if not wscale: # no scale?
516                                         w1=w2= 0.5
517                                 else:
518                                         w1/= wscale
519                                         w2/= wscale
520                                 
521                                 
522                                 # add verts vgroups to eachother
523                                 wd1= vWeightDict[i1] # v1 weight dict
524                                 wd2= vWeightDict[i2] # v1 weight dict
525                                 
526                                 # Make sure vert groups on both verts exist.
527                                 for wd_from, wd_to in ((wd1, wd2), (wd2, wd1)):
528                                         for group_key, weight_value in wd_from.iteritems():
529                                                 try: wd_to[group_key] # We have this weight?
530                                                 except: wd_to[group_key]= 0.0 # Adding a zero weight.
531                                 
532                                 # Mix the weights for vert groups
533                                 for group_key in wd_from.iterkeys():
534                                         wd1[group_key]= wd2[group_key]= (wd1[group_key]*w1) + (wd2[group_key]*w2)
535                                 
536                         
537                         if DO_UV or DO_VCOL:
538                                 # Handel UV's and vert Colors!
539                                 for v, my_weight, other_weight, edge_my_uvs, edge_other_uvs, edge_my_cols, edge_other_cols in (\
540                                 ( ced.v1, vert_weights[ced.key[0]], vert_weights[ced.key[1]], ced.uv1, ced.uv2, ced.col1, ced.col2),\
541                                 ( ced.v2, vert_weights[ced.key[1]], vert_weights[ced.key[0]], ced.uv2, ced.uv1, ced.col2, ced.col1)\
542                                 ):
543                                         
544                                         # Normalize weights
545                                         wscale= my_weight+other_weight
546                                         if not wscale: # no scale?
547                                                 my_weight=other_weight= 0.5
548                                         else:
549                                                 my_weight/= wscale
550                                                 other_weight/= wscale
551                                         
552                                         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))  ]
553                                         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)) ]
554                                         
555                                         for face_vert_index, cfa in vert_face_users[v.index]:
556                                                 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.
557                                                 
558                                                         if DO_UV:
559                                                                 # UV COORDS
560                                                                 uvk=  cfa.orig_uv[face_vert_index] 
561                                                                 try:
562                                                                         tex_index= edge_my_uvs.index(uvk)
563                                                                 except:
564                                                                         tex_index= None
565                                                                         """ # DEBUG!
566                                                                         if DEBUG:
567                                                                                 print 'not found', uvk, 'in', edge_my_uvs, 'ed index', ii, '\nwhat about', edge_other_uvs
568                                                                         """
569                                                                 if tex_index != None: # This face uses a uv in the collapsing face. - do a merge
570                                                                         other_uv= edge_other_uvs[tex_index]
571                                                                         uv_vec= cfa.uv[face_vert_index]
572                                                                         uv_vec.x, uv_vec.y= uvs_mixed[tex_index]
573                                                         
574                                                         # TEXFACE COLORS
575                                                         if DO_VCOL:
576                                                                 colk= cfa.orig_col[face_vert_index] 
577                                                                 try:    tex_index= edge_my_cols.index(colk)
578                                                                 except: pass
579                                                                 if tex_index != None:
580                                                                         other_col= edge_other_cols[tex_index]
581                                                                         col_ob= cfa.col[face_vert_index]
582                                                                         col_ob.r, col_ob.g, col_ob.b= cols_mixed[tex_index]
583                                                         
584                                                         # DEBUG! if DEBUG: rd()
585                                 
586                         # Execute the collapse
587                         ced.v1.sel= ced.v2.sel= True # Select so remove doubles removed the edges and faces that use it
588                         ced.v1.co= ced.v2.co=  ced.collapse_loc
589                                 
590                         # DEBUG! if DEBUG: rd()
591                         if current_face_count <= target_face_count:
592                                 break
593                 
594                 # Copy weights back to the mesh before we remove doubles.
595                 if DO_WEIGHTS:
596                         BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
597                 
598                 doubles= me.remDoubles(0.0001) 
599                 current_face_count= len(me.faces)
600                 
601                 if current_face_count <= target_face_count or not doubles: # not doubles shoule never happen.
602                         break
603         
604         me.update()
605         Blender.Mesh.Mode(OLD_MESH_MODE)
606
607
608 # Example usage
609 def main():
610         Blender.Window.EditMode(0)
611         scn= Blender.Scene.GetCurrent()
612         active_ob= scn.getActiveObject()
613         t= Blender.sys.time()
614         redux(active_ob, 0.5)
615         print '%.4f' % (Blender.sys.time()-t)
616
617 if __name__=='__main__':
618         main()