Lagrange
Loading...
Searching...
No Matches
bind_mesh_cleanup.h
1/*
2 * Copyright 2023 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
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/python/binding.h>
28
29#include <vector>
30
31namespace lagrange::python {
32
33template <typename Scalar, typename Index>
34void bind_mesh_cleanup(nanobind::module_& m)
35{
36 namespace nb = nanobind;
37 using namespace nb::literals;
38 using MeshType = SurfaceMesh<Scalar, Index>;
39
40 m.def(
41 "remove_isolated_vertices",
43 "mesh"_a,
44 R"(Remove isolated vertices from a mesh.
45
46.. note::
47 A vertex is considered isolated if it is not referenced by any facet.
48
49:param mesh: Input mesh (modified in place).)");
50
51 m.def(
52 "detect_degenerate_facets",
54 "mesh"_a,
55 R"(Detect degenerate facets in a mesh.
56
57.. note::
58 Only exactly degenerate facets are detected.
59
60:param mesh: Input mesh.
61
62:returns: List of degenerate facet indices.)");
63
64 m.def(
65 "remove_null_area_facets",
66 [](MeshType& mesh, double null_area_threshold, bool remove_isolated_vertices) {
67 RemoveNullAreaFacetsOptions opts;
68 opts.null_area_threshold = null_area_threshold;
69 opts.remove_isolated_vertices = remove_isolated_vertices;
70 remove_null_area_facets(mesh, opts);
71 },
72 "mesh"_a,
73 "null_area_threshold"_a = 0,
74 "remove_isolated_vertices"_a = false,
75 R"(Remove facets with unsigned facets area <= `null_area_threhsold`.
76
77:param mesh: Input mesh (modified in place).
78:param null_area_threshold: Area threshold below which facets are considered null.
79:param remove_isolated_vertices: Whether to remove isolated vertices after removing null area facets.)");
80
81 m.def(
82 "remove_duplicate_vertices",
83 [](MeshType& mesh,
84 std::optional<std::vector<AttributeId>> extra_attributes,
85 bool boundary_only) {
86 RemoveDuplicateVerticesOptions opts;
87 if (extra_attributes.has_value()) {
88 opts.extra_attributes = std::move(extra_attributes.value());
89 }
90 opts.boundary_only = boundary_only;
91 remove_duplicate_vertices(mesh, opts);
92 },
93 "mesh"_a,
94 "extra_attributes"_a = nb::none(),
95 "boundary_only"_a = false,
96 R"(Remove duplicate vertices from a mesh.
97
98:param mesh: Input mesh (modified in place).
99:param extra_attributes: Additional attributes to consider when detecting duplicates.
100:param boundary_only: Only remove duplicate vertices on the boundary.)");
101
102 m.def(
103 "remove_duplicate_facets",
104 [](MeshType& mesh, bool consider_orientation) {
105 RemoveDuplicateFacetOptions opts;
106 opts.consider_orientation = consider_orientation;
107 remove_duplicate_facets(mesh, opts);
108 },
109 "mesh"_a,
110 "consider_orientation"_a = false,
111 R"(Remove duplicate facets from a mesh.
112
113Facets with different orientations (e.g. (0,1,2) and (2,1,0)) are considered duplicates.
114If both orientations have equal counts, all are removed.
115If one orientation has more duplicates, all but one of the majority orientation are kept.
116
117:param mesh: Input mesh (modified in place).
118:param consider_orientation: Whether to consider orientation when detecting duplicates.)");
119
120 m.def(
121 "remove_topologically_degenerate_facets",
123 "mesh"_a,
124 R"(Remove topologically degenerate facets such as (0,1,1).
125
126For polygons, topological degeneracy means the polygon has at most two unique vertices.
127E.g. quad (0,0,1,1) is degenerate, while (1,1,2,3) is not.
128
129:param mesh: Input mesh (modified in place).)");
130
131 m.def(
132 "remove_short_edges",
133 [](MeshType& mesh,
134 double threshold,
135 std::optional<std::string_view> vertex_importance_attribute) {
136 lagrange::RemoveShortEdgesOptions opts;
137 opts.threshold = threshold;
138 if (vertex_importance_attribute.has_value())
139 opts.vertex_importance_attribute_name = vertex_importance_attribute.value();
140 remove_short_edges(mesh, opts);
141 },
142 "mesh"_a,
143 nb::kw_only(),
144 "threshold"_a = 0,
145 "vertex_importance_attribute"_a = nb::none(),
146 R"(Remove short edges from a mesh.
147
148:param mesh: Input mesh (modified in place).
149:param threshold: Minimum edge length below which edges are considered short.
150:param vertex_importance_attribute: Optional vertex attribute name for importance values used to determine which vertex to keep during edge collapse.)");
151
152 m.def(
153 "resolve_vertex_nonmanifoldness",
155 "mesh"_a,
156 R"(Resolve vertex non-manifoldness in a mesh.
157
158:param mesh: Input mesh (modified in place).
159
160:raises RuntimeError: If the input mesh is not edge-manifold.)");
161
162 m.def(
163 "resolve_nonmanifoldness",
165 "mesh"_a,
166 R"(Resolve both vertex and edge nonmanifoldness in a mesh.
167
168:param mesh: Input mesh (modified in place).)");
169
170 m.def(
171 "split_long_edges",
172 [](MeshType& mesh,
173 float max_edge_length,
174 bool recursive,
175 std::optional<std::string_view> active_region_attribute,
176 std::optional<std::string_view> edge_length_attribute) {
177 SplitLongEdgesOptions opts;
178 opts.max_edge_length = max_edge_length;
179 opts.recursive = recursive;
180 if (active_region_attribute.has_value())
181 opts.active_region_attribute = active_region_attribute.value();
182 if (edge_length_attribute.has_value())
183 opts.edge_length_attribute = edge_length_attribute.value();
184 split_long_edges(mesh, std::move(opts));
185 },
186 "mesh"_a,
187 "max_edge_length"_a = 0.1f,
188 "recursive"_a = true,
189 "active_region_attribute"_a = nb::none(),
190 "edge_length_attribute"_a = nb::none(),
191 R"(Split edges longer than max_edge_length.
192
193:param mesh: Input mesh (modified in place).
194:param max_edge_length: Maximum edge length threshold.
195:param recursive: If true, apply recursively until no edge exceeds threshold.
196:param active_region_attribute: Facet attribute name for active region (uint8_t type).
197 If None, all edges are considered.
198:param edge_length_attribute: Edge length attribute name.
199 If None, edge lengths are computed.
200)");
201
202 m.def(
203 "remove_degenerate_facets",
205 "mesh"_a,
206 R"(Remove degenerate facets from a mesh.
207
208.. note::
209 Assumes triangular mesh. Use `triangulate_polygonal_facets` for non-triangular meshes.
210 Adjacent non-degenerate facets may be re-triangulated during removal.
211
212:param mesh: Input mesh (modified in place).)");
213
214 m.def(
215 "close_small_holes",
216 [](MeshType& mesh, size_t max_hole_size, bool triangulate_holes) {
217 CloseSmallHolesOptions options;
218 options.max_hole_size = max_hole_size;
219 options.triangulate_holes = triangulate_holes;
220 close_small_holes(mesh, options);
221 },
222 "mesh"_a,
223 "max_hole_size"_a = CloseSmallHolesOptions().max_hole_size,
224 "triangulate_holes"_a = CloseSmallHolesOptions().triangulate_holes,
225 R"(Close small holes in a mesh.
226
227:param mesh: Input mesh (modified in place).
228:param max_hole_size: Maximum number of vertices on a hole to be closed.
229:param triangulate_holes: Whether to triangulate holes (if false, fill with polygons).)");
230
231 m.def(
232 "rescale_uv_charts",
233 [](MeshType& mesh,
234 std::string_view uv_attribute_name,
235 std::string_view chart_id_attribute_name,
236 double uv_area_threshold) {
237 RescaleUVOptions options;
238 options.uv_attribute_name = uv_attribute_name;
239 options.chart_id_attribute_name = chart_id_attribute_name;
240 options.uv_area_threshold = uv_area_threshold;
241 rescale_uv_charts(mesh, options);
242 },
243 "mesh"_a,
244 "uv_attribute_name"_a = RescaleUVOptions().uv_attribute_name,
245 "chart_id_attribute_name"_a = RescaleUVOptions().chart_id_attribute_name,
246 "uv_area_threshold"_a = RescaleUVOptions().uv_area_threshold,
247 R"(Rescale UV charts to match their 3D aspect ratios.
248
249:param mesh: Input mesh (modified in place).
250:param uv_attribute_name: UV attribute name for rescaling.
251 If empty, uses first UV attribute found.
252:param chart_id_attribute_name: Patch ID attribute name.
253 If empty, computes patches from UV chart connectivity.
254:param uv_area_threshold: UV area threshold.
255 Triangles below this threshold don't contribute to scale computation.
256)");
257}
258
259} // namespace lagrange::python
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:65
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
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