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
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.in_mesh); if (args.with_rounding): mesh = pymesh.form_mesh( np.round(mesh.vertices, args.precision), mesh.faces); intersecting_faces = pymesh.detect_self_intersection(mesh); counter = 0; while len(intersecting_faces) > 0 and counter < args.max_iterations: if (args.with_rounding): involved_vertices = np.unique(mesh.faces[intersecting_faces].ravel()); mesh.vertices_ref[involved_vertices, :] =\ np.round(mesh.vertices[involved_vertices, :], args.precision//2); mesh = pymesh.resolve_self_intersection(mesh, "igl"); mesh, __ = pymesh.remove_duplicated_faces(mesh, fins_only=True); if (args.with_rounding): mesh = pymesh.form_mesh( np.round(mesh.vertices, args.precision), mesh.faces); intersecting_faces = pymesh.detect_self_intersection(mesh); counter += 1; if len(intersecting_faces) > 0: logging.warn("Resolving failed: max iteration reached!"); pymesh.save_mesh(args.out_mesh, mesh);
def main(): args = parse_args() mesh = pymesh.load_mesh(args.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 test_corner_corner_touch(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) mesh_2 = generate_box_mesh(np.array([1, 1, 1]), np.array([4, 4, 4])) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) self.assert_no_self_intersect(output_mesh) self.assert_even_adj_faces(output_mesh)
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
def test_face_face_touch_with_different_area(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) mesh_2 = generate_box_mesh(np.array([-1, -1, 1]), np.array([2, 2, 2])) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh)
def test_corner_corner_touch(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); mesh_2 = generate_box_mesh( np.array([1, 1, 1]), np.array([4, 4, 4])); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
def test_simple_intersection(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([2, 2, 2])); mesh_2 = generate_box_mesh( np.array([1, 1, 1]), np.array([4, 4, 4])); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
def test_face_face_touch(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) mesh_2 = generate_box_mesh(np.array([0, 0, 1]), np.array([1, 1, 2])) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) # Since the overlapping faces would be kept so valid nested outer hulls # can be extracted, the resolved mesh would be actually self-intersecting. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh)
def test_no_intersection(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) mesh_2 = generate_box_mesh(np.array([3, 3, 3]), np.array([4, 4, 4])) mesh = merge_meshes((mesh_1, mesh_2)) intersecting_faces = detect_self_intersection(mesh) self.assertEqual(0, len(intersecting_faces)) output_mesh = resolve_self_intersection(mesh) self.assertEqual(mesh.num_vertices, output_mesh.num_vertices) self.assertEqual(mesh.num_faces, output_mesh.num_faces) self.assert_even_adj_faces(output_mesh)
def test_face_face_touch_with_different_area(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); mesh_2 = generate_box_mesh( np.array([-1, -1, 1]), np.array([2, 2, 2])); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
def test_cross_union(self): mesh_1 = generate_box_mesh(np.array([-2, -1, -1]), np.array([2, 1, 1])) mesh_2 = generate_box_mesh(np.array([-1, -2, -1]), np.array([1, 2, 1])) mesh_3 = generate_box_mesh(np.array([-1, -1, -2]), np.array([1, 1, 2])) mesh = merge_meshes((mesh_1, mesh_2, mesh_3)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) # The output mesh contains duplicated faces due to input overlaps. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh)
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
def test_face_face_touch(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); mesh_2 = generate_box_mesh( np.array([0, 0, 1]), np.array([1, 1, 2])); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); # Since the overlapping faces would be kept so valid nested outer hulls # can be extracted, the resolved mesh would be actually self-intersecting. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
def test_no_intersection(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); mesh_2 = generate_box_mesh( np.array([3, 3, 3]), np.array([4, 4, 4])); mesh = merge_meshes((mesh_1, mesh_2)); intersecting_faces = detect_self_intersection(mesh); self.assertEqual(0, len(intersecting_faces)); output_mesh = resolve_self_intersection(mesh); self.assertEqual(mesh.num_vertices, output_mesh.num_vertices); self.assertEqual(mesh.num_faces, output_mesh.num_faces); self.assert_even_adj_faces(output_mesh);
def test_rotate_120(self): mesh_1 = generate_box_mesh(np.array([-2, -1, -1]), np.array([2, 1, 1])) rot = Quaternion.fromAxisAngle(np.array([1.0, 0.0, 0.0]), float(2 * math.pi) / 3) mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T, mesh_1.faces) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) # The output mesh contains degenerated face. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh)
def test_face_corner_touch_off_center(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) rot = Quaternion.fromData(np.array([1, 1, 1], dtype=float), np.array([0, 0, 1], dtype=float)) mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T + np.array([0.6, 0.6, 1.0]), mesh_1.faces) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) self.assert_no_self_intersect(output_mesh) self.assert_even_adj_faces(output_mesh)
def test_cross_union(self): mesh_1 = generate_box_mesh( np.array([-2, -1, -1]), np.array([2, 1, 1])); mesh_2 = generate_box_mesh( np.array([-1, -2, -1]), np.array([1, 2, 1])); mesh_3 = generate_box_mesh( np.array([-1, -1, -2]), np.array([1, 1, 2])); mesh = merge_meshes((mesh_1, mesh_2, mesh_3)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); # The output mesh contains duplicated faces due to input overlaps. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
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
def execute(self, context): scene = context.scene pymesh_props = scene.pymesh obj_a = context.active_object mesh_a = import_object(context, obj_a) pymesh_si = pymesh.detect_self_intersection(mesh_a) if pymesh_si.size == 0: self.report({'ERROR'}, "This mesh has no self-intersections") return {'CANCELLED'} pymesh_r = pymesh.resolve_self_intersection(mesh_a) off_name = "Py.SI." + obj_a.name mesh_r = export_mesh(context, pymesh_r, off_name) add_to_scene(context, mesh_r) return {'FINISHED'}
def main(): args = parse_args(); mesh = pymesh.load_mesh(args.in_mesh); intersecting_faces = pymesh.detect_self_intersection(mesh); counter = 0; while len(intersecting_faces) > 0 and counter < args.max_iterations: mesh = pymesh.resolve_self_intersection(mesh, "igl"); mesh, __ = pymesh.remove_duplicated_faces(mesh, fins_only=True); 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 test_rotate_120(self): mesh_1 = generate_box_mesh( np.array([-2, -1, -1]), np.array([2, 1, 1])); rot = Quaternion.fromAxisAngle( np.array([1.0, 0.0, 0.0]), float(2*math.pi) / 3); mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T, mesh_1.faces); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); # The output mesh contains degenerated face. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
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_intersect_with_rotated_self(self): mesh_1 = generate_box_mesh(np.array([0, 0, 0]), np.array([1, 1, 1])) rot = Quaternion.fromAxisAngle(np.array([1, 1, 0], dtype=float), math.pi * 0.5) mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T + np.array([0.5, 0.5, 0.5]), mesh_1.faces) mesh = merge_meshes((mesh_1, mesh_2)) output_mesh = resolve_self_intersection(mesh) self.assert_self_intersect(mesh) # The output mesh contains degenerated triangles, which cause CGAL # assertion failure. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_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 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 test_face_corner_touch_off_center(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); rot = Quaternion.fromData( np.array([1, 1, 1], dtype=float), np.array([0, 0, 1], dtype=float)); mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T + np.array([0.6, 0.6, 1.0]), mesh_1.faces); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_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;
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
def test_intersect_with_rotated_self(self): mesh_1 = generate_box_mesh( np.array([0, 0, 0]), np.array([1, 1, 1])); rot = Quaternion.fromAxisAngle(np.array([1, 1, 0], dtype=float), math.pi * 0.5); mesh_2 = form_mesh( np.dot(rot.to_matrix(), mesh_1.vertices.T).T + np.array([0.5, 0.5, 0.5]), mesh_1.faces); mesh = merge_meshes((mesh_1, mesh_2)); output_mesh = resolve_self_intersection(mesh); self.assert_self_intersect(mesh); # The output mesh contains degenerated triangles, which cause CGAL # assertion failure. #self.assert_no_self_intersect(output_mesh); self.assert_even_adj_faces(output_mesh);
def __fix_mesh(self, mesh, improvement_thres=0.8): mesh, __ = pymesh.split_long_edges(mesh, self.mesh_target_len) num_vertices = len(mesh.vertices) for __ in range(10): mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6) mesh, __ = pymesh.collapse_short_edges( mesh, self.mesh_target_len, preserve_feature=True ) mesh, __ = pymesh.remove_obtuse_triangles(mesh, 150.0, 100) if len(mesh.vertices) < num_vertices * improvement_thres: break mesh = pymesh.resolve_self_intersection(mesh) mesh, __ = pymesh.collapse_short_edges(mesh, 1e-6) mesh, __ = pymesh.remove_duplicated_faces(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 old_fix_mesh(vertices, faces, detail="normal"): bbox_min = np.amin(vertices, axis=0) bbox_max = np.amax(vertices, axis=0) 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 vertices, faces = pymesh.split_long_edges(vertices, faces, target_len) num_vertices = len(vertices) while True: vertices, faces = pymesh.collapse_short_edges(vertices, faces, 1e-6) vertices, faces = pymesh.collapse_short_edges(vertices, faces, target_len, preserve_feature=True) vertices, faces = pymesh.remove_obtuse_triangles( vertices, faces, 150.0, 100) if num_vertices == len(vertices): break num_vertices = len(vertices) print("#v: {}".format(num_vertices)) count += 1 if count > 10: break vertices, faces = pymesh.resolve_self_intersection(vertices, faces) vertices, faces = pymesh.remove_duplicated_faces(vertices, faces) vertices, faces, _ = pymesh.compute_outer_hull(vertices, faces, False) vertices, faces = pymesh.remove_duplicated_faces(vertices, faces) vertices, faces = pymesh.remove_obtuse_triangles(vertices, faces, 179.0, 5) vertices, faces, voxels = pymesh.remove_isolated_vertices(vertices, faces) return vertices, faces
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;
def main(tetra_path, savedir, create_adjlists, adjnodes, rates): print "######### Tetrahedralize #########" mesh = pymesh.load_mesh(tetra_path) mesh.add_attribute("vertex_normal") mesh.get_attribute("vertex_normal") mesh.get_vertex_attribute("vertex_normal") #some meshes may have self intersection so tetgen cannot deal with it. #Before tetrahedralization here is the self intersection cleanup self_mesh = pymesh.resolve_self_intersection(mesh, engine='auto') self_mesh.add_attribute("vertex_normal") self_mesh.get_attribute("vertex_normal") self_mesh.get_vertex_attribute("vertex_normal") print("Starting Tetgen.............................") tetgen = pymesh.tetgen() tetgen.points = self_mesh.vertices tetgen.triangles = self_mesh.faces # tetgen.max_tet_volume = 0.000001 #Very detailed tetgen.max_tet_volume = 0.0000005 #Very detailed tetgen.verbosity = 0 tetgen.run() outmesh = tetgen.mesh print(outmesh.num_vertices, outmesh.num_faces, outmesh.num_voxels) print(outmesh.dim, outmesh.vertex_per_face, outmesh.vertex_per_voxel) # Save tetrahedral mesh if not os.path.exists(savedir): os.makedirs(savedir) pymesh.meshio.save_mesh(savedir + "/CoarseTetra.ply", outmesh) # Option: Create adjlists for tetranet if create_adjlists: print "#########Create adjlists#########" # Reduce vertices for full connection coarse1 = reduce_points(outmesh.vertices, rates[0]) utils.save_points(coarse1, savedir + "/coarse1.ply") coarse2 = reduce_points(coarse1, rates[1]) utils.save_points(coarse2, savedir + "/coarse2.ply") coarse3 = reduce_points(coarse2, rates[2]) utils.save_points(coarse3, savedir + "/coarse3.ply") coarse4 = reduce_points(coarse3, rates[3]) utils.save_points(coarse4, savedir + "/coarse4.ply") print("Generate node adj list") # Note: utils.make_adjlist returns a list of n nearest nodes (L2 norm) print "Node reduction rates: ", print(rates) n = adjnodes print("Adjacent Nodes: " + str(n)) # For partial connection print("coarse 4 to 3: {} -> {}".format(len(coarse4), len(coarse3))) adjlist = utils.make_adjlist(coarse4, coarse3, n, True) utils.list2csv(adjlist, savedir + "/adjlist_4to3.csv") print("coarse 3 to 2: {} -> {}".format(len(coarse3), len(coarse2))) adjlist = utils.make_adjlist(coarse3, coarse2, n, True) utils.list2csv(adjlist, savedir + "/adjlist_3to2.csv") print("coarse 2 to 1: {} -> {}".format(len(coarse2), len(coarse1))) adjlist = utils.make_adjlist(coarse2, coarse1, n, True) utils.list2csv(adjlist, savedir + "/adjlist_2to1.csv") print("coarse 1 to original: {} -> {}".format(len(coarse1), len(outmesh.vertices))) adjlist = utils.make_adjlist(coarse1, outmesh.vertices, n, True) utils.list2csv(adjlist, savedir + "/adjlist_1to0.csv")
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