def clean(self, vertices, faces, normals=None, tcoords=None, tol=1e-12): import pymesh # Removing duplicated vertices vertices, faces, info = pymesh.remove_duplicated_vertices_raw( vertices, faces, tol) if normals is not None: cleaned_normals = np.empty((vertices.shape[0], normals.shape[1]), dtype=normals.dtype) cleaned_normals[info['index_map'], :] = normals else: cleaned_normals = None if tcoords is not None: cleaned_tcoords = np.empty((vertices.shape[0], tcoords.shape[1]), dtype=tcoords.dtype) cleaned_tcoords[info['index_map'], :] = tcoords else: cleaned_tcoords = None # Removing degenerated triangles # FIXME: Returned vertices are modified (order probably) without a way # to get the map so that to update the attributes... #vertices, faces, info = pymesh.remove_degenerated_triangles_raw(vertices, faces) # Removing degenerated triangles # Only the triangles with duplicated vertices mask = np.logical_or( np.logical_or(faces[:, 0] == faces[:, 1], faces[:, 0] == faces[:, 2]), faces[:, 1] == faces[:, 2]) faces = faces[np.logical_not(mask), :] return vertices, faces, cleaned_normals, cleaned_tcoords
def tet_to_hex(mesh): in_vertices = mesh.vertices in_voxels = mesh.voxels out_vertices = [] out_voxels = [] hexes = np.array([[0, 6, 12, 4, 5, 11, 14, 13], [4, 12, 8, 1, 13, 14, 10, 7], [6, 3, 8, 12, 11, 9, 10, 14], [5, 2, 9, 11, 13, 7, 10, 14]]) for i, tet in enumerate(in_voxels): corners = in_vertices[tet] e01 = (corners[0] + corners[1]) / 2.0 e02 = (corners[0] + corners[2]) / 2.0 e03 = (corners[0] + corners[3]) / 2.0 e12 = (corners[1] + corners[2]) / 2.0 e13 = (corners[3] + corners[1]) / 2.0 e23 = (corners[2] + corners[3]) / 2.0 f0 = (corners[1] + corners[2] + corners[3]) / 3.0 f1 = (corners[0] + corners[2] + corners[3]) / 3.0 f2 = (corners[0] + corners[1] + corners[3]) / 3.0 f3 = (corners[0] + corners[1] + corners[2]) / 3.0 center = np.mean(corners, axis=0).reshape((1, -1)) out_vertices += [ corners, e01, e02, e03, e12, e13, e23, f0, f1, f2, f3, center ] out_voxels.append(hexes + i * 15) out_vertices = np.vstack(out_vertices) out_voxels = np.vstack(out_voxels) out_vertices, out_voxels, __ = pymesh.remove_duplicated_vertices_raw( out_vertices, out_voxels) mesh = pymesh.form_mesh(out_vertices, None, out_voxels) return mesh
def return_sorted_geom_connect(sx, atol): """sort geom array and reindex connect array to match the new geom array""" geom, connect = read_geom_connect(sx) nv = geom.shape[0] try: import pymesh geom, connect, inf = pymesh.remove_duplicated_vertices_raw(geom, connect, tol=atol) print(f"removed {inf['num_vertex_merged']} duplicates out of {nv}") except ModuleNotFoundError: print("pymesh not found, trying trimesh...") import trimesh trimesh.tol.merge = atol mesh = trimesh.Trimesh(geom, connect) mesh.merge_vertices() geom = mesh.vertices connect = mesh.faces print(f"removed {nv-geom.shape[0]} duplicates out of {nv}") ind, ind_inv = lookup_sorted_geom(geom, atol) geom = geom[ind, :] connect = np.array([ind_inv[x] for x in connect.flatten()]).reshape(connect.shape) # sort along line (then we can use multidim_intersect) connect = np.sort(connect, axis=1) return geom, connect
def cleanup(wires): vertices, edges, __ = pymesh.remove_duplicated_vertices_raw( wires.vertices, wires.edges, 0.0) # Trim zero dimension. if wires.dim == 3: assert (np.all(vertices[:, 2] == 0)) vertices = vertices[:, [0, 1]] # Remove duplicated edges. ordered_edges = np.sort(edges, axis=1) __, unique_edge_ids = np.unique(ordered_edges, return_index=True, axis=0) edges = edges[unique_edge_ids, :] # Remove topologically degenerate edges. wires.load(vertices, edges) is_not_topologically_degenerate = edges[:, 0] != edges[:, 1] if not np.all(is_not_topologically_degenerate): wires.filter_edges(is_not_topologically_degenerate) return wires
def cleanup(wires, logger): if wires.num_vertices == 0: return wires start_time = time() tol = 1e-6 vertices, edges, __ = pymesh.remove_duplicated_vertices_raw( wires.vertices, wires.edges, tol) # Remove duplicated edges. ordered_edges = np.sort(edges, axis=1) __, unique_edge_ids, __ = pymesh.unique_rows(ordered_edges) edges = edges[unique_edge_ids, :] wires.load(vertices, edges) # Remove topologically degenerate edges. is_not_topologically_degenerate = edges[:, 0] != edges[:, 1] if not np.all(is_not_topologically_degenerate): wires.filter_edges(is_not_topologically_degenerate) finish_time = time() t = finish_time - start_time logger.info("Cleanup running time: {}".format(t)) return wires
def cleanup(wires, logger): if wires.num_vertices == 0: return wires; start_time = time(); tol = 1e-6; vertices, edges, __ = pymesh.remove_duplicated_vertices_raw( wires.vertices, wires.edges, tol); # Remove duplicated edges. ordered_edges = np.sort(edges, axis=1); __, unique_edge_ids, __ = pymesh.unique_rows(ordered_edges); edges = edges[unique_edge_ids, :]; wires.load(vertices, edges); # Remove topologically degenerate edges. is_not_topologically_degenerate = edges[:,0] != edges[:,1]; if not np.all(is_not_topologically_degenerate): wires.filter_edges(is_not_topologically_degenerate); finish_time = time(); t = finish_time - start_time; logger.info("Cleanup running time: {}".format(t)); return wires;
def tet_to_hex(mesh): in_vertices = mesh.vertices in_voxels = mesh.voxels out_vertices = [] out_voxels = [] hexes = np.array( [ [0, 6, 12, 4, 5, 11, 14, 13], [4, 12, 8, 1, 13, 14, 10, 7], [6, 3, 8, 12, 11, 9, 10, 14], [5, 2, 9, 11, 13, 7, 10, 14], ] ) for i, tet in enumerate(in_voxels): corners = in_vertices[tet] e01 = (corners[0] + corners[1]) / 2.0 e02 = (corners[0] + corners[2]) / 2.0 e03 = (corners[0] + corners[3]) / 2.0 e12 = (corners[1] + corners[2]) / 2.0 e13 = (corners[3] + corners[1]) / 2.0 e23 = (corners[2] + corners[3]) / 2.0 f0 = (corners[1] + corners[2] + corners[3]) / 3.0 f1 = (corners[0] + corners[2] + corners[3]) / 3.0 f2 = (corners[0] + corners[1] + corners[3]) / 3.0 f3 = (corners[0] + corners[1] + corners[2]) / 3.0 center = np.mean(corners, axis=0).reshape((1, -1)) out_vertices += [corners, e01, e02, e03, e12, e13, e23, f0, f1, f2, f3, center] out_voxels.append(hexes + i * 15) out_vertices = np.vstack(out_vertices) out_voxels = np.vstack(out_voxels) out_vertices, out_voxels, __ = pymesh.remove_duplicated_vertices_raw(out_vertices, out_voxels) mesh = pymesh.form_mesh(out_vertices, None, out_voxels) return mesh
def makeMesh(self, path2file, do_clean=False): path, file_name = os.path.split(path2file) fname, fext = os.path.splitext(file_name) # read image stack_tif = io.imread(path2file, plugin='tifffile') print('tif shape: {}'.format(stack_tif.shape), flush=True) bin_stack_tif = stack_tif.astype(bool).astype(np.int16) # clean from isolated and small pixels (optional) if do_clean: bin_stack_tif = self.erode_converge(bin_stack_tif) bin_stack_tif = self.clean_not_attached(bin_stack_tif) print('Saving image after cleaning', flush=True) io.imsave("aux.tif", (255*bin_stack_tif).astype(np.uint8), plugin='tifffile') bin_stack_tif = binary_erosion(bin_stack_tif, iterations=1) # create 1 pixel layer to make close meshes NZ = bin_stack_tif.shape[0] NY = bin_stack_tif.shape[1] NX = bin_stack_tif.shape[2] aux = np.zeros((NZ + 10, NY + 10, NX + 10)) aux[5:NZ + 5, 5:NY + 5, 5:NX + 5] = bin_stack_tif print('Triangulation of a set of points ...', flush=True) aux = aux.astype(np.float32) aux = filters.gaussian_filter(aux, 2.0, truncate=1.0) maxA = np.max(aux) aux = aux / maxA * 4.0 - 1.0 # aux = aux * 4.0 - 1.0 minA = np.min(aux) maxA = np.max(aux) print('Cleaning memory', flush=True) del stack_tif del bin_stack_tif gc.collect() print("min aux: {} max aux: {}".format(minA, maxA), flush=True) verts, faces, normals, values = measure.marching_cubes_lewiner(aux, 0) print('Cleaning memory', flush=True) del aux gc.collect() print("Mesh data 0:",flush=True) print("Verts: {} Faces: {}".format(verts.shape, faces.shape)) print('Remove isolated vertices', flush=True) verts, faces, info = pymesh.remove_isolated_vertices_raw(verts, faces) print(info, flush=True) print("Mesh data 1:",flush=True) print("Verts: {} Faces: {}".format(verts.shape, faces.shape)) print('Remove duplicated vertices', flush=True) verts, faces, info = pymesh.remove_duplicated_vertices_raw(verts, faces) print(info, flush=True) print("Mesh data 2:",flush=True) print("Verts: {} Faces: {}".format(verts.shape, faces.shape)) verts = verts - 5.0 print('Set the mesh ...', flush=True) final_mesh = pymesh.form_mesh(verts, faces) savefilename = "{}.obj".format(fname) savefilepath = os.path.join('./', savefilename) print("saving file {}".format(savefilename), flush=True) pymesh.save_mesh(savefilepath, final_mesh) print('done.', flush=True)
def bevel(wires, logger, remove_holes, dist): bbox_min, bbox_max = wires.bbox #wires = uniform_sampling(wires); mesh = constrained_triangulate(wires, logger, remove_holes) cell_ids = mesh.get_attribute("cell").ravel().astype(int) num_cells = np.amax(cell_ids) + 1 comps = [] for i in range(num_cells): to_keep = np.arange(mesh.num_faces, dtype=int)[cell_ids == i] if not np.any(to_keep): continue cut_mesh = pymesh.submesh(mesh, to_keep, 0) bd_edges = cut_mesh.boundary_edges vertices, edges, __ = pymesh.remove_isolated_vertices_raw( cut_mesh.vertices, bd_edges) bd_wires = pymesh.wires.WireNetwork.create_from_data(vertices, edges) offset_dir = np.zeros((bd_wires.num_vertices, 2)) for ei in edges: v0 = ei[0] v1 = ei[1] adj_vts = bd_wires.get_vertex_neighbors(v0) if len(adj_vts) == 2: if adj_vts[0] == v1: vp = adj_vts[1] else: vp = adj_vts[0] e0 = vertices[v1] - vertices[v0] e1 = vertices[vp] - vertices[v0] e0 /= norm(e0) e1 /= norm(e1) theta = math.atan2(e0[0] * e1[1] - e0[1] * e1[0], e0.dot(e1)) if abs(theta) > 0.99 * math.pi: offset = np.array([-e0[1], e0[0]]) scale = 1.0 else: if theta > 0: offset = e0 + e1 scale = math.sin(theta / 2) else: offset = -e0 - e1 scale = math.cos(math.pi / 2 + theta / 2) offset /= norm(offset) offset_dir[v0] = offset * dist / scale offset_vertices = vertices + offset_dir vertices = np.vstack((vertices, offset_vertices)) edges = np.vstack((edges, edges + bd_wires.num_vertices)) vertices, edges, __ = pymesh.remove_duplicated_vertices_raw( vertices, edges, dist / 2) comp_wires = pymesh.wires.WireNetwork.create_from_data( vertices, edges) comp = constrained_triangulate(comp_wires, logger, True) comps.append(comp) mesh = pymesh.merge_meshes(comps) bd_vertices = mesh.boundary_vertices is_inside = np.ones(mesh.num_vertices, dtype=bool) is_inside[bd_vertices] = False vertices = np.hstack((mesh.vertices, np.zeros((mesh.num_vertices, 1)))) vertices[is_inside, 2] = dist mesh = pymesh.form_mesh(vertices, mesh.faces) return mesh
def bevel(wires, logger, remove_holes, dist): bbox_min, bbox_max = wires.bbox; #wires = uniform_sampling(wires); mesh = constrained_triangulate(wires, logger, remove_holes); cell_ids = mesh.get_attribute("cell").ravel().astype(int); num_cells = np.amax(cell_ids) + 1; comps = []; for i in range(num_cells): to_keep = np.arange(mesh.num_faces, dtype=int)[cell_ids == i]; if not np.any(to_keep): continue; cut_mesh = pymesh.submesh(mesh, to_keep, 0); bd_edges = cut_mesh.boundary_edges; vertices, edges, __ = pymesh.remove_isolated_vertices_raw( cut_mesh.vertices, bd_edges); bd_wires = pymesh.wires.WireNetwork.create_from_data(vertices, edges); offset_dir = np.zeros((bd_wires.num_vertices, 2)); for ei in edges: v0 = ei[0]; v1 = ei[1]; adj_vts = bd_wires.get_vertex_neighbors(v0); if len(adj_vts) == 2: if adj_vts[0] == v1: vp = adj_vts[1]; else: vp = adj_vts[0]; e0 = vertices[v1] - vertices[v0]; e1 = vertices[vp] - vertices[v0]; e0 /= norm(e0); e1 /= norm(e1); theta = math.atan2(e0[0]*e1[1] - e0[1]*e1[0], e0.dot(e1)); if abs(theta) > 0.99 * math.pi: offset = np.array([-e0[1], e0[0]]); scale = 1.0; else: if theta > 0: offset = e0 + e1; scale = math.sin(theta/2); else: offset = -e0 - e1 scale = math.cos(math.pi/2 + theta/2); offset /= norm(offset); offset_dir[v0] = offset * dist / scale; offset_vertices = vertices + offset_dir; vertices = np.vstack((vertices, offset_vertices)); edges = np.vstack((edges, edges + bd_wires.num_vertices)); vertices, edges, __ = pymesh.remove_duplicated_vertices_raw( vertices, edges, dist/2); comp_wires = pymesh.wires.WireNetwork.create_from_data(vertices, edges); comp = constrained_triangulate(comp_wires, logger, True); comps.append(comp); mesh = pymesh.merge_meshes(comps); bd_vertices = mesh.boundary_vertices; is_inside = np.ones(mesh.num_vertices, dtype=bool); is_inside[bd_vertices] = False; vertices = np.hstack((mesh.vertices, np.zeros((mesh.num_vertices, 1)))); vertices[is_inside, 2] = dist; mesh = pymesh.form_mesh(vertices, mesh.faces); return mesh;