Lagrange
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
28// clang-format off
29#include <lagrange/utils/warnoff.h>
30#include <nanobind/nanobind.h>
31#include <nanobind/stl/optional.h>
32#include <nanobind/stl/vector.h>
33#include <lagrange/utils/warnon.h>
34// clang-format on
35
36#include <vector>
37
38namespace lagrange::python {
39
40template <typename Scalar, typename Index>
41void bind_mesh_cleanup(nanobind::module_& m)
42{
43 namespace nb = nanobind;
44 using namespace nb::literals;
46
47 m.def(
48 "remove_isolated_vertices",
49 &remove_isolated_vertices<Scalar, Index>,
50 "mesh"_a,
51 R"(Remove isolated vertices from a mesh.
52
53.. note::
54 A vertex is considered isolated if it is not referenced by any facet.
55
56:param mesh: The input mesh for inplace modification.)");
57
58 m.def(
59 "detect_degenerate_facets",
60 &detect_degenerate_facets<Scalar, Index>,
61 "mesh"_a,
62 R"(Detect degenerate facets in a mesh.
63
64.. note::
65 Only __exaxtly__ degenerate facets are detected.
66
67:param mesh: The input mesh.
68
69:returns: A list of indices of degenerate facets.)");
70
71 m.def(
72 "remove_null_area_facets",
73 [](MeshType& mesh, double null_area_threshold, bool remove_isolated_vertices) {
74 RemoveNullAreaFacetsOptions opts;
75 opts.null_area_threshold = null_area_threshold;
76 opts.remove_isolated_vertices = remove_isolated_vertices;
77 remove_null_area_facets(mesh, opts);
78 },
79 "mesh"_a,
80 "null_area_threshold"_a = 0,
81 "remove_isolated_vertices"_a = false,
82 R"(Remove facets with unsigned facets area <= `null_area_threhsold`.
83
84:param mesh: The input mesh.
85:param null_area_threshold: The area threshold below which a facet is considered as null facet.
86:param remove_isolated_vertices: Whether to remove isolated vertices after removing null area facets.)");
87
88 m.def(
89 "remove_duplicate_vertices",
90 [](MeshType& mesh,
91 std::optional<std::vector<AttributeId>> extra_attributes,
92 bool boundary_only) {
93 RemoveDuplicateVerticesOptions opts;
94 if (extra_attributes.has_value()) {
95 opts.extra_attributes = std::move(extra_attributes.value());
96 }
97 opts.boundary_only = boundary_only;
98 remove_duplicate_vertices(mesh, opts);
99 },
100 "mesh"_a,
101 "extra_attributes"_a = nb::none(),
102 "boundary_only"_a = false,
103 R"(Remove duplicate vertices from a mesh.
104
105:param mesh: The input mesh.
106:param extra_attributes: Two vertices are considered duplicates if they have the same position and the same values for all attributes in `extra_attributes`.
107:param boundary_only: Only remove duplicate vertices on the boundary.)");
108
109 m.def(
110 "remove_duplicate_facets",
111 [](MeshType& mesh, bool consider_orientation) {
112 RemoveDuplicateFacetOptions opts;
113 opts.consider_orientation = consider_orientation;
114 remove_duplicate_facets(mesh, opts);
115 },
116 "mesh"_a,
117 "consider_orientation"_a = false,
118 R"(Remove duplicate facets from a mesh.
119
120Facets of different orientations (e.g. (0, 1, 2) and (2, 1, 0)) are considered as duplicates.
121If both orientations have equal number of duplicate facets, all of them are removed.
122If one orientation has more duplicate facets, all but one facet with the majority orientation are removed.
123
124:param mesh: The input mesh. The mesh is modified in place.
125:param consider_orientation: Whether to consider orientation when detecting duplicate facets.)");
126
127 m.def(
128 "remove_topologically_degenerate_facets",
129 &remove_topologically_degenerate_facets<Scalar, Index>,
130 "mesh"_a,
131 R"(Remove topologically degenerate facets such as (0, 1, 1)
132
133For general polygons, topologically degeneracy means that the polygon is made of at most two unique
134vertices. E.g. a quad of the form (0, 0, 1, 1) is degenerate, while a quad of the form (1, 1, 2, 3)
135is not.
136
137:param mesh: The input mesh for inplace modification.)");
138
139 m.def(
140 "remove_short_edges",
141 &remove_short_edges<Scalar, Index>,
142 "mesh"_a,
143 "threshold"_a = 0,
144 R"(Remove short edges from a mesh.
145
146:param mesh: The input mesh for inplace modification.
147:param threshold: The minimum edge length below which an edge is considered short.)");
148
149 m.def(
150 "resolve_vertex_nonmanifoldness",
151 &resolve_vertex_nonmanifoldness<Scalar, Index>,
152 "mesh"_a,
153 R"(Resolve vertex non-manifoldness in a mesh.
154
155:param mesh: The input mesh for inplace modification.
156
157:raises RuntimeError: If the input mesh is not edge-manifold.)");
158
159 m.def(
160 "resolve_nonmanifoldness",
161 &resolve_nonmanifoldness<Scalar, Index>,
162 "mesh"_a,
163 R"(Resolve both vertex and edge nonmanifoldness in a mesh.
164
165:param mesh: The input mesh for inplace modification.)");
166
167 m.def(
168 "split_long_edges",
169 [](MeshType& mesh,
170 float max_edge_length,
171 bool recursive,
172 std::optional<std::string_view> active_region_attribute,
173 std::optional<std::string_view> edge_length_attribute) {
174 SplitLongEdgesOptions opts;
175 opts.max_edge_length = max_edge_length;
176 opts.recursive = recursive;
177 if (active_region_attribute.has_value())
178 opts.active_region_attribute = active_region_attribute.value();
179 if (edge_length_attribute.has_value())
180 opts.edge_length_attribute = edge_length_attribute.value();
181 split_long_edges(mesh, std::move(opts));
182 },
183 "mesh"_a,
184 "max_edge_length"_a = 0.1f,
185 "recursive"_a = true,
186 "active_region_attribute"_a = nb::none(),
187 "edge_length_attribute"_a = nb::none(),
188 R"(Split edges that are longer than `max_edge_length`.
189
190:param mesh: The input mesh for inplace modification.
191:param max_edge_length: Maximum edge length. Edges longer than this value will be split.
192:param recursive: If true, the operation will be applied recursively until no edge is longer than `max_edge_length`.
193:param active_region_attribute: The facet attribute name that will be used to determine the active region.
194 If `None`, all edges will be considered.
195 Otherwise, only edges that are part of the active region will be split.
196 The attribute must be of value type `uint8_t`.
197:param edge_length_attribute: The edge length attribute name that will be used to determine the edge length.
198 If `None`, the function will compute the edge lengths.
199)");
200
201 m.def(
202 "remove_degenerate_facets",
203 &remove_degenerate_facets<Scalar, Index>,
204 "mesh"_a,
205 R"(Remove degenerate facets from a mesh.
206
207.. note::
208 The current method assumes input mesh is triangular.
209 Use `triangulate_polygonal_facets` if the input mesh is not triangular.
210 Non-degenerate facets adjacent to degenerte facets may be re-triangulated as a result of the removal.
211
212:param mesh: The mesh for inplace modification.)");
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: The input mesh for inplace modification.
228:param max_hole_size: The maximum number of vertices on a hole to be closed.
229:param triangulate_holes: Whether to triangulate holes. If false, holes will be filled by 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 such that their aspect ratios match their 3D images.
248
249:param mesh: The input mesh for inplace modification.
250:param uv_attribute_name: The uv attribute name to use for rescaling.
251 If empty, the first UV attribute found will be used.
252:param chart_id_attribute_name: The name used for storing the patch ID attribute.
253 If empty, patches will be computed based on UV chart connectivity.
254:param uv_area_threshold: The threshold for UV area.
255 UV triangles with area smaller than this threshold will not contribute to scale factor computation.
256)");
257}
258
259} // namespace lagrange::python
Definition: Mesh.h:48
A general purpose polygonal mesh class.
Definition: SurfaceMesh.h:66
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 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_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:66
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