def supports(self, c): other = pymesh.form_mesh( c.component_mesh.vertices - numpy.array([0, 0, (c.z - self.z)]), c.component_mesh.faces) pymesh.remove_duplicated_vertices(other) pymesh.remove_duplicated_faces(other) intersection = pymesh.boolean(self.component_mesh, other, 'intersection') pymesh.remove_duplicated_vertices(intersection) pymesh.remove_duplicated_faces(intersection) intersection.add_attribute("face_area") intersection_area = sum(intersection.get_attribute("face_area")) return intersection_area >= 0.2 * self.area()
def clean_up_mesh(mesh, tol): new_mesh, _ = pymesh.remove_isolated_vertices(mesh) new_mesh, _ = pymesh.remove_duplicated_vertices(new_mesh) new_mesh, _ = pymesh.remove_duplicated_faces(new_mesh) new_mesh, _ = pymesh.remove_degenerated_triangles(new_mesh) new_mesh, _ = pymesh.collapse_short_edges(new_mesh, rel_threshold=0.2) return new_mesh
def clean_mesh(mesh, connected=True, fill_internals=False): print('\t - Cleaning mesh (this may take a moment)') vert_list = [] mesh, info = pymesh.remove_isolated_vertices(mesh) mesh, info = pymesh.remove_duplicated_vertices(mesh) mesh, info = pymesh.remove_degenerated_triangles(mesh) mesh, info = pymesh.remove_duplicated_faces(mesh) if connected or fill_internals: mesh_list = pymesh.separate_mesh(mesh, 'auto') max_verts = 0 print(' - Total number of meshes (ideally 1): %d' % len(mesh_list)) for mesh_obj in mesh_list: nverts = mesh_obj.num_vertices if nverts > max_verts: max_verts = nverts mesh = mesh_obj if fill_internals: for mesh_obj in mesh_list: if mesh_obj.num_vertices != max_verts: vert_list.append(mesh_obj.vertices) return mesh, vert_list return mesh, vert_list
def remove_duplicated_points(points, tol=0.0001): ''' input is a numpy array: Nx3 ''' fake_faces = np.array([[0, 1, 2]]) mesh = pymesh.form_mesh(points, fake_faces) mesh, info = pymesh.remove_duplicated_vertices(mesh, tol) print('#Merged points: {}'.format(info["num_vertex_merged"])) return mesh.vertices
def load_mesh(path: Path) -> "threedframe.lib.PyMesh.python.Mesh.Mesh": mesh = pymesh.load_mesh(str(path)) mesh, _ = pymesh.remove_duplicated_faces(mesh) mesh, _ = pymesh.remove_duplicated_vertices(mesh) mesh.add_attribute("face_area") mesh.add_attribute("face_normal") mesh.add_attribute("face_centroid") mesh.add_attribute("vertex_normal") mesh.enable_connectivity() return mesh
def fix_mesh(mesh, resolution, detail="normal"): bbox_min, bbox_max = mesh.bbox diag_len = norm(bbox_max - bbox_min) if detail == "normal": target_len = diag_len * 5e-3 elif detail == "high": target_len = diag_len * 2.5e-3 elif detail == "low": target_len = diag_len * 1e-2 target_len = resolution #print("Target resolution: {} mm".format(target_len)); # PGC 2017: Remove duplicated vertices first mesh, _ = pymesh.remove_duplicated_vertices(mesh, 0.001) count = 0 print "Removing degenerated triangles" mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100) mesh, __ = pymesh.split_long_edges(mesh, target_len) num_vertices = mesh.num_vertices while True: mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6) mesh, __ = pymesh.collapse_short_edges(mesh, target_len, preserve_feature=True) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100) if mesh.num_vertices == num_vertices: break num_vertices = mesh.num_vertices #print("#v: {}".format(num_vertices)); count += 1 if count > 10: break mesh = pymesh.resolve_self_intersection(mesh) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh = pymesh.compute_outer_hull(mesh) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5) mesh, __ = pymesh.remove_isolated_vertices(mesh) mesh, _ = pymesh.remove_duplicated_vertices(mesh, 0.001) return mesh
def clean(vertices, faces, duplicate_tol=1e-12): mesh = pymesh.meshio.form_mesh(vertices, faces) mesh = pymesh.remove_isolated_vertices(mesh)[0] mesh = pymesh.remove_duplicated_vertices(mesh, tol=duplicate_tol)[0] mesh = remove_duplicated_faces(mesh)[0] # mesh = pymesh.remove_duplicated_faces(mesh, fins_only=True)[0] mesh = pymesh.remove_degenerated_triangles(mesh)[0] mesh = pymesh.resolve_self_intersection(mesh) # meshes = pymesh.separate_mesh(mesh) # for i, mesh in enumerate(meshes): # meshes[i] = make_face_normals_consistent(mesh) # mesh = pymesh.merge_meshes(meshes) return mesh.vertices, mesh.faces
def link(path1): """ This function takes a path to the orginal shapenet model and subsample it nicely """ obj1 = pymesh.load_mesh(path1) # obj1, info = pymesh.remove_isolated_vertices(mesh) obj1, info = pymesh.remove_isolated_vertices(obj1) print("Removed {} isolated vertices".format(info["num_vertex_removed"])) obj1, info = pymesh.remove_duplicated_vertices(obj1) print("Merged {} duplicated vertices".format(info["num_vertex_merged"])) obj1, info = pymesh.remove_degenerated_triangles(obj1) new_mesh = pymesh.form_mesh( normalize_points.BoundingBox(torch.from_numpy(obj1.vertices)).numpy(), obj1.faces) return new_mesh
def remesh(path1): """ This function takes a path to the orginal shapenet model and subsample it nicely """ obj1 = pymesh.load_mesh(path1) obj1, info = pymesh.remove_isolated_vertices(obj1) print("Removed {} isolated vertices".format(info["num_vertex_removed"])) obj1, info = pymesh.remove_duplicated_vertices(obj1) print("Merged {} duplicated vertices".format(info["num_vertex_merged"])) obj1, _ = pymesh.remove_degenerated_triangles(obj1) if len(obj1.vertices) < 5000: while len(obj1.vertices) < 5000: obj1 = pymesh.subdivide(obj1) obj1 = pymesh.form_mesh(obj1.vertices, obj1.faces) return obj1
def read_trimesh(path, normal=False, clean=True): mesh = pymesh.load_mesh(path) if clean: mesh, info = pymesh.remove_isolated_vertices(mesh) print("Removed {} isolated vertices".format(info["num_vertex_removed"])) mesh, info = pymesh.remove_duplicated_vertices(mesh) print("Merged {} duplicated vertices".format(info["num_vertex_merged"])) mesh, info = pymesh.remove_degenerated_triangles(mesh) mesh = pymesh.form_mesh(mesh.vertices, mesh.faces) vertices = mesh.vertices if normal: mesh.add_attribute("vertex_normal") vertex_normals = mesh.get_attribute("vertex_normal").reshape(-1, 3) vertices = np.concatenate([vertices, vertex_normals], axis=-1) return vertices, mesh.faces
def execute(self, context): scene = context.scene pymesh_props = scene.pymesh obj_a = context.active_object mesh_a = import_object(context, obj_a) pymesh_r, info = pymesh.remove_degenerated_triangles(mesh_a) pymesh_r, info = pymesh.remove_obtuse_triangles(pymesh_r) pymesh_r, info = pymesh.remove_duplicated_faces(pymesh_r) pymesh_r, info = pymesh.collapse_short_edges(pymesh_r) pymesh_r, info = pymesh.remove_duplicated_vertices(pymesh_r) pymesh_r, info = pymesh.remove_isolated_vertices(pymesh_r) off_name = "Py.Clean." + obj_a.name mesh_r = export_mesh(context, pymesh_r, off_name) add_to_scene(context, mesh_r) return {'FINISHED'}
def fix_mesh(mesh, detail=5e-3): # "normal": 5e-3 # "high": 2.5e-3 # "low": 2e-2 # "vlow": 2.5e-2 bbox_min, bbox_max = mesh.bbox diag_len = np.linalg.norm(bbox_max - bbox_min) if detail is None: detail = 5e-3 target_len = diag_len * detail print("Target resolution: {} mm".format(target_len)) count = 0 mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100) mesh, __ = pymesh.split_long_edges(mesh, target_len) num_vertices = mesh.num_vertices while True: mesh, __ = pymesh.collapse_short_edges(mesh, 1e-4) mesh, __ = pymesh.collapse_short_edges(mesh, target_len, preserve_feature=True) mesh, __ = pymesh.remove_isolated_vertices(mesh) mesh, __ = pymesh.remove_duplicated_vertices(mesh, tol=1e-4) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh, __ = pymesh.remove_degenerated_triangles(mesh) mesh, __ = pymesh.remove_isolated_vertices(mesh) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100) if mesh.num_vertices == num_vertices: break num_vertices = mesh.num_vertices print("fix #v: {}".format(num_vertices)) count += 1 if count > 10: break mesh = pymesh.resolve_self_intersection(mesh) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh = pymesh.compute_outer_hull(mesh) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5) mesh, __ = pymesh.remove_isolated_vertices(mesh) return mesh
def merge(mesh_list): resulting_vertices = [] resulting_faces = [] count_vertices = 0 # Add all the faces and all the vertices from the next patch to the resulting mesh for mesh in mesh_list: for vertice in mesh.vertices: resulting_vertices.append(vertice) for face in mesh.faces: resulting_faces.append(np.add(face, count_vertices)) count_vertices += mesh.num_vertices merged_mesh = pymesh.form_mesh(np.array(resulting_vertices), np.array(resulting_faces)) resulting_mesh, _ = pymesh.remove_duplicated_vertices(merged_mesh) return resulting_mesh
def extract_texture_boundary(self): if self.boundary_color is None: color = color_table["black"] elif self.boundary_color == "random": color = ColorMap("RdYlBu").get_color( random.choice([0.1, 0.3, 0.5, 0.7, 0.9])) else: assert (self.boundary_color in color_table) color = color_table[self.boundary_color] vertices = self.mesh.vertices faces = self.mesh.faces uv = self.texture_coordinates num_faces, vertex_per_face = faces.shape assert (len(uv) == num_faces * vertex_per_face) uv_faces = np.arange(len(uv), dtype=int).reshape((-1, vertex_per_face)) mesh = pymesh.form_mesh(uv, uv_faces) mesh, info = pymesh.remove_duplicated_vertices(mesh) index_map = info["index_map"] input_vertex_index = faces.ravel() output_vertex_index = np.ones(mesh.num_vertices, dtype=int) * -1 output_vertex_index[index_map] = input_vertex_index assert (np.all(output_vertex_index >= 0)) radius = self.boundary_radius / self.scale bd_edges = output_vertex_index[mesh.boundary_edges] for e in bd_edges: v0 = vertices[e[0]] v1 = vertices[e[1]] assert (np.all(np.isfinite(v0))) assert (np.all(np.isfinite(v1))) if numpy.linalg.norm(v0 - v1) <= radius: continue cylinder = Cylinder(v0, v1, radius) cylinder.color = color self.primitives.append(cylinder) bd_vertices = np.unique(bd_edges.ravel()) for v in bd_vertices: ball = Sphere(vertices[v, :], radius) ball.color = color self.primitives.append(ball)
def load_mesh(path): mesh = pymesh.load_mesh(path) pymesh.remove_duplicated_vertices(mesh, tol=1e-12, importance=None) return mesh
def main(): args = parse_args() mesh = pymesh.load_mesh(args.input_mesh) if not mesh.has_attribute("corner_texture"): raise RuntimeError("Mesh contains no uv!") mesh.add_attribute("face_area") uv = mesh.get_attribute("corner_texture").reshape((-1, 2)) if len(uv) == 0: raise RuntimeError("Invalid uv size.") faces = np.arange(mesh.num_faces * mesh.vertex_per_face).reshape( (-1, mesh.vertex_per_face)) uv_mesh = pymesh.form_mesh(uv, faces) uv_mesh.add_attribute("face_area") ori_area = mesh.get_face_attribute("face_area") uv_area = uv_mesh.get_face_attribute("face_area") area_ratio = np.divide(uv_area, ori_area) uv_mesh.add_attribute("area_ratio") uv_mesh.set_attribute("area_ratio", area_ratio) mesh.add_attribute("area_ratio") mesh.set_attribute("area_ratio", area_ratio) mesh.add_attribute("u") mesh.set_attribute("u", uv[:, 0]) if (args.separate): uv_mesh, info = pymesh.remove_duplicated_vertices(uv_mesh) comps = pymesh.separate_mesh(uv_mesh) segments = [] uv = uv.reshape((-1, 6), order="C") vertices = mesh.vertices combined_uv = [] for comp in comps: ori_vertex_indices = comp.get_attribute( "ori_vertex_index").ravel() ori_elem_indices = comp.get_attribute( "ori_elem_index").ravel().astype(int) segment = pymesh.submesh(mesh, ori_elem_indices, 0) ori_face_indices = segment.get_attribute( "ori_face_index").ravel().astype(int) ori_uv = uv[ori_face_indices] combined_uv.append(ori_uv) segment.add_attribute("corner_texture") segment.set_attribute("corner_texture", ori_uv.ravel()) segments.append(segment) combined_uv = np.vstack(combined_uv) mesh = pymesh.merge_meshes(segments) mesh.add_attribute("corner_texture") mesh.set_attribute("corner_texture", combined_uv.ravel(order="C")) elif args.cut: uv_mesh, info = pymesh.remove_duplicated_vertices(uv_mesh) index_map = info["index_map"] vertices = mesh.vertices faces = mesh.faces vertices = vertices[faces.ravel(order="C")] new_vertices = np.zeros((uv_mesh.num_vertices, 3)) new_vertices[index_map] = vertices mesh = pymesh.form_mesh(new_vertices, uv_mesh.faces) mesh.add_attribute("corner_texture") mesh.set_attribute("corner_texture", uv) if args.save_uv: pymesh.save_mesh(args.output_mesh, uv_mesh, *uv_mesh.attribute_names) else: pymesh.save_mesh(args.output_mesh, mesh, *mesh.attribute_names)
def fix_meshes(mesh, detail="normal"): """ A pipeline to optimise and fix mesh based on pymesh Mesh object. 1. A box is created around the mesh. 2. A target length is found based on diagonal of the mesh box. 3. You can choose between 3 level of details, normal details settings seems to be a good compromise between final mesh size and sufficient number of vertices. It highly depends on your final goal. 4. Remove degenerated triangles aka collinear triangles composed of 3 aligned points. The number of iterations is 5 and should remove all degenerated triangles 5. Remove isolated vertices, not connected to any faces or edges 6. Remove self intersection edges and faces which is not realistic 7. Remove duplicated faces 8. The removing of duplicated faces can leave some vertices alone, we will removed them 9. The calculation of outer hull volume is useful to be sure that the mesh is still ok 10. Remove obtuse triangles > 179 who is not realistic and increase computation time 11. We will remove potential duplicated faces again 12. And duplicated vertices again 13. Finally we will look if the mesh is broken or not. If yes we will try lower settings, if the lowest settings broke the mesh we will return the initial mesh. If not, we will return the optimised mesh. :param mesh: Pymesh Mesh object to optimise :param detail: string 'high', 'normal' or 'low' ('normal' as default), or float/int Settings to choose the targeting minimum length of edges :return: Pymesh Mesh object An optimised mesh or not depending on detail settings and mesh quality """ meshCopy = mesh # copy/pasta of pymesh script fix_mesh from qnzhou, see pymesh on GitHub bbox_min, bbox_max = mesh.bbox diag_len = np.linalg.norm(bbox_max - bbox_min) if detail == "normal": target_len = diag_len * 5e-3 elif detail == "high": target_len = diag_len * 2.5e-3 elif detail == "low": target_len = diag_len * 1e-2 elif detail is float or detail is int and detail > 0: target_len = diag_len * detail else: print( 'Details settings is invalid, must be "low", "normal", "high" or positive int or float' ) quit() count = 0 mesh, __ = pymesh.remove_degenerated_triangles(mesh, 5) mesh, __ = pymesh.split_long_edges(mesh, target_len) num_vertices = mesh.num_vertices while True: mesh, __ = pymesh.collapse_short_edges(mesh, target_len, preserve_feature=True) mesh, info = pymesh.remove_obtuse_triangles(mesh, 179.0, 5) if mesh.num_vertices == num_vertices: break num_vertices = mesh.num_vertices count += 1 if count > 10: break mesh, __ = pymesh.remove_duplicated_vertices(mesh) mesh, __ = pymesh.remove_isolated_vertices(mesh) mesh = pymesh.resolve_self_intersection(mesh) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh, __ = pymesh.remove_duplicated_vertices(mesh) mesh = pymesh.compute_outer_hull(mesh) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5) mesh, __ = pymesh.remove_duplicated_faces(mesh) mesh, __ = pymesh.remove_isolated_vertices(mesh) if is_mesh_broken(mesh, meshCopy) is True: if detail == "high": print( f'The function fix_meshes broke mesh, trying with lower details settings' ) fix_meshes(meshCopy, detail="normal") return mesh if detail == "normal": print( f'The function fix_meshes broke mesh, trying with lower details settings' ) mesh = fix_meshes(meshCopy, detail="low") return mesh if detail == "low": print( f'The function fix_meshes broke mesh, no lower settings can be applied, no fix was done' ) return meshCopy else: return mesh
def symmetrize(verts, faces, eps=1e-3): """ verts: N,3 (x,y,z) tensor faces: F,3 (0,1,2) tensor Modifies mesh to make it symmetric about y-z plane - Cut mesh into half - Copy left half into right half - merge, remove duplicate vertices """ # Snap vertices close to centre to centre verts_centre_mask = (verts[:,0].abs() < eps) verts[verts_centre_mask, 0] = 0 # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/1.obj', pmesh) # Categorize vertices into left (-1), centre(0), right(1) verts_side = torch.sign(verts[:,0]) # Categorize faces into left (-1), centre(0), right(1) face_verts_side = torch.index_select(verts_side, 0, faces.view(-1)) face_verts_side = face_verts_side.contiguous().view(faces.shape[0], 3) face_left_mask = (face_verts_side[:,0]==-1) & (face_verts_side[:,1]==-1) & (face_verts_side[:,2]==-1) face_right_mask = (face_verts_side[:,0]==1) & (face_verts_side[:,1]==1) & (face_verts_side[:,2]==1) face_intesects_yz = (~face_left_mask) & (~face_right_mask) # Split intersecting faces new_verts = [] new_faces = [] for f in face_intesects_yz.nonzero().squeeze(1): i0, i1, i2 = faces[f] if verts_side[i0]==verts_side[i1]: i0, i1, i2 = i2, i0, i1 elif verts_side[i2]==verts_side[i1]: i0, i1, i2 = i0, i1, i2 elif verts_side[i0]==verts_side[i2]: i0, i1, i2 = i1, i0, i2 elif verts_side[i0]==-1: i0, i1, i2 = i0, i1, i2 elif verts_side[i1]==-1: i0, i1, i2 = i1, i0, i2 elif verts_side[i2]==-1: i0, i1, i2 = i2, i0, i1 else: import ipdb; ipdb.set_trace() # yz axis intersects i0->i1 & i0->i2 assert(verts_side[i0] != verts_side[i1]) assert(verts_side[i0] != verts_side[i2]) v0 = verts[i0] v1 = verts[i1] v2 = verts[i2] v_n1 = (v0 * v1[0] - v1 * v0[0])/(v1[0] - v0[0]) v_n2 = (v0 * v2[0] - v2 * v0[0])/(v2[0] - v0[0]) i_n1 = verts.shape[0] + len(new_verts) i_n2 = verts.shape[0] + len(new_verts) + 1 new_verts.append(v_n1) new_verts.append(v_n2) new_faces.append((i0, i_n1, i_n2)) new_faces.append((i1, i_n1, i_n2)) new_faces.append((i1, i2, i_n2)) new_verts = torch.stack(new_verts, dim=0) new_faces = torch.tensor(new_faces, dtype=faces.dtype, device=faces.device) verts = torch.cat([verts, new_verts], dim=0) faces = torch.index_select(faces, 0, (~face_intesects_yz).nonzero().squeeze(1)) faces = torch.cat([faces, new_faces], dim=0) # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/2.obj', pmesh) # Merge vertices that are very close together vertex_mapping = [] verts_new = [] for v_id in range(verts.shape[0]): if v_id==0: vertex_mapping.append(0) verts_new.append(verts[0]) continue min_d, min_idx = (verts[0:v_id] - verts[v_id]).norm(dim=-1).min(dim=0) if min_d < eps: vertex_mapping.append(vertex_mapping[min_idx]) else: vertex_mapping.append(len(verts_new)) verts_new.append(verts[v_id]) assert(len(vertex_mapping)==verts.shape[0]) vertex_mapping = torch.tensor(vertex_mapping, dtype=faces.dtype, device=faces.device) verts = torch.stack(verts_new, dim=0) faces = vertex_mapping[faces] # Remove degenerate faces faces_degenerate = (faces[:,0]==faces[:,1]) | (faces[:,0]==faces[:,2]) | (faces[:,2]==faces[:,1]) faces = torch.index_select(faces, 0, (~faces_degenerate).nonzero().squeeze(1)) # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/3.obj', pmesh) # Delete faces that lie on right side (verts_side==1) verts_centre_mask = (verts[:,0].abs() < eps) verts[verts_centre_mask, 0] = 0 verts_side = torch.sign(verts[:,0]) face_verts_side = torch.index_select(verts_side, 0, faces.view(-1)) face_verts_side = face_verts_side.contiguous().view(faces.shape[0], 3) face_right_mask = (face_verts_side[:,0]==1) | (face_verts_side[:,1]==1) | (face_verts_side[:,2]==1) faces = torch.index_select(faces, 0, (~face_right_mask).nonzero().squeeze(1)) # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/4.obj', pmesh) # Flip mesh, merge faces_flip = faces + verts.shape[0] faces = torch.cat([faces, faces_flip], dim=0) verts_flip = verts * torch.tensor([-1,1,1], dtype=verts.dtype, device=verts.device) vertex_mapping_flip = torch.arange(verts.shape[0], dtype=faces.dtype, device=faces.device) vertex_mapping_flip[~verts_centre_mask] += verts.shape[0] vertex_mapping = torch.arange(verts.shape[0], dtype=faces.dtype, device=faces.device) vertex_mapping = torch.cat([vertex_mapping, vertex_mapping_flip], dim=0) verts = torch.cat([verts, verts_flip], dim=0) faces = vertex_mapping[faces] # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/5.obj', pmesh) pymesh.save_mesh(f'csm_mesh/debug/5.8.obj', pmesh) numv = pmesh.num_vertices while True: pmesh, __ = pymesh.collapse_short_edges(pmesh, rel_threshold=0.4) verts = torch.as_tensor(pmesh.vertices, dtype=verts.dtype, device=verts.device) faces = torch.as_tensor(pmesh.faces, dtype=faces.dtype, device=faces.device) verts_centre_mask = (verts[:,0].abs() < 1e-3) verts[verts_centre_mask, 0] = 0 pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pmesh, __ = pymesh.remove_isolated_vertices(pmesh) pmesh, __ = pymesh.remove_duplicated_vertices(pmesh, tol=eps) pmesh, __ = pymesh.remove_duplicated_faces(pmesh) pmesh, __ = pymesh.remove_degenerated_triangles(pmesh) pmesh, __ = pymesh.remove_isolated_vertices(pmesh) # pmesh, __ = pymesh.remove_obtuse_triangles(pmesh, 120.0, 100) pmesh, __ = pymesh.collapse_short_edges(pmesh, 1e-2) pmesh, __ = pymesh.remove_duplicated_vertices(pmesh, tol=eps) if pmesh.num_vertices==numv: break numv = pmesh.num_vertices pymesh.save_mesh(f'csm_mesh/debug/5.9.obj', pmesh) verts = torch.as_tensor(pmesh.vertices, dtype=verts.dtype, device=verts.device) faces = torch.as_tensor(pmesh.faces, dtype=faces.dtype, device=faces.device) # Remove unused vertices vertices_used = torch.unique(faces.view(-1)) vertex_mapping = torch.zeros((verts.shape[0], ), dtype=faces.dtype, device=faces.device) vertex_mapping[vertices_used] = torch.arange(vertices_used.shape[0], dtype=faces.dtype, device=faces.device) faces = vertex_mapping[faces] verts = verts[vertices_used] # import pymesh pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy()) pymesh.save_mesh(f'csm_mesh/debug/6.obj', pmesh) return verts, faces
def mesh(self): path = self.path() triangles = [] prev = path[0] prev_u = None prev_v = None first = True r_step = (self.radius_end - self.radius_start) / len(path) r = self.radius_start shift = 0.0 for point in path[1:]: prev_r = r r += r_step # construct an orthonormal basis(u,v) for the plane normal to # the vector point - prev norm = point - prev absnorm = numpy.abs(norm) mindim = numpy.where(absnorm == numpy.min(absnorm)) if mindim == 0: u = numpy.array([0, -norm[2], norm[1]]) v = numpy.array([ norm[1] * norm[1] + norm[2] * norm[2], -norm[0] * norm[1], -norm[0] * norm[2] ]) elif mindim == 1: u = numpy.array([-norm[2], 0, norm[1]]) v = numpy.array([ -norm[1] * norm[0], norm[0] * norm[0] + norm[2] * norm[2], -norm[1] * norm[2] ]) else: u = numpy.array([-norm[1], norm[0], 0]) v = numpy.array([ -norm[2] * norm[0], -norm[2] * norm[1], norm[0] * norm[0] + norm[1] * norm[1] ]) pass u = u * (r / numpy.linalg.norm(u)) v = v * (r / numpy.linalg.norm(v)) # make the base of the tube, a disc in the plane defined by # u,v at point prev if first: first = False prev_u = u prev_v = v for i in range(self.fn): triangles.append( numpy.array([ prev + prev_u * cos( (2 * pi * i) / self.fn) + prev_v * sin( (2 * pi * i) / self.fn), prev, prev + prev_u * cos( (2 * pi * (i + 1)) / self.fn) + prev_v * sin( (2 * pi * (i + 1)) / self.fn) ])) pass pass for i in range(self.fn): # upward pointing triangle from prev to point triangles.append( numpy.array([ prev + prev_u * cos( (2 * pi * (i + shift)) / self.fn) + prev_v * sin( (2 * pi * (i + shift)) / self.fn), prev + prev_u * cos( (2 * pi * (i + shift + 1)) / self.fn) + prev_v * sin( (2 * pi * (i + shift + 1)) / self.fn), point + u * cos( (2 * pi * (i + shift + 0.5)) / self.fn) + v * sin( (2 * pi * (i + shift + 0.5)) / self.fn) ])) # downward pointing triangles from point to prev triangles.append( numpy.array([ prev + prev_u * cos( (2 * pi * (i + shift)) / self.fn) + prev_v * sin( (2 * pi * (i + shift)) / self.fn), point + u * cos( (2 * pi * (i + shift + 0.5)) / self.fn) + v * sin( (2 * pi * (i + shift + 0.5)) / self.fn), point + u * cos( (2 * pi * (i + shift - 0.5)) / self.fn) + v * sin( (2 * pi * (i + shift - 0.5)) / self.fn) ])) pass prev = point prev_u = u prev_v = v shift = (shift + 0.5) % 1 pass # Make the top of the tube, a disc in the plane defined by # prev_u,prev_v at point prev for i in range(self.fn): triangles.append( numpy.array([ prev + prev_u * cos( (2 * pi * (i - 0.5)) / self.fn) + prev_v * sin( (2 * pi * (i - 0.5)) / self.fn), prev + prev_u * cos( (2 * pi * (i + 0.5)) / self.fn) + prev_v * sin( (2 * pi * (i + 0.5)) / self.fn), prev ])) pass vertices = numpy.zeros((len(triangles) * 3, 3)) faces = numpy.zeros((len(triangles), 3)) for i in range(len(triangles)): vertices[3 * i] = triangles[i][0] vertices[3 * i + 1] = triangles[i][1] vertices[3 * i + 2] = triangles[i][2] faces[i] = numpy.array([3 * i, 3 * i + 1, 3 * i + 2]) m = pymesh.form_mesh(vertices, faces) return pymesh.remove_duplicated_vertices(m)[0]
for phrase in ['train', 'test']: type_path = os.path.join(root, type) phrase_path = os.path.join(type_path, phrase) if not os.path.exists(type_path): os.mkdir(os.path.join(new_root, type)) if not os.path.exists(phrase_path): os.mkdir(phrase) files = glob.glob(os.path.join(phrase_path, '*.off')) for file in files: # load mesh mesh = pymesh.load_mesh(file) # clean up mesh, _ = pymesh.remove_isolated_vertices(mesh) mesh, _ = pymesh.remove_duplicated_vertices(mesh) # get elements vertices = mesh.vertices.copy() faces = mesh.faces.copy() # move to center center = (np.max(vertices, 0) + np.min(vertices, 0)) / 2 vertices -= center # normalize max_len = np.max(vertices[:, 0]**2 + vertices[:, 1]**2 + vertices[:, 2]**2) vertices /= np.sqrt(max_len) # get normal vector
def preprocess_mesh(input_mesh, prevent_nonmanifold_edges=True): r"""Removes duplicated vertices, duplicated faces, zero-area faces, and optionally faces the insertion of which would cause an edge of the mesh to become non-manifold. In particular, an iteration is performed over all faces in the mesh, keeping track of the half-edges in each face, and if a face contains a half-edge already found in one of the faces previously processed, it gets removed from the mesh. Args: input_mesh (pymesh.Mesh.Mesh): Mesh to preprocess. prevent_nonmanifold_edges (bool, optional): If True, faces that would cause an edge to become non-manifold are removed from the mesh (cf. above). (default: :obj:`True`) Returns: output_mesh (pymesh.Mesh.Mesh): Mesh after preprocessing. """ halfedges_found = set() new_faces = np.empty([input_mesh.num_faces, 3]) # Remove duplicated vertices. input_mesh = pymesh.remove_duplicated_vertices(input_mesh)[0] # Remove duplicated faces. input_mesh = pymesh.remove_duplicated_faces(input_mesh)[0] num_valid_faces = 0 # Compute face areas so that zero-are faces can be removed. input_mesh.add_attribute("face_area") face_areas = input_mesh.get_face_attribute("face_area") assert (len(face_areas) == len(input_mesh.faces)) for face, face_area in zip(input_mesh.faces, face_areas): # Do not include the face if it does not have three different # vertices. if (face[0] == face[1] or face[0] == face[2] or face[1] == face[2]): continue # Do not include zero-area faces. if (face_area == 0): continue # Optionally prevent non-manifold edges. if (prevent_nonmanifold_edges): new_halfedges_in_face = set() for idx in range(3): halfedge = (face[idx], face[(idx + 1) % 3]) if (halfedge[0] != halfedge[1]): # Exclude self-loops. if (halfedge not in halfedges_found): # Half-edge not found previously. -> The edge is # manifold so far. new_halfedges_in_face.add(halfedge) if (len(new_halfedges_in_face) == 3): # Face does not introduce non-manifold edges. halfedges_found.update(new_halfedges_in_face) new_faces[num_valid_faces] = face num_valid_faces += 1 # Here one can compute the face features already. else: new_faces[num_valid_faces] = face num_valid_faces += 1 new_faces = new_faces[:num_valid_faces] output_mesh = pymesh.form_mesh(input_mesh.vertices, new_faces) # Not including faces might have caused vertices to become isolated. output_mesh = pymesh.remove_isolated_vertices(output_mesh)[0] return output_mesh