@@ -330,9 +330,7 @@ def property_topomesh_triangle_regularization_force(topomesh):
330330 #triangle_unitary_force = 0.02*area_unitary_force + 8.0*sinus_unitary_force
331331 # triangle_unitary_force = 0.02*area_unitary_force
332332
333- triangle_force = np .transpose ([nd .sum (triangle_unitary_force [:,0 ],triangle_vertices [:,0 ],index = list (topomesh .wisps (0 ))),
334- nd .sum (triangle_unitary_force [:,1 ],triangle_vertices [:,0 ],index = list (topomesh .wisps (0 ))),
335- nd .sum (triangle_unitary_force [:,2 ],triangle_vertices [:,0 ],index = list (topomesh .wisps (0 )))])
333+ triangle_force = np .transpose ([nd .sum (triangle_unitary_force [:,k ],triangle_vertices [:,0 ],index = list (topomesh .wisps (0 ))) for k in xrange (3 )])
336334
337335 return triangle_force
338336
@@ -962,13 +960,15 @@ def topomesh_remove_vertex(topomesh,pid,kept_fid=None,triangulate=True):
962960
963961
964962def topomesh_collapse_edge (topomesh ,eid ,kept_pid = None ,manifold = True ):
963+ initial_topomesh = deepcopy (topomesh )
964+
965965 try :
966966 if manifold :
967967 assert len (list (topomesh .regions (1 ,eid ))) == 2
968968
969969 pid_to_keep , pid_to_delete = topomesh .borders (1 ,eid )
970970
971- print "--> Collapsing edge" ,eid ," : " ,pid_to_keep ," ; " ,pid_to_delete
971+ print "--> Trying to collapse edge" ,eid ," : " ,pid_to_keep ," ; " ,pid_to_delete
972972
973973 if kept_pid is not None and pid_to_keep != kept_pid :
974974 pid_to_delete , pid_to_keep = topomesh .borders (1 ,eid )
@@ -1000,8 +1000,8 @@ def topomesh_collapse_edge(topomesh,eid,kept_pid=None,manifold=True):
10001000 eid_to_keep = list (set (topomesh .borders (2 ,fid )).intersection (set (eids_to_keep )).difference ({eid }))[0 ]
10011001 eid_to_delete = list (set (topomesh .borders (2 ,fid )).intersection (set (eids_to_delete )).difference ({eid }))[0 ]
10021002
1003- print " --> Kept eid : " ,eid_to_keep ,list (topomesh .regions (1 ,eid_to_keep ))
1004- print " --> Deleted eid : " ,eid_to_delete ,list (topomesh .regions (1 ,eid_to_delete ))
1003+ print " --> Kept eid : " ,eid_to_keep ,list (topomesh .regions (1 ,eid_to_keep ))
1004+ print " --> Deleted eid : " ,eid_to_delete ,list (topomesh .regions (1 ,eid_to_delete ))
10051005
10061006 topomesh .unlink (2 ,fid ,eid )
10071007 topomesh .unlink (2 ,fid ,eid_to_keep )
@@ -1041,18 +1041,32 @@ def topomesh_collapse_edge(topomesh,eid,kept_pid=None,manifold=True):
10411041
10421042 topomesh .remove_wisp (1 ,eid )
10431043
1044- print "<-- Collapsed edge" ,eid ," : " ,pid_to_keep
1044+ if np .max ([topomesh .nb_regions (1 ,e ) for e in topomesh .wisps (1 )])> 2 :
1045+ print " --> Error while collapsing! Non-manifold edges :" ,np .array (list (topomesh .wisps (1 )))[np .array ([topomesh .nb_regions (1 ,e ) for e in topomesh .wisps (1 )])> 2 ]
1046+
1047+ assert np .max ([topomesh .nb_regions (1 ,e ) for e in topomesh .wisps (1 )])== 2
1048+
1049+ print "<-- Collapsed edge" ,eid ," : " ,pid_to_keep ," (" ,topomesh .nb_wisps (2 )," Faces )"
1050+ #raw_input()
10451051
10461052 return True
10471053
10481054 # edge_vertices = np.sort(np.array([list(topomesh.borders(1,e)) for e in topomesh.wisps(1) if topomesh.nb_borders(1,e) == 2]))
10491055 # edge_vertex_id = edge_vertices[:,0]*10000 + edge_vertices[:,1]
10501056 # if edge_vertices.shape[0] != array_unique(edge_vertices).shape[0]:
1051- # print eid," collapse error : (",pid_to_keep,pid_to_delete,")",np.array(list(topomesh.wisps(1)))[nd.sum(np.ones_like(edge_vertex_id),edge_vertex_id,index=edge_vertex_id)>1]
1057+ # print eid," collapse error : (",pid_to_keep,pid_to_delete,@ ")",np.array(list(topomesh.wisps(1)))[nd.sum(np.ones_like(edge_vertex_id),edge_vertex_id,index=edge_vertex_id)>1]
10521058 # raw_input()
10531059
10541060 except AssertionError :
1055- print "Impossible to collapse edge : wrong configuration ( " ,len (list (topomesh .regions (1 ,eid )))," regions)"
1061+ print "<-- Impossible to collapse edge : wrong configuration ( " ,len (list (initial_topomesh .regions (1 ,eid )))," regions)"
1062+ assert np .max ([initial_topomesh .nb_regions (1 ,e ) for e in initial_topomesh .wisps (1 )])== 2
1063+ topomesh ._borders = deepcopy (initial_topomesh ._borders )
1064+ topomesh ._regions = deepcopy (initial_topomesh ._regions )
1065+ topomesh .update_wisp_property ('barycenter' ,0 ,initial_topomesh .wisp_property ('barycenter' ,0 ))
1066+ assert np .max ([topomesh .nb_regions (1 ,e ) for e in topomesh .wisps (1 )])== 2
1067+
1068+ print "<-- Failed to collapse edge" ,eid ," : " ,pid_to_keep ," (" ,topomesh .nb_wisps (2 )," Faces )"
1069+ # raw_input()
10561070 return False
10571071
10581072
@@ -1490,7 +1504,6 @@ def property_topomesh_edge_flip_optimization(topomesh,omega_energies=dict([('reg
14901504
14911505 if omega_energies .has_key ('neighborhood' ):
14921506
1493-
14941507 compute_topomesh_property (topomesh ,'valence' ,0 )
14951508
14961509 nested_mesh = kwargs .get ("nested_mesh" ,False )
@@ -1570,23 +1583,104 @@ def property_topomesh_edge_split_optimization(topomesh, maximal_length=None, ite
15701583 return n_splits
15711584
15721585
1573- def property_topomesh_isotropic_remeshing (initial_topomesh , maximal_length = None , iterations = 1 ):
1586+ def property_topomesh_edge_collapse_optimization (topomesh , omega_energies = dict ([('length' ,0.01 ),('error_quadrics' ,0.65 )]), minimal_length = None , target_triangles = None , iterations = 1 ):
1587+
1588+ compute_topomesh_property (topomesh ,'vertices' ,1 )
1589+ compute_topomesh_property (topomesh ,'vertices' ,2 )
1590+ compute_topomesh_property (topomesh ,'length' ,1 )
1591+ compute_topomesh_property (topomesh ,'area' ,2 )
1592+
1593+ if minimal_length is None :
1594+ target_length = np .percentile (topomesh .wisp_property ('length' ,1 ).values (),70 )
1595+ minimal_length = 3. / 4. * target_length
1596+
1597+ if target_triangles is None :
1598+ target_triangles = topomesh .nb_wisps (2 )/ 4
1599+
1600+ if 'error_quadrics' in omega_energies .keys ():
1601+ maximal_quadrics_error = np .power (minimal_length ,2. )
1602+
1603+ compute_topomesh_property (topomesh ,'triangles' ,0 )
1604+ vertices_positions = topomesh .wisp_property ('barycenter' ,0 ).values (topomesh .wisp_property ('vertices' ,2 ).values ())
1605+ normal_vectors = np .cross (vertices_positions [:,1 ]- vertices_positions [:,0 ],vertices_positions [:,2 ]- vertices_positions [:,0 ])
1606+ normal_norms = np .linalg .norm (normal_vectors ,axis = 1 )
1607+ normal_vectors = normal_vectors / normal_norms [:,np .newaxis ]
1608+ plane_d = - np .einsum ('...ij,...ij->...i' ,normal_vectors ,vertices_positions [:,0 ])
1609+ triangle_planes = np .concatenate ([normal_vectors ,plane_d [:,np .newaxis ]],axis = 1 )
1610+
1611+ triangle_plane_quadrics = array_dict (np .einsum ('...i,...j->...ij' ,triangle_planes ,triangle_planes ),list (topomesh .wisps (2 )))
1612+ vertex_triangles = deepcopy (topomesh .wisp_property ('triangles' ,0 ))
1613+
1614+ # vertex_quadrics = triangle_plane_quadrics.values([t[0]+1 for t in vertex_triangles])
1615+ # homogeneous_positions = np.concatenate([topomesh.wisp_property('barycenter',0).values(),np.ones((topomesh.nb_wisps(0),1))],axis=1)
1616+
1617+ for iterations in xrange (iterations ):
1618+ compute_topomesh_property (topomesh ,'vertices' ,1 )
1619+ compute_topomesh_property (topomesh ,'length' ,1 )
1620+
1621+ edge_energy_variation = np .zeros_like (list (topomesh .wisps (1 )),np .float )
1622+
1623+ if 'length' in omega_energies .keys ():
1624+ edge_energy_variation += omega_energies ['length' ]* (topomesh .wisp_property ('length' ,1 ).values (list (topomesh .wisps (1 ))))
1625+
1626+ if 'error_quadrics' in omega_energies .keys ():
1627+
1628+ edge_vertices = topomesh .wisp_property ('vertices' ,1 ).values ()
1629+ edge_middles = topomesh .wisp_property ('barycenter' ,0 ).values (edge_vertices ).mean (axis = 1 )
1630+ edge_middles_homogeneous = np .concatenate ([edge_middles ,np .ones ((topomesh .nb_wisps (1 ),1 ))],axis = 1 )
1631+
1632+ edge_vertex_faces = np .array ([np .concatenate (vertex_triangles .values (v )) for v in edge_vertices ])
1633+ edge_vertex_face_quadrics = np .array ([triangle_plane_quadrics .values (t ) for t in edge_vertex_faces ])
1634+ edge_vertex_face_middles = np .array ([ [e for t in e_v_t ] for e ,e_v_t in zip (edge_middles_homogeneous ,edge_vertex_faces )])
1635+ edge_quadrics_errors = np .array ([np .abs (np .einsum ('...ij,...ij->...i' ,m ,np .einsum ('...ij,...j->...i' ,q ,m ))).sum () for q ,m in zip (edge_vertex_face_quadrics ,edge_vertex_face_middles )])
1636+ edge_quadrics_errors = array_dict (edge_quadrics_errors ,list (topomesh .wisps (1 )))
1637+
1638+ edge_energy_variation += omega_energies ['error_quadrics' ]* (edge_quadrics_errors .values (list (topomesh .wisps (1 ))))
1639+
1640+ sorted_energy_variation_edges = np .array (list (topomesh .wisps (1 )))[np .argsort (edge_energy_variation )]
1641+ if 'error_quadrics' in omega_energies .keys ():
1642+ sorted_energy_variation_edges = sorted_energy_variation_edges [edge_quadrics_errors .values (sorted_energy_variation_edges ) < maximal_quadrics_error ]
1643+ sorted_energy_variation_edges = sorted_energy_variation_edges [topomesh .wisp_property ('length' ,1 ).values (sorted_energy_variation_edges ) < minimal_length ]
1644+ # sorted_quadrics_errors_edges = sorted_quadrics_errors_edges[np.sort(edge_quadrics_errors.values()) < np.percentile(edge_quadrics_errors.values(),5)]
1645+
1646+ modified_edges = set ()
1647+ n_collapses = 0
1648+ for eid in sorted_energy_variation_edges :
1649+ if not eid in modified_edges and topomesh .nb_wisps (2 )> target_triangles :
1650+ # print " <-- Collapsing edge ",eid," [",np.min(map(len,map(np.unique,[list(topomesh.borders(1,e)) for e in topomesh.wisps(1)]))),"]"
1651+ modified_edges = modified_edges .union (set (np .unique (list (topomesh .border_neighbors (1 ,eid ))))).union ({eid })
1652+ collapsed = topomesh_collapse_edge (topomesh ,eid ,manifold = False )
1653+ n_collapses += collapsed
1654+ # print " <-- Collapsed edge ",eid," [",np.min(map(len,map(np.unique,[list(topomesh.borders(1,e)) for e in topomesh.wisps(1)]))),"]"
1655+ print "--> Collapsed " ,n_collapses ," edges[" ,np .min (map (len ,map (np .unique ,[list (topomesh .borders (1 ,e )) for e in topomesh .wisps (1 )]))),"]"
1656+
1657+ return n_collapses
1658+
1659+
1660+ def property_topomesh_isotropic_remeshing (initial_topomesh , maximal_length = None , minimal_length = None , collapse = False , iterations = 1 ):
15741661
15751662 topomesh = deepcopy (initial_topomesh )
15761663
15771664 n_flips = topomesh .nb_wisps (1 )
15781665 n_splits = topomesh .nb_wisps (1 )
1666+ n_collapses = topomesh .nb_wisps (1 )
15791667
1668+ compute_topomesh_property (topomesh ,'length' ,1 )
1669+ target_length = np .percentile (topomesh .wisp_property ('length' ,1 ).values (),80 )
15801670 if maximal_length is None :
1581- compute_topomesh_property (topomesh ,'length' ,1 )
1582- target_length = np .percentile (topomesh .wisp_property ('length' ,1 ).values (),50 )
15831671 maximal_length = 4. / 3. * target_length
1672+ if minimal_length is None :
1673+ minimal_length = 3. / 4. * target_length
15841674
15851675 iteration = 0
1586- while (n_flips + n_splits > topomesh .nb_wisps (1 )/ 100. ) and (iteration < iterations ):
1676+ while (n_flips + n_splits + n_collapses > topomesh .nb_wisps (1 )/ 100. ) and (iteration < iterations ):
1677+ if collapse :
1678+ n_collapses = property_topomesh_edge_collapse_optimization (topomesh , minimal_length = minimal_length , iterations = 1 )
1679+ else :
1680+ n_collapses = 0
15871681 n_splits = property_topomesh_edge_split_optimization (topomesh , maximal_length = maximal_length , iterations = 1 )
1588- n_flips = property_topomesh_edge_flip_optimization (topomesh ,omega_energies = dict ([('neighborhood' ,0.65 )]),simulated_annealing = False ,iterations = 3 )
1589- property_topomesh_vertices_deformation (topomesh ,omega_forces = dict ([('taubin_smoothing' ,0.33 )]))
1682+ n_flips = property_topomesh_edge_flip_optimization (topomesh ,omega_energies = dict ([('neighborhood' ,0.65 )]),simulated_annealing = False ,iterations = 5 )
1683+ property_topomesh_vertices_deformation (topomesh ,omega_forces = dict ([('taubin_smoothing' ,0.33 )]), iterations = 5 )
15901684 iteration += 1
15911685
15921686 return topomesh
0 commit comments