def clean_up_mesh(mesh, tol):
    new_mesh, _ = pymesh.remove_isolated_vertices(mesh)
    new_mesh, _ = pymesh.remove_duplicated_vertices(new_mesh)
    new_mesh, _ = pymesh.remove_duplicated_faces(new_mesh)
    new_mesh, _ = pymesh.remove_degenerated_triangles(new_mesh)
    new_mesh, _ = pymesh.collapse_short_edges(new_mesh, rel_threshold=0.2)
    return new_mesh
Example #2
0
def clean_mesh(mesh, connected=True, fill_internals=False):
    print('\t - Cleaning mesh (this may take a moment)')
    vert_list = []
    mesh, info = pymesh.remove_isolated_vertices(mesh)
    mesh, info = pymesh.remove_duplicated_vertices(mesh)
    mesh, info = pymesh.remove_degenerated_triangles(mesh)
    mesh, info = pymesh.remove_duplicated_faces(mesh)

    if connected or fill_internals:
        mesh_list = pymesh.separate_mesh(mesh, 'auto')
        max_verts = 0
        print(' - Total number of meshes (ideally 1): %d' % len(mesh_list))
        for mesh_obj in mesh_list:
            nverts = mesh_obj.num_vertices
            if nverts > max_verts:
                max_verts = nverts
                mesh = mesh_obj

        if fill_internals:
            for mesh_obj in mesh_list:
                if mesh_obj.num_vertices != max_verts:
                    vert_list.append(mesh_obj.vertices)

            return mesh, vert_list

    return mesh, vert_list
Example #3
0
def fix_mesh(mesh, resolution):
    bbox_min, bbox_max = mesh.bbox
    diag_len = norm(bbox_max - bbox_min)

    target_len = diag_len * resolution

    rospy.loginfo("\tTarget resolution: {} mm".format(target_len))

    count = 0
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices
    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6)
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100)
        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        rospy.loginfo("\t#vertices: {}".format(num_vertices))
        count += 1
        if count > 2: break

    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    return mesh
Example #4
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;
def downsample_mesh(invert, target_size, in_file, out_file):

    min_file_size = target_size * .9
    max_file_size = target_size * 1.1

    file_size = os.path.getsize(in_file)
    mesh = pymesh.meshio.load_mesh(in_file)

    temp_file = "/tmp/temp.stl"
    segment_len = .40
    last_size = None
    increment = .1
    if file_size > max_file_size:
        print("initial size: %sKb, target size: %sKb" %
              (f'{file_size // 1024:,}', f'{target_size // 1024:,}'))
        while True:
            mesh = fix_mesh(mesh, segment_len)
            pymesh.meshio.save_mesh(temp_file, mesh)
            new_size = os.path.getsize(temp_file)
            print("size: %sKb" % (f'{new_size // 1024:,}'))

            if new_size <= max_file_size and new_size >= min_file_size:
                break

            done = False
            if last_size:
                if new_size > max_file_size and last_size < min_file_size:
                    increment /= 2.0
                    segment_len += increment
                    done = True
                elif new_size < min_file_size and last_size > max_file_size:
                    increment /= 2.0
                    segment_len -= increment
                    done = True

            if not done:
                diff = abs(target_size - new_size)
                if diff > (target_size * 3):
                    adjust = 3
                elif diff > (target_size * 2):
                    adjust = 2
                else:
                    adjust = 1
                if new_size > target_size:
                    segment_len += (adjust * increment)
                else:
                    segment_len -= (adjust * increment)

            mesh = pymesh.meshio.load_mesh(in_file)
            last_size = new_size

    try:
        os.unlink(temp_file)
    except IOError:
        pass

    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh = center_around_origin(mesh)
    pymesh.meshio.save_mesh(out_file, mesh)
    def test_no_degeneracy(self):
        mesh = generate_box_mesh(np.ones(3)*-1, np.ones(3));

        result, info = remove_degenerated_triangles(mesh);
        self.assertEqual(8, result.num_vertices);
        self.assertEqual(12, result.num_faces);
        self.assert_array_equal(np.arange(12),
                sorted(info["ori_face_indices"]));
Example #7
0
def fix_mesh(mesh, detail=5e-3):
    # "normal": 5e-3
    # "high":   2.5e-3
    # "low":    2e-2
    # "vlow":   2.5e-2
    bbox_min, bbox_max = mesh.bbox
    diag_len = np.linalg.norm(bbox_max - bbox_min)
    if detail is None:
        detail = 5e-3
    target_len = diag_len * detail
    print("Target resolution: {} mm".format(target_len))

    count = 0
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices
    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-4)
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)

        mesh, __ = pymesh.remove_isolated_vertices(mesh)
        mesh, __ = pymesh.remove_duplicated_vertices(mesh, tol=1e-4)
        mesh, __ = pymesh.remove_duplicated_faces(mesh)
        mesh, __ = pymesh.remove_degenerated_triangles(mesh)
        mesh, __ = pymesh.remove_isolated_vertices(mesh)

        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100)
        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        print("fix #v: {}".format(num_vertices))
        count += 1
        if count > 10:
            break

    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    return mesh
    def test_no_degeneracy(self):
        mesh = generate_box_mesh(np.ones(3) * -1, np.ones(3))

        result, info = remove_degenerated_triangles(mesh)
        self.assertEqual(8, result.num_vertices)
        self.assertEqual(12, result.num_faces)
        self.assert_array_equal(np.arange(12),
                                sorted(info["ori_face_indices"]))
Example #9
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
Example #10
0
def fix_meshes(mesh, detail="normal"):
    meshCopy = mesh

    # copy/pasta of pymesh script fix_mesh from qnzhou, see pymesh on GitHub
    bbox_min, bbox_max = mesh.bbox
    diag_len = np.linalg.norm(bbox_max - bbox_min)
    if detail == "normal":
        target_len = diag_len * 5e-3
    elif detail == "high":
        target_len = diag_len * 2.5e-3
    elif detail == "low":
        target_len = diag_len * 1e-2

    count = 0
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices
    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6)
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100)
        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        count += 1
        if count > 10:
            break

    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    if is_mesh_broken(mesh, meshCopy) is True:
        if detail == "high":
            print(
                f'The function fix_meshes broke mesh, trying with lower details settings'
            )
            fix_meshes(mesh, detail="normal")
        if detail == "normal":
            print(
                f'The function fix_meshes broke mesh, trying with lower details settings'
            )
            fix_meshes(mesh, detail="low")
        if detail == "low":
            print(
                f'The function fix_meshes broke mesh, no lower settings can be applied, no fix was done'
            )
            return meshCopy
    else:
        return mesh
Example #11
0
def clean(vertices, faces, duplicate_tol=1e-12):
    mesh = pymesh.meshio.form_mesh(vertices, faces)
    mesh = pymesh.remove_isolated_vertices(mesh)[0]
    mesh = pymesh.remove_duplicated_vertices(mesh, tol=duplicate_tol)[0]
    mesh = remove_duplicated_faces(mesh)[0]
    # mesh = pymesh.remove_duplicated_faces(mesh, fins_only=True)[0]
    mesh = pymesh.remove_degenerated_triangles(mesh)[0]
    mesh = pymesh.resolve_self_intersection(mesh)
    # meshes = pymesh.separate_mesh(mesh)
    # for i, mesh in enumerate(meshes):
    #     meshes[i] = make_face_normals_consistent(mesh)
    # mesh = pymesh.merge_meshes(meshes)
    return mesh.vertices, mesh.faces
Example #12
0
def remesh(path1):
    """
    This function takes a path to the orginal shapenet model and subsample it nicely
    """
    obj1 = pymesh.load_mesh(path1)
    obj1, info = pymesh.remove_isolated_vertices(obj1)
    print("Removed {} isolated vertices".format(info["num_vertex_removed"]))
    obj1, info = pymesh.remove_duplicated_vertices(obj1)
    print("Merged {} duplicated vertices".format(info["num_vertex_merged"]))
    obj1, _ = pymesh.remove_degenerated_triangles(obj1)
    if len(obj1.vertices) < 5000:
        while len(obj1.vertices) < 5000:
            obj1 = pymesh.subdivide(obj1)
    obj1 = pymesh.form_mesh(obj1.vertices, obj1.faces)
    return obj1
def link(path1):
    """
	This function takes a path to the orginal shapenet model and subsample it nicely
	"""
    obj1 = pymesh.load_mesh(path1)
    # obj1, info = pymesh.remove_isolated_vertices(mesh)
    obj1, info = pymesh.remove_isolated_vertices(obj1)
    print("Removed {} isolated vertices".format(info["num_vertex_removed"]))
    obj1, info = pymesh.remove_duplicated_vertices(obj1)
    print("Merged {} duplicated vertices".format(info["num_vertex_merged"]))
    obj1, info = pymesh.remove_degenerated_triangles(obj1)
    new_mesh = pymesh.form_mesh(
        normalize_points.BoundingBox(torch.from_numpy(obj1.vertices)).numpy(),
        obj1.faces)
    return new_mesh
Example #14
0
def gre_remesh(mesh, detail="normal"):
    bbox_min, bbox_max = mesh.bbox
    diag_len = norm(bbox_max - bbox_min)
    if detail == "normal":
        target_len = diag_len * 5e-3
    elif detail == "high":
        target_len = diag_len * 2.5e-3
    elif detail == "low":
        target_len = diag_len * 1e-2
    print("Target resolution: {} mm".format(target_len))

    # -- pseudocódigo --
    # while L is not empty
    #     pop one bad facet f from L
    #     cf = dual(f) ∩ S
    #     insert cf to P
    #     update Del(P)
    #     update DelS (P)
    #     update L, i.e.,
    #       remove facets of L that are no longer facets of DelS (P)
    #       add new bad facets of DelS (P) to L

    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    num_vertices = mesh.num_vertices

    p = mesh.vertices  #conjunto P de pontos da malha
    del_p, dels_p, bad_facets = delaunay_triang(p)  # triagulação de Delauney
    l = bad_facets  #lista L de faceta ruins de Dels(P)

    while True:
        if len(l) == 0: break
        f = l.pop()
        cf = intersection(dual(f), mesh)
        p.append(cf)
        del_p, dels_p, bad_facets = delaunay_triang(p)
        l = bad_facets

        num_vertices = mesh.num_vertices
        print("#v: {}".format(num_vertices))

    # mesh = pymesh.resolve_self_intersection(mesh)
    # mesh, __ = pymesh.remove_duplicated_faces(mesh)
    # mesh = pymesh.compute_outer_hull(mesh)
    # mesh, __ = pymesh.remove_duplicated_faces(mesh)
    # mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    # mesh, __ = pymesh.remove_isolated_vertices(mesh)

    return mesh
Example #15
0
def fix_mesh(mesh):
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    log_mesh(mesh, "Remove degenerate faces")
    mesh, __ = pymesh.collapse_short_edges(mesh, MIN_RES, preserve_feature=True)
    log_mesh(mesh, "Collapse short edges")
    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    log_mesh(mesh, "Remove self intersections")
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    log_mesh(mesh, "New hull, remove duplicates")
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.5, 5)
    log_mesh(mesh, "Remote obtuse faces")
    mesh, __ = pymesh.remove_isolated_vertices(mesh)
    log_mesh(mesh, "Remove isolated vertices")
    return mesh
Example #16
0
def read_trimesh(path, normal=False, clean=True):
    mesh = pymesh.load_mesh(path)
    if clean:
        mesh, info = pymesh.remove_isolated_vertices(mesh)
        print("Removed {} isolated vertices".format(info["num_vertex_removed"]))
        mesh, info = pymesh.remove_duplicated_vertices(mesh)
        print("Merged {} duplicated vertices".format(info["num_vertex_merged"]))
        mesh, info = pymesh.remove_degenerated_triangles(mesh)
        mesh = pymesh.form_mesh(mesh.vertices, mesh.faces)

    vertices = mesh.vertices
    if normal:
        mesh.add_attribute("vertex_normal")
        vertex_normals = mesh.get_attribute("vertex_normal").reshape(-1, 3)
        vertices = np.concatenate([vertices, vertex_normals], axis=-1)
    return vertices, mesh.faces
    def execute(self, context):
        scene = context.scene
        pymesh_props = scene.pymesh

        obj_a = context.active_object
        mesh_a = import_object(context, obj_a)
        pymesh_r, info = pymesh.remove_degenerated_triangles(mesh_a)
        pymesh_r, info = pymesh.remove_obtuse_triangles(pymesh_r)
        pymesh_r, info = pymesh.remove_duplicated_faces(pymesh_r)
        pymesh_r, info = pymesh.collapse_short_edges(pymesh_r)
        pymesh_r, info = pymesh.remove_duplicated_vertices(pymesh_r)
        pymesh_r, info = pymesh.remove_isolated_vertices(pymesh_r)
        off_name = "Py.Clean." + obj_a.name
        mesh_r = export_mesh(context, pymesh_r, off_name)
        add_to_scene(context, mesh_r)

        return {'FINISHED'}
Example #18
0
def fix_mesh(mesh, detail="normal"):

    bbox_min, bbox_max = mesh.bbox
    diag_len = norm(bbox_max - bbox_min)

    if detail == "normal":
        target_len = diag_len * 1e-2

    elif detail == "high":
        target_len = diag_len * 5e-3

    elif detail == "low":
        target_len = diag_len * 0.03

    print("Target resolution: {} mm".format(target_len))

    count = 0
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices

    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6)
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100)

        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        print("#v: {}".format(num_vertices))
        count += 1
        if count > 10:
            break

    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    return mesh
def scale_mesh(invert, len, in_file, out_file):

    mesh = pymesh.meshio.load_mesh(in_file)

    mesh = fix_mesh(mesh, len)
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh = center_around_origin(mesh)

    if mesh.num_vertices == 0 or mesh.num_faces == 0:
        printf("ERROR: segment len is too small, no vertices/faces are left.")
        return

    if invert:
        mesh = flip_mesh(mesh)
        print(" flip: %d vertexes, %d faces." %
              (mesh.num_vertices, mesh.num_faces))

    pymesh.meshio.save_mesh(out_file, mesh)
def fix_mesh(mesh, detail="normal"):
    bbox_min, bbox_max = mesh.bbox;
    diag_len = norm(bbox_max - bbox_min);
    if detail == "normal":
        target_len = diag_len * 1e-2;
        #target_len = diag_len * 5e-3;
    elif detail == "enormal":
        target_len = diag_len * 5e-3
    elif detail == "high":
        target_len = diag_len * 3e-3
        #target_len = diag_len * 2.5e-3;
    elif detail == "low":
        target_len = diag_len * 1e-2;
    elif detail == "ehigh":
        target_len = diag_len * 1e-3;
    print("Target resolution: {} mm".format(target_len));

    count = 0;
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100);
    mesh, __ = pymesh.split_long_edges(mesh, target_len);
    num_vertices = mesh.num_vertices;
    while True:
        #mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6);
        if detail == "low":
            mesh, __ = pymesh.collapse_short_edges(mesh, target_len, preserve_feature=False);
        else:
            mesh, __ = pymesh.collapse_short_edges(mesh, target_len, preserve_feature=True);
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100);
        if mesh.num_vertices == num_vertices:
            break;

        num_vertices = mesh.num_vertices;
        print("#v: {}".format(num_vertices));
        count += 1;
        if count > 10: break;

    mesh = pymesh.resolve_self_intersection(mesh);
    mesh, __ = pymesh.remove_duplicated_faces(mesh);
    mesh = pymesh.compute_outer_hull(mesh);
    mesh, __ = pymesh.remove_duplicated_faces(mesh);
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5);
    mesh, __ = pymesh.remove_isolated_vertices(mesh);

    return mesh;
Example #21
0
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);
Example #22
0
def fix_mesh(mesh, target_len):
    bbox_min, bbox_max = mesh.bbox
    diag_len = np.linalg.norm(bbox_max - bbox_min)

    count = 0
    print("  remove degenerated triangles")
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    print("  split long edges")
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices
    while True:
        print("  pass %d" % count)
        print("    collapse short edges #1")
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6)
        print("    collapse short edges #2")
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)
        print("    remove obtuse triangles")
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100)
        print("    %d of %s vertices." % (num_vertices, mesh.num_vertices))

        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        count += 1
        if count > 10: break

    print("  resolve self intersection")
    mesh = pymesh.resolve_self_intersection(mesh)
    print("  remove duplicated faces")
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    print("  computer outer hull")
    mesh = pymesh.compute_outer_hull(mesh)
    print("  remove duplicated faces")
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    print("  remove obtuse triangles")
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    print("  remove isolated vertices")
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    return mesh
Example #23
0
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)
Example #24
0
    def filter(self):
        mesh = pymesh.form_mesh(self.mesh.points.values,
                                self.mesh.cells.values)

        mesh, _ = pymesh.remove_degenerated_triangles(mesh,
                                                      self.max_iterations)
        mesh, _ = pymesh.split_long_edges(mesh, self.size)
        num_vertices = mesh.num_vertices
        for _ in range(self.max_iterations):
            mesh, _ = pymesh.collapse_short_edges(mesh,
                                                  self.size,
                                                  preserve_feature=True)
            mesh, _ = pymesh.remove_obtuse_triangles(mesh, self.max_angle,
                                                     self.max_iterations)

            if mesh.num_vertices == num_vertices:
                break

            num_vertices = mesh.num_vertices

        return self.mesh.mesh_class()(mesh, parents=[self.mesh])
Example #25
0
def fix_mesh(mesh, detail="normal"):
    bbox_min, bbox_max = mesh.bbox;
    diag_len = norm(bbox_max - bbox_min);
    if detail == "normal":
        target_len = diag_len * 5e-3;
    elif detail == "high":
        target_len = diag_len * 2.5e-3;
    elif detail == "low":
        target_len = diag_len * 1e-2;
    print("Target resolution: {} mm".format(target_len));

    count = 0;
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100);
    mesh, __ = pymesh.split_long_edges(mesh, target_len);
    num_vertices = mesh.num_vertices;
    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6);
        mesh, __ = pymesh.collapse_short_edges(mesh, target_len,
                preserve_feature=True);
        mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100);
        if mesh.num_vertices == num_vertices:
            break;

        num_vertices = mesh.num_vertices;
        print("#v: {}".format(num_vertices));
        count += 1;
        if count > 10: break;

    mesh = pymesh.resolve_self_intersection(mesh);
    mesh, __ = pymesh.remove_duplicated_faces(mesh);
    mesh = pymesh.compute_outer_hull(mesh);
    mesh, __ = pymesh.remove_duplicated_faces(mesh);
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5);
    mesh, __ = pymesh.remove_isolated_vertices(mesh);

    return mesh;
Example #26
0
def main():
    args = parse_args()
    mesh = pymesh.load_mesh(args.input_mesh)
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, args.num_iterations)
    pymesh.save_mesh(args.output_mesh, mesh)
Example #27
0
def regularise_mesh(mesh, tol):
    """Takes mesh & resizes triangles to tol size"""
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 100)
    mesh, _info = pymesh.split_long_edges(mesh, tol)
    mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6)
    mesh, _info = pymesh.collapse_short_edges(mesh, tol, preserve_feature=True)
Example #28
0
def fix_meshes(mesh, detail="normal"):
    """
    A pipeline to optimise and fix mesh based on pymesh Mesh object.

    1. A box is created around the mesh.
    2. A target length is found based on diagonal of the mesh box.
    3. You can choose between 3 level of details, normal details settings seems to be a good compromise between
    final mesh size and sufficient number of vertices. It highly depends on your final goal.
    4. Remove degenerated triangles aka collinear triangles composed of 3 aligned points. The number of iterations is 5
    and should remove all degenerated triangles
    5. Remove isolated vertices, not connected to any faces or edges
    6. Remove self intersection edges and faces which is not realistic
    7. Remove duplicated faces
    8. The removing of duplicated faces can leave some vertices alone, we will removed them
    9. The calculation of outer hull volume is useful to be sure that the mesh is still ok
    10. Remove obtuse triangles > 179 who is not realistic and increase computation time
    11. We will remove potential duplicated faces again
    12. And duplicated vertices again
    13. Finally we will look if the mesh is broken or not. If yes we will try lower settings, if the lowest settings
    broke the mesh we will return the initial mesh. If not, we will return the optimised mesh.

    :param mesh: Pymesh Mesh object to optimise
    :param detail: string 'high', 'normal' or 'low' ('normal' as default), or float/int
    Settings to choose the targeting minimum length of edges
    :return: Pymesh Mesh object
    An optimised mesh or not depending on detail settings and mesh quality
    """
    meshCopy = mesh

    # copy/pasta of pymesh script fix_mesh from qnzhou, see pymesh on GitHub
    bbox_min, bbox_max = mesh.bbox
    diag_len = np.linalg.norm(bbox_max - bbox_min)
    if detail == "normal":
        target_len = diag_len * 5e-3
    elif detail == "high":
        target_len = diag_len * 2.5e-3
    elif detail == "low":
        target_len = diag_len * 1e-2
    elif detail is float or detail is int and detail > 0:
        target_len = diag_len * detail
    else:
        print(
            'Details settings is invalid, must be "low", "normal", "high" or positive int or float'
        )
        quit()

    count = 0
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, 5)
    mesh, __ = pymesh.split_long_edges(mesh, target_len)
    num_vertices = mesh.num_vertices
    while True:
        mesh, __ = pymesh.collapse_short_edges(mesh,
                                               target_len,
                                               preserve_feature=True)
        mesh, info = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
        if mesh.num_vertices == num_vertices:
            break

        num_vertices = mesh.num_vertices
        count += 1
        if count > 10:
            break

    mesh, __ = pymesh.remove_duplicated_vertices(mesh)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)
    mesh = pymesh.resolve_self_intersection(mesh)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_duplicated_vertices(mesh)
    mesh = pymesh.compute_outer_hull(mesh)
    mesh, __ = pymesh.remove_obtuse_triangles(mesh, 179.0, 5)
    mesh, __ = pymesh.remove_duplicated_faces(mesh)
    mesh, __ = pymesh.remove_isolated_vertices(mesh)

    if is_mesh_broken(mesh, meshCopy) is True:
        if detail == "high":
            print(
                f'The function fix_meshes broke mesh, trying with lower details settings'
            )
            fix_meshes(meshCopy, detail="normal")
            return mesh
        if detail == "normal":
            print(
                f'The function fix_meshes broke mesh, trying with lower details settings'
            )
            mesh = fix_meshes(meshCopy, detail="low")
            return mesh
        if detail == "low":
            print(
                f'The function fix_meshes broke mesh, no lower settings can be applied, no fix was done'
            )
            return meshCopy
    else:
        return mesh
def main():
    args = parse_args();
    mesh = pymesh.load_mesh(args.input_mesh);
    mesh, __ = pymesh.remove_degenerated_triangles(mesh, args.num_iterations);
    pymesh.save_mesh(args.output_mesh, mesh);
Example #30
0
def symmetrize(verts, faces, eps=1e-3):
    """
    verts: N,3 (x,y,z) tensor
    faces: F,3 (0,1,2) tensor

    Modifies mesh to make it symmetric about y-z plane
    - Cut mesh into half
    - Copy left half into right half
    - merge, remove duplicate vertices
    """
    # Snap vertices close to centre to centre
    verts_centre_mask = (verts[:,0].abs() < eps)
    verts[verts_centre_mask, 0] = 0

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/1.obj', pmesh)

    # Categorize vertices into left (-1), centre(0), right(1)
    verts_side = torch.sign(verts[:,0])

    # Categorize faces into left (-1), centre(0), right(1)
    face_verts_side = torch.index_select(verts_side, 0, faces.view(-1))
    face_verts_side = face_verts_side.contiguous().view(faces.shape[0], 3)
    face_left_mask = (face_verts_side[:,0]==-1) & (face_verts_side[:,1]==-1) & (face_verts_side[:,2]==-1)
    face_right_mask = (face_verts_side[:,0]==1) & (face_verts_side[:,1]==1) & (face_verts_side[:,2]==1)
    face_intesects_yz = (~face_left_mask) & (~face_right_mask)

    # Split intersecting faces
    new_verts = []
    new_faces = []
    for f in face_intesects_yz.nonzero().squeeze(1):
        i0, i1, i2 = faces[f]
        if verts_side[i0]==verts_side[i1]:
            i0, i1, i2 = i2, i0, i1
        elif verts_side[i2]==verts_side[i1]:
            i0, i1, i2 = i0, i1, i2
        elif verts_side[i0]==verts_side[i2]:
            i0, i1, i2 = i1, i0, i2
        elif verts_side[i0]==-1:
            i0, i1, i2 = i0, i1, i2
        elif verts_side[i1]==-1:
            i0, i1, i2 = i1, i0, i2
        elif verts_side[i2]==-1:
            i0, i1, i2 = i2, i0, i1
        else:
            import ipdb; ipdb.set_trace()

        # yz axis intersects i0->i1 & i0->i2
        assert(verts_side[i0] != verts_side[i1])
        assert(verts_side[i0] != verts_side[i2])

        v0 = verts[i0]
        v1 = verts[i1]
        v2 = verts[i2]

        v_n1 = (v0 * v1[0] - v1 * v0[0])/(v1[0] - v0[0])
        v_n2 = (v0 * v2[0] - v2 * v0[0])/(v2[0] - v0[0])

        i_n1 = verts.shape[0] + len(new_verts)
        i_n2 = verts.shape[0] + len(new_verts) + 1
        new_verts.append(v_n1)
        new_verts.append(v_n2)

        new_faces.append((i0, i_n1, i_n2))
        new_faces.append((i1, i_n1, i_n2))
        new_faces.append((i1, i2, i_n2))

    new_verts = torch.stack(new_verts, dim=0)
    new_faces = torch.tensor(new_faces, dtype=faces.dtype, device=faces.device)

    verts = torch.cat([verts, new_verts], dim=0)
    faces = torch.index_select(faces, 0, (~face_intesects_yz).nonzero().squeeze(1))
    faces = torch.cat([faces, new_faces], dim=0)

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/2.obj', pmesh)

    # Merge vertices that are very close together
    vertex_mapping = []
    verts_new = []
    for v_id in range(verts.shape[0]):
        if v_id==0:
            vertex_mapping.append(0)
            verts_new.append(verts[0])
            continue
        min_d, min_idx = (verts[0:v_id] - verts[v_id]).norm(dim=-1).min(dim=0)
        if min_d < eps:
            vertex_mapping.append(vertex_mapping[min_idx])
        else:
            vertex_mapping.append(len(verts_new))
            verts_new.append(verts[v_id])
    assert(len(vertex_mapping)==verts.shape[0])
    vertex_mapping = torch.tensor(vertex_mapping, dtype=faces.dtype, device=faces.device)
    verts = torch.stack(verts_new, dim=0)
    faces = vertex_mapping[faces]

    # Remove degenerate faces
    faces_degenerate = (faces[:,0]==faces[:,1]) | (faces[:,0]==faces[:,2]) | (faces[:,2]==faces[:,1])
    faces = torch.index_select(faces, 0, (~faces_degenerate).nonzero().squeeze(1))

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/3.obj', pmesh)

    # Delete faces that lie on right side (verts_side==1)
    verts_centre_mask = (verts[:,0].abs() < eps)
    verts[verts_centre_mask, 0] = 0
    verts_side = torch.sign(verts[:,0])
    face_verts_side = torch.index_select(verts_side, 0, faces.view(-1))
    face_verts_side = face_verts_side.contiguous().view(faces.shape[0], 3)
    face_right_mask = (face_verts_side[:,0]==1) | (face_verts_side[:,1]==1) | (face_verts_side[:,2]==1)
    faces = torch.index_select(faces, 0, (~face_right_mask).nonzero().squeeze(1))

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/4.obj', pmesh)

    # Flip mesh, merge
    faces_flip = faces + verts.shape[0]
    faces = torch.cat([faces, faces_flip], dim=0)
    verts_flip = verts * torch.tensor([-1,1,1], dtype=verts.dtype, device=verts.device)
    vertex_mapping_flip = torch.arange(verts.shape[0], dtype=faces.dtype, device=faces.device)
    vertex_mapping_flip[~verts_centre_mask] += verts.shape[0]
    vertex_mapping = torch.arange(verts.shape[0], dtype=faces.dtype, device=faces.device)
    vertex_mapping = torch.cat([vertex_mapping, vertex_mapping_flip], dim=0)
    verts = torch.cat([verts, verts_flip], dim=0)
    faces = vertex_mapping[faces]

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/5.obj', pmesh)


    pymesh.save_mesh(f'csm_mesh/debug/5.8.obj', pmesh)
    numv = pmesh.num_vertices
    while True:
        pmesh, __ = pymesh.collapse_short_edges(pmesh, rel_threshold=0.4)

        verts = torch.as_tensor(pmesh.vertices, dtype=verts.dtype, device=verts.device)
        faces = torch.as_tensor(pmesh.faces, dtype=faces.dtype, device=faces.device)
        verts_centre_mask = (verts[:,0].abs() < 1e-3)
        verts[verts_centre_mask, 0] = 0

        pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
        pmesh, __ = pymesh.remove_isolated_vertices(pmesh)
        pmesh, __ = pymesh.remove_duplicated_vertices(pmesh, tol=eps)
        pmesh, __ = pymesh.remove_duplicated_faces(pmesh)
        pmesh, __ = pymesh.remove_degenerated_triangles(pmesh)
        pmesh, __ = pymesh.remove_isolated_vertices(pmesh)
        # pmesh, __ = pymesh.remove_obtuse_triangles(pmesh, 120.0, 100)
        pmesh, __ = pymesh.collapse_short_edges(pmesh, 1e-2)
        pmesh, __ = pymesh.remove_duplicated_vertices(pmesh, tol=eps)

        if pmesh.num_vertices==numv:
            break
        numv = pmesh.num_vertices
    pymesh.save_mesh(f'csm_mesh/debug/5.9.obj', pmesh)

    verts = torch.as_tensor(pmesh.vertices, dtype=verts.dtype, device=verts.device)
    faces = torch.as_tensor(pmesh.faces, dtype=faces.dtype, device=faces.device)

    # Remove unused vertices
    vertices_used = torch.unique(faces.view(-1))
    vertex_mapping = torch.zeros((verts.shape[0], ), dtype=faces.dtype, device=faces.device)
    vertex_mapping[vertices_used] = torch.arange(vertices_used.shape[0], dtype=faces.dtype, device=faces.device)
    faces = vertex_mapping[faces]
    verts = verts[vertices_used]

    # import pymesh
    pmesh = pymesh.form_mesh(verts.numpy(), faces.numpy())
    pymesh.save_mesh(f'csm_mesh/debug/6.obj', pmesh)

    return verts, faces