def mesh_from_xy_points(faces_xy, extrude_mm=0.0): index = {} inverse = {} count = 0 for face in faces_xy: for point in face: if tuple(point) not in index: index[tuple(point)] = count inverse[count] = point count += 1 vertices = [] for i in index.values(): vertices.append(inverse[i]) faces = [] for face in faces_xy: new_face = [] for point in face: new_face.append(index[tuple(point)]) faces.append(new_face) if len(faces_xy[0][0]) == 2: return make_3d(pymesh.form_mesh(np.array(vertices), np.array(faces)), extrude_mm) else: return pymesh.form_mesh(np.array(vertices), np.array(faces))
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 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() 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 generate_potential_templates(pose, beta, outmesh_path): # template 0 m.pose[:] = 0 m.betas[:] = beta m.pose[5] = 0.5 m.pose[8] = -0.5 m.pose[53] = -0.5 m.pose[50] = 0.5 point_set = m.r.astype(np.float32) mesh = pymesh.form_mesh(vertices=point_set, faces=m.f) mesh.add_attribute("red") mesh.add_attribute("green") mesh.add_attribute("blue") mesh.set_attribute("red", mesh_ref.get_attribute("vertex_red")) mesh.set_attribute("green", mesh_ref.get_attribute("vertex_green")) mesh.set_attribute("blue", mesh_ref.get_attribute("vertex_blue")) pymesh.meshio.save_mesh('search/template0.ply', mesh, "red", "green", "blue", ascii=True) # template 1 m.pose[:] = 0 point_set = m.r.astype(np.float32) mesh = pymesh.form_mesh(vertices=point_set, faces=m.f) mesh.add_attribute("red") mesh.add_attribute("green") mesh.add_attribute("blue") mesh.set_attribute("red", mesh_ref.get_attribute("vertex_red")) mesh.set_attribute("green", mesh_ref.get_attribute("vertex_green")) mesh.set_attribute("blue", mesh_ref.get_attribute("vertex_blue")) pymesh.meshio.save_mesh('search/template1.ply', mesh, "red", "green", "blue", ascii=True) return
def iou_pymesh(mesh1, mesh2, dim=110): # mesh1 = (vertices1, triangles1) # mesh2 = (vertices2, triangles2) mesh1 = pymesh.form_mesh(mesh1[0], mesh1[1]) grid1 = pymesh.VoxelGrid(2. / dim) grid1.insert_mesh(mesh1) grid1.create_grid() ind1 = ((grid1.mesh.vertices + 1.1) / 2.4 * dim).astype(np.int) v1 = np.zeros([dim, dim, dim]) v1[ind1[:, 0], ind1[:, 1], ind1[:, 2]] = 1 mesh2 = pymesh.form_mesh(mesh2[0], mesh2[1]) grid2 = pymesh.VoxelGrid(2. / dim) grid2.insert_mesh(mesh2) grid2.create_grid() ind2 = ((grid2.mesh.vertices + 1.1) / 2.4 * dim).astype(np.int) v2 = np.zeros([dim, dim, dim]) v2[ind2[:, 0], ind2[:, 1], ind2[:, 2]] = 1 intersection = np.sum(np.logical_and(v1, v2)) union = np.sum(np.logical_or(v1, v2)) return float(intersection) / union
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 split_disconnected_meshes(mesh): connected_faces = [] for _ in range(mesh.num_vertices): connected_faces.append([]) for face in mesh.faces: for i in range(3): connected_faces[face[i]].append(face) connected = np.zeros(mesh.num_vertices, dtype=bool) indexes_match = np.subtract(np.zeros(mesh.num_vertices), 1) vertices = [] faces = [] # Add a first face to the list of conneccted faces q = deque([mesh.faces[0][0]]) connected[mesh.faces[0][0]] = True # Perform a Breadth-First-Search on the faces while len(q) > 0: vertice = q.pop() for face in connected_faces[vertice]: i1, vertices, indexes_match = find_index_or_add( face[0], indexes_match, vertices, mesh.vertices) i2, vertices, indexes_match = find_index_or_add( face[1], indexes_match, vertices, mesh.vertices) i3, vertices, indexes_match = find_index_or_add( face[2], indexes_match, vertices, mesh.vertices) if [i1, i2, i3] not in faces: faces.append([i1, i2, i3]) for i in range(3): if not connected[face[i]]: connected[face[i]] = True q.appendleft(face[i]) # Get on sub-mesh where all the faces are connected resulting_mesh = pymesh.form_mesh(np.array(vertices), np.array(faces)) remaining_faces = [] # List all the faces that do not belong to this specific sub-mesh for face in mesh.faces: if not connected[face[0]]: remaining_faces.append(face) meshes = [resulting_mesh] # If there are still faces that have not been considered restart the process only on those faces if len(remaining_faces) != 0: remaining_mesh = pymesh.form_mesh(np.array(mesh.vertices), np.array(remaining_faces)) meshes.extend(split_disconnected_meshes(remaining_mesh)) return meshes
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 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 genellip(a=1, b=1, c=1, N=250, sigma=0.125, ell=0.3, gid=1): nodes = sphere_points(n=N) elements = ch(nodes).simplices elements = surface_fix(nodes, elements) M = np.diag([a, b, c]) nodes = np.dot(M, nodes.T).T ellipsoid = pymesh.form_mesh(nodes, elements) nodes = deform_mesh(ellipsoid, a, b, c, sigma, ell, gid) gellip = pymesh.form_mesh(nodes, ellipsoid.elements) tris = gellip.elements return nodes, tris
def map_cell_attribute(xyz, connect, attribute, decimated): mesh = pymesh.form_mesh(xyz, connect) mesh.add_attribute("face_scalar") mesh.set_attribute("face_scalar", attribute) decimated_xyz, decimated_connect, _ = poly_data_to_unstr(decimated) decimated_mesh = pymesh.form_mesh(decimated_xyz, decimated_connect) pymesh.map_face_attribute(mesh, decimated_mesh, "face_scalar") print('attributes in new mesh mapped') return decimated_xyz, decimated_connect, decimated_mesh.get_attribute( "face_scalar")
def main(): args = parse_args() mesh = pymesh.load_mesh(args.input_mesh) if mesh.vertex_per_face == 4: logging.warning("Converting quad mesh to triangle mesh.") mesh = pymesh.quad_to_tri(mesh) if args.exact: name, ext = os.path.splitext(args.output_mesh) exact_mesh_file = name + ".xml" else: exact_mesh_file = None if mesh.num_vertices == 0 or mesh.num_faces == 0: # Empty input mesh, output empty mesh as well. result = pymesh.form_mesh(np.zeros((0, 3), dtype=float), np.zeros((0, 3), dtype=int)) if args.timing: update_info(args.output_mesh, 0) else: if args.engine == "igl": empty = pymesh.form_mesh(np.zeros((0, 3)), np.zeros((0, 3))) r = pymesh.boolean(mesh, empty, "union", engine=args.engine, with_timing=args.timing, exact_mesh_file=exact_mesh_file) else: # Empty mesh is valid for these libraries, using bbox instead. bbox = mesh.bbox center = (bbox[0] + bbox[1]) * 0.5 box = pymesh.generate_box_mesh(bbox[0] - np.ones(mesh.dim), bbox[1] + np.ones(mesh.dim)) r = pymesh.boolean(mesh, box, "intersection", engine=args.engine, with_timing=args.timing, exact_mesh_file=exact_mesh_file) if args.timing: result, timing = r update_info(args.output_mesh, timing) else: result = r pymesh.save_mesh(args.output_mesh, result)
def carve_mesh(target_mesh, block, N, batch_size, out_name, initial_N=0, save_intermediate=True, debug=False): name, ext = os.path.splitext(out_name); tree = pymesh.AABBTree(); tree.load_mesh(target_mesh); dodecahedron = pymesh.generate_dodecahedron(1.0, np.zeros(3)); vertices = dodecahedron.vertices; faces = dodecahedron.faces; for i in range(initial_N, N, batch_size): pts = block.vertices; squared_dist, face_indices, closest_pts = \ tree.look_up_with_closest_points(pts); n = np.min([batch_size, N-i, len(pts)]); indices = np.argsort(squared_dist)[::-1][:n]; radii = np.sqrt(squared_dist[indices]); centers = pts[indices, :]; to_remove = [pymesh.form_mesh( np.dot(pymesh.Quaternion(numpy.random.rand(4)).to_matrix(), vertices.T * r).T + p, faces) for r,p in zip(radii, centers)]; to_remove = pymesh.merge_meshes(to_remove); if debug: pymesh.save_mesh("deubg_block.msh", block); pymesh.save_mesh("deubg_cut.msh", to_remove); block = pymesh.boolean(block, to_remove, "difference", engine="igl"); block, __ = pymesh.collapse_short_edges(block, 1e-12, False); if save_intermediate: pymesh.save_mesh("{}_{:06}{}".format(name, i, ext), block); return block;
def sphere_perturb(sphere, devrand, aratio1, aratio2): #devrand = 0.05 # how far to perturb each vertex nv = len(sphere.vertices) f = sphere.faces v = np.copy(sphere.vertices) # add perturbations to x,y,z to all vertices for i in range(nv): dx = devrand * random.uniform(-1, 1) dy = devrand * random.uniform(-1, 1) dz = devrand * random.uniform(-1, 1) v[i, 0] += dx v[i, 1] += dy v[i, 2] += dz # aratio1 = c/a this gives c = aratio1*a # aratio2 = b/a this gives b = aratio2*a # volume = 4/3 pi a*b*c for an ellipsoid # vol = 1*aratio1*aratio2 # rad_cor = pow(vol,-1./3.) # v[:,2] *= aratio1*rad_cor # make oblate, adjusts z coords # v[:,1] *= aratio2*rad_cor # make elongated in xy plane , adjusts y coords # v[:,0] *= rad_cor # adjusts x coords # volume should now stay the same sub_com(v) # subtract center of mass from vertex positions psphere = pymesh.form_mesh(v, f) psphere.add_attribute("face_area") psphere.add_attribute("face_normal") psphere.add_attribute("face_centroid") sbody = body_stretch(psphere, aratio1, aratio2) # do the stretching return sbody
def triangulate(wires, engine, stage, eps, logger, wire_file, json_file): if wires.num_vertices == 0: return pymesh.form_mesh(np.zeros((0, 2)), np.zeros((0,3))); basename = os.path.splitext(wire_file)[0]; if engine == "triwild": out_mesh = "{}_linear.msh".format(basename); log_file = "{}_triwild.log".format(basename); if json_file is not None: command = "TriWild --choice TRI --is-log 0 --epsilon {} --stage {} --log-file {} --int-edge-length 20 --feature-input {} --output-debug-mesh=0 --skip-eps --input {} --output {}".format( eps, stage, log_file, json_file, wire_file, basename); else: command = "TriWild --choice TRI --is-log 0 --epsilon {} --stage {} --log-file {} --int-edge-length 20 --output-debug-mesh=0 --skip-eps --input {} --output {}".format( eps, stage, log_file, wire_file, basename); print(command); start_time = time(); check_call(command.split()); finish_time = time(); t = finish_time - start_time; mesh = pymesh.load_mesh(out_mesh, drop_zero_dim=True); else: mesh, t = pymesh.triangulate_beta(wires.vertices, wires.edges, engine=engine, with_timing=True); logger.info("Triangulation running time: {}".format(t)); return mesh;
def frontMesh(data, distance, cos, height, ratio): vertices = [] faces = [] for i in range(len(data['vertices'])): y = data['vertices'][i][1] * ratio z = data['vertices'][i][0] * ratio c = cos s = -math.sqrt(1 - c * c) x = distance * 2 / cos vertices.append([c * x - s * y - distance, s * x + c * y + height, z]) #vertices.append([thickness,data['vertices'][i][1]*ratio,data['vertices'][i][0]*ratio]) vertices.append([-distance, height, 0]) vertices = np.array(vertices) for i in range(len(data['triangles'])): faces.append([ data['triangles'][i][0], data['triangles'][i][2], data['triangles'][i][1] ]) for i in range(1500): if i != 1499: faces.append([i, len(data['vertices']), i + 1]) else: faces.append([1499, len(data['vertices']), 0]) faces = np.array(faces) return pymesh.form_mesh(vertices, faces)
def backMeshHR(data, distance, cos, height, ratio): vertices = [] faces = [] for i in range(len(data['vertices'])): y = data['vertices'][i][1] * ratio * 0.5 z = -data['vertices'][i][0] * ratio * 0.5 c = cos s = math.sqrt(1 - c * c) x = -distance / cos vertices.append([c * x - s * y + distance, s * x + c * y + height, z]) #(data['vertices'][i][1]-42)*ratio,-data['vertices'][i][0]*ratio]) vertices.append([distance, height, 0]) vertices = np.array(vertices) for i in range(len(data['triangles'])): faces.append([ data['triangles'][i][0], data['triangles'][i][1], data['triangles'][i][2] ]) for i in range(1500): if i != 1499: faces.append([i, i + 1, len(data['vertices'])]) else: faces.append([1499, 0, len(data['vertices'])]) faces = np.array(faces) return pymesh.form_mesh(vertices, faces)
def save_obj_file(path, verts, faces): verts = verts.detach().cpu().numpy() if torch.is_tensor(verts) else verts faces = faces.detach().cpu().numpy() if torch.is_tensor(faces) else faces # import pymesh pmesh = pymesh.form_mesh(verts, faces) pymesh.save_mesh(path, pmesh)
def generate_mesh(self, latent_vector): assert latent_vector.size(0) == 1, "input should have batch size 1!" import pymesh input_points = [ self.template[i].get_regular_points(self.nb_pts_in_primitive, latent_vector.device) for i in range(self.opt.nb_primitives) ] input_points = [input_points[i] for i in range(self.opt.nb_primitives)] # Deform each patch output_points = [ self.decoder[i](input_points[i], latent_vector.unsqueeze(2)).squeeze() for i in range(0, self.opt.nb_primitives) ] output_meshes = [ pymesh.form_mesh(vertices=output_points[i].transpose( 1, 0).contiguous().cpu().numpy(), faces=self.template[i].mesh.faces) for i in range(self.opt.nb_primitives) ] # Deform return the deformed pointcloud mesh = pymesh.merge_meshes(output_meshes) return mesh
def __save_temp_mesh(self, active_view): basename, ext = os.path.splitext(self.image_name); path, name = os.path.split(basename); now = datetime.datetime.now() stamp = now.isoformat(); tmp_dir = tempfile.gettempdir(); tmp_mesh_name = os.path.join(tmp_dir, "{}_{}.ply".format(name, stamp)); vertices = active_view.vertices; faces = active_view.faces; voxels = active_view.voxels; dim = vertices.shape[1]; num_faces, vertex_per_face = faces.shape; vertices = vertices[faces.ravel(order="C")]; colors = active_view.vertex_colors.reshape((-1, 4), order="C"); colors *= 255; faces = np.arange(len(vertices), dtype=int).reshape( (num_faces, vertex_per_face), order="C"); mesh = pymesh.form_mesh(vertices, faces); mesh.add_attribute("red"); mesh.set_attribute("red", colors[:,0].ravel()); mesh.add_attribute("green"); mesh.set_attribute("green", colors[:,1].ravel()); mesh.add_attribute("blue"); mesh.set_attribute("blue", colors[:,2].ravel()); pymesh.save_mesh(tmp_mesh_name, mesh, "red", "green", "blue", ascii=True, use_float=True, anonymous=True); return tmp_mesh_name;
def minimize(A, b, markers, save_mesh=True): start = timer() x_0 = np.append(source_mesh.vertices.flatten(), [ calc_normal(source_mesh.vertices[source_mesh.faces[triangle]])[3] for triangle in range(source_mesh.num_faces) ]) x_0 = set_marker_positions(x_0, markers) x = sparse.linalg.lsqr(A, b, x0=x_0, show=True)[0] x = set_marker_positions(x, markers) end = timer() print(end - start) deformed_mesh = pymesh.form_mesh( x[:source_mesh.num_vertices * 3].reshape( (source_mesh.num_vertices, 3)), source_mesh.faces, source_mesh.voxels) deformed_mesh.add_attribute("vertex_normal") deformed_mesh.enable_connectivity() if save_mesh: pymesh.save_mesh("source_mesh_deformed.obj", deformed_mesh) return deformed_mesh
def output_patch_coords(subv, subf, subn, i, neigh_i, theta, rho): """ For debugging purposes, save a patch to visualize it. """ mesh = pymesh.form_mesh(subv, subf) n1 = subn[:, 0] n2 = subn[:, 1] n3 = subn[:, 2] mesh.add_attribute('vertex_nx') mesh.set_attribute('vertex_nx', n1) mesh.add_attribute('vertex_ny') mesh.set_attribute('vertex_ny', n2) mesh.add_attribute('vertex_nz') mesh.set_attribute('vertex_nz', n3) rho = np.array([rho[0, ix] for ix in range(rho.shape[1]) if ix in neigh_i]) mesh.add_attribute('rho') mesh.set_attribute('rho', rho) theta = np.array( [theta[ix] for ix in range((theta.shape[0])) if ix in neigh_i]) mesh.add_attribute('theta') mesh.set_attribute('theta', theta) charge = np.zeros(len(neigh_i)) mesh.add_attribute('charge') mesh.set_attribute('charge', charge) pymesh.save_mesh('v{}.ply'.format(i), mesh, *mesh.get_attribute_names(), use_float=True, ascii=True)
def extrude(mesh, depth, logger): start_time = time(); num_vertices = mesh.num_vertices; num_faces = mesh.num_faces; top_vertices = mesh.vertices; top_faces = mesh.faces; if mesh.dim == 2: top_vertices = np.hstack((top_vertices, np.zeros((mesh.num_vertices, 1)))); bottom_vertices = np.copy(top_vertices); bottom_vertices[:,2] -= depth; bottom_faces = top_faces[:,[0,2,1]] + num_vertices; bd_edges = mesh.boundary_edges; bd_faces = np.array([ [e[1], e[0], e[0]+num_vertices, e[0]+num_vertices, e[1]+num_vertices, e[1] ] for e in bd_edges]).reshape((-1, 3), order="C"); vertices = np.vstack([top_vertices, bottom_vertices]); faces = np.vstack([top_faces, bottom_faces, bd_faces]); finish_time = time(); t = finish_time - start_time; logger.info("Extrusion running time: {}".format(t)); return pymesh.form_mesh(vertices, faces);
def _shape_similarity(self, element1, element2): """ Similarity function that compares the bounding boxes of <element1> and <element2> Inputs: element1 (ProjectObject) element2 (ProjectObject) Return: float - bounding box IoU (Equation 1 in SUMO white paper) """ # quick intersection test. If bounding boxes don't overlap on any single axis, # then the enclosed object cannot overlap bbox1 = element1.posed_bbox bbox2 = element2.posed_bbox for axis in range(3): if (bbox1.min_corner[axis] > bbox2.max_corner[axis]) or \ (bbox2.min_corner[axis] > bbox1.max_corner[axis]): return 0 box1 = _bbox2pymesh(element1) box2 = _bbox2pymesh(element2) inter = pymesh.boolean(box1, box2, operation='intersection') ivert, ifaces, _ = remove_duplicated_vertices_raw( inter.vertices, inter.faces) inter_mesh = pymesh.form_mesh(ivert, ifaces) # Note: pymesh may give -volume depending on surface normals # or maybe vertex ordering intersection = abs(inter_mesh.volume) union = abs(box1.volume) + abs(box2.volume) - intersection return intersection / union
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 read_room_obj(obj_fn, room_parts_dir): import pymesh #mesh0 = pymesh.load_mesh(obj_fn) mesh_parts = read_obj_parts(obj_fn) bboxs_roomparts = [part['bbox'] for part in mesh_parts] room_name = os.path.splitext(os.path.basename(obj_fn))[0] is_save_part = False if is_save_part: print(f'save room parts in:\n {room_parts_dir}') for i in range(len(mesh_parts)): part_i = mesh_parts[i] bbox_i = part_i['bbox'] part_bbox_fn = os.path.join( room_parts_dir, 'bbox_' + room_name + '_' + part_i['name'] + '.ply') Bbox3D.save_bbox_ply(part_bbox_fn, bbox_i, 'Y') for i in range(len(mesh_parts)): part_i = mesh_parts[i] mesh_i = pymesh.form_mesh(part_i['vertices'], part_i['face_vidx']) mesh_i.add_attribute('face_norm') mesh_i.set_attribute('face_norm', part_i['face_norms']) part_obj_fn = os.path.join( room_parts_dir, room_name + '_' + part_i['name'] + '.ply') pymesh.save_mesh(part_obj_fn, mesh_i, ascii=True, use_float=True) return bboxs_roomparts
def define_model(self, ): opts = self.opts self.img_size = opts.img_size self.model = icn_net.ICPNet(opts) self.load_network(self.model, 'pred', self.opts.num_train_epoch) self.model.cuda() self.upsample_img_size = ((opts.img_size // 64) * (2**6), (opts.img_size // 64) * (2**6)) self.camera_solver = geom_utils.CameraSolver(self.Tensor, self.device) self.offset_z = 5.0 self.uv2points = cub_parse.UVTo3D(self.mean_shape) self.model_obj = pymesh.form_mesh( self.mean_shape['verts'].data.cpu().numpy(), self.mean_shape['faces'].data.cpu().numpy()) self.model_obj_path = osp.join(self.opts.cachedir, 'cub', 'model', 'mean_bird.obj') self.grid = cub_parse.get_sample_grid(self.upsample_img_size).repeat( opts.batch_size, 1, 1, 1).to(self.device) self.init_render() # self.verts_obj = self.mean_shape['verts'] # faces_np = self.mean_shape['faces'].data.cpu().numpy() # verts_np = self.mean_shape['verts'].data.cpu().numpy() # self.vis_rend = bird_vis.VisRenderer(opts.img_size, faces_np) # uv_sampler = mesh.compute_uvsampler(verts_np, faces_np, tex_size=opts.tex_size) # uv_sampler = torch.from_numpy(uv_sampler).float().cuda() # self.uv_sampler = uv_sampler.view(-1, len(faces_np), opts.tex_size*opts.tex_size, 2) self.model.eval() # self.render_mean_bird_with_uv() return
def test_cube_refine(self): vertices = np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0]]) # A cube can be splitted into 5 tets or 6 tets without adding steiner # points. We start with 5 tets and see if tetgen can refine it to the 6 # tet configuration. tets = np.array([[0, 1, 2, 5], [0, 2, 3, 7], [4, 7, 5, 0], [5, 7, 6, 2], [0, 5, 2, 7]], dtype=int) mesh = pymesh.form_mesh(vertices, np.array([]), tets) mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertAlmostEqual(1.0, np.sum(volumes)) tetgen = pymesh.tetgen() tetgen.points = mesh.vertices tetgen.triangles = mesh.faces tetgen.tetrahedra = mesh.voxels tetgen.max_tet_volume = 0.17 tetgen.max_num_steiner_points = 0 tetgen.split_boundary = False tetgen.verbosity = 0 tetgen.run() mesh = tetgen.mesh mesh.add_attribute("voxel_volume") volumes = mesh.get_attribute("voxel_volume") self.assertAlmostEqual(1.0, np.sum(volumes)) # No tetgen cannot recover the 6 tet configuration... # So tet_volume constaint is actually not satisfied by the result. self.assertEqual(8, len(tetgen.vertices)) self.assertEqual(5, len(tetgen.voxels))
def extrude(mesh, depth, logger): start_time = time() num_vertices = mesh.num_vertices num_faces = mesh.num_faces top_vertices = mesh.vertices top_faces = mesh.faces if mesh.dim == 2: top_vertices = np.hstack((top_vertices, np.zeros( (mesh.num_vertices, 1)))) bottom_vertices = np.copy(top_vertices) bottom_vertices[:, 2] -= depth bottom_faces = top_faces[:, [0, 2, 1]] + num_vertices bd_edges = mesh.boundary_edges bd_faces = np.array([[ e[1], e[0], e[0] + num_vertices, e[0] + num_vertices, e[1] + num_vertices, e[1] ] for e in bd_edges]).reshape((-1, 3), order="C") vertices = np.vstack([top_vertices, bottom_vertices]) faces = np.vstack([top_faces, bottom_faces, bd_faces]) finish_time = time() t = finish_time - start_time logger.info("Extrusion running time: {}".format(t)) return pymesh.form_mesh(vertices, faces)
def __save_temp_mesh(self, active_view): basename, ext = os.path.splitext(self.image_name) path, name = os.path.split(basename) now = datetime.datetime.now() stamp = now.isoformat() tmp_dir = tempfile.gettempdir() ext = ".serialized" tmp_mesh_name = os.path.join(tmp_dir, "{}_{}{}".format(name, stamp, ext)) vertices = active_view.vertices faces = active_view.faces voxels = active_view.voxels dim = vertices.shape[1] num_faces, vertex_per_face = faces.shape vertices = vertices[faces.ravel(order="C")] colors = active_view.vertex_colors.reshape((-1, 4), order="C") faces = np.arange(len(vertices), dtype=int).reshape( (num_faces, vertex_per_face), order="C") mesh = pymesh.form_mesh(vertices, faces) if self.with_texture_coordinates: uvs = active_view.texture_coordinates else: uvs = None data = serialize_mesh(mesh, None, colors, uvs) with open(tmp_mesh_name, 'wb') as fout: fout.write(data) return tmp_mesh_name, ext
def get_one(self, path, subsamplemesh=False): if path in self.cache: return self.cache[path] else: try: h5_f = h5py.File(path) if (self.maxnverts != -1 and h5_f['verts'].shape[0] > self.maxnverts) or (self.maxntris != -1 and h5_f['tris'].shape[0] > self.maxntris): raise Exception() verts, tris = h5_f['verts'][:], h5_f['tris'][:] except: h5_f.close() self.cache[path] = None return None if self.normalize: centroid = None verts, _ = self.pc_normalize(verts, centroid) if subsamplemesh: mesh = pymesh.form_mesh(verts, tris) mesh, _ = pymesh.split_long_edges(mesh, 0.05) verts, tris = mesh.vertices, mesh.faces if (self.maxnverts != -1 and verts.shape[0] > self.maxnverts) or (self.maxntris != -1 and tris.shape[0] > self.maxntris): return None if len(self.cache) < self.cache_size: self.cache[path] = (verts, tris) h5_f.close() return verts, tris
def _shape_similarity(self, element1, element2): """ Similarity function that compares the bounding boxes of <element1> and <element2> Inputs: element1 (ProjectObject) element2 (ProjectObject) Return: float - bounding box IoU (Equation 1 in SUMO white paper) """ box1 = _bbox2pymesh(element1) box2 = _bbox2pymesh(element2) inter = pymesh.boolean(box1, box2, operation='intersection', engine='cgal') ivert, ifaces, _ = remove_duplicated_vertices_raw( inter.vertices, inter.faces) inter_mesh = pymesh.form_mesh(ivert, ifaces) # Note: pymesh may give -volume depending on surface normals # or maybe vertex ordering intersection = abs(inter_mesh.volume) union = abs(box1.volume) + abs(box2.volume) - intersection return intersection / union
def tilt_obliq(mesh, obliquity, phi, phi_prec): f = mesh.faces v = np.copy(mesh.vertices) nv = len(v) # precession angle is phi_prec axist = np.array([np.cos(phi_prec), np.sin(phi_prec), 0]) qt = pymesh.Quaternion.fromAxisAngle(axist, obliquity) zaxis = np.array([0, 0, 1]) zrot = qt.rotate(zaxis) # body principal axis will become zrot # spin rotation about now tilted principal body axis qs = pymesh.Quaternion.fromAxisAngle(zrot, phi) # loop over all vertices and do two rotations for i in range(nv): v[i] = qt.rotate(v[i]) # tilt it over v[i] = qs.rotate(v[i]) # spin new_mesh = pymesh.form_mesh(v, f) new_mesh.add_attribute("face_area") new_mesh.add_attribute("face_normal") new_mesh.add_attribute("face_centroid") return new_mesh, zrot
def triangulate(wires, engine, stage, eps, logger, wire_file, json_file): if wires.num_vertices == 0: return pymesh.form_mesh(np.zeros((0, 2)), np.zeros((0, 3))) basename = os.path.splitext(wire_file)[0] if engine == "triwild": out_mesh = "{}_linear.msh".format(basename) log_file = "{}_triwild.log".format(basename) if json_file is not None: command = "TriWild --mute-log --feature-envelope-r {} --stage {} --log-file {} --feature-input {} --output-linear-mesh --skip-eps --input {} --output {}".format( eps, stage, log_file, json_file, wire_file, basename) else: command = "TriWild --mute-log --feature-envelope-r {} --stage {} --log-file {} --output-linear-mesh --skip-eps --input {} --output {}".format( eps, stage, log_file, wire_file, basename) print(command) start_time = time() check_call(command.split()) finish_time = time() t = finish_time - start_time mesh = pymesh.load_mesh(out_mesh, drop_zero_dim=True) else: mesh, t = pymesh.triangulate_beta(wires.vertices, wires.edges, engine=engine, with_timing=True) logger.info("Triangulation running time: {}".format(t)) return mesh
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); if mesh.vertex_per_face == 4: logging.warning("Converting quad mesh to triangle mesh."); mesh = pymesh.quad_to_tri(mesh); if args.exact: name,ext = os.path.splitext(args.output_mesh); exact_mesh_file = name + ".xml"; else: exact_mesh_file = None; if mesh.num_vertices ==0 or mesh.num_faces == 0: # Empty input mesh, output empty mesh as well. result = pymesh.form_mesh(np.zeros((0,3),dtype=float), np.zeros((0,3),dtype=int)); if args.timing: update_info(args.output_mesh, 0); else: if args.engine == "igl": empty = pymesh.form_mesh(np.zeros((0,3)), np.zeros((0,3))); r = pymesh.boolean( mesh, empty, "union", engine=args.engine, with_timing = args.timing, exact_mesh_file=exact_mesh_file); else: # Empty mesh is valid for these libraries, using bbox instead. bbox = mesh.bbox; center = (bbox[0] + bbox[1]) * 0.5; box = pymesh.generate_box_mesh( bbox[0] - np.ones(mesh.dim), bbox[1] + np.ones(mesh.dim)); r = pymesh.boolean( mesh, box, "intersection", engine=args.engine, with_timing = args.timing, exact_mesh_file=exact_mesh_file); if args.timing: result, timing = r; update_info(args.output_mesh, timing); else: result = r; pymesh.save_mesh(args.output_mesh, result);
def fit_into_unit_sphere(mesh): bbox_min, bbox_max = mesh.bbox; bbox_radius = numpy.linalg.norm(bbox_max - bbox_min) * 0.5; bbox_center = 0.5 * (bbox_min + bbox_max); if bbox_radius == 0.0: raise IOError("Input mesh is degenerate to a single point."); vertices = mesh.vertices; tris = mesh.faces; tets = mesh.voxels; vertices = (vertices - bbox_center) / bbox_radius; return pymesh.form_mesh(vertices, tris, tets);
def single_triangle_2D_setup(self): vertices = np.array([ [0.0, 0.0], [1.0, 0.0], [0.0, 1.0], ]); faces = np.array([ [0, 1, 2] ]); mesh = form_mesh(vertices, faces); material = Material.create_isotropic(2, 1.0, 1.0, 0.0); assembler = Assembler(mesh, material); return assembler;
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 test_face_to_voxel(self): vertices = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ]); voxels = np.array([[0,1,2,3]],dtype=int); mesh = form_mesh(vertices, np.zeros((0, 3)), voxels); attr = np.ones(mesh.num_faces); attr2 = convert_to_voxel_attribute(mesh, attr).ravel(); self.assert_array_equal([1], attr2);
def extract_intersecting_faces(mesh, selection): face_pairs = pymesh.detect_self_intersection(mesh); selected_faces = np.zeros(mesh.num_faces, dtype=bool); if selection is not None: selected_pairs = np.any(face_pairs == selection, axis=1); face_pairs = face_pairs[selected_pairs]; selected_faces[face_pairs[:,0]] = True; selected_faces[face_pairs[:,1]] = True; faces = mesh.faces[selected_faces]; intersecting_mesh = pymesh.form_mesh(mesh.vertices, faces); intersecting_mesh, __ = pymesh.remove_isolated_vertices(intersecting_mesh); return intersecting_mesh;
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_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 test_2D_meshes(self): vertices = np.array([ [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0] ], dtype=float); faces = np.array([ [0, 1, 2], [0, 2, 3] ], dtype=int); mesh_1 = form_mesh(vertices, faces); out_mesh = merge_meshes([mesh_1] * 3); self.assertEqual(mesh_1.num_vertices * 3, out_mesh.num_vertices); self.assertEqual(mesh_1.num_faces * 3, out_mesh.num_faces); self.assertEqual(mesh_1.num_voxels * 3, out_mesh.num_voxels);
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 tutte_3D(mesh, bd_vertex_indices, bd_vertex_positions): assembler = pymesh.Assembler(mesh); L = assembler.assemble("laplacian"); is_constraint = np.zeros(mesh.num_vertices, dtype=bool); is_constraint[bd_vertex_indices] = True; is_variable = np.logical_not(is_constraint); M, rhs = init_harmonic_system(L, bd_vertex_indices, bd_vertex_positions, is_variable); x = solve(M, rhs, "SparseLU", 1e-6); out_vertices = np.zeros((mesh.num_vertices, 3)); out_vertices[is_constraint,:] = bd_vertex_positions; out_vertices[is_variable,:] = x; return pymesh.form_mesh(out_vertices, np.zeros((0, 3)), mesh.voxels);
def test_square(self): vertices = np.array([ [ 0.0, 0.0, 0.0], [ 1.0, 0.0, 0.0], [ 0.0, 1.0, 0.0], [ 1.0, 1.0, 0.0], ], dtype=float); faces = np.array([ [0, 1, 2], [2, 1, 3], ], dtype=int); mesh = form_mesh(vertices, faces); cutted_mesh = cut_mesh(mesh, [0, 1]); self.assertEqual(6, cutted_mesh.num_vertices); self.assertEqual(2, cutted_mesh.num_components);
def test_anonymous(self): vertices = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], ]); faces = np.array([[0, 1, 2]]); mesh = form_mesh(vertices, faces); for ext in [".msh", ".obj", ".ply", ".mesh", ".off"]: mesh1 = self.write_and_load(mesh, "anonymous_test{}".format(ext), use_ascii=True, anonymous=True); self.assert_mesh_equal(mesh, mesh1); mesh2 = self.write_and_load(mesh, "anonymous_test{}".format(ext), use_ascii=False, anonymous=True); self.assert_mesh_equal(mesh, mesh2);
def test_refine(self): vertices = np.array([ [0, 0, 0], [1, 0, 1], [1, 1, 1], [0, 1, 0] ], dtype=float); faces = np.array([ [0, 1, 2], [0, 2, 3] ]); in_mesh = pymesh.form_mesh(vertices, faces); out_mesh = retriangulate(in_mesh, 0.1); self.assertLess(in_mesh.num_vertices, out_mesh.num_vertices); self.assertLess(in_mesh.num_faces, out_mesh.num_faces); in_mesh.add_attribute("face_area"); in_areas = in_mesh.get_attribute("face_area"); out_mesh.add_attribute("face_area"); out_areas = out_mesh.get_attribute("face_area"); self.assertAlmostEqual(np.sum(in_areas), np.sum(out_areas));
def test_cube_refine(self): vertices = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0] ]); # A cube can be splitted into 5 tets or 6 tets without adding steiner # points. We start with 5 tets and see if tetgen can refine it to the 6 # tet configuration. tets = np.array([ [0, 1, 2, 5], [0, 2, 3, 7], [4, 7, 5, 0], [5, 7, 6, 2], [0, 5, 2, 7] ], dtype=int); mesh = pymesh.form_mesh(vertices, np.array([]), tets); mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertAlmostEqual(1.0, np.sum(volumes)); tetgen = pymesh.tetgen(); tetgen.points = mesh.vertices; tetgen.triangles = mesh.faces; tetgen.tetrahedra = mesh.voxels; tetgen.max_tet_volume = 0.17; tetgen.max_num_steiner_points = 0; tetgen.split_boundary = False; tetgen.verbosity = 0; tetgen.run(); mesh = tetgen.mesh; mesh.add_attribute("voxel_volume"); volumes = mesh.get_attribute("voxel_volume"); self.assertAlmostEqual(1.0, np.sum(volumes)); # No tetgen cannot recover the 6 tet configuration... # So tet_volume constaint is actually not satisfied by the result. self.assertEqual(8, len(tetgen.vertices)); self.assertEqual(5, len(tetgen.voxels));
def repousse_all(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; out_mesh, info = pymesh.remove_degenerated_triangles(mesh, 100); cell_ids = cell_ids[info["ori_face_indices"]].ravel(); mesh, info = pymesh.split_long_edges(out_mesh, tol); cell_ids = cell_ids[info["ori_face_indices"]].ravel(); mesh.enable_connectivity(); is_border = [len(np.unique(cell_ids[mesh.get_vertex_adjacent_faces(vi)])) > 1 for vi in range(mesh.num_vertices)]; start_time = time(); dof = mesh.num_vertices; assembler = pymesh.Assembler(mesh); L = assembler.assemble("laplacian"); M = assembler.assemble("mass"); L_rhs = M * np.ones(dof) * -1 * 1e-1; bd_indices = np.arange(mesh.num_vertices, dtype=int)[is_border]; 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((mesh.vertices, z)); finish_time = time(); t = finish_time - start_time; logger.info("Repousse running time: {}".format(t)); return pymesh.form_mesh(vertices, mesh.faces);
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); if args.initial_block is not None: block = pymesh.load_mesh(args.initial_block); else: bbox_min, bbox_max = mesh.bbox; block = pymesh.generate_box_mesh(bbox_min, bbox_max, 2, keep_symmetry=True); block = pymesh.form_mesh(block.vertices, block.faces); block, __ = pymesh.remove_isolated_vertices(block); carved = carve_mesh(mesh, block, args.N, args.batch_size, args.output_mesh, args.initial_N, args.save_intermediate, args.debug); pymesh.save_mesh(args.output_mesh, carved);
def test_face_connectivity(self): vertices = np.array([ [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 1], ], dtype=float); faces = np.array([ [0, 1, 2], [2, 3, 4], ]); mesh = form_mesh(vertices, faces); components = separate_mesh(mesh, "vertex"); self.assertEqual(1, len(components)); components = separate_mesh(mesh, "face"); self.assertEqual(2, len(components));
def test_tet_connectivity(self): vertices = np.array([ [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 0, 1] ]); voxels = np.array([ [0, 2, 1, 4], [1, 2, 3, 4] ]); mesh = pymesh.form_mesh(vertices, np.array([]), voxels); mesh.enable_connectivity(); self.assertEqual(5, mesh.num_vertices); self.assertEqual(6, mesh.num_faces); self.assertEqual(2, mesh.num_voxels); self.assert_array_equal([1], mesh.get_voxel_adjacent_voxels(0)); self.assert_array_equal([0], mesh.get_voxel_adjacent_voxels(1));
def test_3D_area_attribute(self): vertices = np.array([ [0.0, 0.0, 0.1], [1.0, 0.0, 0.1], [0.0, 1.0, 0.1] ]); faces = np.array([ [0, 1, 2], [0, 0, 1], [1, 0, 2] ]); mesh = pymesh.form_mesh(vertices, faces); mesh.add_attribute("face_area"); self.assertTrue(mesh.has_attribute("face_area")); areas = mesh.get_face_attribute("face_area").ravel(); self.assertEqual(3, len(areas)); self.assertAlmostEqual(0.5, areas[0]); self.assertAlmostEqual(0, areas[1]); self.assertAlmostEqual(0.5, areas[2]);
def extract_submesh(mesh, face_indices, n_ring): vertices = mesh.vertices; selected_faces = np.zeros(mesh.num_faces, dtype=bool); selected_faces[face_indices] = True; ring_index = np.zeros(mesh.num_faces, dtype=int); ring_index[selected_faces] += 1; for i in range(n_ring): selected_faces = expand_by_one_ring(mesh, selected_faces); ring_index[selected_faces] += 1; selected_face_indices = np.arange(mesh.num_faces)[selected_faces]; faces = mesh.faces[selected_faces]; ring_index = (ring_index[selected_faces] - n_ring - 1) * (-1); vertices, faces, __ = pymesh.remove_isolated_vertices_raw(vertices, faces); mesh = pymesh.form_mesh(vertices, faces); mesh.add_attribute("ori_face_index"); mesh.set_attribute("ori_face_index", selected_face_indices); mesh.add_attribute("ring"); mesh.set_attribute("ring", ring_index); return mesh;
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.input_mesh); pymesh.is_vertex_manifold(mesh); pymesh.is_edge_manifold(mesh); faces = mesh.faces; edge_manifold = mesh.get_attribute("edge_manifold").reshape((-3, mesh.vertex_per_face)); is_edge_nonmanifold = np.any(edge_manifold==0, axis=1); involved_faces = faces[is_edge_nonmanifold]; mesh.add_attribute("faces_adj_nonmanifold_edge"); mesh.set_attribute("faces_adj_nonmanifold_edge", is_edge_nonmanifold); print("Faces adjacent to nonmanifold edges"); print("Indices: {}".format(np.arange(mesh.num_faces)[is_edge_nonmanifold])); print(involved_faces); pymesh.save_matrix("nonmanifold_edges.dmat", involved_faces); vertex_manifold = mesh.get_attribute("vertex_manifold").ravel(); is_vertex_nonmanifold = np.any(vertex_manifold[faces] == 0, axis=1); involved_faces = faces[is_vertex_nonmanifold]; mesh.add_attribute("faces_adj_nonmanifold_vertex"); mesh.set_attribute("faces_adj_nonmanifold_vertex", is_vertex_nonmanifold); print("Faces adjacent to nonmanifold vertices"); print("Indices: {}".format(np.arange(mesh.num_faces)[is_vertex_nonmanifold])); print(involved_faces); pymesh.save_matrix("nonmanifold_vertices.dmat", involved_faces); pymesh.save_mesh(args.output_mesh, mesh, *mesh.attribute_names); if args.debug_output is not None: debug_faces = faces[is_edge_nonmanifold]; debug_mesh = pymesh.form_mesh(mesh.vertices, debug_faces); pymesh.save_mesh(args.debug_output, debug_mesh);
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 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;