def main(): args = parse_args(); input_meshes = [load_mesh(filename) for filename in args.input_meshes]; output_mesh = merge_meshes(input_meshes); save_mesh(args.output, output_mesh, *output_mesh.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); mesh.add_attribute("vertex_index"); mesh.add_attribute("face_index"); mesh.add_attribute("voxel_index"); pymesh.save_mesh(args.output_mesh, mesh, *mesh.attribute_names);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); f_indices = pymesh.get_degenerated_faces(mesh); if (len(f_indices) == 0): print("mesh does not have any degenerated faces"); return; v_indices = mesh.faces[f_indices].ravel(); degenerated_faces = np.zeros(mesh.num_faces); degenerated_faces[f_indices] = 1.0; degenerated = np.zeros(mesh.num_vertices); degenerated[v_indices] = 1.0; mesh.add_attribute("degenerated_faces"); mesh.set_attribute("degenerated_faces", degenerated_faces); mesh.add_attribute("degenerated"); mesh.set_attribute("degenerated", degenerated); print("{} degenerated faces, consisting of {} vertices.".format( len(f_indices), np.count_nonzero(degenerated))); if args.verbose: print("Degenerated faces indices: {}".format(f_indices)); pymesh.save_mesh(args.output_mesh, mesh, "degenerated", "degenerated_faces"); if args.extract_region is not None: region = pymesh.submesh(mesh, f_indices, 0); pymesh.save_mesh(args.extract_region, region, *region.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.in_mesh); if (args.with_rounding): mesh = pymesh.form_mesh( np.round(mesh.vertices, args.precision), mesh.faces); intersecting_faces = pymesh.detect_self_intersection(mesh); counter = 0; while len(intersecting_faces) > 0 and counter < args.max_iterations: if (args.with_rounding): involved_vertices = np.unique(mesh.faces[intersecting_faces].ravel()); mesh.vertices_ref[involved_vertices, :] =\ np.round(mesh.vertices[involved_vertices, :], args.precision//2); mesh = pymesh.resolve_self_intersection(mesh, "igl"); mesh, __ = pymesh.remove_duplicated_faces(mesh, fins_only=True); if (args.with_rounding): mesh = pymesh.form_mesh( np.round(mesh.vertices, args.precision), mesh.faces); intersecting_faces = pymesh.detect_self_intersection(mesh); counter += 1; if len(intersecting_faces) > 0: logging.warn("Resolving failed: max iteration reached!"); pymesh.save_mesh(args.out_mesh, mesh);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); if (mesh.vertex_per_face != 4): raise IOError("input mesh is not a quad mesh"); mesh = pymesh.quad_to_tri(mesh, args.keep_symmetry); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names());
def main(): args = parse_args(); mesh = load_mesh(args.mesh_in); comps = separate_mesh(mesh, args.connectivity_type); if args.highlight: if (args.connectivity_type == "face"): comp_indicator = np.zeros(mesh.num_faces); elif (args.connectivity_type == "voxel"): comp_indicator = np.zeros(mesh.num_voxels); elif (mesh.num_voxels > 0): comp_indicator = np.zeros(mesh.num_voxels); else: comp_indicator = np.zeros(mesh.num_faces); for i in range(len(comps)): elem_sources = comps[i].get_attribute("ori_elem_index")\ .ravel().astype(int); comp_indicator[elem_sources] = i; mesh.add_attribute("component_index"); mesh.set_attribute("component_index", comp_indicator); save_mesh(args.mesh_out, mesh, *mesh.get_attribute_names()); else: basename, ext = os.path.splitext(args.mesh_out); for i,comp in enumerate(comps): filename = "{}_cc{}{}".format(basename, i, ext); save_mesh(filename, comp, *comp.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); vertices = mesh.vertices; voxels = mesh.voxels; num_voxels = mesh.num_voxels; orientation = np.zeros(num_voxels); for i in range(num_voxels): tet = voxels[i]; orientation[i] = pymesh.orient_3D( vertices[tet[1]], vertices[tet[0]], vertices[tet[2]], vertices[tet[3]]); if orientation[i] < 0: orientation[i] = -1; elif orientation[i] > 0: orientation[i] = 1; mesh.add_attribute("orientation"); mesh.set_attribute("orientation", orientation); pymesh.save_mesh(args.output_mesh, mesh, "orientation");
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); mesh.add_attribute("vertex_gaussian_curvature"); mesh.add_attribute("vertex_mean_curvature"); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); grid = pymesh.VoxelGrid(args.cell_size, mesh.dim); grid.insert_mesh(mesh); out_mesh = grid.mesh; pymesh.save_mesh(args.output_mesh, out_mesh);
def map_boundary_to_sphere(mesh, basename): bd_mesh = pymesh.form_mesh(mesh.vertices, mesh.faces); bd_mesh, info = pymesh.remove_isolated_vertices(bd_mesh); bd_vertices = np.copy(bd_mesh.vertices); assembler = pymesh.Assembler(bd_mesh); L = assembler.assemble("graph_laplacian"); # First, Laplacian smoothing to improve triangle quality. for i in range(100): c = np.mean(bd_vertices, axis=0); bd_vertices = bd_vertices - c; r = np.amax(norm(bd_vertices, axis=1)); bd_vertices /= r; bd_vertices += L * bd_vertices; if i%10 == 0: sphere_mesh = pymesh.form_mesh(bd_vertices, bd_mesh.faces); pymesh.save_mesh("{}_flow_{:03}.msh".format(basename, i), sphere_mesh); # Then, run mean curvature flow. sphere_mesh = pymesh.form_mesh(bd_vertices, bd_mesh.faces); sphere_mesh = mean_curvature_flow(sphere_mesh, 100, use_graph_laplacian=True); pymesh.save_mesh("{}_flow_final.msh".format(basename), sphere_mesh); bd_vertices = sphere_mesh.vertices; # Lastly, project vertices onto unit sphere. bd_vertex_indices = info["ori_vertex_index"]; bd_vertex_positions = np.divide(bd_vertices, norm(bd_vertices, axis=1).reshape((-1,1))); return bd_vertex_indices, bd_vertex_positions;
def main(): args = parse_args(); config = parse_config_file(args.config_file); if args.output is not None: config["output"] = args.output; mesh = tile(config); pymesh.save_mesh(config["output"], mesh, *mesh.get_attribute_names());
def repousse(mesh, logger): cell_ids = mesh.get_attribute("cell").ravel().astype(int); mesh.add_attribute("edge_length"); tol = np.amax(mesh.get_attribute("edge_length")) * 0.1; bbox_min, bbox_max = mesh.bbox; scaling = 2.0 / norm(bbox_max - bbox_min); start_time = time(); num_cells = np.amax(cell_ids)+1; results = []; 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); pymesh.save_mesh("debug.msh", cut_mesh); cut_mesh, __ = pymesh.remove_degenerated_triangles(cut_mesh, 100); cut_mesh, __ = pymesh.split_long_edges(cut_mesh, tol); dof = cut_mesh.num_vertices; assembler = pymesh.Assembler(cut_mesh); L = assembler.assemble("laplacian"); M = assembler.assemble("mass"); L_rhs = M * np.ones(dof) * -0.5; bd_indices = cut_mesh.boundary_vertices; n = len(bd_indices); C = scipy.sparse.coo_matrix((np.ones(n), (np.arange(n, dtype=int), bd_indices)), shape=(n, dof)); C_rhs = np.zeros(n); A = scipy.sparse.bmat([ [-L, C.T], [C, None] ]); rhs = np.concatenate((L_rhs.ravel(), C_rhs)); solver = pymesh.SparseSolver.create("SparseLU"); solver.compute(A); x = solver.solve(rhs); z = x[:dof].reshape((-1, 1)); vertices = np.hstack((cut_mesh.vertices, z)); out_mesh = pymesh.form_mesh(vertices, cut_mesh.faces); results.append(out_mesh); finish_time = time(); t = finish_time - start_time; logger.info("Repousse running time: {}".format(t)); mesh = pymesh.merge_meshes(results); vertices = mesh.vertices[:,:2]; mesh_2d = pymesh.form_mesh(vertices, mesh.faces); pymesh.save_mesh("out_2d.msh", mesh_2d) return mesh;
def main(): args = parse_args() mesh = pymesh.load_mesh(args.input_mesh) wires = pymesh.wires.WireNetwork.create_from_file(args.path) path = chain_wires(wires) result = pymesh.minkowski_sum(mesh, path) pymesh.save_mesh(args.output_mesh, result)
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); mesh = pymesh.retriangulate(mesh, args.max_area, not args.no_split_boundary, not args.no_steiner_points); pymesh.save_mesh(args.output_mesh, mesh);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh, drop_zero_dim=True); mesh,__ = pymesh.split_long_edges(mesh, 0.01); points = mesh.vertices[mesh.boundary_vertices,:]; mesh = pymesh.triangulate_beta(points, args.engine); pymesh.save_mesh(args.output_mesh, mesh); pymesh.timethis.summarize();
def main(): args = parse_args() mesh = pymesh.load_mesh(args.input_mesh) if mesh.num_voxels <= 0: raise RuntimeError("Input mesh contains 0 voxels.") if mesh.vertex_per_voxel != 4: raise RuntimeError("Input mesh is not a tet mesh.") mesh = tet_to_hex(mesh) pymesh.save_mesh(args.output_mesh, mesh)
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); out_mesh, info = pymesh.split_long_edges(mesh, args.max_edge_length); if mesh.has_attribute("corner_texture"): pymesh.map_corner_attribute(mesh, out_mesh, "corner_texture"); pymesh.save_mesh(args.output_mesh, out_mesh, *out_mesh.attribute_names);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); if (mesh.vertex_per_face != 4): logging.warning("Input mesh is not quad mesh."); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names()); else: mesh = pymesh.quad_to_tri(mesh, args.keep_symmetry); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh, drop_zero_dim=True); r = pymesh.convex_hull(mesh, args.engine, args.with_timing); if args.with_timing: hull, running_time = r; print("Running time: {}s".format(running_time)); else: hull = r; pymesh.save_mesh(args.output_mesh, hull, *hull.attribute_names);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); bd_edges = mesh.boundary_edges; bd_vertices = np.unique(bd_edges.ravel()); is_bd_vertices = np.zeros(mesh.num_vertices); is_bd_vertices[bd_vertices] = True; mesh.add_attribute("boundary_vertices"); mesh.set_attribute("boundary_vertices", is_bd_vertices); pymesh.save_mesh(args.output_mesh, mesh, *mesh.attribute_names);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); intersecting_faces = pymesh.detect_self_intersection(mesh); intersection_marker = np.zeros(mesh.num_faces); for i,face_pair in enumerate(intersecting_faces): intersection_marker[face_pair] = i+1; mesh.add_attribute("intersecting_faces"); mesh.set_attribute("intersecting_faces", intersection_marker); pymesh.save_mesh(args.output_mesh, mesh, "intersecting_faces");
def main(): args = parse_args(); scale = np.ones(3) * args.scale; if args.scale_x is not None: scale[0] = args.scale_x; if args.scale_y is not None: scale[1] = args.scale_y; if args.scale_z is not None: scale[2] = args.scale_z; mesh = pymesh.load_mesh(args.input_mesh); mesh = pymesh.form_mesh(mesh.vertices * scale, mesh.faces, mesh.voxels); pymesh.save_mesh(args.output_mesh, mesh);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); mesh.add_attribute("face_area"); areas = mesh.get_attribute("face_area"); zero_faces = areas == 0.0; roi = np.zeros(mesh.num_vertices); roi[mesh.faces[zero_faces]] = 1.0; mesh.add_attribute("zero_faces"); mesh.set_attribute("zero_faces", roi); pymesh.save_mesh(args.output_mesh, mesh, "zero_faces");
def main(): args = parse_args(); mesh1 = pymesh.load_mesh(args.input_mesh_1); mesh2 = pymesh.load_mesh(args.input_mesh_2); if args.timing: mesh, t = pymesh.boolean(mesh1, mesh2, args.operation, args.engine, with_timing = args.timing, exact_mesh_file = args.exact); print("Timing: {}".format(t)); else: mesh = pymesh.boolean(mesh1, mesh2, args.operation, args.engine, exact_mesh_file = args.exact); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names());
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); assert(mesh.has_attribute("corner_texture")); mesh.enable_connectivity(); vertices = np.copy(mesh.vertices); bad_vertex = np.logical_not(np.all(np.isfinite(mesh.vertices), axis=1)); bad_vertex_indices = np.arange(mesh.num_vertices, dtype=int)[bad_vertex]; for i in bad_vertex_indices: adj_v = mesh.get_vertex_adjacent_vertices(i); adj_v = adj_v[np.logical_not(bad_vertex[adj_v])]; vertices[i] = np.mean(vertices[adj_v,:], axis=0); out_mesh = pymesh.form_mesh(vertices, mesh.faces); if mesh.has_attribute("corner_texture"): faces = mesh.faces; uv = np.copy(mesh.get_attribute("corner_texture").reshape((-1, 2))); bad_uv = np.logical_not(np.all(np.isfinite(uv), axis=1)); bad_uv_indices = np.arange(len(uv), dtype=int)[bad_uv]; for i in bad_uv_indices: fi = i // mesh.vertex_per_face; ci = i % mesh.vertex_per_face; vi = faces[fi, ci]; adj_f = mesh.get_vertex_adjacent_faces(vi); adj_c = [adj_f[j]*mesh.vertex_per_face + np.argwhere(f==vi).ravel()[0] for j,f in enumerate(faces[adj_f, :])]; adj_c = np.array(adj_c, dtype=int); adj_c = adj_c[np.logical_not(bad_uv[adj_c])]; if len(adj_c) > 0: uv[i] = np.mean(uv[adj_c,:], axis=0); else: # All corner uv at this vertex is NaN. Average the one-ring instead. adj_c = [adj_f[j]*mesh.vertex_per_face + (np.argwhere(f==vi).ravel()[0]+1)%mesh.vertex_per_face for j,f in enumerate(faces[adj_f, :])]; adj_c += [adj_f[j]*mesh.vertex_per_face + (np.argwhere(f==vi).ravel()[0]+2)%mesh.vertex_per_face for j,f in enumerate(faces[adj_f, :])]; adj_c = np.array(adj_c, dtype=int); adj_c = adj_c[np.logical_not(bad_uv[adj_c])]; uv[i] = np.mean(uv[adj_c,:], axis=0); out_mesh.add_attribute("corner_texture"); out_mesh.set_attribute("corner_texture", uv); pymesh.save_mesh(args.output_mesh, out_mesh);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); formula = Formula(mesh); values = formula.eval_formula(args.formula); if isinstance(values, list): values = np.array(values, dtype=float); elif isinstance(values, (int, float)): values = np.ones(mesh.num_elements, dtype=float) * values; mesh.add_attribute(args.name); mesh.set_attribute(args.name, values); pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names());
def main(): args = parse_args(); mesh_1 = pymesh.load_mesh(args.input_mesh_1); mesh_2 = pymesh.load_mesh(args.input_mesh_2); assert(mesh_1.dim == 3); assert(mesh_2.dim == 3); bbox_min_1, bbox_max_1 = mesh_1.bbox; bbox_min_2, bbox_max_2 = mesh_2.bbox; bbox_min = np.minimum(bbox_min_1, bbox_min_2); bbox_max = np.maximum(bbox_max_1, bbox_max_2); #queries = grid_sample(bbox_min, bbox_max, args.num_samples); queries = random_sample(bbox_min, bbox_max, args.num_samples); winding_number_1 = pymesh.compute_winding_number(mesh_1, queries, engine=args.winding_number_engine) > 0.5; winding_number_2 = pymesh.compute_winding_number(mesh_2, queries, engine=args.winding_number_engine) > 0.5; diff = np.logical_xor(winding_number_1, winding_number_2); num_diff = np.count_nonzero(diff); print("Winding numbers of {} out of {} samples differ".format( num_diff, len(queries))); if args.output is not None: r = np.amax(bbox_max - bbox_min) * 0.01; box = pymesh.generate_box_mesh(np.ones(3) * -r, np.ones(3) * r); vertices = []; faces = []; for i in range(len(queries)): vertices.append(box.vertices + queries[i]); faces.append(box.faces + box.num_vertices * i); vertices = np.vstack(vertices); faces = np.vstack(faces); mesh = pymesh.form_mesh(vertices, faces); mesh.add_attribute("diff"); mesh.set_attribute("diff", np.repeat(diff, box.num_faces)); pymesh.save_mesh(args.output, mesh, "diff"); if args.export: info = load_info(args.input_mesh_2); info["diff"] = num_diff dump_info(args.input_mesh_2, info); if args.timing: pymesh.timethis.summarize();
def main(): args = parse_args(); in_mesh = load_mesh(args.input_mesh); in_mesh.add_attribute("vertex_normal"); v_normals = in_mesh.get_vertex_attribute("vertex_normal"); out_mesh = form_mesh(in_mesh.vertices, np.zeros((0, 3), dtype=int)); out_mesh.add_attribute("nx"); out_mesh.add_attribute("ny"); out_mesh.add_attribute("nz"); out_mesh.set_attribute("nx", v_normals[:,0].ravel()); out_mesh.set_attribute("ny", v_normals[:,1].ravel()); out_mesh.set_attribute("nz", v_normals[:,2].ravel()); save_mesh(args.output_mesh, out_mesh, "nx", "ny", "nz");
def main(): args = parse_args(); # Config 0 Config 1 Config 2 Config 3 # 1 +----+ 2 3 +----+ 2 1 +----+ 0 3 + + 0 # | | | | | | # 0 + + 3 0 +----+ 1 2 +----+ 3 2 +----+ 1 configs = np.array([ [[0, 3],[1, 2]], [[0, 1],[3, 2]], [[2, 3],[1, 0]], [[2, 1],[3, 0]] ], dtype=int); config_transform = np.array([ [1, 0, 0, 2], [0, 1, 1, 3], [3, 2, 2, 0], [2, 3, 3, 1] ], dtype=int); pts = np.zeros((4**args.resolution, 2)); for x in range(2**args.resolution): for y in range(2**args.resolution): X = x; Y = y; p = np.copy([x,y]); quadrant = 0; index = 0; for order in range(args.resolution): exp = args.resolution-order-1; mask = 1 << exp; step = 2**(args.resolution-order-1); i = (X & mask) >> exp; j = (Y & mask) >> exp; local_index = configs[quadrant][i][j]; quadrant = config_transform[quadrant][local_index]; index += local_index * step*step; X >> 1; Y >> 1; pts[index,:] = p; vertices = np.array(pts); edges = np.array([[i, i+1] for i in range(4**args.resolution - 1)]); wires = pymesh.wires.WireNetwork.create_from_data(vertices, edges); wires.write_to_file("tmp.obj"); inflator = pymesh.wires.Inflator(wires); inflator.inflate(0.2); mesh = inflator.mesh; pymesh.save_mesh(args.output_mesh, mesh);
def main(): args = parse_args(); basename, ext = os.path.splitext(args.output_mesh); mesh = pymesh.load_mesh(args.input_mesh); mesh = generate_tet_mesh(mesh); pymesh.save_mesh("{}_source.msh".format(basename), mesh); bd_vertex_indices, bd_vertex_positions = map_boundary_to_sphere( mesh, basename); out_mesh = tutte_3D(mesh, bd_vertex_indices, bd_vertex_positions); tet_orientations = pymesh.get_tet_orientations(out_mesh); out_mesh.add_attribute("orientation"); out_mesh.set_attribute("orientation", tet_orientations); pymesh.save_mesh(args.output_mesh, out_mesh, "orientation");
elif back != None: c = back a, info = pymesh.remove_isolated_vertices(a) b, info = pymesh.remove_isolated_vertices(b) c, info = pymesh.remove_isolated_vertices(c) a, info = pymesh.remove_duplicated_faces(a) b, info = pymesh.remove_duplicated_faces(b) c, info = pymesh.remove_duplicated_faces(c) if a == None and b == None and c == None: print('No mesh to convert') exit() elif a != None and b != None and c != None: a = pymesh.boolean(a, c, operation="intersection") # a = pymesh.boolean(b, a, operation="intersection") elif a != None and b != None and c == None: a = pymesh.boolean(a, b, operation="intersection") elif a != None and b == None and c != None: a = pymesh.boolean(a, c, operation="intersection") if a == None and b != None and c != None: a = pymesh.boolean(b, c, operation="intersection") pymesh.save_mesh(save_path + "/mesh.obj", a, ascii=True) print('Koniec')
def __init__(self, nested_view, plane, interior_color, exterior_color, cut_ratio): super(ClippedView, self).__init__(nested_view); self.plane = plane; bbox_min, bbox_max = self.mesh.bbox; center = bbox_min * cut_ratio + bbox_max * (1.0 - cut_ratio); if plane == "+X": should_keep = lambda v: v[0] > center[0]; elif plane == "+Y": should_keep = lambda v: v[1] > center[1]; elif plane == "+Z": should_keep = lambda v: v[2] > center[2]; elif plane == "-X": should_keep = lambda v: v[0] < center[0]; elif plane == "-Y": should_keep = lambda v: v[1] < center[1]; elif plane == "-Z": should_keep = lambda v: v[2] < center[2]; elif isinstance(plane, list) and len(plane) == 4: n = np.array(plane[:3]); n = n / norm(n); should_keep = lambda v: np.dot(v-center, n) < plane[3]; else: raise NotImplementedError("Unknown plane type: {}".format(plane)); if len(self.view.voxels) > 0: mesh = pymesh.form_mesh(self.view.vertices, np.array([]), self.view.voxels); mesh.add_attribute("voxel_centroid"); mesh.add_attribute("voxel_face_index"); voxel_face_id = mesh.get_voxel_attribute("voxel_face_index").astype(int); centroids = mesh.get_voxel_attribute("voxel_centroid"); self.V_to_keep = [should_keep(v) for v in centroids]; voxels_to_keep = self.view.voxels[self.V_to_keep]; voxel_face_id = voxel_face_id[self.V_to_keep]; self.mesh = pymesh.form_mesh(self.view.vertices, np.zeros((0,3)), voxels_to_keep); self.mesh.add_attribute("voxel_face_index"); new_voxel_face_id = self.mesh.get_voxel_attribute("voxel_face_index").astype(int); old_vertex_colors = self.vertex_colors; new_vertex_colors = np.zeros((self.mesh.num_faces, 3, 4)); cut_color = color_table[interior_color]; cut_color = np.array([cut_color.red, cut_color.green, cut_color.blue, cut_color.alpha]); is_interface = np.zeros(self.mesh.num_faces, dtype=bool); for old_i,new_i in zip(voxel_face_id.ravel(), new_voxel_face_id.ravel()): if new_i >= 0 and old_i >= 0: new_vertex_colors[new_i] = old_vertex_colors[old_i]; elif new_i >= 0: is_interface[new_i] = True; new_vertex_colors[new_i,:] = cut_color; self.vertex_colors = new_vertex_colors; self.interface = pymesh.form_mesh(self.mesh.vertices, self.mesh.faces[is_interface]); self.boundary = pymesh.form_mesh(self.mesh.vertices, self.mesh.faces[np.logical_not(is_interface)]); #self.vertex_colors = new_vertex_colors[is_interface]; tmp_dir = tempfile.gettempdir(); m = hashlib.md5(); m.update(self.mesh.vertices); name = m.hexdigest(); bd_mesh_file = os.path.join(tmp_dir, "{}_bd.msh".format(name)); interface_mesh_file = os.path.join(tmp_dir, "{}_interface.msh".format(name)); pymesh.save_mesh(bd_mesh_file, self.boundary); pymesh.save_mesh(interface_mesh_file, self.interface); self.subviews = [ MeshView.create_from_setting({ "type": "mesh_only", "mesh": bd_mesh_file, "color": exterior_color, "wire_frame": True, "bbox": [self.bmin, self.bmax] }), MeshView.create_from_setting({ "type": "mesh_only", "mesh": interface_mesh_file, "color": interior_color, "wire_frame": True, "bbox": [self.bmin, self.bmax] }), ]; else: centroids = np.mean(self.view.vertices[self.view.faces], axis=1); self.V_to_keep = [should_keep(v) for v in centroids]; faces = self.view.faces[self.V_to_keep]; self.mesh = pymesh.form_mesh(self.view.vertices, faces); self.mesh.add_attribute("face_normal");
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 insert(filename_in, filename_out='out.obj', data=32 * [0, 1], secret=123456, strength=100, partitions=-1): print print '########## Embedding started ##########' # Scramble the data scrambled_data = scramble(data, secret) mesh = pymesh.load_mesh(filename_in) if partitions == -1: partitions = mesh.num_vertices / 500 # Partition the mesh into patches patches, mapping = partitioning.mesh_partitioning(filename_in, mesh, partitions) print 'Step 1: Mesh patched' processed = 0 bits_inserted = 0 updated_vertices = [] # Set a progress bar utils.progress(processed, len(patches), 'Inserting data in patches...') # Insert the data in all the patches for i, patch in enumerate(patches): # Get the eigenVectors, either by computing them or by reading the from a file if they already have been computed npy_file = 'saved_eig/' + str(partitions) + '/embedding_' + str( i) + '.npy' if os.path.exists(npy_file): B = np.load(npy_file) else: B = compute_eigenvectors(patch.num_vertices, patch.faces) np.save(npy_file, B) # Get the spectral coefficients P = np.matmul(B, patch.vertices[:, 0]) Q = np.matmul(B, patch.vertices[:, 1]) R = np.matmul(B, patch.vertices[:, 2]) # Insert the data P, Q, R, count = embedding.write(P, Q, R, scrambled_data, strength) BM1 = np.linalg.pinv(B) new_vertices = np.zeros((patch.num_vertices, 3)) # Retrieve the new coordinates of the vertices new_vertices[:, 0] = np.matmul(BM1, P) new_vertices[:, 1] = np.matmul(BM1, Q) new_vertices[:, 2] = np.matmul(BM1, R) updated_vertices.append(pymesh.form_mesh(new_vertices, patch.faces)) bits_inserted += count # Update the progress bar processed += 1 utils.progress(processed, len(patches), 'Inserting data in patches...') print print 'Step 2: Data inserted' if bits_inserted < len(data): print 'Only %s bits inserted, the bit error rate when retriving data might be significant' % ( bits_inserted) new_vertices = np.zeros((mesh.num_vertices, 3)) # Merge the different patches into a single mesh for i in range(mesh.num_vertices): for j in range(partitions): val = mapping[j][i] if val != -1: new_vertices[i] = updated_vertices[j].vertices[int(val)] continue pymesh.save_mesh(filename_out, pymesh.form_mesh(new_vertices, mesh.faces)) print '########## Embedding finished ##########' print return bits_inserted
def automatic_marching_cube_reconstruction(dirpath, filename): """ An automatic routine to convert image stacks to 3D triangular meshes. :param dirpath: The full directory path where the file(s) are stored, ex : /home/user/Documents/spineReconstruction/Images/ or C:/Users/Documents/spineReconstruction/Images :param filename: The name of the image stack file in tiff, ex : stackimage.tiff or stackimage.tif :return: """ print(f'Computing : {filename.strip(".tif").split("/")[-1]}_mesh') imageStack = io.imread(f'{dirpath}/{filename}') zSpacing = 0.35 / 1.518 levelThreshold = 0 stability = True mesh = construct_mesh_from_lewiner(imageStack, (zSpacing, 0.05, 0.05), levelThreshold) # Todo : refactoring and cut into more functions while stability: if levelThreshold > 200: print( 'Image seems to be mostly noise, or resolution is super good. Stopping at levelTreshold 200' ) stability = False levelThreshold = 200 mesh2 = construct_mesh_from_lewiner(imageStack, (zSpacing, 0.05, 0.05), levelThreshold) meshList = optimise.get_size_of_meshes(optimise.create_graph(mesh2)) meshList.sort(reverse=True) meshList = [x if x > 0.01 * np.sum(meshList) else 0 for x in meshList] if len(meshList) > 1: if (meshList[0] + meshList[1]) / np.sum( meshList) > 0.9 and meshList[0] / np.sum(meshList) < 0.8: stability = False stability2 = False # neck and head are dissociated while not stability2: levelThreshold -= 1 mesh2 = construct_mesh_from_lewiner( imageStack, (zSpacing, 0.05, 0.05), levelThreshold) meshL = optimise.remove_noise(mesh2) if len(meshL) > 1: if ((len(meshL[0].vertices) + len(meshL[1].vertices)) / len(mesh2.vertices) <= 0.9 or len(meshL[0].vertices) / len(mesh2.vertices) >= 0.8): levelThreshold -= levelThreshold / 10 break else: levelThreshold -= levelThreshold / 10 break else: levelThreshold += 5 else: levelThreshold += 5 # Look for narrow reconstruction mesh3 = construct_and_optimise_from_lewiner(imageStack, (zSpacing, 0.05, 0.05), levelThreshold) print( f'Saving mesh with level threshold : {levelThreshold} in optimisedMeshes' ) save_mesh( f'optimisedMeshes/{filename.split(".")[0]}_{levelThreshold}_optimised.stl', mesh3)
def sample_modelnet_40(input_dir, output_dir, point_cloud_size): """ Sample ModelNet40 meshes into point clouds of certain sizes (random sampling). Args: input_dir (str): Input dir of ModelNet40. output_dir (str): Output dir with sampled point clouds. point_cloud_size (int): Output size of sampling algorithm. """ # Is data present? if not os.path.exists(input_dir): raise AssertionError( 'No ModelNet40 dataset found in the following directory: ' + input_dir) ####################################################################### # load data ####################################################################### train_clouds = [] train_labels = [] test_clouds = [] test_labels = [] clsnms = [] # Classes dir class_names = [ el for el in os.listdir(input_dir) if os.path.isdir(os.path.join(input_dir, el)) ] class_dirs = [os.path.join(input_dir, el) for el in class_names] for idx, class_dir in enumerate(class_dirs): # Class name class_name = os.path.split(class_dir)[1] clsnms.append(class_name) # For train/test split for part_name in ['train', 'test']: # For each file in part print('Converting', part_name, 'part of', class_name, 'class') part_dir = os.path.join(class_dir, part_name) for filename in tqdm( [f for f in os.listdir(part_dir) if '.off' in f]): # Load object f = filename.split('.') f_ply = str(f[0]) + '.ply' f_pcd = str(f[0]) + '_' + str(point_cloud_size) + '_' + '.pcd' object_path = os.path.join(os.path.join(part_dir, filename)) pymesh_path = os.path.join(os.path.join(part_dir, f_ply)) pclpcd_path = os.path.join(os.path.join(part_dir, f_pcd)) # No off if not os.path.exists(object_path): print('No OFF path at: ', object_path) continue try: # OFF file fix need_fix = False with open(object_path) as f: first_line = f.readline() if 'OFF' in first_line and len(first_line) != 4: need_fix = True content = f.readlines() if need_fix: with open(object_path, 'w') as f: f.write(first_line[:3] + '\n') f.write(first_line[3:]) for c in content: f.write(c) # Pymesh mesh = pymesh.load_mesh(object_path) pymesh.save_mesh(pymesh_path, mesh) # pyntcloud pynt = pyntcloud.PyntCloud.from_file(pymesh_path) cloud = pynt.get_sample('mesh_random', n=point_cloud_size) cloud = cloud.values # Zero mean for dim in [0, 1, 2]: dim_mean = np.mean(cloud[:, dim]) cloud[:, dim] -= dim_mean # Scale to unit-ball distances = [np.linalg.norm(point) for point in cloud] scale = 1. / np.max(distances) cloud *= scale # PCD pcd = pcl.PointCloud(cloud) pcl.save(pcd, pclpcd_path) # Append if part_name == 'train': train_clouds.append(cloud) train_labels.append(idx) else: test_clouds.append(cloud) test_labels.append(idx) except ValueError: print("An exception occurred: " + object_path) return # Numpy arr train_clouds = np.array(train_clouds) train_labels = np.array(train_labels) test_clouds = np.array(test_clouds) test_labels = np.array(test_labels) clsnms = np.array(clsnms) ####################################################################### # Flat pointclouds and shuffle ####################################################################### train_shuffle_idx = np.arange(len(train_clouds)) np.random.shuffle(train_shuffle_idx) train_clouds = train_clouds[train_shuffle_idx] train_labels = train_labels[train_shuffle_idx] test_shuffle_idx = np.arange(len(test_clouds)) np.random.shuffle(test_shuffle_idx) test_clouds = test_clouds[test_shuffle_idx] test_labels = test_labels[test_shuffle_idx] ####################################################################### # Flat pointclouds and shuffle ####################################################################### file_max_length = 2048 for file_idx in range(math.ceil(len(train_clouds) / file_max_length)): filename = 'data_train_' + str(file_idx) + '.h5' filepath = os.path.join(output_dir, filename) file = h5py.File(filepath) start_idx = file_max_length * file_idx end_idx = min(len(train_clouds), file_max_length * (file_idx + 1)) file['data'] = train_clouds[start_idx:end_idx] file['label'] = train_labels[start_idx:end_idx] file.close() for file_idx in range(math.ceil(len(test_clouds) / file_max_length)): filename = 'data_test_' + str(file_idx) + '.h5' filepath = os.path.join(output_dir, filename) file = h5py.File(filepath) start_idx = file_max_length * file_idx end_idx = min(len(test_clouds), file_max_length * (file_idx + 1)) file['data'] = test_clouds[start_idx:end_idx] file['label'] = test_labels[start_idx:end_idx] file.close() with open(os.path.join(output_dir, 'shape_names.txt'), 'w') as f: for class_name in clsnms: f.write(class_name + '\n')
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); add_attribute(mesh, args.attribute_name); pymesh.save_mesh(args.output_mesh, mesh, args.attribute_name); pymesh.timethis.summarize();
box_min = np.array ( [ -0.11, +0.10, -0.01 ] ) box_max = np.array ( [ -0.04, +0.17, +0.06 ] ) box = pymesh.generate_box_mesh ( box_min, box_max ) # In[36]: bunny = pymesh.load_mesh ( "bunny.obj" ) # In[37]: intersection = pymesh.boolean ( box, bunny, "intersection", "carve") # In[38]: pymesh.save_mesh("output.obj", intersection, ascii=True) # In[39]: mesh = gel.obj_load("output.obj") js.display(mesh, smooth=False)
def main(): args = parse_args() mesh = pymesh.load_mesh(args.input_mesh) mesh = pymesh.submesh(mesh, args.face_indices, args.n_ring) pymesh.save_mesh(args.output_mesh, mesh, *mesh.get_attribute_names())