예제 #1
0
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))
예제 #2
0
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;
예제 #3
0
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)
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #9
0
파일: repousse.py 프로젝트: qnzhou/PyMesh
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;
예제 #10
0
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
예제 #11
0
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")
예제 #13
0
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)
예제 #14
0
파일: carve.py 프로젝트: qnzhou/PyMesh
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;
예제 #15
0
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
예제 #16
0
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)
예제 #19
0
파일: mesh.py 프로젝트: junzhezhang/ucmr
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)
예제 #20
0
    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
예제 #21
0
    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;
예제 #22
0
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)
예제 #24
0
파일: repousse.py 프로젝트: qnzhou/PyMesh
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);
예제 #25
0
    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
예제 #26
0
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
예제 #28
0
    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
예제 #29
0
    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))
예제 #30
0
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)
예제 #31
0
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;
예제 #32
0
    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
예제 #33
0
    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
예제 #34
0
    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
예제 #35
0
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
예제 #36
0
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
예제 #37
0
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);
예제 #38
0
파일: fem_check.py 프로젝트: qnzhou/PyMesh
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);
예제 #39
0
 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;
예제 #40
0
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);
예제 #41
0
    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;
예제 #43
0
파일: remove_nan.py 프로젝트: qnzhou/PyMesh
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);
예제 #44
0
파일: mesh_diff.py 프로젝트: qnzhou/PyMesh
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();
예제 #45
0
    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);
예제 #46
0
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");
예제 #47
0
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);
예제 #48
0
    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);
예제 #49
0
    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);
예제 #50
0
 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));
예제 #51
0
    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));
예제 #52
0
파일: repousse.py 프로젝트: qnzhou/PyMesh
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);
예제 #53
0
파일: carve.py 프로젝트: qnzhou/PyMesh
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);
예제 #54
0
    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));
예제 #55
0
파일: test_mesh.py 프로젝트: qnzhou/PyMesh
    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));
예제 #56
0
파일: test_mesh.py 프로젝트: qnzhou/PyMesh
    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]);
예제 #57
0
파일: submesh.py 프로젝트: gaoyue17/PyMesh
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;
예제 #58
0
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);
예제 #59
0
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
예제 #60
0
파일: repousse.py 프로젝트: qnzhou/PyMesh
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;