Lagrange
Loading...
Searching...
No Matches
bind_utilities.h
1/*
2 * Copyright 2022 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/AttributeTypes.h>
15#include <lagrange/NormalWeightingType.h>
16#include <lagrange/cast_attribute.h>
17#include <lagrange/combine_meshes.h>
18#include <lagrange/compute_area.h>
19#include <lagrange/compute_centroid.h>
20#include <lagrange/compute_components.h>
21#include <lagrange/compute_dihedral_angles.h>
22#include <lagrange/compute_dijkstra_distance.h>
23#include <lagrange/compute_edge_lengths.h>
24#include <lagrange/compute_facet_circumcenter.h>
25#include <lagrange/compute_facet_normal.h>
26#include <lagrange/compute_greedy_coloring.h>
27#include <lagrange/compute_mesh_covariance.h>
28#include <lagrange/compute_normal.h>
29#include <lagrange/compute_pointcloud_pca.h>
30#include <lagrange/compute_seam_edges.h>
31#include <lagrange/compute_tangent_bitangent.h>
32#include <lagrange/compute_uv_charts.h>
33#include <lagrange/compute_uv_distortion.h>
34#include <lagrange/compute_vertex_normal.h>
35#include <lagrange/compute_vertex_valence.h>
36#include <lagrange/extract_submesh.h>
37#include <lagrange/filter_attributes.h>
38#include <lagrange/internal/constants.h>
39#include <lagrange/isoline.h>
40#include <lagrange/map_attribute.h>
41#include <lagrange/normalize_meshes.h>
42#include <lagrange/orient_outward.h>
43#include <lagrange/orientation.h>
44#include <lagrange/permute_facets.h>
45#include <lagrange/permute_vertices.h>
46#include <lagrange/python/binding.h>
47#include <lagrange/python/tensor_utils.h>
48#include <lagrange/python/utils/StackVector.h>
49#include <lagrange/remap_vertices.h>
50#include <lagrange/reorder_mesh.h>
51#include <lagrange/select_facets_by_normal_similarity.h>
52#include <lagrange/select_facets_in_frustum.h>
53#include <lagrange/separate_by_components.h>
54#include <lagrange/separate_by_facet_groups.h>
55#include <lagrange/split_facets_by_material.h>
56#include <lagrange/thicken_and_close_mesh.h>
57#include <lagrange/topology.h>
58#include <lagrange/transform_mesh.h>
59#include <lagrange/triangulate_polygonal_facets.h>
60#include <lagrange/unify_index_buffer.h>
61#include <lagrange/utils/invalid.h>
62#include <lagrange/uv_mesh.h>
63#include <lagrange/weld_indexed_attribute.h>
64
65#include <optional>
66#include <string_view>
67#include <vector>
68
69namespace lagrange::python {
70
71template <typename Scalar, typename Index>
72void bind_utilities(nanobind::module_& m)
73{
74 namespace nb = nanobind;
75 using namespace nb::literals;
76 using MeshType = SurfaceMesh<Scalar, Index>;
77
78 nb::enum_<NormalWeightingType>(m, "NormalWeightingType", "Normal weighting type.")
79 .value("Uniform", NormalWeightingType::Uniform, "Uniform weighting")
80 .value(
81 "CornerTriangleArea",
83 "Weight by corner triangle area")
84 .value("Angle", NormalWeightingType::Angle, "Weight by corner angle");
85
86 nb::class_<VertexNormalOptions>(
87 m,
88 "VertexNormalOptions",
89 "Options for computing vertex normals")
90 .def(nb::init<>())
91 .def_rw(
92 "output_attribute_name",
94 "Output attribute name. Default is `@vertex_normal`.")
95 .def_rw(
96 "weight_type",
98 "Weighting type for normal computation. Default is Angle.")
99 .def_rw(
100 "weighted_corner_normal_attribute_name",
102 R"(Precomputed weighted corner normals attribute name (default: @weighted_corner_normal).
103
104If attribute exists, the precomputed weighted corner normal will be used.)")
105 .def_rw(
106 "recompute_weighted_corner_normals",
108 "Whether to recompute weighted corner normals (default: false).")
109 .def_rw(
110 "keep_weighted_corner_normals",
112 "Whether to keep the weighted corner normal attribute (default: false).")
113 .def_rw(
114 "distance_tolerance",
116 "Distance tolerance for degenerate edge check in polygon facets.");
117
118 m.def(
119 "compute_vertex_normal",
121 "mesh"_a,
122 "options"_a = VertexNormalOptions(),
123 R"(Compute vertex normal.
124
125:param mesh: Input mesh.
126:param options: Options for computing vertex normals.
127
128:returns: Vertex normal attribute id.)");
129
130 m.def(
131 "compute_vertex_normal",
132 [](MeshType& mesh,
133 std::optional<std::string_view> output_attribute_name,
134 std::optional<NormalWeightingType> weight_type,
135 std::optional<std::string_view> weighted_corner_normal_attribute_name,
136 std::optional<bool> recompute_weighted_corner_normals,
137 std::optional<bool> keep_weighted_corner_normals,
138 std::optional<float> distance_tolerance) {
139 VertexNormalOptions options;
140 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
141 if (weight_type) options.weight_type = *weight_type;
142 if (weighted_corner_normal_attribute_name)
143 options.weighted_corner_normal_attribute_name =
144 *weighted_corner_normal_attribute_name;
145 if (recompute_weighted_corner_normals)
146 options.recompute_weighted_corner_normals = *recompute_weighted_corner_normals;
147 if (keep_weighted_corner_normals)
148 options.keep_weighted_corner_normals = *keep_weighted_corner_normals;
149 if (distance_tolerance) options.distance_tolerance = *distance_tolerance;
150
151 return compute_vertex_normal<Scalar, Index>(mesh, options);
152 },
153 "mesh"_a,
154 "output_attribute_name"_a = nb::none(),
155 "weight_type"_a = nb::none(),
156 "weighted_corner_normal_attribute_name"_a = nb::none(),
157 "recompute_weighted_corner_normals"_a = nb::none(),
158 "keep_weighted_corner_normals"_a = nb::none(),
159 "distance_tolerance"_a = nb::none(),
160 R"(Compute vertex normal (Pythonic API).
161
162:param mesh: Input mesh.
163:param output_attribute_name: Output attribute name.
164:param weight_type: Weighting type for normal computation.
165:param weighted_corner_normal_attribute_name: Precomputed weighted corner normals attribute name.
166:param recompute_weighted_corner_normals: Whether to recompute weighted corner normals.
167:param keep_weighted_corner_normals: Whether to keep the weighted corner normal attribute.
168:param distance_tolerance: Distance tolerance for degenerate edge check.
169 (Only used to bypass degenerate edge in polygon facets.)
170
171:returns: Vertex normal attribute id.)");
172
173 nb::class_<FacetNormalOptions>(m, "FacetNormalOptions", "Facet normal computation options.")
174 .def(nb::init<>())
175 .def_rw(
176 "output_attribute_name",
178 "Output attribute name. Default: `@facet_normal`");
179
180 m.def(
181 "compute_facet_normal",
183 "mesh"_a,
184 "options"_a = FacetNormalOptions(),
185 R"(Compute facet normal.
186
187:param mesh: Input mesh.
188:param options: Options for computing facet normals.
189
190:returns: Facet normal attribute id.)");
191
192 m.def(
193 "compute_facet_normal",
194 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
195 FacetNormalOptions options;
196 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
197 return compute_facet_normal<Scalar, Index>(mesh, options);
198 },
199 "mesh"_a,
200 "output_attribute_name"_a = nb::none(),
201 R"(Compute facet normal (Pythonic API).
202
203:param mesh: Input mesh.
204:param output_attribute_name: Output attribute name.
205
206:returns: Facet normal attribute id.)");
207
208 nb::class_<NormalOptions>(m, "NormalOptions", "Normal computation options.")
209 .def(nb::init<>())
210 .def_rw(
211 "output_attribute_name",
213 "Output attribute name. Default: `@normal`")
214 .def_rw(
215 "weight_type",
217 "Weighting type for normal computation. Default is Angle.")
218 .def_rw(
219 "facet_normal_attribute_name",
221 "Facet normal attribute name to use. Default is `@facet_normal`.")
222 .def_rw(
223 "recompute_facet_normals",
225 "Whether to recompute facet normals. Default is false.")
226 .def_rw(
227 "keep_facet_normals",
229 "Whether to keep the computed facet normal attribute. Default is false.")
230 .def_rw(
231 "distance_tolerance",
233 "Distance tolerance for degenerate edge check. (Only used to bypass degenerate edge in "
234 "polygon facets.)");
235
236 m.def(
237 "compute_normal",
238 [](MeshType& mesh,
239 Scalar feature_angle_threshold,
240 nb::object cone_vertices,
241 std::optional<NormalOptions> normal_options) {
242 NormalOptions options;
243 if (normal_options.has_value()) {
244 options = std::move(normal_options.value());
245 }
246
247 if (cone_vertices.is_none()) {
248 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, {}, options);
249 } else if (nb::isinstance<nb::list>(cone_vertices)) {
250 auto cone_vertices_list = nb::cast<std::vector<Index>>(cone_vertices);
251 span<const Index> data{cone_vertices_list.data(), cone_vertices_list.size()};
252 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, data, options);
253 } else if (nb::isinstance<Tensor<Index>>(cone_vertices)) {
254 auto cone_vertices_tensor = nb::cast<Tensor<Index>>(cone_vertices);
255 auto [data, shape, stride] = tensor_to_span(cone_vertices_tensor);
256 la_runtime_assert(is_dense(shape, stride));
257 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, data, options);
258 } else {
259 throw std::runtime_error("Invalid cone_vertices type");
260 }
261 },
262 "mesh"_a,
263 "feature_angle_threshold"_a = lagrange::internal::pi / 4,
264 "cone_vertices"_a = nb::none(),
265 "options"_a = nb::none(),
266 R"(Compute indexed normal attribute.
267
268Edge with dihedral angles larger than `feature_angle_threshold` are considered as sharp edges.
269Vertices listed in `cone_vertices` are considered as cone vertices, which is always sharp.
270
271:param mesh: input mesh
272:param feature_angle_threshold: feature angle threshold
273:param cone_vertices: cone vertices
274:param options: normal options
275
276:returns: the id of the indexed normal attribute.
277)");
278
279 m.def(
280 "compute_normal",
281 [](MeshType& mesh,
282 Scalar feature_angle_threshold,
283 nb::object cone_vertices,
284 std::optional<std::string_view> output_attribute_name,
285 std::optional<NormalWeightingType> weight_type,
286 std::optional<std::string_view> facet_normal_attribute_name,
287 std::optional<bool> recompute_facet_normals,
288 std::optional<bool> keep_facet_normals,
289 std::optional<float> distance_tolerance) {
290 NormalOptions options;
291 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
292 if (weight_type) options.weight_type = *weight_type;
293 if (facet_normal_attribute_name)
294 options.facet_normal_attribute_name = *facet_normal_attribute_name;
295 if (recompute_facet_normals) options.recompute_facet_normals = *recompute_facet_normals;
296 if (keep_facet_normals) options.keep_facet_normals = *keep_facet_normals;
297 if (distance_tolerance) options.distance_tolerance = *distance_tolerance;
298
299 if (cone_vertices.is_none()) {
300 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, {}, options);
301 } else if (nb::isinstance<nb::list>(cone_vertices)) {
302 auto cone_vertices_list = nb::cast<std::vector<Index>>(cone_vertices);
303 span<const Index> data{cone_vertices_list.data(), cone_vertices_list.size()};
304 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, data, options);
305 } else if (nb::isinstance<Tensor<Index>>(cone_vertices)) {
306 auto cone_vertices_tensor = nb::cast<Tensor<Index>>(cone_vertices);
307 auto [data, shape, stride] = tensor_to_span(cone_vertices_tensor);
308 la_runtime_assert(is_dense(shape, stride));
309 return compute_normal<Scalar, Index>(mesh, feature_angle_threshold, data, options);
310 } else {
311 throw std::runtime_error("Invalid cone_vertices type");
312 }
313 },
314 "mesh"_a,
315 "feature_angle_threshold"_a = lagrange::internal::pi / 4,
316 "cone_vertices"_a = nb::none(),
317 "output_attribute_name"_a = nb::none(),
318 "weight_type"_a = nb::none(),
319 "facet_normal_attribute_name"_a = nb::none(),
320 "recompute_facet_normals"_a = nb::none(),
321 "keep_facet_normals"_a = nb::none(),
322 "distance_tolerance"_a = nb::none(),
323 R"(Compute indexed normal attribute (Pythonic API).
324
325:param mesh: input mesh
326:param feature_angle_threshold: feature angle threshold
327:param cone_vertices: cone vertices
328:param output_attribute_name: output normal attribute name
329:param weight_type: normal weighting type
330:param facet_normal_attribute_name: facet normal attribute name
331:param recompute_facet_normals: whether to recompute facet normals
332:param keep_facet_normals: whether to keep the computed facet normal attribute
333:param distance_tolerance: distance tolerance for degenerate edge check
334 (only used to bypass degenerate edges in polygon facets)
335
336:returns: the id of the indexed normal attribute.)");
337
338 using ConstArray3d = nb::ndarray<const double, nb::shape<-1, 3>, nb::c_contig, nb::device::cpu>;
339 m.def(
340 "compute_pointcloud_pca",
341 [](ConstArray3d points, bool shift_centroid, bool normalize) {
342 ComputePointcloudPCAOptions options;
343 options.shift_centroid = shift_centroid;
344 options.normalize = normalize;
345 PointcloudPCAOutput<Scalar> output =
346 compute_pointcloud_pca<Scalar>({points.data(), points.size()}, options);
347 return std::make_tuple(output.center, output.eigenvectors, output.eigenvalues);
348 },
349 "points"_a,
350 "shift_centroid"_a = ComputePointcloudPCAOptions().shift_centroid,
351 "normalize"_a = ComputePointcloudPCAOptions().normalize,
352 R"(Compute principal components of a point cloud.
353
354:param points: Input points.
355:param shift_centroid: When true: covariance = (P-centroid)^T (P-centroid), when false: covariance = (P)^T (P).
356:param normalize: Should we divide the result by number of points?
357
358:returns: tuple of (center, eigenvectors, eigenvalues).)");
359
360 m.def(
361 "compute_greedy_coloring",
362 [](MeshType& mesh,
363 AttributeElement element_type,
364 size_t num_color_used,
365 std::optional<std::string_view> output_attribute_name) {
366 GreedyColoringOptions options;
367 options.element_type = element_type;
368 options.num_color_used = num_color_used;
369 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
370 return compute_greedy_coloring<Scalar, Index>(mesh, options);
371 },
372 "mesh"_a,
373 "element_type"_a = AttributeElement::Facet,
374 "num_color_used"_a = 8,
375 "output_attribute_name"_a = nb::none(),
376 R"(Compute greedy coloring of mesh elements.
377
378:param mesh: Input mesh.
379:param element_type: Element type to be colored. Can be either Vertex or Facet.
380:param num_color_used: Minimum number of colors to use. The algorithm will cycle through them but may use more.
381:param output_attribute_name: Output attribute name.
382
383:returns: Color attribute id.)");
384
385 m.def(
386 "normalize_mesh_with_transform",
387 [](MeshType& mesh,
388 bool normalize_normals,
389 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 4, 4> {
390 TransformOptions options;
391 options.normalize_normals = normalize_normals;
392 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
393 return normalize_mesh_with_transform(mesh, options).matrix();
394 },
395 "mesh"_a,
396 "normalize_normals"_a = TransformOptions().normalize_normals,
397 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
398 R"(Normalize a mesh to fit into a unit box centered at the origin.
399
400:param mesh: Input mesh.
401:param normalize_normals: Whether to normalize normals.
402:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
403
404:return Inverse transform, can be used to undo the normalization process.)");
405
406
407 m.def(
408 "normalize_mesh_with_transform_2d",
409 [](MeshType& mesh,
410 bool normalize_normals,
411 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 3, 3> {
412 TransformOptions options;
413 options.normalize_normals = normalize_normals;
414 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
415 return normalize_mesh_with_transform<2>(mesh, options).matrix();
416 },
417 "mesh"_a,
418 "normalize_normals"_a = TransformOptions().normalize_normals,
419 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
420 R"(Normalize a mesh to fit into a unit box centered at the origin.
421
422:param mesh: Input mesh.
423:param normalize_normals: Whether to normalize normals.
424:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
425
426:return Inverse transform, can be used to undo the normalization process.)");
427
428 m.def(
429 "normalize_mesh",
430 [](MeshType& mesh, bool normalize_normals, bool normalize_tangents_bitangents) -> void {
431 TransformOptions options;
432 options.normalize_normals = normalize_normals;
433 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
434 normalize_mesh(mesh, options);
435 },
436 "mesh"_a,
437 "normalize_normals"_a = TransformOptions().normalize_normals,
438 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
439 R"(Normalize a mesh to fit into a unit box centered at the origin.
440
441:param mesh: Input mesh.
442:param normalize_normals: Whether to normalize normals.
443:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.)");
444
445 m.def(
446 "normalize_meshes_with_transform",
447 [](std::vector<MeshType*> meshes,
448 bool normalize_normals,
449 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 4, 4> {
450 TransformOptions options;
451 options.normalize_normals = normalize_normals;
452 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
453 span<MeshType*> meshes_span(meshes.data(), meshes.size());
454 return normalize_meshes_with_transform(meshes_span, options).matrix();
455 },
456 "meshes"_a,
457 "normalize_normals"_a = TransformOptions().normalize_normals,
458 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
459 R"(Normalize a mesh to fit into a unit box centered at the origin.
460
461:param meshes: Input meshes.
462:param normalize_normals: Whether to normalize normals.
463:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
464
465:return Inverse transform, can be used to undo the normalization process.)");
466
467 m.def(
468 "normalize_meshes_with_transform_2d",
469 [](std::vector<MeshType*> meshes,
470 bool normalize_normals,
471 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 3, 3> {
472 TransformOptions options;
473 options.normalize_normals = normalize_normals;
474 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
475 span<MeshType*> meshes_span(meshes.data(), meshes.size());
476 return normalize_meshes_with_transform<2>(meshes_span, options).matrix();
477 },
478 "meshes"_a,
479 "normalize_normals"_a = TransformOptions().normalize_normals,
480 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
481 R"(Normalize a mesh to fit into a unit box centered at the origin.
482
483:param meshes: Input meshes.
484:param normalize_normals: Whether to normalize normals.
485:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
486
487:return Inverse transform, can be used to undo the normalization process.)");
488
489
490 m.def(
491 "normalize_meshes",
492 [](std::vector<MeshType*> meshes,
493 bool normalize_normals,
494 bool normalize_tangents_bitangents) {
495 TransformOptions options;
496 options.normalize_normals = normalize_normals;
497 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
498 span<MeshType*> meshes_span(meshes.data(), meshes.size());
499 normalize_meshes(meshes_span, options);
500 },
501 "meshes"_a,
502 "normalize_normals"_a = TransformOptions().normalize_normals,
503 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
504 R"(Normalize a list of meshes to fit into a unit box centered at the origin.
505
506:param meshes: Input meshes.
507:param normalize_normals: Whether to normalize normals.
508:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.)");
509
510 m.def(
511 "combine_meshes",
512 [](std::vector<MeshType*> meshes, bool preserve_vertices) {
514 meshes.size(),
515 [&](size_t i) -> const MeshType& { return *meshes[i]; },
516 preserve_vertices);
517 },
518 "meshes"_a,
519 "preserve_attributes"_a = true,
520 R"(Combine a list of meshes into a single mesh.
521
522:param meshes: Input meshes.
523:param preserve_attributes: Whether to preserve attributes.
524
525:returns: The combined mesh.)");
526
527 m.def(
528 "compute_seam_edges",
529 [](MeshType& mesh,
530 AttributeId indexed_attribute_id,
531 std::optional<std::string_view> output_attribute_name) {
532 SeamEdgesOptions options;
533 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
534 return compute_seam_edges<Scalar, Index>(mesh, indexed_attribute_id, options);
535 },
536 "mesh"_a,
537 "indexed_attribute_id"_a,
538 "output_attribute_name"_a = nb::none(),
539 R"(Compute seam edges for a given indexed attribute.
540
541:param mesh: Input mesh.
542:param indexed_attribute_id: Input indexed attribute id.
543:param output_attribute_name: Output attribute name.
544
545:returns: Attribute id for the output per-edge seam attribute (1 is a seam, 0 is not).)");
546
547 m.def(
548 "orient_outward",
549 [](MeshType& mesh, bool positive) {
550 OrientOptions options;
551 options.positive = positive;
552 orient_outward<Scalar, Index>(mesh, options);
553 },
554 "mesh"_a,
555 "positive"_a = OrientOptions().positive,
556 R"(Orient mesh facets to ensure positive or negative signed volume.
557
558:param mesh: Input mesh.
559:param positive: Whether to orient volumes positively or negatively.)");
560
561 m.def(
562 "unify_index_buffer",
563 [](MeshType& mesh) { return unify_index_buffer(mesh); },
564 "mesh"_a,
565 R"(Unify the index buffer for all indexed attributes.
566
567:param mesh: Input mesh.
568
569:returns: Unified mesh.)");
570
571 m.def(
572 "unify_index_buffer",
574 "mesh"_a,
575 "attribute_ids"_a,
576 R"(Unify the index buffer for selected attributes.
577
578:param mesh: Input mesh.
579:param attribute_ids: Attribute IDs to unify.
580
581:returns: Unified mesh.)");
582
583 m.def(
584 "unify_index_buffer",
586 "mesh"_a,
587 "attribute_names"_a,
588 R"(Unify the index buffer for selected attributes.
589
590:param mesh: Input mesh.
591:param attribute_names: Attribute names to unify.
592
593:returns: Unified mesh.)");
594
595 m.def(
596 "triangulate_polygonal_facets",
597 [](MeshType& mesh, std::string_view scheme) {
598 lagrange::TriangulationOptions opt;
599 if (scheme == "earcut") {
601 } else if (scheme == "centroid_fan") {
603 } else {
604 throw Error(fmt::format("Unsupported triangulation scheme {}", scheme));
605 }
607 },
608 "mesh"_a,
609 "scheme"_a = "earcut",
610 R"(Triangulate polygonal facets of the mesh.
611
612:param mesh: The input mesh to be triangulated in place.
613:param scheme: The triangulation scheme (options are 'earcut' and 'centroid_fan'))");
614
615 nb::enum_<ComponentOptions::ConnectivityType>(m, "ConnectivityType", "Mesh connectivity type")
616 .value(
617 "Vertex",
618 ComponentOptions::ConnectivityType::Vertex,
619 "Two facets are connected if they share a vertex")
620 .value(
621 "Edge",
622 ComponentOptions::ConnectivityType::Edge,
623 "Two facets are connected if they share an edge");
624
625 m.def(
626 "compute_components",
627 [](MeshType& mesh,
628 std::optional<std::string_view> output_attribute_name,
629 std::optional<lagrange::ConnectivityType> connectivity_type,
630 std::optional<nb::list>& blocker_elements) {
631 lagrange::ComponentOptions opt;
632 if (output_attribute_name.has_value()) {
633 opt.output_attribute_name = output_attribute_name.value();
634 }
635 if (connectivity_type.has_value()) {
636 opt.connectivity_type = connectivity_type.value();
637 }
638 std::vector<Index> blocker_elements_vec;
639 if (blocker_elements.has_value()) {
640 for (auto val : blocker_elements.value()) {
641 blocker_elements_vec.push_back(nb::cast<Index>(val));
642 }
643 }
644 return lagrange::compute_components<Scalar, Index>(mesh, blocker_elements_vec, opt);
645 },
646 "mesh"_a,
647 "output_attribute_name"_a = nb::none(),
648 "connectivity_type"_a = nb::none(),
649 "blocker_elements"_a = nb::none(),
650 R"(Compute connected components.
651
652This method will create a per-facet component id attribute named by the `output_attribute_name`
653argument. Each component id is in [0, num_components-1] range.
654
655:param mesh: The input mesh.
656:param output_attribute_name: The name of the output attribute.
657:param connectivity_type: The connectivity type. Either "Vertex" or "Edge".
658:param blocker_elements: The list of blocker element indices. If `connectivity_type` is `Edge`, facets adjacent to a blocker edge are not considered as connected through this edge. If `connectivity_type` is `Vertex`, facets sharing a blocker vertex are not considered as connected through this vertex.
659
660:returns: The total number of components.)");
661
662 nb::class_<VertexValenceOptions>(m, "VertexValenceOptions", "Vertex valence options")
663 .def(nb::init<>())
664 .def_rw(
665 "output_attribute_name",
667 "The name of the output attribute")
668 .def_rw(
669 "induced_by_attribute",
671 "Optional per-edge attribute used as indicator function to restrict the graph used for "
672 "vertex valence computation");
673
674 m.def(
675 "compute_vertex_valence",
677 "mesh"_a,
678 "options"_a = VertexValenceOptions(),
679 R"(Compute vertex valence
680
681:param mesh: The input mesh.
682:param options: The vertex valence options.
683
684:returns: The vertex valence attribute id.)");
685
686 m.def(
687 "compute_vertex_valence",
688 [](MeshType& mesh,
689 std::optional<std::string_view> output_attribute_name,
690 std::optional<std::string_view> induced_by_attribute) {
691 VertexValenceOptions opt;
692 if (output_attribute_name.has_value()) {
693 opt.output_attribute_name = output_attribute_name.value();
694 }
695 if (induced_by_attribute.has_value()) {
696 opt.induced_by_attribute = induced_by_attribute.value();
697 }
699 },
700 "mesh"_a,
701 "output_attribute_name"_a = nb::none(),
702 "induced_by_attribute"_a = nb::none(),
703 R"(Compute vertex valence);
704
705:param mesh: The input mesh.
706:param output_attribute_name: The name of the output attribute.
707:param induced_by_attribute: Optional per-edge attribute used as indicator function to restrict the graph used for vertex valence computation.
708
709:returns: The vertex valence attribute id)");
710
711 nb::class_<TangentBitangentOptions>(m, "TangentBitangentOptions", "Tangent bitangent options")
712 .def(nb::init<>())
713 .def_rw(
714 "tangent_attribute_name",
716 "The name of the output tangent attribute, default is `@tangent`")
717 .def_rw(
718 "bitangent_attribute_name",
720 "The name of the output bitangent attribute, default is `@bitangent`")
721 .def_rw(
722 "uv_attribute_name",
724 "The name of the uv attribute")
725 .def_rw(
726 "normal_attribute_name",
728 "The name of the normal attribute")
729 .def_rw(
730 "output_element_type",
732 "The output element type")
733 .def_rw(
734 "pad_with_sign",
736 "Whether to pad the output tangent/bitangent with sign")
737 .def_rw(
738 "orthogonalize_bitangent",
740 "Whether to compute the bitangent as cross(normal, tangent). If false, the bitangent "
741 "is computed as the derivative of v-coordinate")
742 .def_rw(
743 "keep_existing_tangent",
745 "Whether to recompute tangent if the tangent attribute (specified by "
746 "tangent_attribute_name) already exists. If true, bitangent is computed by normalizing "
747 "cross(normal, tangent) and param orthogonalize_bitangent must be true.");
748 nb::class_<TangentBitangentResult>(m, "TangentBitangentResult", "Tangent bitangent result")
749 .def(nb::init<>())
750 .def_rw(
751 "tangent_id",
753 "The output tangent attribute id")
754 .def_rw(
755 "bitangent_id",
757 "The output bitangent attribute id");
758
759 m.def(
760 "compute_tangent_bitangent",
762 "mesh"_a,
763 "options"_a = TangentBitangentOptions(),
764 R"(Compute tangent and bitangent vector attributes.
765
766:param mesh: The input mesh.
767:param options: The tangent bitangent options.
768
769:returns: The tangent and bitangent attribute ids)");
770
771 m.def(
772 "compute_tangent_bitangent",
773 [](MeshType& mesh,
774 std::optional<std::string_view>(tangent_attribute_name),
775 std::optional<std::string_view>(bitangent_attribute_name),
776 std::optional<std::string_view>(uv_attribute_name),
777 std::optional<std::string_view>(normal_attribute_name),
778 std::optional<AttributeElement>(output_attribute_type),
779 std::optional<bool>(pad_with_sign),
780 std::optional<bool>(orthogonalize_bitangent),
781 std::optional<bool>(keep_existing_tangent)) {
782 TangentBitangentOptions opt;
783 if (tangent_attribute_name.has_value()) {
784 opt.tangent_attribute_name = tangent_attribute_name.value();
785 }
786 if (bitangent_attribute_name.has_value()) {
787 opt.bitangent_attribute_name = bitangent_attribute_name.value();
788 }
789 if (uv_attribute_name.has_value()) {
790 opt.uv_attribute_name = uv_attribute_name.value();
791 }
792 if (normal_attribute_name.has_value()) {
793 opt.normal_attribute_name = normal_attribute_name.value();
794 }
795 if (output_attribute_type.has_value()) {
796 opt.output_element_type = output_attribute_type.value();
797 }
798 if (pad_with_sign.has_value()) {
799 opt.pad_with_sign = pad_with_sign.value();
800 }
801 if (orthogonalize_bitangent.has_value()) {
802 opt.orthogonalize_bitangent = orthogonalize_bitangent.value();
803 }
804 if (keep_existing_tangent.has_value()) {
805 opt.keep_existing_tangent = keep_existing_tangent.value();
806 }
807
809 return std::make_tuple(r.tangent_id, r.bitangent_id);
810 },
811 "mesh"_a,
812 "tangent_attribute_name"_a = nb::none(),
813 "bitangent_attribute_name"_a = nb::none(),
814 "uv_attribute_name"_a = nb::none(),
815 "normal_attribute_name"_a = nb::none(),
816 "output_attribute_type"_a = nb::none(),
817 "pad_with_sign"_a = nb::none(),
818 "orthogonalize_bitangent"_a = nb::none(),
819 "keep_existing_tangent"_a = nb::none(),
820 R"(Compute tangent and bitangent vector attributes (Pythonic API).
821
822:param mesh: The input mesh.
823:param tangent_attribute_name: The name of the output tangent attribute.
824:param bitangent_attribute_name: The name of the output bitangent attribute.
825:param uv_attribute_name: The name of the uv attribute.
826:param normal_attribute_name: The name of the normal attribute.
827:param output_attribute_type: The output element type.
828:param pad_with_sign: Whether to pad the output tangent/bitangent with sign.
829:param orthogonalize_bitangent: Whether to compute the bitangent as sign * cross(normal, tangent).
830:param keep_existing_tangent: Whether to recompute tangent if the tangent attribute (specified by tangent_attribute_name) already exists. If true, bitangent is computed by normalizing cross(normal, tangent) and param orthogonalize_bitangent must be true.
831
832:returns: The tangent and bitangent attribute ids)");
833
834 m.def(
835 "map_attribute",
836 static_cast<AttributeId (*)(MeshType&, AttributeId, std::string_view, AttributeElement)>(
838 "mesh"_a,
839 "old_attribute_id"_a,
840 "new_attribute_name"_a,
841 "new_element"_a,
842 R"(Map an attribute to a new element type.
843
844:param mesh: The input mesh.
845:param old_attribute_id: The id of the input attribute.
846:param new_attribute_name: The name of the new attribute.
847:param new_element: The new element type.
848
849:returns: The id of the new attribute.)");
850
851 m.def(
852 "map_attribute",
853 static_cast<
854 AttributeId (*)(MeshType&, std::string_view, std::string_view, AttributeElement)>(
856 "mesh"_a,
857 "old_attribute_name"_a,
858 "new_attribute_name"_a,
859 "new_element"_a,
860 R"(Map an attribute to a new element type.
861
862:param mesh: The input mesh.
863:param old_attribute_name: The name of the input attribute.
864:param new_attribute_name: The name of the new attribute.
865:param new_element: The new element type.
866
867:returns: The id of the new attribute.)");
868
869 m.def(
870 "map_attribute_in_place",
871 static_cast<AttributeId (*)(MeshType&, AttributeId, AttributeElement)>(
873 "mesh"_a,
874 "id"_a,
875 "new_element"_a,
876 R"(Map an attribute to a new element type in place.
877
878:param mesh: The input mesh.
879:param id: The id of the input attribute.
880:param new_element: The new element type.
881
882:returns: The id of the new attribute.)");
883
884 m.def(
885 "map_attribute_in_place",
886 static_cast<AttributeId (*)(MeshType&, std::string_view, AttributeElement)>(
888 "mesh"_a,
889 "name"_a,
890 "new_element"_a,
891 R"(Map an attribute to a new element type in place.
892
893:param mesh: The input mesh.
894:param name: The name of the input attribute.
895:param new_element: The new element type.
896
897:returns: The id of the new attribute.)");
898
899 nb::class_<FacetAreaOptions>(m, "FacetAreaOptions", "Options for computing facet area.")
900 .def(nb::init<>())
901 .def_rw(
902 "output_attribute_name",
904 "The name of the output attribute.");
905
906 m.def(
907 "compute_facet_area",
909 "mesh"_a,
910 "options"_a = FacetAreaOptions(),
911 R"(Compute facet area.
912
913:param mesh: The input mesh.
914:param options: The options for computing facet area.
915
916:returns: The id of the new attribute.)");
917
918 m.def(
919 "compute_facet_area",
920 [](MeshType& mesh, std::optional<std::string_view> name) {
921 FacetAreaOptions opt;
922 if (name.has_value()) {
923 opt.output_attribute_name = name.value();
924 }
926 },
927 "mesh"_a,
928 "output_attribute_name"_a = nb::none(),
929 R"(Compute facet area (Pythonic API).
930
931:param mesh: The input mesh.
932:param output_attribute_name: The name of the output attribute.
933
934:returns: The id of the new attribute.)");
935
936 m.def(
937 "compute_facet_vector_area",
938 [](MeshType& mesh, std::optional<std::string_view> name) {
939 FacetVectorAreaOptions opt;
940 if (name.has_value()) {
941 opt.output_attribute_name = name.value();
942 }
944 },
945 "mesh"_a,
946 "output_attribute_name"_a = nb::none(),
947 R"(Compute facet vector area (Pythonic API).
948
949Vector area is defined as the area multiplied by the facet normal.
950For triangular facets, it is equivalent to half of the cross product of two edges.
951For non-planar polygonal facets, the vector area offers a robust way to compute the area and normal.
952The magnitude of the vector area is the largest area of any orthogonal projection of the facet.
953The direction of the vector area is the normal direction that maximizes the projected area [1, 2].
954
955[1] Sullivan, John M. "Curvatures of smooth and discrete surfaces." Discrete differential geometry.
956Basel: Birkhäuser Basel, 2008. 175-188.
957
958[2] Alexa, Marc, and Max Wardetzky. "Discrete Laplacians on general polygonal meshes." ACM SIGGRAPH
9592011 papers. 2011. 1-10.
960
961:param mesh: The input mesh.
962:param output_attribute_name: The name of the output attribute.
963
964:returns: The id of the new attribute.)");
965
966 nb::class_<MeshAreaOptions>(m, "MeshAreaOptions", "Options for computing mesh area.")
967 .def(nb::init<>())
968 .def_rw(
969 "input_attribute_name",
971 "The name of the pre-computed facet area attribute, default is `@facet_area`.")
972 .def_rw(
973 "use_signed_area",
975 "Whether to use signed area.");
976
977 m.def(
978 "compute_mesh_area",
980 "mesh"_a,
981 "options"_a = MeshAreaOptions(),
982 R"(Compute mesh area.
983
984:param mesh: The input mesh.
985:param options: The options for computing mesh area.
986
987:returns: The mesh area.)");
988
989 m.def(
990 "compute_uv_area",
992 "mesh"_a,
993 "options"_a = MeshAreaOptions(),
994 R"(Compute UV mesh area.
995
996:param mesh: The input mesh.
997:param options: The options for computing mesh area.
998
999:returns: The UV mesh area.)");
1000
1001 m.def(
1002 "compute_mesh_area",
1003 [](MeshType& mesh,
1004 std::optional<std::string_view> input_attribute_name,
1005 std::optional<bool> use_signed_area) {
1006 MeshAreaOptions opt;
1007 if (input_attribute_name.has_value()) {
1008 opt.input_attribute_name = input_attribute_name.value();
1009 }
1010 if (use_signed_area.has_value()) {
1011 opt.use_signed_area = use_signed_area.value();
1012 }
1013 return compute_mesh_area(mesh, opt);
1014 },
1015 "mesh"_a,
1016 "input_attribute_name"_a = nb::none(),
1017 "use_signed_area"_a = nb::none(),
1018 R"(Compute mesh area (Pythonic API).
1019
1020:param mesh: The input mesh.
1021:param input_attribute_name: The name of the pre-computed facet area attribute.
1022:param use_signed_area: Whether to use signed area.
1023
1024:returns: The mesh area.)");
1025
1026 nb::class_<FacetCentroidOptions>(m, "FacetCentroidOptions", "Facet centroid options.")
1027 .def(nb::init<>())
1028 .def_rw(
1029 "output_attribute_name",
1031 "The name of the output attribute.");
1032 m.def(
1033 "compute_facet_centroid",
1035 "mesh"_a,
1036 "options"_a = FacetCentroidOptions(),
1037 R"(Compute facet centroid.
1038
1039:param mesh: The input mesh.
1040:param options: The options for computing facet centroid.
1041
1042:returns: The id of the new attribute.)");
1043
1044 m.def(
1045 "compute_facet_centroid",
1046 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1047 FacetCentroidOptions opt;
1048 if (output_attribute_name.has_value()) {
1049 opt.output_attribute_name = output_attribute_name.value();
1050 }
1052 },
1053 "mesh"_a,
1054 "output_attribute_name"_a = nb::none(),
1055 R"(Compute facet centroid (Pythonic API).
1056
1057:param mesh: Input mesh.
1058:param output_attribute_name: Output attribute name.
1059
1060:returns: Attribute ID.)");
1061
1062 m.def(
1063 "compute_facet_circumcenter",
1064 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1065 FacetCircumcenterOptions opt;
1066 if (output_attribute_name.has_value()) {
1067 opt.output_attribute_name = output_attribute_name.value();
1068 }
1070 },
1071 "mesh"_a,
1072 "output_attribute_name"_a = nb::none(),
1073 R"(Compute facet circumcenter (Pythonic API).
1074
1075:param mesh: The input mesh.
1076:param output_attribute_name: The name of the output attribute.
1077
1078:returns: The id of the new attribute.)");
1079
1080 nb::enum_<MeshCentroidOptions::WeightingType>(
1081 m,
1082 "CentroidWeightingType",
1083 "Centroid weighting type.")
1084 .value("Uniform", MeshCentroidOptions::Uniform, "Uniform weighting.")
1085 .value("Area", MeshCentroidOptions::Area, "Area weighting.");
1086
1087 nb::class_<MeshCentroidOptions>(m, "MeshCentroidOptions", "Mesh centroid options.")
1088 .def(nb::init<>())
1089 .def_rw("weighting_type", &MeshCentroidOptions::weighting_type, "The weighting type.")
1090 .def_rw(
1091 "facet_centroid_attribute_name",
1093 "The name of the pre-computed facet centroid attribute if available.")
1094 .def_rw(
1095 "facet_area_attribute_name",
1097 "The name of the pre-computed facet area attribute if available.");
1098
1099 m.def(
1100 "compute_mesh_centroid",
1101 [](const MeshType& mesh, MeshCentroidOptions opt) {
1102 const Index dim = mesh.get_dimension();
1103 std::vector<Scalar> centroid(dim, invalid<Scalar>());
1104 compute_mesh_centroid<Scalar, Index>(mesh, centroid, opt);
1105 return centroid;
1106 },
1107 "mesh"_a,
1108 "options"_a = MeshCentroidOptions(),
1109 R"(Compute mesh centroid.
1110
1111:param mesh: Input mesh.
1112:param options: Centroid computation options.
1113
1114:returns: Mesh centroid coordinates.)");
1115
1116 m.def(
1117 "compute_mesh_centroid",
1118 [](MeshType& mesh,
1119 std::optional<MeshCentroidOptions::WeightingType> weighting_type,
1120 std::optional<std::string_view> facet_centroid_attribute_name,
1121 std::optional<std::string_view> facet_area_attribute_name) {
1122 MeshCentroidOptions opt;
1123 if (weighting_type.has_value()) {
1124 opt.weighting_type = weighting_type.value();
1125 }
1126 if (facet_centroid_attribute_name.has_value()) {
1127 opt.facet_centroid_attribute_name = facet_centroid_attribute_name.value();
1128 }
1129 if (facet_area_attribute_name.has_value()) {
1130 opt.facet_area_attribute_name = facet_area_attribute_name.value();
1131 }
1132 const Index dim = mesh.get_dimension();
1133 std::vector<Scalar> centroid(dim, invalid<Scalar>());
1134 compute_mesh_centroid<Scalar, Index>(mesh, centroid, opt);
1135 return centroid;
1136 },
1137 "mesh"_a,
1138 "weighting_type"_a = nb::none(),
1139 "facet_centroid_attribute_name"_a = nb::none(),
1140 "facet_area_attribute_name"_a = nb::none(),
1141 R"(Compute mesh centroid (Pythonic API).
1142
1143:param mesh: Input mesh.
1144:param weighting_type: Weighting type (default: Area).
1145:param facet_centroid_attribute_name: Pre-computed facet centroid attribute name.
1146:param facet_area_attribute_name: Pre-computed facet area attribute name.
1147
1148:returns: Mesh centroid coordinates.)");
1149
1150 m.def(
1151 "permute_vertices",
1152 [](MeshType& mesh, Tensor<Index> new_to_old) {
1153 auto [data, shape, stride] = tensor_to_span(new_to_old);
1154 la_runtime_assert(is_dense(shape, stride));
1156 },
1157 "mesh"_a,
1158 "new_to_old"_a,
1159 R"(Reorder vertices of a mesh in place based on a permutation.
1160
1161:param mesh: input mesh
1162:param new_to_old: permutation vector for vertices)");
1163
1164 m.def(
1165 "permute_facets",
1166 [](MeshType& mesh, Tensor<Index> new_to_old) {
1167 auto [data, shape, stride] = tensor_to_span(new_to_old);
1168 la_runtime_assert(is_dense(shape, stride));
1170 },
1171 "mesh"_a,
1172 "new_to_old"_a,
1173 R"(Reorder facets of a mesh in place based on a permutation.
1174
1175:param mesh: input mesh
1176:param new_to_old: permutation vector for facets)");
1177
1178 nb::enum_<MappingPolicy>(m, "MappingPolicy", "Mapping policy for handling collisions.")
1179 .value("Average", MappingPolicy::Average, "Compute the average of the collided values.")
1180 .value("KeepFirst", MappingPolicy::KeepFirst, "Keep the first collided value.")
1181 .value("Error", MappingPolicy::Error, "Throw an error when collision happens.");
1182
1183 nb::class_<RemapVerticesOptions>(m, "RemapVerticesOptions", "Options for remapping vertices.")
1184 .def(nb::init<>())
1185 .def_rw(
1186 "collision_policy_float",
1188 "The collision policy for float attributes.")
1189 .def_rw(
1190 "collision_policy_integral",
1192 "The collision policy for integral attributes.");
1193
1194 m.def(
1195 "remap_vertices",
1196 [](MeshType& mesh, Tensor<Index> old_to_new, RemapVerticesOptions opt) {
1197 auto [data, shape, stride] = tensor_to_span(old_to_new);
1198 la_runtime_assert(is_dense(shape, stride));
1199 remap_vertices<Scalar, Index>(mesh, data, opt);
1200 },
1201 "mesh"_a,
1202 "old_to_new"_a,
1203 "options"_a = RemapVerticesOptions(),
1204 R"(Remap vertices of a mesh in place based on a permutation.
1205
1206:param mesh: input mesh
1207:param old_to_new: permutation vector for vertices
1208:param options: options for remapping vertices)");
1209
1210 m.def(
1211 "remap_vertices",
1212 [](MeshType& mesh,
1213 Tensor<Index> old_to_new,
1214 std::optional<MappingPolicy> collision_policy_float,
1215 std::optional<MappingPolicy> collision_policy_integral) {
1216 RemapVerticesOptions opt;
1217 if (collision_policy_float.has_value()) {
1218 opt.collision_policy_float = collision_policy_float.value();
1219 }
1220 if (collision_policy_integral.has_value()) {
1221 opt.collision_policy_integral = collision_policy_integral.value();
1222 }
1223 auto [data, shape, stride] = tensor_to_span(old_to_new);
1224 la_runtime_assert(is_dense(shape, stride));
1225 remap_vertices<Scalar, Index>(mesh, data, opt);
1226 },
1227 "mesh"_a,
1228 "old_to_new"_a,
1229 "collision_policy_float"_a = nb::none(),
1230 "collision_policy_integral"_a = nb::none(),
1231 R"(Remap vertices of a mesh in place based on a permutation (Pythonic API).
1232
1233:param mesh: input mesh
1234:param old_to_new: permutation vector for vertices
1235:param collision_policy_float: The collision policy for float attributes.
1236:param collision_policy_integral: The collision policy for integral attributes.)");
1237
1238 m.def(
1239 "reorder_mesh",
1240 [](MeshType& mesh, std::string_view method) {
1241 lagrange::ReorderingMethod reorder_method;
1242 if (method == "Lexicographic" || method == "lexicographic") {
1243 reorder_method = ReorderingMethod::Lexicographic;
1244 } else if (method == "Morton" || method == "morton") {
1245 reorder_method = ReorderingMethod::Morton;
1246 } else if (method == "Hilbert" || method == "hilbert") {
1247 reorder_method = ReorderingMethod::Hilbert;
1248 } else if (method == "None" || method == "none") {
1249 reorder_method = ReorderingMethod::None;
1250 } else {
1251 throw std::runtime_error(fmt::format("Invalid reordering method: {}", method));
1252 }
1253
1254 lagrange::reorder_mesh(mesh, reorder_method);
1255 },
1256 "mesh"_a,
1257 "method"_a = "Morton",
1258 R"(Reorder a mesh in place.
1259
1260:param mesh: input mesh
1261:param method: reordering method, options are 'Lexicographic', 'Morton', 'Hilbert', 'None' (default is 'Morton').)",
1262 nb::sig(
1263 "def reorder_mesh(mesh: SurfaceMesh, "
1264 "method: typing.Literal['Lexicographic', 'Morton', 'Hilbert', 'None']) -> None"));
1265
1266 m.def(
1267 "separate_by_facet_groups",
1268 [](MeshType& mesh,
1269 Tensor<Index> facet_group_indices,
1270 std::string_view source_vertex_attr_name,
1271 std::string_view source_facet_attr_name,
1272 bool map_attributes) {
1273 SeparateByFacetGroupsOptions options;
1274 options.source_vertex_attr_name = source_vertex_attr_name;
1275 options.source_facet_attr_name = source_facet_attr_name;
1276 options.map_attributes = map_attributes;
1277 auto [data, shape, stride] = tensor_to_span(facet_group_indices);
1278 la_runtime_assert(is_dense(shape, stride));
1279 return separate_by_facet_groups<Scalar, Index>(mesh, data, options);
1280 },
1281 "mesh"_a,
1282 "facet_group_indices"_a,
1283 "source_vertex_attr_name"_a = "",
1284 "source_facet_attr_name"_a = "",
1285 "map_attributes"_a = false,
1286 R"(Extract a set of submeshes based on facet groups.
1287
1288:param mesh: The source mesh.
1289:param facet_group_indices: The group index for each facet. Each group index must be in the range of [0, max(facet_group_indices)]
1290:param source_vertex_attr_name: The optional attribute name to track source vertices.
1291:param source_facet_attr_name: The optional attribute name to track source facets.
1292
1293:returns: A list of meshes, one for each facet group.
1294)");
1295
1296 m.def(
1297 "separate_by_components",
1298 [](MeshType& mesh,
1299 std::string_view source_vertex_attr_name,
1300 std::string_view source_facet_attr_name,
1301 bool map_attributes,
1302 ConnectivityType connectivity_type) {
1303 SeparateByComponentsOptions options;
1304 options.source_vertex_attr_name = source_vertex_attr_name;
1305 options.source_facet_attr_name = source_facet_attr_name;
1306 options.map_attributes = map_attributes;
1307 options.connectivity_type = connectivity_type;
1308 return separate_by_components(mesh, options);
1309 },
1310 "mesh"_a,
1311 "source_vertex_attr_name"_a = "",
1312 "source_facet_attr_name"_a = "",
1313 "map_attributes"_a = false,
1314 "connectivity_type"_a = ConnectivityType::Edge,
1315 R"(Extract a set of submeshes based on connected components.
1316
1317:param mesh: The source mesh.
1318:param source_vertex_attr_name: The optional attribute name to track source vertices.
1319:param source_facet_attr_name: The optional attribute name to track source facets.
1320:param map_attributes: Map attributes from the source to target meshes.
1321:param connectivity_type: The connectivity used for component computation.
1322
1323:returns: A list of meshes, one for each connected component.
1324)");
1325
1326 m.def(
1327 "extract_submesh",
1328 [](MeshType& mesh,
1329 std::variant<Tensor<Index>, nb::list> selected_facets,
1330 std::string_view source_vertex_attr_name,
1331 std::string_view source_facet_attr_name,
1332 bool map_attributes) {
1333 SubmeshOptions options;
1334 options.source_vertex_attr_name = source_vertex_attr_name;
1335 options.source_facet_attr_name = source_facet_attr_name;
1336 options.map_attributes = map_attributes;
1337 if (std::holds_alternative<nb::list>(selected_facets)) {
1338 auto selected_facets_list =
1339 nb::cast<std::vector<Index>>(std::get<nb::list>(selected_facets));
1340 span<const Index> data{selected_facets_list.data(), selected_facets_list.size()};
1341 return extract_submesh<Scalar, Index>(mesh, data, options);
1342 } else {
1343 auto selected_facets_tensor = std::get<Tensor<Index>>(selected_facets);
1344 auto [data, shape, stride] = tensor_to_span(selected_facets_tensor);
1345 la_runtime_assert(is_dense(shape, stride));
1346 return extract_submesh<Scalar, Index>(mesh, data, options);
1347 }
1348 },
1349 "mesh"_a,
1350 "selected_facets"_a,
1351 "source_vertex_attr_name"_a = "",
1352 "source_facet_attr_name"_a = "",
1353 "map_attributes"_a = false,
1354 R"(Extract a submesh based on the selected facets.
1355
1356:param mesh: The source mesh.
1357:param selected_facets: A list or tensor of facet ids to extract.
1358:param source_vertex_attr_name: The optional attribute name to track source vertices.
1359:param source_facet_attr_name: The optional attribute name to track source facets.
1360:param map_attributes: Map attributes from the source to target meshes.
1361
1362:returns: A mesh that contains only the selected facets.
1363)");
1364
1365 m.def(
1366 "compute_dihedral_angles",
1367 [](MeshType& mesh,
1368 std::optional<std::string_view> output_attribute_name,
1369 std::optional<std::string_view> facet_normal_attribute_name,
1370 std::optional<bool> recompute_facet_normals,
1371 std::optional<bool> keep_facet_normals) {
1372 DihedralAngleOptions options;
1373 if (output_attribute_name.has_value()) {
1374 options.output_attribute_name = output_attribute_name.value();
1375 }
1376 if (facet_normal_attribute_name.has_value()) {
1377 options.facet_normal_attribute_name = facet_normal_attribute_name.value();
1378 }
1379 if (recompute_facet_normals.has_value()) {
1380 options.recompute_facet_normals = recompute_facet_normals.value();
1381 }
1382 if (keep_facet_normals.has_value()) {
1383 options.keep_facet_normals = keep_facet_normals.value();
1384 }
1385 return compute_dihedral_angles(mesh, options);
1386 },
1387 "mesh"_a,
1388 "output_attribute_name"_a = nb::none(),
1389 "facet_normal_attribute_name"_a = nb::none(),
1390 "recompute_facet_normals"_a = nb::none(),
1391 "keep_facet_normals"_a = nb::none(),
1392 R"(Compute dihedral angles for each edge.
1393
1394The dihedral angle of an edge is defined as the angle between the __normals__ of two facets adjacent
1395to the edge. The dihedral angle is always in the range [0, pi] for manifold edges. For boundary
1396edges, the dihedral angle defaults to 0. For non-manifold edges, the dihedral angle is not
1397well-defined and will be set to the special value 2 * π.
1398
1399:param mesh: The source mesh.
1400:param output_attribute_name: The optional edge attribute name to store the dihedral angles.
1401:param facet_normal_attribute_name: The optional attribute name to store the facet normals.
1402:param recompute_facet_normals: Whether to recompute facet normals.
1403:param keep_facet_normals: Whether to keep newly computed facet normals. It has no effect on pre-existing facet normals.
1404
1405:return: The edge attribute id of dihedral angles.)");
1406
1407 m.def(
1408 "compute_edge_lengths",
1409 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1410 EdgeLengthOptions options;
1411 if (output_attribute_name.has_value())
1412 options.output_attribute_name = output_attribute_name.value();
1413 return compute_edge_lengths(mesh, options);
1414 },
1415 "mesh"_a,
1416 "output_attribute_name"_a = nb::none(),
1417 R"(Compute edge lengths.
1418
1419:param mesh: The source mesh.
1420:param output_attribute_name: The optional edge attribute name to store the edge lengths.
1421
1422:return: The edge attribute id of edge lengths.)");
1423
1424 m.def(
1425 "compute_dijkstra_distance",
1426 [](MeshType& mesh,
1427 Index seed_facet,
1428 const nb::list& barycentric_coords,
1429 std::optional<Scalar> radius,
1430 std::string_view output_attribute_name,
1431 bool output_involved_vertices) {
1432 DijkstraDistanceOptions<Scalar, Index> options;
1433 options.seed_facet = seed_facet;
1434 for (auto val : barycentric_coords) {
1435 options.barycentric_coords.push_back(nb::cast<Scalar>(val));
1436 }
1437 if (radius.has_value()) {
1438 options.radius = radius.value();
1439 }
1440 options.output_attribute_name = output_attribute_name;
1441 options.output_involved_vertices = output_involved_vertices;
1442 return compute_dijkstra_distance(mesh, options);
1443 },
1444 "mesh"_a,
1445 "seed_facet"_a,
1446 "barycentric_coords"_a,
1447 "radius"_a = nb::none(),
1448 "output_attribute_name"_a = DijkstraDistanceOptions<Scalar, Index>{}.output_attribute_name,
1449 "output_involved_vertices"_a =
1450 DijkstraDistanceOptions<Scalar, Index>{}.output_involved_vertices,
1451 R"(Compute Dijkstra distance from a seed facet.
1452
1453:param mesh: The source mesh.
1454:param seed_facet: The seed facet index.
1455:param barycentric_coords: The barycentric coordinates of the seed facet.
1456:param radius: The maximum radius of the dijkstra distance.
1457:param output_attribute_name: The output attribute name to store the dijkstra distance.
1458:param output_involved_vertices: Whether to output the list of involved vertices.)");
1459
1460 m.def(
1461 "weld_indexed_attribute",
1462 [](MeshType& mesh,
1463 AttributeId attribute_id,
1464 std::optional<double> epsilon_rel,
1465 std::optional<double> epsilon_abs,
1466 std::optional<double> angle_abs,
1467 std::optional<std::vector<size_t>> exclude_vertices) {
1468 WeldOptions options;
1469 options.epsilon_rel = epsilon_rel;
1470 options.epsilon_abs = epsilon_abs;
1471 options.angle_abs = angle_abs;
1472 if (exclude_vertices.has_value()) {
1473 const auto& exclude_vertices_vec = exclude_vertices.value();
1474 options.exclude_vertices = {
1475 exclude_vertices_vec.data(),
1476 exclude_vertices_vec.size()};
1477 }
1478 return weld_indexed_attribute(mesh, attribute_id, options);
1479 },
1480 "mesh"_a,
1481 "attribute_id"_a,
1482 "epsilon_rel"_a = nb::none(),
1483 "epsilon_abs"_a = nb::none(),
1484 "angle_abs"_a = nb::none(),
1485 "exclude_vertices"_a = nb::none(),
1486 R"(Weld indexed attribute.
1487
1488:param mesh: The source mesh to be updated in place.
1489:param attribute_id: The indexed attribute id to weld.
1490:param epsilon_rel: The relative tolerance for welding.
1491:param epsilon_abs: The absolute tolerance for welding.
1492:param angle_abs: The absolute angle tolerance for welding.
1493:param exclude_vertices: Optional list of vertex indices to exclude from welding.)");
1494
1495 m.def(
1496 "compute_euler",
1498 "mesh"_a,
1499 R"(Compute the Euler characteristic.
1500
1501:param mesh: The source mesh.
1502
1503:return: The Euler characteristic.)");
1504
1505 m.def(
1506 "is_closed",
1508 "mesh"_a,
1509 R"(Check if the mesh is closed.
1510
1511A mesh is considered closed if it has no boundary edges.
1512
1513:param mesh: The source mesh.
1514
1515:return: Whether the mesh is closed.)");
1516
1517 m.def(
1518 "is_vertex_manifold",
1520 "mesh"_a,
1521 R"(Check if the mesh is vertex manifold.
1522
1523:param mesh: The source mesh.
1524
1525:return: Whether the mesh is vertex manifold.)");
1526
1527 m.def(
1528 "is_edge_manifold",
1530 "mesh"_a,
1531 R"(Check if the mesh is edge manifold.
1532
1533:param mesh: The source mesh.
1534
1535:return: Whether the mesh is edge manifold.)");
1536
1537 m.def("is_manifold", &is_manifold<Scalar, Index>, "mesh"_a, R"(Check if the mesh is manifold.
1538
1539A mesh considered as manifold if it is both vertex and edge manifold.
1540
1541:param mesh: The source mesh.
1542
1543:return: Whether the mesh is manifold.)");
1544
1545 m.def(
1546 "compute_vertex_is_manifold",
1547 [](MeshType& mesh, std::string_view output_attribute_name) {
1548 VertexManifoldOptions options;
1549 options.output_attribute_name = output_attribute_name;
1550 return compute_vertex_is_manifold(mesh, options);
1551 },
1552 "mesh"_a,
1553 "output_attribute_name"_a = VertexManifoldOptions().output_attribute_name,
1554 R"(Compute whether each vertex is manifold.
1555
1556A vertex is considered manifold if its one-ring neighborhood is homeomorphic to a disk.
1557
1558:param mesh: The source mesh.
1559:param output_attribute_name: The output vertex attribute name.
1560
1561:return: The attribute id of a vertex attribute indicating whether a vertex is manifold.)");
1562
1563 m.def(
1564 "compute_edge_is_manifold",
1565 [](MeshType& mesh, std::string_view output_attribute_name) {
1566 EdgeManifoldOptions options;
1567 options.output_attribute_name = output_attribute_name;
1568 return compute_edge_is_manifold(mesh, options);
1569 },
1570 "mesh"_a,
1571 "output_attribute_name"_a = EdgeManifoldOptions().output_attribute_name,
1572 R"(Compute whether each edge is manifold.
1573
1574An edge is considered manifold if it is adjacent to one or two facets.
1575
1576:param mesh: The source mesh.
1577:param output_attribute_name: The output edge attribute name.
1578
1579:return: The attribute id of an edge attribute indicating whether an edge is manifold.)");
1580
1581 m.def(
1582 "is_oriented",
1584 "mesh"_a,
1585 R"(Check if the mesh is oriented.
1586
1587A mesh is oriented if all interior edges are oriented. An interior edge is considered as
1588oriented if it has the same number of half-edges for each edge direction. I.e. the number of
1589facets that use the edge in one direction equals the number of facets that use the edge in the
1590opposite direction. Boundary edges are always considered as oriented.
1591
1592:param mesh: The source mesh.
1593
1594:return: Whether the mesh is oriented.)");
1595
1596 m.def(
1597 "compute_edge_is_oriented",
1598 [](MeshType& mesh, std::string_view output_attribute_name) {
1599 OrientationOptions options;
1600 options.output_attribute_name = output_attribute_name;
1601 return compute_edge_is_oriented(mesh, options);
1602 },
1603 "mesh"_a,
1604 "output_attribute_name"_a = OrientationOptions().output_attribute_name,
1605 R"(Compute whether each edge is oriented.
1606
1607An interior edge is considered as oriented if it has the same number of half-edges for each edge
1608direction. I.e. the number of facets that use the edge in one direction equals to the number of
1609facets that use the edge in the opposite direction. Boundary edges are always considered as
1610oriented.
1611
1612:param mesh: The source mesh.
1613:param output_attribute_name: The output edge attribute name.
1614
1615:return: The attribute id of an edge attribute indicating whether an edge is oriented.)");
1616
1617 m.def(
1618 "transform_mesh",
1619 [](MeshType& mesh,
1620 Eigen::Matrix<Scalar, 4, 4> affine_transform,
1621 bool normalize_normals,
1622 bool normalize_tangents_bitangents,
1623 bool in_place) -> std::optional<MeshType> {
1624 Eigen::Transform<Scalar, 3, Eigen::Affine> M(affine_transform);
1625 TransformOptions options;
1626 options.normalize_normals = normalize_normals;
1627 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
1628
1629 std::optional<MeshType> result;
1630 if (in_place) {
1631 transform_mesh(mesh, M, options);
1632 } else {
1633 result = transformed_mesh(mesh, M, options);
1634 }
1635 return result;
1636 },
1637 "mesh"_a,
1638 "affine_transform"_a,
1639 "normalize_normals"_a = TransformOptions().normalize_normals,
1640 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
1641 "in_place"_a = true,
1642 R"(Apply affine transformation to a mesh.
1643
1644:param mesh: Input mesh.
1645:param affine_transform: Affine transformation matrix.
1646:param normalize_normals: Whether to normalize normals.
1647:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
1648:param in_place: Whether to apply transformation in place.
1649
1650:returns: Transformed mesh if in_place is False.)");
1651
1652 nb::enum_<DistortionMetric>(m, "DistortionMetric", "Distortion metric.")
1653 .value("Dirichlet", DistortionMetric::Dirichlet, "Dirichlet energy")
1654 .value("InverseDirichlet", DistortionMetric::InverseDirichlet, "Inverse Dirichlet energy")
1655 .value(
1656 "SymmetricDirichlet",
1658 "Symmetric Dirichlet energy")
1659 .value("AreaRatio", DistortionMetric::AreaRatio, "Area ratio")
1660 .value("MIPS", DistortionMetric::MIPS, "Most isotropic parameterization energy");
1661
1662 m.def(
1663 "compute_uv_distortion",
1664 [](MeshType& mesh,
1665 std::string_view uv_attribute_name,
1666 std::string_view output_attribute_name,
1667 DistortionMetric metric) {
1668 UVDistortionOptions opt;
1669 opt.uv_attribute_name = uv_attribute_name;
1670 opt.output_attribute_name = output_attribute_name;
1671 opt.metric = metric;
1672 return compute_uv_distortion(mesh, opt);
1673 },
1674 "mesh"_a,
1675 "uv_attribute_name"_a = "@uv",
1676 "output_attribute_name"_a = "@uv_measure",
1678 R"(Compute UV distortion.
1679
1680:param mesh: Input mesh.
1681:param uv_attribute_name: UV attribute name (default: "@uv").
1682:param output_attribute_name: Output attribute name (default: "@uv_measure").
1683:param metric: Distortion metric (default: MIPS).
1684
1685:returns: Facet attribute ID for distortion.)");
1686
1687 m.def(
1688 "trim_by_isoline",
1689 [](const MeshType& mesh,
1690 std::variant<AttributeId, std::string_view> attribute,
1691 double isovalue,
1692 bool keep_below) {
1693 IsolineOptions opt;
1694 if (std::holds_alternative<AttributeId>(attribute)) {
1695 opt.attribute_id = std::get<AttributeId>(attribute);
1696 } else {
1697 opt.attribute_id = mesh.get_attribute_id(std::get<std::string_view>(attribute));
1698 }
1699 opt.isovalue = isovalue;
1700 opt.keep_below = keep_below;
1701 return trim_by_isoline(mesh, opt);
1702 },
1703 "mesh"_a,
1704 "attribute"_a,
1705 "isovalue"_a = IsolineOptions().isovalue,
1706 "keep_below"_a = IsolineOptions().keep_below,
1707 R"(Trim a triangle mesh by an isoline.
1708
1709:param mesh: Input triangle mesh.
1710:param attribute: Attribute ID or name of scalar field (vertex or indexed).
1711:param isovalue: Isovalue to trim with.
1712:param keep_below: Whether to keep the part below the isoline.
1713
1714:returns: Trimmed mesh.)");
1715
1716 m.def(
1717 "extract_isoline",
1718 [](const MeshType& mesh,
1719 std::variant<AttributeId, std::string_view> attribute,
1720 double isovalue) {
1721 IsolineOptions opt;
1722 if (std::holds_alternative<AttributeId>(attribute)) {
1723 opt.attribute_id = std::get<AttributeId>(attribute);
1724 } else {
1725 opt.attribute_id = mesh.get_attribute_id(std::get<std::string_view>(attribute));
1726 }
1727 opt.isovalue = isovalue;
1728 return extract_isoline(mesh, opt);
1729 },
1730 "mesh"_a,
1731 "attribute"_a,
1732 "isovalue"_a = IsolineOptions().isovalue,
1733 R"(Extract the isoline of an implicit function defined on the mesh vertices/corners.
1734
1735The input mesh must be a triangle mesh.
1736
1737:param mesh: Input triangle mesh to extract the isoline from.
1738:param attribute: Attribute id or name of the scalar field to use. Can be a vertex or indexed attribute.
1739:param isovalue: Isovalue to extract.
1740
1741:return: A mesh whose facets is a collection of size 2 elements representing the extracted isoline.)");
1742
1743 using AttributeNameOrId = AttributeFilter::AttributeNameOrId;
1744 m.def(
1745 "filter_attributes",
1746 [](MeshType& mesh,
1747 std::optional<std::vector<AttributeNameOrId>> included_attributes,
1748 std::optional<std::vector<AttributeNameOrId>> excluded_attributes,
1749 std::optional<std::unordered_set<AttributeUsage>> included_usages,
1750 std::optional<std::unordered_set<AttributeElement>> included_element_types) {
1751 AttributeFilter filter;
1752 if (included_attributes.has_value()) {
1753 filter.included_attributes = included_attributes.value();
1754 }
1755 if (excluded_attributes.has_value()) {
1756 filter.excluded_attributes = excluded_attributes.value();
1757 }
1758 if (included_usages.has_value()) {
1759 filter.included_usages.clear_all();
1760 for (auto usage : included_usages.value()) {
1761 filter.included_usages.set(usage);
1762 }
1763 }
1764 if (included_element_types.has_value()) {
1765 filter.included_element_types.clear_all();
1766 for (auto element_type : included_element_types.value()) {
1767 filter.included_element_types.set(element_type);
1768 }
1769 }
1770 return filter_attributes(mesh, filter);
1771 },
1772 "mesh"_a,
1773 "included_attributes"_a = nb::none(),
1774 "excluded_attributes"_a = nb::none(),
1775 "included_usages"_a = nb::none(),
1776 "included_element_types"_a = nb::none(),
1777 R"(Filters the attributes of mesh according to user specifications.
1778
1779:param mesh: Input mesh.
1780:param included_attributes: List of attribute names or ids to include. By default, all attributes are included.
1781:param excluded_attributes: List of attribute names or ids to exclude. By default, no attribute is excluded.
1782:param included_usages: List of attribute usages to include. By default, all usages are included.
1783:param included_element_types: List of attribute element types to include. By default, all element types are included.)");
1784
1785 m.def(
1786 "cast_attribute",
1787 [](MeshType& mesh,
1788 std::variant<AttributeId, std::string_view> input_attribute,
1789 nb::type_object dtype,
1790 std::optional<std::string_view> output_attribute_name) {
1792 auto cast = [&](AttributeId attr_id) {
1793 auto np = nb::module_::import_("numpy");
1794 if (output_attribute_name.has_value()) {
1795 auto name = output_attribute_name.value();
1796 if (dtype.is(&PyFloat_Type)) {
1797 // Native python float is a C double.
1798 return cast_attribute<double>(mesh, attr_id, name);
1799 } else if (dtype.is(&PyLong_Type)) {
1800 // Native python int maps to int64.
1801 return cast_attribute<int64_t>(mesh, attr_id, name);
1802 } else if (dtype.is(np.attr("float32"))) {
1803 return cast_attribute<float>(mesh, attr_id, name);
1804 } else if (dtype.is(np.attr("float64"))) {
1805 return cast_attribute<double>(mesh, attr_id, name);
1806 } else if (dtype.is(np.attr("int8"))) {
1807 return cast_attribute<int8_t>(mesh, attr_id, name);
1808 } else if (dtype.is(np.attr("int16"))) {
1809 return cast_attribute<int16_t>(mesh, attr_id, name);
1810 } else if (dtype.is(np.attr("int32"))) {
1811 return cast_attribute<int32_t>(mesh, attr_id, name);
1812 } else if (dtype.is(np.attr("int64"))) {
1813 return cast_attribute<int64_t>(mesh, attr_id, name);
1814 } else if (dtype.is(np.attr("uint8"))) {
1815 return cast_attribute<uint8_t>(mesh, attr_id, name);
1816 } else if (dtype.is(np.attr("uint16"))) {
1817 return cast_attribute<uint16_t>(mesh, attr_id, name);
1818 } else if (dtype.is(np.attr("uint32"))) {
1819 return cast_attribute<uint32_t>(mesh, attr_id, name);
1820 } else if (dtype.is(np.attr("uint64"))) {
1821 return cast_attribute<uint64_t>(mesh, attr_id, name);
1822 } else {
1823 throw nb::type_error("Unsupported `dtype`!");
1824 }
1825 } else {
1826 if (dtype.is(&PyFloat_Type)) {
1827 // Native python float is a C double.
1828 return cast_attribute_in_place<double>(mesh, attr_id);
1829 } else if (dtype.is(&PyLong_Type)) {
1830 // Native python int maps to int64.
1831 return cast_attribute_in_place<int64_t>(mesh, attr_id);
1832 } else if (dtype.is(np.attr("float32"))) {
1833 return cast_attribute_in_place<float>(mesh, attr_id);
1834 } else if (dtype.is(np.attr("float64"))) {
1835 return cast_attribute_in_place<double>(mesh, attr_id);
1836 } else if (dtype.is(np.attr("int8"))) {
1837 return cast_attribute_in_place<int8_t>(mesh, attr_id);
1838 } else if (dtype.is(np.attr("int16"))) {
1839 return cast_attribute_in_place<int16_t>(mesh, attr_id);
1840 } else if (dtype.is(np.attr("int32"))) {
1841 return cast_attribute_in_place<int32_t>(mesh, attr_id);
1842 } else if (dtype.is(np.attr("int64"))) {
1843 return cast_attribute_in_place<int64_t>(mesh, attr_id);
1844 } else if (dtype.is(np.attr("uint8"))) {
1845 return cast_attribute_in_place<uint8_t>(mesh, attr_id);
1846 } else if (dtype.is(np.attr("uint16"))) {
1847 return cast_attribute_in_place<uint16_t>(mesh, attr_id);
1848 } else if (dtype.is(np.attr("uint32"))) {
1849 return cast_attribute_in_place<uint32_t>(mesh, attr_id);
1850 } else if (dtype.is(np.attr("uint64"))) {
1851 return cast_attribute_in_place<uint64_t>(mesh, attr_id);
1852 } else {
1853 throw nb::type_error("Unsupported `dtype`!");
1854 }
1855 }
1856 };
1857
1858 if (std::holds_alternative<AttributeId>(input_attribute)) {
1859 return cast(std::get<AttributeId>(input_attribute));
1860 } else {
1861 AttributeId id = mesh.get_attribute_id(std::get<std::string_view>(input_attribute));
1862 return cast(id);
1863 }
1864 },
1865 "mesh"_a,
1866 "input_attribute"_a,
1867 "dtype"_a,
1868 "output_attribute_name"_a = nb::none(),
1869 R"(Cast an attribute to a new dtype.
1870
1871:param mesh: The input mesh.
1872:param input_attribute: The input attribute id or name.
1873:param dtype: The new dtype.
1874:param output_attribute_name: The output attribute name. If none, cast will replace the input attribute.
1875
1876:returns: The id of the new attribute.)");
1877
1878 m.def(
1879 "compute_mesh_covariance",
1880 [](MeshType& mesh,
1881 std::array<Scalar, 3> center,
1882 std::optional<std::string_view> active_facets_attribute_name)
1883 -> std::array<std::array<Scalar, 3>, 3> {
1884 MeshCovarianceOptions options;
1885 options.center = center;
1886 options.active_facets_attribute_name = active_facets_attribute_name;
1887 return compute_mesh_covariance<Scalar, Index>(mesh, options);
1888 },
1889 "mesh"_a,
1890 "center"_a,
1891 "active_facets_attribute_name"_a = nb::none(),
1892 R"(Compute the covariance matrix of a mesh w.r.t. a center (Pythonic API).
1893
1894:param mesh: Input mesh.
1895:param center: The center of the covariance computation.
1896:param active_facets_attribute_name: (optional) Attribute name of whether a facet should be considered in the computation.
1897
1898:returns: The 3 by 3 covariance matrix, which should be symmetric.)");
1899
1900 m.def(
1901 "select_facets_by_normal_similarity",
1902 [](MeshType& mesh,
1903 Index seed_facet_id,
1904 std::optional<double> flood_error_limit,
1905 std::optional<double> flood_second_to_first_order_limit_ratio,
1906 std::optional<std::string_view> facet_normal_attribute_name,
1907 std::optional<std::string_view> is_facet_selectable_attribute_name,
1908 std::optional<std::string_view> output_attribute_name,
1909 std::optional<std::string_view> search_type,
1910 std::optional<int> num_smooth_iterations) {
1911 // Set options in the C++ struct
1912 SelectFacetsByNormalSimilarityOptions options;
1913 if (flood_error_limit.has_value())
1914 options.flood_error_limit = flood_error_limit.value();
1915 if (flood_second_to_first_order_limit_ratio.has_value())
1916 options.flood_second_to_first_order_limit_ratio =
1917 flood_second_to_first_order_limit_ratio.value();
1918 if (facet_normal_attribute_name.has_value())
1919 options.facet_normal_attribute_name = facet_normal_attribute_name.value();
1920 if (is_facet_selectable_attribute_name.has_value()) {
1921 options.is_facet_selectable_attribute_name = is_facet_selectable_attribute_name;
1922 }
1923 if (output_attribute_name.has_value())
1924 options.output_attribute_name = output_attribute_name.value();
1925 if (search_type.has_value()) {
1926 if (search_type.value() == "BFS")
1928 else if (search_type.value() == "DFS")
1930 else
1931 throw std::runtime_error(
1932 fmt::format("Invalid search type: {}", search_type.value()));
1933 }
1934 if (num_smooth_iterations.has_value())
1935 options.num_smooth_iterations = num_smooth_iterations.value();
1936
1937 return select_facets_by_normal_similarity<Scalar, Index>(mesh, seed_facet_id, options);
1938 },
1939 "mesh"_a, /* `_a` is a literal for nanobind to create nb::args, a required argument */
1940 "seed_facet_id"_a,
1941 "flood_error_limit"_a = nb::none(),
1942 "flood_second_to_first_order_limit_ratio"_a = nb::none(),
1943 "facet_normal_attribute_name"_a = nb::none(),
1944 "is_facet_selectable_attribute_name"_a = nb::none(),
1945 "output_attribute_name"_a = nb::none(),
1946 "search_type"_a = nb::none(),
1947 "num_smooth_iterations"_a = nb::none(),
1948 R"(Select facets by normal similarity (Pythonic API).
1949
1950:param mesh: Input mesh.
1951:param seed_facet_id: Index of the seed facet.
1952:param flood_error_limit: Tolerance for normals of the seed and the selected facets. Higher limit leads to larger selected region.
1953:param flood_second_to_first_order_limit_ratio: Ratio of the flood_error_limit and the tolerance for normals of neighboring selected facets. Higher ratio leads to more curvature in selected region.
1954:param facet_normal_attribute_name: Attribute name of the facets normal. If the mesh doesn't have this attribute, it will call compute_facet_normal to compute it.
1955:param is_facet_selectable_attribute_name: If provided, this function will look for this attribute to determine if a facet is selectable.
1956:param output_attribute_name: Attribute name of whether a facet is selected.
1957:param search_type: Use 'BFS' for breadth-first search or 'DFS' for depth-first search.
1958:param num_smooth_iterations: Number of iterations to smooth the boundary of the selected region.
1959
1960:returns: Id of the attribute on whether a facet is selected.)",
1961 nb::sig(
1962 "def select_facets_by_normal_similarity(mesh: SurfaceMesh, "
1963 "seed_facet_id: int, "
1964 "flood_error_limit: float | None = None, "
1965 "flood_second_to_first_order_limit_ratio: float | None = None, "
1966 "facet_normal_attribute_name: str | None = None, "
1967 "is_facet_selectable_attribute_name: str | None = None, "
1968 "output_attribute_name: str | None = None, "
1969 "search_type: typing.Literal['BFS', 'DFS'] | None = None,"
1970 "num_smooth_iterations: int | None = None) -> int"));
1971
1972 m.def(
1973 "select_facets_in_frustum",
1974 [](MeshType& mesh,
1975 std::array<std::array<Scalar, 3>, 4> frustum_plane_points,
1976 std::array<std::array<Scalar, 3>, 4> frustum_plane_normals,
1977 std::optional<bool> greedy,
1978 std::optional<std::string_view> output_attribute_name) {
1979 // Set options in the C++ struct
1980 Frustum<Scalar> frustum;
1981 for (size_t i = 0; i < 4; ++i) {
1982 frustum.planes[i].point = frustum_plane_points[i];
1983 frustum.planes[i].normal = frustum_plane_normals[i];
1984 }
1985 FrustumSelectionOptions options;
1986 if (greedy.has_value()) options.greedy = greedy.value();
1987 if (output_attribute_name.has_value())
1988 options.output_attribute_name = output_attribute_name.value();
1989
1990 return select_facets_in_frustum<Scalar, Index>(mesh, frustum, options);
1991 },
1992 "mesh"_a,
1993 "frustum_plane_points"_a,
1994 "frustum_plane_normals"_a,
1995 "greedy"_a = nb::none(),
1996 "output_attribute_name"_a = nb::none(),
1997 R"(Select facets in a frustum (Pythonic API).
1998
1999:param mesh: Input mesh.
2000:param frustum_plane_points: Four points on each of the frustum planes.
2001:param frustum_plane_normals: Four normals of each of the frustum planes.
2002:param greedy: If true, the function returns as soon as the first facet is found.
2003:param output_attribute_name: Attribute name of whether a facet is selected.
2004
2005:returns: Whether any facets got selected.)");
2006
2007 m.def(
2008 "thicken_and_close_mesh",
2009 [](MeshType& mesh,
2010 std::optional<Scalar> offset_amount,
2011 std::variant<std::monostate, std::array<double, 3>, std::string_view> direction,
2012 std::optional<double> mirror_ratio,
2013 std::optional<size_t> num_segments,
2014 std::optional<std::vector<std::string>> indexed_attributes) {
2015 ThickenAndCloseOptions options;
2016
2017 if (auto array_val = std::get_if<std::array<double, 3>>(&direction)) {
2018 options.direction = *array_val;
2019 } else if (auto string_val = std::get_if<std::string_view>(&direction)) {
2020 options.direction = *string_val;
2021 }
2022 options.offset_amount = offset_amount.value_or(options.offset_amount);
2023 options.mirror_ratio = std::move(mirror_ratio);
2024 options.num_segments = num_segments.value_or(options.num_segments);
2025 options.indexed_attributes = indexed_attributes.value_or(options.indexed_attributes);
2026
2027 return thicken_and_close_mesh<Scalar, Index>(mesh, options);
2028 },
2029 "mesh"_a,
2030 "offset_amount"_a = nb::none(),
2031 "direction"_a = nb::none(),
2032 "mirror_ratio"_a = nb::none(),
2033 "num_segments"_a = nb::none(),
2034 "indexed_attributes"_a = nb::none(),
2035 R"(Thicken a mesh by offsetting it, and close the shape into a thick 3D solid.
2036
2037:param mesh: Input mesh.
2038:param direction: Direction of the offset. Can be an attribute name or a fixed 3D vector.
2039:param offset_amount: Amount of offset.
2040:param mirror_ratio: Ratio of the offset amount to mirror the mesh.
2041:param num_segments: Number of segments to use for the thickening.
2042:param indexed_attributes: List of indexed attributes to copy to the new mesh.
2043
2044:returns: The thickened and closed mesh.)");
2045
2046 m.def(
2047 "extract_boundary_loops",
2049 "mesh"_a,
2050 R"(Extract boundary loops from a mesh.
2051
2052:param mesh: Input mesh.
2053
2054:returns: A list of boundary loops, each represented as a list of vertex indices.)");
2055
2056 m.def(
2057 "extract_boundary_edges",
2058 [](MeshType& mesh) {
2059 mesh.initialize_edges();
2060 Index num_edges = mesh.get_num_edges();
2061 std::vector<Index> bd_edges;
2062 bd_edges.reserve(num_edges);
2063 for (Index ei = 0; ei < num_edges; ++ei) {
2064 if (mesh.is_boundary_edge(ei)) {
2065 bd_edges.push_back(ei);
2066 }
2067 }
2068 return bd_edges;
2069 },
2070 "mesh"_a,
2071 R"(Extract boundary edges from a mesh.
2072
2073:param mesh: Input mesh.
2074
2075:returns: A list of boundary edge indices.)");
2076
2077 m.def(
2078 "compute_uv_charts",
2079 [](MeshType& mesh,
2080 std::string_view uv_attribute_name,
2081 std::string_view output_attribute_name,
2082 std::string_view connectivity_type) {
2083 UVChartOptions options;
2084 options.uv_attribute_name = uv_attribute_name;
2085 options.output_attribute_name = output_attribute_name;
2086 if (connectivity_type == "Vertex") {
2087 options.connectivity_type = UVChartOptions::ConnectivityType::Vertex;
2088 } else if (connectivity_type == "Edge") {
2089 options.connectivity_type = UVChartOptions::ConnectivityType::Edge;
2090 } else {
2091 throw std::runtime_error(
2092 fmt::format("Invalid connectivity type: {}", connectivity_type));
2093 }
2094 return compute_uv_charts(mesh, options);
2095 },
2096 "mesh"_a,
2097 "uv_attribute_name"_a = UVChartOptions().uv_attribute_name,
2098 "output_attribute_name"_a = UVChartOptions().output_attribute_name,
2099 "connectivity_type"_a = "Edge",
2100 R"(Compute UV charts.
2101
2102@param mesh: Input mesh.
2103@param uv_attribute_name: Name of the UV attribute.
2104@param output_attribute_name: Name of the output attribute to store the chart ids.
2105@param connectivity_type: Type of connectivity to use for chart computation. Can be "Vertex" or "Edge".
2106
2107@returns: A list of chart ids for each vertex.)");
2108
2109 m.def(
2110 "uv_mesh_view",
2111 [](const MeshType& mesh, std::string_view uv_attribute_name) {
2112 UVMeshOptions options;
2113 options.uv_attribute_name = uv_attribute_name;
2114 return uv_mesh_view(mesh, options);
2115 },
2116 "mesh"_a,
2117 "uv_attribute_name"_a = UVMeshOptions().uv_attribute_name,
2118 R"(Extract a UV mesh view from a 3D mesh.
2119
2120:param mesh: Input mesh.
2121:param uv_attribute_name: Name of the (indexed or vertex) UV attribute.
2122
2123:return: A new mesh representing the UV mesh.)");
2124 m.def(
2125 "uv_mesh_ref",
2126 [](MeshType& mesh, std::string_view uv_attribute_name) {
2127 UVMeshOptions options;
2128 options.uv_attribute_name = uv_attribute_name;
2129 return uv_mesh_ref(mesh, options);
2130 },
2131 "mesh"_a,
2132 "uv_attribute_name"_a = UVMeshOptions().uv_attribute_name,
2133 R"(Extract a UV mesh reference from a 3D mesh.
2134
2135:param mesh: Input mesh.
2136:param uv_attribute_name: Name of the (indexed or vertex) UV attribute.
2137
2138:return: A new mesh representing the UV mesh.)");
2139
2140 m.def(
2141 "split_facets_by_material",
2143 "mesh"_a,
2144 "material_attribute_name"_a,
2145 R"(Split mesh facets based on a material attribute.
2146
2147@param mesh: Input mesh on which material segmentation will be applied in place.
2148@param material_attribute_name: Name of the material attribute to use for inserting boundaries.
2149
2150@note The material attribute should be n by k vertex attribute, where n is the number of vertices,
2151and k is the number of materials. The value at row i and column j indicates the probability of vertex
2152i belonging to material j. The function will insert boundaries between different materials based on
2153the material attribute.
2154)");
2155}
2156
2157} // namespace lagrange::python
SurfaceMesh< Scalar, Index > unify_named_index_buffer(const SurfaceMesh< Scalar, Index > &mesh, const std::vector< std::string_view > &attribute_names)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition unify_index_buffer.cpp:277
AttributeId map_attribute_in_place(SurfaceMesh< Scalar, Index > &mesh, AttributeId id, AttributeElement new_element)
Map attribute values to a different element type.
Definition map_attribute.cpp:291
AttributeId map_attribute(SurfaceMesh< Scalar, Index > &mesh, AttributeId id, std::string_view new_name, AttributeElement new_element)
Map attribute values to a new attribute with a different element type.
Definition map_attribute.cpp:264
SurfaceMesh< Scalar, Index > unify_index_buffer(const SurfaceMesh< Scalar, Index > &mesh, const std::vector< AttributeId > &attribute_ids={})
Unify index buffers of the input mesh for all attributes specified in attribute_ids.
Definition unify_index_buffer.cpp:34
uint32_t AttributeId
Identified to be used to access an attribute.
Definition AttributeFwd.h:73
AttributeElement
Type of element to which the attribute is attached.
Definition AttributeFwd.h:26
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
@ Facet
Per-facet mesh attributes.
Definition AttributeFwd.h:31
AttributeId compute_normal(SurfaceMesh< Scalar, Index > &mesh, function_ref< bool(Index)> is_edge_smooth, span< const Index > cone_vertices={}, NormalOptions options={})
Compute smooth normals based on specified sharp edges and cone vertices.
Definition compute_normal.cpp:198
SurfaceMesh< Scalar, Index > trim_by_isoline(const SurfaceMesh< Scalar, Index > &mesh, const IsolineOptions &options={})
Trim a mesh by the isoline of an implicit function defined on the mesh vertices/corners.
Definition isoline.cpp:325
AttributeId cast_attribute_in_place(SurfaceMesh< Scalar, Index > &mesh, AttributeId attribute_id)
Cast an attribute in place to a different value type.
Definition cast_attribute.cpp:68
bool is_closed(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is closed.
Definition topology.cpp:51
Scalar compute_uv_area(const SurfaceMesh< Scalar, Index > &mesh, MeshAreaOptions options={})
Compute UV mesh area.
Definition compute_area.cpp:388
std::array< std::array< Scalar, 3 >, 3 > compute_mesh_covariance(const SurfaceMesh< Scalar, Index > &mesh, const MeshCovarianceOptions &options={})
Compute the covariance matrix w.r.t.
Definition compute_mesh_covariance.cpp:98
size_t compute_uv_charts(SurfaceMesh< Scalar, Index > &mesh, const UVChartOptions &options={})
Compute UV charts of an input mesh.
Definition compute_uv_charts.cpp:23
int compute_euler(const SurfaceMesh< Scalar, Index > &mesh)
Compute Euler characteristic of a mesh.
Definition topology.cpp:35
bool select_facets_in_frustum(SurfaceMesh< Scalar, Index > &mesh, const Frustum< Scalar > &frustum, const FrustumSelectionOptions &options={})
Select all facets that intersect the cone/frustrum bounded by 4 planes defined by (n_i,...
Definition select_facets_in_frustum.cpp:44
AttributeId compute_greedy_coloring(SurfaceMesh< Scalar, Index > &mesh, const GreedyColoringOptions &options={})
Compute a greedy graph coloring of the mesh.
Definition compute_greedy_coloring.cpp:153
AttributeId compute_edge_is_oriented(SurfaceMesh< Scalar, Index > &mesh, const OrientationOptions &options={})
Compute a mesh attribute indicating whether an edge is oriented.
Definition orientation.cpp:82
SurfaceMesh< Scalar, Index > combine_meshes(std::initializer_list< const SurfaceMesh< Scalar, Index > * > meshes, bool preserve_attributes=true)
Combine multiple meshes into a single mesh.
Definition combine_meshes.cpp:330
std::vector< SurfaceMesh< Scalar, Index > > separate_by_facet_groups(const SurfaceMesh< Scalar, Index > &mesh, size_t num_groups, span< const Index > facet_group_indices, const SeparateByFacetGroupsOptions &options={})
Extract a set of submeshes based on facet groups.
Definition separate_by_facet_groups.cpp:29
ReorderingMethod
Mesh reordering method to apply before decimation.
Definition reorder_mesh.h:26
bool is_oriented(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is oriented.
Definition orientation.cpp:57
SurfaceMesh< Scalar, Index > thicken_and_close_mesh(SurfaceMesh< Scalar, Index > input_mesh, const ThickenAndCloseOptions &options={})
Thicken a mesh by offsetting it, and close the shape into a thick 3D solid.
Definition thicken_and_close_mesh.cpp:270
bool is_manifold(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is both vertex-manifold and edge-manifold.
Definition topology.h:98
void permute_facets(SurfaceMesh< Scalar, Index > &mesh, span< const Index > new_to_old)
Reorder facets of a mesh based on a given permutation.
Definition permute_facets.cpp:26
AttributeId compute_facet_normal(SurfaceMesh< Scalar, Index > &mesh, FacetNormalOptions options={})
Compute facet normals.
Definition compute_facet_normal.cpp:34
void orient_outward(lagrange::SurfaceMesh< Scalar, Index > &mesh, const OrientOptions &options={})
Orient the facets of a mesh so that the signed volume of each connected component is positive or nega...
Definition orient_outward.cpp:126
bool is_edge_manifold(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is edge-manifold.
Definition topology.cpp:125
AttributeId compute_facet_area(SurfaceMesh< Scalar, Index > &mesh, FacetAreaOptions options={})
Compute per-facet area.
Definition compute_area.cpp:307
AttributeId compute_edge_lengths(SurfaceMesh< Scalar, Index > &mesh, const EdgeLengthOptions &options={})
Computes edge lengths attribute.
Definition compute_edge_lengths.cpp:28
AttributeId cast_attribute(SurfaceMesh< Scalar, Index > &mesh, AttributeId source_id, std::string_view target_name)
Cast an attribute in place to a different value type.
Definition cast_attribute.cpp:25
std::optional< std::vector< Index > > compute_dijkstra_distance(SurfaceMesh< Scalar, Index > &mesh, const DijkstraDistanceOptions< Scalar, Index > &options={})
Computes dijkstra distance from a seed facet.
Definition compute_dijkstra_distance.cpp:24
Scalar compute_mesh_area(const SurfaceMesh< Scalar, Index > &mesh, MeshAreaOptions options={})
Compute mesh area.
Definition compute_area.cpp:366
AttributeId compute_vertex_valence(SurfaceMesh< Scalar, Index > &mesh, VertexValenceOptions options={})
Compute vertex valence.
Definition compute_vertex_valence.cpp:27
SurfaceMesh< Scalar, Index > extract_submesh(const SurfaceMesh< Scalar, Index > &mesh, span< const Index > selected_facets, const SubmeshOptions &options={})
Extract a submesh that consists of a subset of the facets of the source mesh.
Definition extract_submesh.cpp:26
void normalize_mesh(SurfaceMesh< Scalar, Index > &mesh, const TransformOptions &options={})
Normalize a mesh to fit in a unit box centered at the origin.
Definition normalize_meshes.cpp:56
AttributeId compute_facet_vector_area(SurfaceMesh< Scalar, Index > &mesh, FacetVectorAreaOptions options={})
Compute per-facet vector area.
Definition compute_area.cpp:325
void split_facets_by_material(SurfaceMesh< Scalar, Index > &mesh, std::string_view material_attribute_name)
Split mesh facets based on material labels.
Definition split_facets_by_material.cpp:57
void remap_vertices(SurfaceMesh< Scalar, Index > &mesh, span< const Index > forward_mapping, RemapVerticesOptions options={})
Remap vertices of a mesh based on provided forward mapping.
Definition remap_vertices.cpp:138
void triangulate_polygonal_facets(SurfaceMesh< Scalar, Index > &mesh, const TriangulationOptions &options={})
Triangulate polygonal facets of a mesh using a prescribed set of rules.
Definition triangulate_polygonal_facets.cpp:520
auto normalize_mesh_with_transform(SurfaceMesh< Scalar, Index > &mesh, const TransformOptions &options={}) -> Eigen::Transform< Scalar, Dimension, Eigen::Affine >
Normalize a mesh to fit in a unit box centered at the origin.
Definition normalize_meshes.cpp:29
void permute_vertices(SurfaceMesh< Scalar, Index > &mesh, span< const Index > new_to_old)
Reorder vertices of a mesh based on a given permutation.
Definition permute_vertices.cpp:26
AttributeId compute_vertex_normal(SurfaceMesh< Scalar, Index > &mesh, VertexNormalOptions options={})
Compute per-vertex normals based on specified weighting type.
Definition compute_vertex_normal.cpp:34
bool is_vertex_manifold(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is vertex-manifold.
Definition topology.cpp:98
AttributeId compute_facet_centroid(SurfaceMesh< Scalar, Index > &mesh, FacetCentroidOptions options={})
Compute per-facet centroid.
Definition compute_centroid.cpp:31
PointcloudPCAOutput< Scalar > compute_pointcloud_pca(span< const Scalar > points, ComputePointcloudPCAOptions options={})
Finds the principal components for a pointcloud.
Definition compute_pointcloud_pca.cpp:23
std::vector< SurfaceMesh< Scalar, Index > > separate_by_components(const SurfaceMesh< Scalar, Index > &mesh, const SeparateByComponentsOptions &options={})
Separate a mesh by connected components.
Definition separate_by_components.cpp:21
SurfaceMesh< UVScalar, Index > uv_mesh_view(const SurfaceMesh< Scalar, Index > &mesh, const UVMeshOptions &options={})
Extract a UV mesh view from an input mesh.
Definition uv_mesh.cpp:86
AttributeId compute_vertex_is_manifold(SurfaceMesh< Scalar, Index > &mesh, const VertexManifoldOptions &options={})
Compute a mesh attribute of value type uint8_t indicating vertex manifoldness.
Definition topology.cpp:142
AttributeId select_facets_by_normal_similarity(SurfaceMesh< Scalar, Index > &mesh, const Index seed_facet_id, const SelectFacetsByNormalSimilarityOptions &options={})
Given a seed facet, selects facets around it based on the change in triangle normals.
Definition select_facets_by_normal_similarity.cpp:27
std::vector< std::vector< Index > > extract_boundary_loops(const SurfaceMesh< Scalar, Index > &mesh)
Extract boundary loops from a surface mesh.
Definition extract_boundary_loops.cpp:24
AttributeId compute_dihedral_angles(SurfaceMesh< Scalar, Index > &mesh, const DihedralAngleOptions &options={})
Computes dihedral angles for each edge in the mesh.
Definition compute_dihedral_angles.cpp:33
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
TangentBitangentResult compute_tangent_bitangent(SurfaceMesh< Scalar, Index > &mesh, TangentBitangentOptions options={})
Compute mesh tangent and bitangent vectors orthogonal to the input mesh normals.
Definition compute_tangent_bitangent.cpp:534
AttributeId compute_edge_is_manifold(SurfaceMesh< Scalar, Index > &mesh, const EdgeManifoldOptions &options={})
Compute a mesh attribute of value type uint8_t indicating edge manifoldness.
Definition topology.cpp:168
SurfaceMesh< Scalar, Index > transformed_mesh(SurfaceMesh< Scalar, Index > mesh, const Eigen::Transform< Scalar, Dimension, Eigen::Affine > &transform, const TransformOptions &options={})
Apply an affine transform to a mesh and return the transformed mesh.
Definition transform_mesh.cpp:208
SurfaceMesh< Scalar, Index > filter_attributes(SurfaceMesh< Scalar, Index > source_mesh, const AttributeFilter &options={})
Filters the attributes of mesh according to user specifications.
Definition filter_attributes.cpp:116
void normalize_meshes(span< SurfaceMesh< Scalar, Index > * > meshes, const TransformOptions &options={})
Normalize a list of meshes to fit in a unit box centered at the origin.
Definition normalize_meshes.cpp:106
void transform_mesh(SurfaceMesh< Scalar, Index > &mesh, const Eigen::Transform< Scalar, Dimension, Eigen::Affine > &transform, const TransformOptions &options={})
Apply an affine transform to a mesh in-place.
Definition transform_mesh.cpp:199
AttributeId compute_facet_circumcenter(SurfaceMesh< Scalar, Index > &mesh, FacetCircumcenterOptions options={})
Compute per-facet circumcenter.
Definition compute_facet_circumcenter.cpp:32
AttributeId compute_uv_distortion(SurfaceMesh< Scalar, Index > &mesh, const UVDistortionOptions &options={})
Compute uv distortion using the selected distortion measure.
Definition compute_uv_distortion.cpp:31
DistortionMetric
UV distortion metric type.
Definition DistortionMetric.h:26
void compute_mesh_centroid(const SurfaceMesh< Scalar, Index > &mesh, span< Scalar > centroid, MeshCentroidOptions options={})
Compute mesh centroid, where mesh centroid is defined as the weighted sum of facet centroids.
Definition compute_centroid.cpp:74
SurfaceMesh< UVScalar, Index > uv_mesh_ref(SurfaceMesh< Scalar, Index > &mesh, const UVMeshOptions &options={})
Extract a UV mesh reference from an input mesh.
Definition uv_mesh.cpp:40
AttributeId compute_seam_edges(SurfaceMesh< Scalar, Index > &mesh, AttributeId indexed_attribute_id, const SeamEdgesOptions &options={})
Computes the seam edges for a given indexed attribute.
Definition compute_seam_edges.cpp:35
void reorder_mesh(SurfaceMesh< Scalar, Index > &mesh, ReorderingMethod method)
Mesh reordering to improve cache locality.
Definition reorder_mesh.cpp:178
size_t compute_components(SurfaceMesh< Scalar, Index > &mesh, ComponentOptions options={})
Compute connected components of an input mesh.
Definition compute_components.cpp:127
SurfaceMesh< Scalar, Index > extract_isoline(const SurfaceMesh< Scalar, Index > &mesh, const IsolineOptions &options={})
Extract the isoline of an implicit function defined on the mesh vertices/corners.
Definition isoline.cpp:333
auto normalize_meshes_with_transform(span< SurfaceMesh< Scalar, Index > * > meshes, const TransformOptions &options={}) -> Eigen::Transform< Scalar, Dimension, Eigen::Affine >
Normalize a list of meshes to fit in a unit box centered at the origin.
Definition normalize_meshes.cpp:66
@ Lexicographic
Sort vertices/facets lexicographically.
Definition reorder_mesh.h:27
@ None
Do not reorder mesh vertices/facets.
Definition reorder_mesh.h:30
@ Hilbert
Spatial sort vertices/facets using Hilbert curve.
Definition reorder_mesh.h:29
@ Morton
Spatial sort vertices/facets using Morton encoding.
Definition reorder_mesh.h:28
@ Angle
Incident face normals are averaged weighted by incident angle of vertex.
Definition NormalWeightingType.h:36
@ CornerTriangleArea
Incident face normals are averaged weighted by area of the corner triangle.
Definition NormalWeightingType.h:33
@ Uniform
Incident face normals have uniform influence on vertex normal.
Definition NormalWeightingType.h:29
@ MIPS
UV triangle area / 3D triangle area.
Definition DistortionMetric.h:31
@ InverseDirichlet
Inverse Dirichlet energy.
Definition DistortionMetric.h:28
@ SymmetricDirichlet
Symmetric Dirichlet energy.
Definition DistortionMetric.h:29
@ Dirichlet
Dirichlet energy.
Definition DistortionMetric.h:27
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition span.h:27
constexpr T invalid()
You can use invalid<T>() to get a value that can represent "invalid" values, such as invalid indices ...
Definition invalid.h:40
void map_attributes(const SurfaceMesh< Scalar, Index > &source_mesh, SurfaceMesh< Scalar, Index > &target_mesh, span< const Index > mapping_data, span< const Index > mapping_offsets={}, const MapAttributesOptions &options={})
Map attributes from the source mesh to the target mesh.
Definition map_attributes.cpp:26
ConnectivityType
This type defines the condition when two facets are considered as "connected".
Definition ConnectivityType.h:19
@ Edge
Two facets are considered connected if they share an edge.
Definition ConnectivityType.h:21
@ KeepFirst
Keep the value of the first elements.
Definition MappingPolicy.h:23
@ Error
Throw an error if collision is detected.
Definition MappingPolicy.h:24
@ Average
Take the average of all involved elements.
Definition MappingPolicy.h:22
std::variant< AttributeId, std::string > AttributeNameOrId
Variant identifying an attribute by its name or id.
Definition filter_attributes.h:39
ConnectivityType connectivity_type
Connectivity type used for component computation.
Definition compute_components.h:38
std::string_view output_attribute_name
Output component id attribute name.
Definition compute_components.h:35
std::string_view output_attribute_name
Output attribute name for facet area.
Definition compute_area.h:34
std::string_view output_attribute_name
Ouptut facet centroid attribute name.
Definition compute_centroid.h:33
std::string_view output_attribute_name
Output normal attribute name.
Definition compute_facet_normal.h:35
std::string_view input_attribute_name
Precomputed facet area attribute name.
Definition compute_area.h:119
bool use_signed_area
For 2D mesh only: whether the computed facet area (if any) should be signed.
Definition compute_area.h:122
std::string_view facet_centroid_attribute_name
Precomputed facet centroid attribute name.
Definition compute_centroid.h:66
@ Area
Per-facet centroid are weighted by facet area.
Definition compute_centroid.h:61
@ Uniform
Per-facet centroid are weighted uniformly.
Definition compute_centroid.h:60
std::string_view facet_area_attribute_name
Precomputed facet area attribute name.
Definition compute_centroid.h:70
bool keep_facet_normals
Whether to keep any newly added facet normal attribute.
Definition compute_normal.h:55
std::string_view facet_normal_attribute_name
Precomputed facet normal attribute name.
Definition compute_normal.h:48
bool recompute_facet_normals
Whether to recompute the facet normal attribute, or reuse existing cached values if present.
Definition compute_normal.h:51
std::string_view output_attribute_name
Output normal attribute name.
Definition compute_normal.h:41
float distance_tolerance
Tolerance for degenerate edge check. (only used to bypass degenerate edges in polygon facets)
Definition compute_normal.h:58
NormalWeightingType weight_type
Per-vertex normal averaging weighting type.
Definition compute_normal.h:44
CollisionPolicy collision_policy_integral
Collision policy for integral valued attributes.
Definition remap_vertices.h:39
CollisionPolicy collision_policy_float
Collision policy for float or double valued attributes.
Definition remap_vertices.h:36
@ BFS
Breadth-First Search.
Definition select_facets_by_normal_similarity.h:62
@ DFS
Depth-First Search.
Definition select_facets_by_normal_similarity.h:63
std::string_view bitangent_attribute_name
Output bitangent attribute name.
Definition compute_tangent_bitangent.h:41
bool keep_existing_tangent
Whether to recompute tangent if the tangent attribute (specified by tangent_attribute_name) already e...
Definition compute_tangent_bitangent.h:70
std::string_view normal_attribute_name
Normal attribute name used to compute the BTN frame.
Definition compute_tangent_bitangent.h:52
std::string_view tangent_attribute_name
Output tangent attribute name.
Definition compute_tangent_bitangent.h:38
AttributeElement output_element_type
Output element type. Can be either Corner or Indexed.
Definition compute_tangent_bitangent.h:55
bool pad_with_sign
Whether to pad the tangent/bitangent vectors with a 4th coordinate indicating the sign of the UV tria...
Definition compute_tangent_bitangent.h:59
bool orthogonalize_bitangent
Whether to compute the bitangent as sign * cross(normal, tangent) If false, the bitangent is computed...
Definition compute_tangent_bitangent.h:63
std::string_view uv_attribute_name
UV attribute name used to orient the BTN frame.
Definition compute_tangent_bitangent.h:45
AttributeId tangent_id
Tangent vector attribute id.
Definition compute_tangent_bitangent.h:77
AttributeId bitangent_id
Bitangent vector attribute id.
Definition compute_tangent_bitangent.h:80
@ Earcut
Use earcut algorithm to triangulate polygons.
Definition triangulate_polygonal_facets.h:31
@ CentroidFan
Connect facet centroid to polygon edges to form a fan of triangles.
Definition triangulate_polygonal_facets.h:32
Scheme scheme
Triangulation scheme to use.
Definition triangulate_polygonal_facets.h:35
bool keep_weighted_corner_normals
Whether to keep any newly added weighted corner normal attribute.
Definition compute_vertex_normal.h:56
std::string_view weighted_corner_normal_attribute_name
Precomputed weighted corner attribute name.
Definition compute_vertex_normal.h:47
std::string_view output_attribute_name
Output normal attribute name.
Definition compute_vertex_normal.h:39
float distance_tolerance
Tolerance for degenerate edge check. (only used to bypass degenerate edges in polygon facets)
Definition compute_vertex_normal.h:59
bool recompute_weighted_corner_normals
Whether to recompute the weighted corner normal attribute, or reuse existing cached values if present...
Definition compute_vertex_normal.h:51
NormalWeightingType weight_type
Per-vertex normal averaging weighting type.
Definition compute_vertex_normal.h:42
std::string_view induced_by_attribute
Optional per-edge attribute used as indicator function to restrict the graph used for vertex valence ...
Definition compute_vertex_valence.h:39
std::string_view output_attribute_name
Output vertex valence attribute name.
Definition compute_vertex_valence.h:42