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",
134 "mesh"_a,
135 "threshold"_a = 0,
136 R"(Remove short edges from a mesh.
137
138:param mesh: Input mesh (modified in place).
139:param threshold: Minimum edge length below which edges are considered short.)");
140
141 m.def(
142 "resolve_vertex_nonmanifoldness",
144 "mesh"_a,
145 R"(Resolve vertex non-manifoldness in a mesh.
146
147:param mesh: Input mesh (modified in place).
148
149:raises RuntimeError: If the input mesh is not edge-manifold.)");
150
151 m.def(
152 "resolve_nonmanifoldness",
154 "mesh"_a,
155 R"(Resolve both vertex and edge nonmanifoldness in a mesh.
156
157:param mesh: Input mesh (modified in place).)");
158
159 m.def(
160 "split_long_edges",
161 [](MeshType& mesh,
162 float max_edge_length,
163 bool recursive,
164 std::optional<std::string_view> active_region_attribute,
165 std::optional<std::string_view> edge_length_attribute) {
166 SplitLongEdgesOptions opts;
167 opts.max_edge_length = max_edge_length;
168 opts.recursive = recursive;
169 if (active_region_attribute.has_value())
170 opts.active_region_attribute = active_region_attribute.value();
171 if (edge_length_attribute.has_value())
172 opts.edge_length_attribute = edge_length_attribute.value();
173 split_long_edges(mesh, std::move(opts));
174 },
175 "mesh"_a,
176 "max_edge_length"_a = 0.1f,
177 "recursive"_a = true,
178 "active_region_attribute"_a = nb::none(),
179 "edge_length_attribute"_a = nb::none(),
180 R"(Split edges longer than max_edge_length.
181
182:param mesh: Input mesh (modified in place).
183:param max_edge_length: Maximum edge length threshold.
184:param recursive: If true, apply recursively until no edge exceeds threshold.
185:param active_region_attribute: Facet attribute name for active region (uint8_t type).
186 If None, all edges are considered.
187:param edge_length_attribute: Edge length attribute name.
188 If None, edge lengths are computed.
189)");
190
191 m.def(
192 "remove_degenerate_facets",
194 "mesh"_a,
195 R"(Remove degenerate facets from a mesh.
196
197.. note::
198 Assumes triangular mesh. Use `triangulate_polygonal_facets` for non-triangular meshes.
199 Adjacent non-degenerate facets may be re-triangulated during removal.
200
201:param mesh: Input mesh (modified in place).)");
202
203 m.def(
204 "close_small_holes",
205 [](MeshType& mesh, size_t max_hole_size, bool triangulate_holes) {
206 CloseSmallHolesOptions options;
207 options.max_hole_size = max_hole_size;
208 options.triangulate_holes = triangulate_holes;
209 close_small_holes(mesh, options);
210 },
211 "mesh"_a,
212 "max_hole_size"_a = CloseSmallHolesOptions().max_hole_size,
213 "triangulate_holes"_a = CloseSmallHolesOptions().triangulate_holes,
214 R"(Close small holes in a mesh.
215
216:param mesh: Input mesh (modified in place).
217:param max_hole_size: Maximum number of vertices on a hole to be closed.
218:param triangulate_holes: Whether to triangulate holes (if false, fill with polygons).)");
219
220 m.def(
221 "rescale_uv_charts",
222 [](MeshType& mesh,
223 std::string_view uv_attribute_name,
224 std::string_view chart_id_attribute_name,
225 double uv_area_threshold) {
226 RescaleUVOptions options;
227 options.uv_attribute_name = uv_attribute_name;
228 options.chart_id_attribute_name = chart_id_attribute_name;
229 options.uv_area_threshold = uv_area_threshold;
230 rescale_uv_charts(mesh, options);
231 },
232 "mesh"_a,
233 "uv_attribute_name"_a = RescaleUVOptions().uv_attribute_name,
234 "chart_id_attribute_name"_a = RescaleUVOptions().chart_id_attribute_name,
235 "uv_area_threshold"_a = RescaleUVOptions().uv_area_threshold,
236 R"(Rescale UV charts to match their 3D aspect ratios.
237
238:param mesh: Input mesh (modified in place).
239:param uv_attribute_name: UV attribute name for rescaling.
240 If empty, uses first UV attribute found.
241:param chart_id_attribute_name: Patch ID attribute name.
242 If empty, computes patches from UV chart connectivity.
243:param uv_area_threshold: UV area threshold.
244 Triangles below this threshold don't contribute to scale computation.
245)");
246}
247
248} // 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:29
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