14#include <lagrange/mesh_cleanup/close_small_holes.h>
15#include <lagrange/mesh_cleanup/detect_degenerate_facets.h>
16#include <lagrange/mesh_cleanup/remove_degenerate_facets.h>
17#include <lagrange/mesh_cleanup/remove_duplicate_facets.h>
18#include <lagrange/mesh_cleanup/remove_duplicate_vertices.h>
19#include <lagrange/mesh_cleanup/remove_isolated_vertices.h>
20#include <lagrange/mesh_cleanup/remove_null_area_facets.h>
21#include <lagrange/mesh_cleanup/remove_short_edges.h>
22#include <lagrange/mesh_cleanup/remove_topologically_degenerate_facets.h>
23#include <lagrange/mesh_cleanup/rescale_uv_charts.h>
24#include <lagrange/mesh_cleanup/resolve_nonmanifoldness.h>
25#include <lagrange/mesh_cleanup/resolve_vertex_nonmanifoldness.h>
26#include <lagrange/mesh_cleanup/split_long_edges.h>
27#include <lagrange/mesh_cleanup/split_obtuse_triangles.h>
28#include <lagrange/python/binding.h>
32namespace lagrange::python {
34template <
typename Scalar,
typename Index>
35void bind_mesh_cleanup(nanobind::module_& m)
37 namespace nb = nanobind;
38 using namespace nb::literals;
39 using MeshType = SurfaceMesh<Scalar, Index>;
42 "remove_isolated_vertices",
45 R
"(Remove isolated vertices from a mesh.
48 A vertex is considered isolated if it is not referenced by any facet.
50:param mesh: Input mesh (modified in place).)");
53 "detect_degenerate_facets",
56 R
"(Detect degenerate facets in a mesh.
59 Only exactly degenerate facets are detected.
61:param mesh: Input mesh.
63:returns: List of degenerate facet indices.)");
66 "remove_null_area_facets",
68 RemoveNullAreaFacetsOptions opts;
69 opts.null_area_threshold = null_area_threshold;
74 "null_area_threshold"_a = 0,
75 "remove_isolated_vertices"_a =
false,
76 R
"(Remove facets with unsigned facets area <= `null_area_threhsold`.
78:param mesh: Input mesh (modified in place).
79:param null_area_threshold: Area threshold below which facets are considered null.
80:param remove_isolated_vertices: Whether to remove isolated vertices after removing null area facets.)");
83 "remove_duplicate_vertices",
85 std::optional<std::vector<AttributeId>> extra_attributes,
87 RemoveDuplicateVerticesOptions opts;
88 if (extra_attributes.has_value()) {
89 opts.extra_attributes = std::move(extra_attributes.value());
91 opts.boundary_only = boundary_only;
95 "extra_attributes"_a = nb::none(),
96 "boundary_only"_a =
false,
97 R
"(Remove duplicate vertices from a mesh.
99:param mesh: Input mesh (modified in place).
100:param extra_attributes: Additional attributes to consider when detecting duplicates.
101:param boundary_only: Only remove duplicate vertices on the boundary.)");
104 "remove_duplicate_facets",
105 [](MeshType& mesh,
bool consider_orientation) {
106 RemoveDuplicateFacetOptions opts;
107 opts.consider_orientation = consider_orientation;
111 "consider_orientation"_a =
false,
112 R
"(Remove duplicate facets from a mesh.
114Facets with different orientations (e.g. (0,1,2) and (2,1,0)) are considered duplicates.
115If both orientations have equal counts, all are removed.
116If one orientation has more duplicates, all but one of the majority orientation are kept.
118:param mesh: Input mesh (modified in place).
119:param consider_orientation: Whether to consider orientation when detecting duplicates.)");
122 "remove_topologically_degenerate_facets",
125 R
"(Remove topologically degenerate facets such as (0,1,1).
127For polygons, topological degeneracy means the polygon has at most two unique vertices.
128E.g. quad (0,0,1,1) is degenerate, while (1,1,2,3) is not.
130:param mesh: Input mesh (modified in place).)");
133 "remove_short_edges",
136 std::optional<std::string_view> vertex_importance_attribute) {
137 lagrange::RemoveShortEdgesOptions opts;
139 if (vertex_importance_attribute.has_value())
146 "vertex_importance_attribute"_a = nb::none(),
147 R
"(Remove short edges from a mesh.
149:param mesh: Input mesh (modified in place).
150:param threshold: Minimum edge length below which edges are considered short.
151:param vertex_importance_attribute: Optional vertex attribute name for importance values used to determine which vertex to keep during edge collapse.)");
154 "resolve_vertex_nonmanifoldness",
157 R
"(Resolve vertex non-manifoldness in a mesh.
159:param mesh: Input mesh (modified in place).
161:raises RuntimeError: If the input mesh is not edge-manifold.)");
164 "resolve_nonmanifoldness",
167 R
"(Resolve both vertex and edge nonmanifoldness in a mesh.
169:param mesh: Input mesh (modified in place).)");
174 float max_edge_length,
176 std::optional<std::string_view> active_region_attribute,
177 std::optional<std::string_view> edge_length_attribute) {
178 SplitLongEdgesOptions opts;
179 opts.max_edge_length = max_edge_length;
180 opts.recursive = recursive;
181 if (active_region_attribute.has_value())
182 opts.active_region_attribute = active_region_attribute.value();
183 if (edge_length_attribute.has_value())
184 opts.edge_length_attribute = edge_length_attribute.value();
188 "max_edge_length"_a = 0.1f,
189 "recursive"_a =
true,
190 "active_region_attribute"_a = nb::none(),
191 "edge_length_attribute"_a = nb::none(),
192 R
"(Split edges longer than max_edge_length.
194:param mesh: Input mesh (modified in place).
195:param max_edge_length: Maximum edge length threshold.
196:param recursive: If true, apply recursively until no edge exceeds threshold.
197:param active_region_attribute: Facet attribute name for active region (uint8_t type).
198 If None, all edges are considered.
199:param edge_length_attribute: Edge length attribute name.
200 If None, edge lengths are computed.
204 "split_obtuse_triangles",
207 size_t max_iterations,
208 std::optional<std::string_view> active_region_attribute) {
209 SplitObtuseTrianglesOptions opts;
210 opts.max_angle = max_angle;
211 opts.max_iterations = max_iterations;
212 if (active_region_attribute.has_value())
213 opts.active_region_attribute = active_region_attribute.value();
218 "max_angle"_a = SplitObtuseTrianglesOptions().max_angle,
219 "max_iterations"_a = SplitObtuseTrianglesOptions().max_iterations,
220 "active_region_attribute"_a = nb::none(),
221 R
"(Iteratively split obtuse triangles by splitting their longest edge at the
222projection of the obtuse vertex.
224:param mesh: Input mesh (modified in place).
225:param max_angle: Maximum allowed interior angle in radians. Triangles with any interior
226 angle strictly greater than this value are split. Default is pi/2.
227:param max_iterations: Maximum number of split passes. Use 0 to iterate until convergence.
228:param active_region_attribute: Optional facet attribute name (uint8_t) restricting which
229 facets are considered. If None, all facets are checked.
231:returns: Total number of triangle splits performed across all iterations (a triangle
232 re-split in a later iteration is counted again).
236 "remove_degenerate_facets",
239 R
"(Remove degenerate facets from a mesh.
242 Assumes triangular mesh. Use `triangulate_polygonal_facets` for non-triangular meshes.
243 Adjacent non-degenerate facets may be re-triangulated during removal.
245:param mesh: Input mesh (modified in place).)");
249 [](MeshType& mesh,
size_t max_hole_size,
bool triangulate_holes) {
250 CloseSmallHolesOptions options;
251 options.max_hole_size = max_hole_size;
252 options.triangulate_holes = triangulate_holes;
256 "max_hole_size"_a = CloseSmallHolesOptions().max_hole_size,
257 "triangulate_holes"_a = CloseSmallHolesOptions().triangulate_holes,
258 R
"(Close small holes in a mesh.
260:param mesh: Input mesh (modified in place).
261:param max_hole_size: Maximum number of vertices on a hole to be closed.
262:param triangulate_holes: Whether to triangulate holes (if false, fill with polygons).)");
267 std::string_view uv_attribute_name,
268 std::string_view chart_id_attribute_name,
269 double uv_area_threshold) {
270 RescaleUVOptions options;
271 options.uv_attribute_name = uv_attribute_name;
272 options.chart_id_attribute_name = chart_id_attribute_name;
273 options.uv_area_threshold = uv_area_threshold;
277 "uv_attribute_name"_a = RescaleUVOptions().uv_attribute_name,
278 "chart_id_attribute_name"_a = RescaleUVOptions().chart_id_attribute_name,
279 "uv_area_threshold"_a = RescaleUVOptions().uv_area_threshold,
280 R
"(Rescale UV charts to match their 3D aspect ratios.
282:param mesh: Input mesh (modified in place).
283:param uv_attribute_name: UV attribute name for rescaling.
284 If empty, uses first UV attribute found.
285:param chart_id_attribute_name: Patch ID attribute name.
286 If empty, computes patches from UV chart connectivity.
287:param uv_area_threshold: UV area threshold.
288 Triangles below this threshold don't contribute to scale computation.
void remove_duplicate_vertices(SurfaceMesh< Scalar, Index > &mesh, const RemoveDuplicateVerticesOptions &options={})
Removes duplicate vertices from a mesh.
Definition remove_duplicate_vertices.cpp:33
void remove_duplicate_facets(SurfaceMesh< Scalar, Index > &mesh, const RemoveDuplicateFacetOptions &opts={})
Remove duplicate facets in the mesh.
Definition remove_duplicate_facets.cpp:235
void resolve_vertex_nonmanifoldness(SurfaceMesh< Scalar, Index > &mesh)
Resolve nonmanifold vertices by pulling disconnected 1-ring neighborhood apart.
Definition resolve_vertex_nonmanifoldness.cpp:35
void rescale_uv_charts(SurfaceMesh< Scalar, Index > &mesh, const RescaleUVOptions &options={})
Rescale UV charts such that they are isotropic to their 3D images.
Definition rescale_uv_charts.cpp:70
void remove_topologically_degenerate_facets(SurfaceMesh< Scalar, Index > &mesh)
Remove topologically degenerate facets (i.e.
Definition remove_topologically_degenerate_facets.cpp:21
void remove_isolated_vertices(SurfaceMesh< Scalar, Index > &mesh)
Removes isolated vertices of a mesh.
Definition remove_isolated_vertices.cpp:20
void split_long_edges(SurfaceMesh< Scalar, Index > &mesh, SplitLongEdgesOptions options={})
Split edges that are longer than options.max_edge_length.
Definition split_long_edges.cpp:37
void close_small_holes(SurfaceMesh< Scalar, Index > &mesh, CloseSmallHolesOptions options={})
Close small topological holes.
Definition close_small_holes.cpp:538
void remove_null_area_facets(SurfaceMesh< Scalar, Index > &mesh, const RemoveNullAreaFacetsOptions &options={})
Removes all facets with unsigned area <= options.null_area_threshold.
Definition remove_null_area_facets.cpp:22
void remove_degenerate_facets(SurfaceMesh< Scalar, Index > &mesh)
Removes degenerate facets from a mesh.
Definition remove_degenerate_facets.cpp:32
std::vector< Index > detect_degenerate_facets(const SurfaceMesh< Scalar, Index > &mesh)
Detects degenerate facets in a mesh.
Definition detect_degenerate_facets.cpp:32
void remove_short_edges(SurfaceMesh< Scalar, Index > &mesh, Scalar threshold=0)
Collapse all edges shorter than a given tolerance.
Definition remove_short_edges.cpp:424
size_t split_obtuse_triangles(SurfaceMesh< Scalar, Index > &mesh, SplitObtuseTrianglesOptions options={})
Iteratively split obtuse triangles by splitting their longest edge at the projection of the opposite ...
Definition split_obtuse_triangles.cpp:41
void resolve_nonmanifoldness(SurfaceMesh< Scalar, Index > &mesh)
Resolve both non-manifold vertices and non-manifold edges in the input mesh.
Definition resolve_nonmanifoldness.cpp:35
std::string_view vertex_importance_attribute_name
Optional: User-defined per-vertex importance attribute name.
Definition remove_short_edges.h:44
double threshold
Edge length threshold for removal. Edges with length <= threshold will be removed.
Definition remove_short_edges.h:36