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_uv_orientation.h>
35#include <lagrange/compute_vertex_normal.h>
36#include <lagrange/compute_vertex_valence.h>
37#include <lagrange/disconnect_uv_charts.h>
38#include <lagrange/extract_submesh.h>
39#include <lagrange/filter_attributes.h>
40#include <lagrange/get_unique_attribute_name.h>
41#include <lagrange/internal/constants.h>
42#include <lagrange/isoline.h>
43#include <lagrange/map_attribute.h>
44#include <lagrange/normalize_meshes.h>
45#include <lagrange/orient_outward.h>
46#include <lagrange/orientation.h>
47#include <lagrange/permute_facets.h>
48#include <lagrange/permute_vertices.h>
49#include <lagrange/python/binding.h>
50#include <lagrange/python/tensor_utils.h>
51#include <lagrange/python/utils/StackVector.h>
52#include <lagrange/python/utils/StubType.h>
53#include <lagrange/remap_vertices.h>
54#include <lagrange/reorder_mesh.h>
55#include <lagrange/select_facets_by_normal_similarity.h>
56#include <lagrange/select_facets_in_frustum.h>
57#include <lagrange/separate_by_components.h>
58#include <lagrange/separate_by_facet_groups.h>
59#include <lagrange/split_facets_by_material.h>
60#include <lagrange/thicken_and_close_mesh.h>
61#include <lagrange/topology.h>
62#include <lagrange/transform_mesh.h>
63#include <lagrange/triangulate_polygonal_facets.h>
64#include <lagrange/unflip_uv_charts.h>
65#include <lagrange/unify_index_buffer.h>
66#include <lagrange/utils/fmt/format.h>
67#include <lagrange/utils/invalid.h>
68#include <lagrange/uv_mesh.h>
69#include <lagrange/weld_indexed_attribute.h>
75namespace lagrange::python {
77LA_STUB_HINT(IterableUsageHint,
"collections.abc.Iterable[AttributeUsage]");
78LA_STUB_HINT(IterableElementHint,
"collections.abc.Iterable[AttributeElement]");
80template <
typename Scalar,
typename Index>
81void bind_utilities(nanobind::module_& m)
83 namespace nb = nanobind;
84 using namespace nb::literals;
85 using MeshType = SurfaceMesh<Scalar, Index>;
87 nb::enum_<NormalWeightingType>(m,
"NormalWeightingType",
"Normal weighting type.")
92 "Weight by corner triangle area")
95 nb::class_<VertexNormalOptions>(
97 "VertexNormalOptions",
98 "Options for computing vertex normals")
101 "output_attribute_name",
103 "Output attribute name. Default is `@vertex_normal`.")
107 "Weighting type for normal computation. Default is Angle.")
109 "weighted_corner_normal_attribute_name",
111 R
"(Precomputed weighted corner normals attribute name (default: @weighted_corner_normal).
113If attribute exists, the precomputed weighted corner normal will be used.)")
115 "recompute_weighted_corner_normals",
117 "Whether to recompute weighted corner normals (default: false).")
119 "keep_weighted_corner_normals",
121 "Whether to keep the weighted corner normal attribute (default: false).")
123 "distance_tolerance",
125 "Distance tolerance for degenerate edge check in polygon facets.");
128 "compute_vertex_normal",
131 "options"_a = VertexNormalOptions(),
132 R
"(Compute vertex normal.
134:param mesh: Input mesh.
135:param options: Options for computing vertex normals.
137:returns: Vertex normal attribute id.)");
140 "compute_vertex_normal",
142 std::optional<std::string_view> output_attribute_name,
143 std::optional<NormalWeightingType> weight_type,
144 std::optional<std::string_view> weighted_corner_normal_attribute_name,
145 std::optional<bool> recompute_weighted_corner_normals,
146 std::optional<bool> keep_weighted_corner_normals,
147 std::optional<float> distance_tolerance) {
148 VertexNormalOptions options;
149 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
150 if (weight_type) options.weight_type = *weight_type;
151 if (weighted_corner_normal_attribute_name)
152 options.weighted_corner_normal_attribute_name =
153 *weighted_corner_normal_attribute_name;
154 if (recompute_weighted_corner_normals)
155 options.recompute_weighted_corner_normals = *recompute_weighted_corner_normals;
156 if (keep_weighted_corner_normals)
157 options.keep_weighted_corner_normals = *keep_weighted_corner_normals;
158 if (distance_tolerance) options.distance_tolerance = *distance_tolerance;
163 "output_attribute_name"_a = nb::none(),
164 "weight_type"_a = nb::none(),
165 "weighted_corner_normal_attribute_name"_a = nb::none(),
166 "recompute_weighted_corner_normals"_a = nb::none(),
167 "keep_weighted_corner_normals"_a = nb::none(),
168 "distance_tolerance"_a = nb::none(),
169 R
"(Compute vertex normal (Pythonic API).
171:param mesh: Input mesh.
172:param output_attribute_name: Output attribute name.
173:param weight_type: Weighting type for normal computation.
174:param weighted_corner_normal_attribute_name: Precomputed weighted corner normals attribute name.
175:param recompute_weighted_corner_normals: Whether to recompute weighted corner normals.
176:param keep_weighted_corner_normals: Whether to keep the weighted corner normal attribute.
177:param distance_tolerance: Distance tolerance for degenerate edge check.
178 (Only used to bypass degenerate edge in polygon facets.)
180:returns: Vertex normal attribute id.)");
182 nb::class_<FacetNormalOptions>(m, "FacetNormalOptions",
"Facet normal computation options.")
185 "output_attribute_name",
187 "Output attribute name. Default: `@facet_normal`");
190 "compute_facet_normal",
193 "options"_a = FacetNormalOptions(),
194 R
"(Compute facet normal.
196:param mesh: Input mesh.
197:param options: Options for computing facet normals.
199:returns: Facet normal attribute id.)");
202 "compute_facet_normal",
203 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
204 FacetNormalOptions options;
205 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
209 "output_attribute_name"_a = nb::none(),
210 R
"(Compute facet normal (Pythonic API).
212:param mesh: Input mesh.
213:param output_attribute_name: Output attribute name.
215:returns: Facet normal attribute id.)");
217 nb::class_<NormalOptions>(m, "NormalOptions",
"Normal computation options.")
220 "output_attribute_name",
222 "Output attribute name. Default: `@normal`")
226 "Weighting type for normal computation. Default is Angle.")
228 "facet_normal_attribute_name",
230 "Facet normal attribute name to use. Default is `@facet_normal`.")
232 "recompute_facet_normals",
234 "Whether to recompute facet normals. Default is false.")
236 "keep_facet_normals",
238 "Whether to keep the computed facet normal attribute. Default is false.")
240 "distance_tolerance",
242 "Distance tolerance for degenerate edge check. (Only used to bypass degenerate edge in "
248 Scalar feature_angle_threshold,
249 nb::object cone_vertices,
250 std::optional<NormalOptions> normal_options) {
251 NormalOptions options;
252 if (normal_options.has_value()) {
253 options = std::move(normal_options.value());
256 if (cone_vertices.is_none()) {
258 }
else if (nb::isinstance<nb::list>(cone_vertices)) {
259 auto cone_vertices_list = nb::cast<std::vector<Index>>(cone_vertices);
262 }
else if (nb::isinstance<Tensor<Index>>(cone_vertices)) {
263 auto cone_vertices_tensor = nb::cast<Tensor<Index>>(cone_vertices);
264 auto [data, shape, stride] = tensor_to_span(cone_vertices_tensor);
268 throw std::runtime_error(
"Invalid cone_vertices type");
272 "feature_angle_threshold"_a = lagrange::internal::pi / 4,
273 "cone_vertices"_a = nb::none(),
274 "options"_a = nb::none(),
275 R
"(Compute indexed normal attribute.
277Edge with dihedral angles larger than `feature_angle_threshold` are considered as sharp edges.
278Vertices listed in `cone_vertices` are considered as cone vertices, which is always sharp.
280:param mesh: input mesh
281:param feature_angle_threshold: feature angle threshold
282:param cone_vertices: cone vertices
283:param options: normal options
285:returns: the id of the indexed normal attribute.
291 Scalar feature_angle_threshold,
292 nb::object cone_vertices,
293 std::optional<std::string_view> output_attribute_name,
294 std::optional<NormalWeightingType> weight_type,
295 std::optional<std::string_view> facet_normal_attribute_name,
296 std::optional<bool> recompute_facet_normals,
297 std::optional<bool> keep_facet_normals,
298 std::optional<float> distance_tolerance) {
299 NormalOptions options;
300 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
301 if (weight_type) options.weight_type = *weight_type;
302 if (facet_normal_attribute_name)
303 options.facet_normal_attribute_name = *facet_normal_attribute_name;
304 if (recompute_facet_normals) options.recompute_facet_normals = *recompute_facet_normals;
305 if (keep_facet_normals) options.keep_facet_normals = *keep_facet_normals;
306 if (distance_tolerance) options.distance_tolerance = *distance_tolerance;
308 if (cone_vertices.is_none()) {
310 }
else if (nb::isinstance<nb::list>(cone_vertices)) {
311 auto cone_vertices_list = nb::cast<std::vector<Index>>(cone_vertices);
314 }
else if (nb::isinstance<Tensor<Index>>(cone_vertices)) {
315 auto cone_vertices_tensor = nb::cast<Tensor<Index>>(cone_vertices);
316 auto [data, shape, stride] = tensor_to_span(cone_vertices_tensor);
320 throw std::runtime_error(
"Invalid cone_vertices type");
324 "feature_angle_threshold"_a = lagrange::internal::pi / 4,
325 "cone_vertices"_a = nb::none(),
326 "output_attribute_name"_a = nb::none(),
327 "weight_type"_a = nb::none(),
328 "facet_normal_attribute_name"_a = nb::none(),
329 "recompute_facet_normals"_a = nb::none(),
330 "keep_facet_normals"_a = nb::none(),
331 "distance_tolerance"_a = nb::none(),
332 R
"(Compute indexed normal attribute (Pythonic API).
334:param mesh: input mesh
335:param feature_angle_threshold: feature angle threshold
336:param cone_vertices: cone vertices
337:param output_attribute_name: output normal attribute name
338:param weight_type: normal weighting type
339:param facet_normal_attribute_name: facet normal attribute name
340:param recompute_facet_normals: whether to recompute facet normals
341:param keep_facet_normals: whether to keep the computed facet normal attribute
342:param distance_tolerance: distance tolerance for degenerate edge check
343 (only used to bypass degenerate edges in polygon facets)
345:returns: the id of the indexed normal attribute.)");
347 using ConstArray3d = nb::ndarray<
const double, nb::shape<-1, 3>, nb::c_contig, nb::device::cpu>;
349 "compute_pointcloud_pca",
350 [](ConstArray3d points,
bool shift_centroid,
bool normalize) {
351 ComputePointcloudPCAOptions options;
352 options.shift_centroid = shift_centroid;
353 options.normalize = normalize;
354 PointcloudPCAOutput<Scalar> output =
356 return std::make_tuple(output.center, output.eigenvectors, output.eigenvalues);
359 "shift_centroid"_a = ComputePointcloudPCAOptions().shift_centroid,
360 "normalize"_a = ComputePointcloudPCAOptions().normalize,
361 R
"(Compute principal components of a point cloud.
363:param points: Input points.
364:param shift_centroid: When true: covariance = (P-centroid)^T (P-centroid), when false: covariance = (P)^T (P).
365:param normalize: Should we divide the result by number of points?
367:returns: tuple of (center, eigenvectors, eigenvalues).)");
370 "compute_greedy_coloring",
373 size_t num_color_used,
374 std::optional<std::string_view> output_attribute_name) {
375 GreedyColoringOptions options;
376 options.element_type = element_type;
377 options.num_color_used = num_color_used;
378 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
383 "num_color_used"_a = 8,
384 "output_attribute_name"_a = nb::none(),
385 R
"(Compute greedy coloring of mesh elements.
387:param mesh: Input mesh.
388:param element_type: Element type to be colored. Can be either Vertex or Facet.
389:param num_color_used: Minimum number of colors to use. The algorithm will cycle through them but may use more.
390:param output_attribute_name: Output attribute name.
392:returns: Color attribute id.)");
395 "normalize_mesh_with_transform",
397 bool normalize_normals,
398 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 4, 4> {
399 TransformOptions options;
400 options.normalize_normals = normalize_normals;
401 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
405 "normalize_normals"_a = TransformOptions().normalize_normals,
406 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
407 R
"(Normalize a mesh to fit into a unit box centered at the origin.
409:param mesh: Input mesh.
410:param normalize_normals: Whether to normalize normals.
411:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
413:return Inverse transform, can be used to undo the normalization process.)");
417 "normalize_mesh_with_transform_2d",
419 bool normalize_normals,
420 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 3, 3> {
421 TransformOptions options;
422 options.normalize_normals = normalize_normals;
423 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
427 "normalize_normals"_a = TransformOptions().normalize_normals,
428 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
429 R
"(Normalize a mesh to fit into a unit box centered at the origin.
431:param mesh: Input mesh.
432:param normalize_normals: Whether to normalize normals.
433:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
435:return Inverse transform, can be used to undo the normalization process.)");
439 [](MeshType& mesh,
bool normalize_normals,
bool normalize_tangents_bitangents) ->
void {
440 TransformOptions options;
441 options.normalize_normals = normalize_normals;
442 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
446 "normalize_normals"_a = TransformOptions().normalize_normals,
447 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
448 R
"(Normalize a mesh to fit into a unit box centered at the origin.
450:param mesh: Input mesh.
451:param normalize_normals: Whether to normalize normals.
452:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.)");
455 "normalize_meshes_with_transform",
456 [](std::vector<MeshType*> meshes,
457 bool normalize_normals,
458 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 4, 4> {
459 TransformOptions options;
460 options.normalize_normals = normalize_normals;
461 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
466 "normalize_normals"_a = TransformOptions().normalize_normals,
467 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
468 R
"(Normalize a mesh to fit into a unit box centered at the origin.
470:param meshes: Input meshes.
471:param normalize_normals: Whether to normalize normals.
472:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
474:return Inverse transform, can be used to undo the normalization process.)");
477 "normalize_meshes_with_transform_2d",
478 [](std::vector<MeshType*> meshes,
479 bool normalize_normals,
480 bool normalize_tangents_bitangents) -> Eigen::Matrix<Scalar, 3, 3> {
481 TransformOptions options;
482 options.normalize_normals = normalize_normals;
483 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
488 "normalize_normals"_a = TransformOptions().normalize_normals,
489 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
490 R
"(Normalize a mesh to fit into a unit box centered at the origin.
492:param meshes: Input meshes.
493:param normalize_normals: Whether to normalize normals.
494:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
496:return Inverse transform, can be used to undo the normalization process.)");
501 [](std::vector<MeshType*> meshes,
502 bool normalize_normals,
503 bool normalize_tangents_bitangents) {
504 TransformOptions options;
505 options.normalize_normals = normalize_normals;
506 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
511 "normalize_normals"_a = TransformOptions().normalize_normals,
512 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
513 R
"(Normalize a list of meshes to fit into a unit box centered at the origin.
515:param meshes: Input meshes.
516:param normalize_normals: Whether to normalize normals.
517:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.)");
521 [](std::vector<MeshType*> meshes,
bool preserve_vertices) {
524 [&](
size_t i) ->
const MeshType& { return *meshes[i]; },
528 "preserve_attributes"_a =
true,
529 R
"(Combine a list of meshes into a single mesh.
531:param meshes: Input meshes.
532:param preserve_attributes: Whether to preserve attributes.
534:returns: The combined mesh.)");
537 "compute_seam_edges",
540 std::optional<std::string_view> output_attribute_name,
541 bool include_boundary_edges) {
542 SeamEdgesOptions options;
543 if (output_attribute_name) options.output_attribute_name = *output_attribute_name;
544 options.include_boundary_edges = include_boundary_edges;
548 "indexed_attribute_id"_a,
549 "output_attribute_name"_a = nb::none(),
550 "include_boundary_edges"_a = SeamEdgesOptions().include_boundary_edges,
551 R
"(Compute seam edges for a given indexed attribute.
553:param mesh: Input mesh.
554:param indexed_attribute_id: Input indexed attribute id.
555:param output_attribute_name: Output attribute name.
556:param include_boundary_edges: If true, boundary edges are also marked as seam edges.
558:returns: Attribute id for the output per-edge seam attribute (1 is a seam, 0 is not).)");
562 [](MeshType& mesh,
bool positive) {
563 OrientOptions options;
564 options.positive = positive;
568 "positive"_a = OrientOptions().positive,
569 R
"(Orient mesh facets to ensure positive or negative signed volume.
571:param mesh: Input mesh.
572:param positive: Whether to orient volumes positively or negatively.)");
575 "unify_index_buffer",
578 R
"(Unify the index buffer for all indexed attributes.
580:param mesh: Input mesh.
582:returns: Unified mesh.)");
585 "unify_index_buffer",
589 R
"(Unify the index buffer for selected attributes.
591:param mesh: Input mesh.
592:param attribute_ids: Attribute IDs to unify.
594:returns: Unified mesh.)");
597 "unify_index_buffer",
601 R
"(Unify the index buffer for selected attributes.
603:param mesh: Input mesh.
604:param attribute_names: Attribute names to unify.
606:returns: Unified mesh.)");
609 "triangulate_polygonal_facets",
610 [](MeshType& mesh, std::string_view scheme) {
611 lagrange::TriangulationOptions opt;
612 if (scheme ==
"earcut") {
614 }
else if (scheme ==
"centroid_fan") {
617 throw Error(lagrange::format(
"Unsupported triangulation scheme {}", scheme));
622 "scheme"_a =
"earcut",
623 R
"(Triangulate polygonal facets of the mesh.
625:param mesh: The input mesh to be triangulated in place.
626:param scheme: The triangulation scheme (options are 'earcut' and 'centroid_fan'))");
628 nb::enum_<ComponentOptions::ConnectivityType>(m, "ConnectivityType",
"Mesh connectivity type")
631 ComponentOptions::ConnectivityType::Vertex,
632 "Two facets are connected if they share a vertex")
635 ComponentOptions::ConnectivityType::Edge,
636 "Two facets are connected if they share an edge");
639 "compute_components",
641 std::optional<std::string_view> output_attribute_name,
642 std::optional<lagrange::ConnectivityType> connectivity_type,
643 std::optional<nb::list>& blocker_elements) {
644 lagrange::ComponentOptions opt;
645 if (output_attribute_name.has_value()) {
648 if (connectivity_type.has_value()) {
651 std::vector<Index> blocker_elements_vec;
652 if (blocker_elements.has_value()) {
653 for (
auto val : blocker_elements.value()) {
654 blocker_elements_vec.push_back(nb::cast<Index>(val));
660 "output_attribute_name"_a = nb::none(),
661 "connectivity_type"_a = nb::none(),
662 "blocker_elements"_a = nb::none(),
663 R
"(Compute connected components.
665This method will create a per-facet component id attribute named by the `output_attribute_name`
666argument. Each component id is in [0, num_components-1] range.
668:param mesh: The input mesh.
669:param output_attribute_name: The name of the output attribute.
670:param connectivity_type: The connectivity type. Either "Vertex" or "Edge".
671: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.
673:returns: The total number of components.)");
675 nb::class_<VertexValenceOptions>(m, "VertexValenceOptions",
"Vertex valence options")
678 "output_attribute_name",
680 "The name of the output attribute")
682 "induced_by_attribute",
684 "Optional per-edge attribute used as indicator function to restrict the graph used for "
685 "vertex valence computation");
688 "compute_vertex_valence",
691 "options"_a = VertexValenceOptions(),
692 R
"(Compute vertex valence
694:param mesh: The input mesh.
695:param options: The vertex valence options.
697:returns: The vertex valence attribute id.)");
700 "compute_vertex_valence",
702 std::optional<std::string_view> output_attribute_name,
703 std::optional<std::string_view> induced_by_attribute) {
704 VertexValenceOptions opt;
705 if (output_attribute_name.has_value()) {
706 opt.output_attribute_name = output_attribute_name.value();
708 if (induced_by_attribute.has_value()) {
709 opt.induced_by_attribute = induced_by_attribute.value();
714 "output_attribute_name"_a = nb::none(),
715 "induced_by_attribute"_a = nb::none(),
716 R
"(Compute vertex valence);
718:param mesh: The input mesh.
719:param output_attribute_name: The name of the output attribute.
720:param induced_by_attribute: Optional per-edge attribute used as indicator function to restrict the graph used for vertex valence computation.
722:returns: The vertex valence attribute id)");
724 nb::class_<TangentBitangentOptions>(m, "TangentBitangentOptions",
"Tangent bitangent options")
727 "tangent_attribute_name",
729 "The name of the output tangent attribute, default is `@tangent`")
731 "bitangent_attribute_name",
733 "The name of the output bitangent attribute, default is `@bitangent`")
737 "The name of the uv attribute")
739 "normal_attribute_name",
741 "The name of the normal attribute")
743 "output_element_type",
745 "The output element type")
749 "Whether to pad the output tangent/bitangent with sign")
751 "orthogonalize_bitangent",
753 "Whether to compute the bitangent as cross(normal, tangent). If false, the bitangent "
754 "is computed as the derivative of v-coordinate")
756 "keep_existing_tangent",
758 "Whether to recompute tangent if the tangent attribute (specified by "
759 "tangent_attribute_name) already exists. If true, bitangent is computed by normalizing "
760 "cross(normal, tangent) and param orthogonalize_bitangent must be true.");
761 nb::class_<TangentBitangentResult>(m,
"TangentBitangentResult",
"Tangent bitangent result")
766 "The output tangent attribute id")
770 "The output bitangent attribute id");
773 "compute_tangent_bitangent",
776 "options"_a = TangentBitangentOptions(),
777 R
"(Compute tangent and bitangent vector attributes.
779:param mesh: The input mesh.
780:param options: The tangent bitangent options.
782:returns: The tangent and bitangent attribute ids)");
785 "compute_tangent_bitangent",
787 std::optional<std::string_view>(tangent_attribute_name),
788 std::optional<std::string_view>(bitangent_attribute_name),
789 std::optional<std::string_view>(uv_attribute_name),
790 std::optional<std::string_view>(normal_attribute_name),
791 std::optional<AttributeElement>(output_attribute_type),
792 std::optional<bool>(pad_with_sign),
793 std::optional<bool>(orthogonalize_bitangent),
794 std::optional<bool>(keep_existing_tangent)) {
795 TangentBitangentOptions opt;
796 if (tangent_attribute_name.has_value()) {
797 opt.tangent_attribute_name = tangent_attribute_name.value();
799 if (bitangent_attribute_name.has_value()) {
800 opt.bitangent_attribute_name = bitangent_attribute_name.value();
802 if (uv_attribute_name.has_value()) {
803 opt.uv_attribute_name = uv_attribute_name.value();
805 if (normal_attribute_name.has_value()) {
806 opt.normal_attribute_name = normal_attribute_name.value();
808 if (output_attribute_type.has_value()) {
809 opt.output_element_type = output_attribute_type.value();
811 if (pad_with_sign.has_value()) {
812 opt.pad_with_sign = pad_with_sign.value();
814 if (orthogonalize_bitangent.has_value()) {
815 opt.orthogonalize_bitangent = orthogonalize_bitangent.value();
817 if (keep_existing_tangent.has_value()) {
818 opt.keep_existing_tangent = keep_existing_tangent.value();
822 return std::make_tuple(r.tangent_id, r.bitangent_id);
825 "tangent_attribute_name"_a = nb::none(),
826 "bitangent_attribute_name"_a = nb::none(),
827 "uv_attribute_name"_a = nb::none(),
828 "normal_attribute_name"_a = nb::none(),
829 "output_attribute_type"_a = nb::none(),
830 "pad_with_sign"_a = nb::none(),
831 "orthogonalize_bitangent"_a = nb::none(),
832 "keep_existing_tangent"_a = nb::none(),
833 R
"(Compute tangent and bitangent vector attributes (Pythonic API).
835:param mesh: The input mesh.
836:param tangent_attribute_name: The name of the output tangent attribute.
837:param bitangent_attribute_name: The name of the output bitangent attribute.
838:param uv_attribute_name: The name of the uv attribute.
839:param normal_attribute_name: The name of the normal attribute.
840:param output_attribute_type: The output element type.
841:param pad_with_sign: Whether to pad the output tangent/bitangent with sign.
842:param orthogonalize_bitangent: Whether to compute the bitangent as sign * cross(normal, tangent).
843: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.
845:returns: The tangent and bitangent attribute ids)");
852 "old_attribute_id"_a,
853 "new_attribute_name"_a,
855 R
"(Map an attribute to a new element type.
857:param mesh: The input mesh.
858:param old_attribute_id: The id of the input attribute.
859:param new_attribute_name: The name of the new attribute.
860:param new_element: The new element type.
862:returns: The id of the new attribute.)");
870 "old_attribute_name"_a,
871 "new_attribute_name"_a,
873 R
"(Map an attribute to a new element type.
875:param mesh: The input mesh.
876:param old_attribute_name: The name of the input attribute.
877:param new_attribute_name: The name of the new attribute.
878:param new_element: The new element type.
880:returns: The id of the new attribute.)");
883 "map_attribute_in_place",
889 R
"(Map an attribute to a new element type in place.
891:param mesh: The input mesh.
892:param id: The id of the input attribute.
893:param new_element: The new element type.
895:returns: The id of the new attribute.)");
898 "map_attribute_in_place",
904 R
"(Map an attribute to a new element type in place.
906:param mesh: The input mesh.
907:param name: The name of the input attribute.
908:param new_element: The new element type.
910:returns: The id of the new attribute.)");
912 nb::class_<FacetAreaOptions>(m, "FacetAreaOptions",
"Options for computing facet area.")
915 "output_attribute_name",
917 "The name of the output attribute.");
920 "compute_facet_area",
923 "options"_a = FacetAreaOptions(),
924 R
"(Compute facet area.
926:param mesh: The input mesh.
927:param options: The options for computing facet area.
929:returns: The id of the new attribute.)");
932 "compute_facet_area",
933 [](MeshType& mesh, std::optional<std::string_view> name) {
934 FacetAreaOptions opt;
935 if (name.has_value()) {
936 opt.output_attribute_name = name.value();
941 "output_attribute_name"_a = nb::none(),
942 R
"(Compute facet area (Pythonic API).
944:param mesh: The input mesh.
945:param output_attribute_name: The name of the output attribute.
947:returns: The id of the new attribute.)");
950 "compute_facet_vector_area",
951 [](MeshType& mesh, std::optional<std::string_view> name) {
952 FacetVectorAreaOptions opt;
953 if (name.has_value()) {
954 opt.output_attribute_name = name.value();
959 "output_attribute_name"_a = nb::none(),
960 R
"(Compute facet vector area (Pythonic API).
962Vector area is defined as the area multiplied by the facet normal.
963For triangular facets, it is equivalent to half of the cross product of two edges.
964For non-planar polygonal facets, the vector area offers a robust way to compute the area and normal.
965The magnitude of the vector area is the largest area of any orthogonal projection of the facet.
966The direction of the vector area is the normal direction that maximizes the projected area [1, 2].
968[1] Sullivan, John M. "Curvatures of smooth and discrete surfaces." Discrete differential geometry.
969Basel: Birkhäuser Basel, 2008. 175-188.
971[2] Alexa, Marc, and Max Wardetzky. "Discrete Laplacians on general polygonal meshes." ACM SIGGRAPH
9722011 papers. 2011. 1-10.
974:param mesh: The input mesh.
975:param output_attribute_name: The name of the output attribute.
977:returns: The id of the new attribute.)");
979 nb::class_<MeshAreaOptions>(m, "MeshAreaOptions",
"Options for computing mesh area.")
982 "input_attribute_name",
984 "The name of the pre-computed facet area attribute, default is `@facet_area`.")
988 "Whether to use signed area.");
994 "options"_a = MeshAreaOptions(),
995 R
"(Compute mesh area.
997:param mesh: The input mesh.
998:param options: The options for computing mesh area.
1000:returns: The mesh area.)");
1006 "options"_a = MeshAreaOptions(),
1007 R
"(Compute UV mesh area.
1009:param mesh: The input mesh.
1010:param options: The options for computing mesh area.
1012:returns: The UV mesh area.)");
1015 "compute_mesh_area",
1017 std::optional<std::string_view> input_attribute_name,
1018 std::optional<bool> use_signed_area) {
1019 MeshAreaOptions opt;
1020 if (input_attribute_name.has_value()) {
1021 opt.input_attribute_name = input_attribute_name.value();
1023 if (use_signed_area.has_value()) {
1024 opt.use_signed_area = use_signed_area.value();
1029 "input_attribute_name"_a = nb::none(),
1030 "use_signed_area"_a = nb::none(),
1031 R
"(Compute mesh area (Pythonic API).
1033:param mesh: The input mesh.
1034:param input_attribute_name: The name of the pre-computed facet area attribute.
1035:param use_signed_area: Whether to use signed area.
1037:returns: The mesh area.)");
1039 nb::class_<FacetCentroidOptions>(m, "FacetCentroidOptions",
"Facet centroid options.")
1042 "output_attribute_name",
1044 "The name of the output attribute.");
1046 "compute_facet_centroid",
1049 "options"_a = FacetCentroidOptions(),
1050 R
"(Compute facet centroid.
1052:param mesh: The input mesh.
1053:param options: The options for computing facet centroid.
1055:returns: The id of the new attribute.)");
1058 "compute_facet_centroid",
1059 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1060 FacetCentroidOptions opt;
1061 if (output_attribute_name.has_value()) {
1062 opt.output_attribute_name = output_attribute_name.value();
1067 "output_attribute_name"_a = nb::none(),
1068 R
"(Compute facet centroid (Pythonic API).
1070:param mesh: Input mesh.
1071:param output_attribute_name: Output attribute name.
1073:returns: Attribute ID.)");
1076 "compute_facet_circumcenter",
1077 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1078 FacetCircumcenterOptions opt;
1079 if (output_attribute_name.has_value()) {
1080 opt.output_attribute_name = output_attribute_name.value();
1085 "output_attribute_name"_a = nb::none(),
1086 R
"(Compute facet circumcenter (Pythonic API).
1088:param mesh: The input mesh.
1089:param output_attribute_name: The name of the output attribute.
1091:returns: The id of the new attribute.)");
1093 nb::enum_<MeshCentroidOptions::WeightingType>(
1095 "CentroidWeightingType",
1096 "Centroid weighting type.")
1100 nb::class_<MeshCentroidOptions>(m,
"MeshCentroidOptions",
"Mesh centroid options.")
1102 .def_rw(
"weighting_type", &MeshCentroidOptions::weighting_type,
"The weighting type.")
1104 "facet_centroid_attribute_name",
1106 "The name of the pre-computed facet centroid attribute if available.")
1108 "facet_area_attribute_name",
1110 "The name of the pre-computed facet area attribute if available.");
1113 "compute_mesh_centroid",
1114 [](
const MeshType& mesh, MeshCentroidOptions opt) {
1115 const Index dim = mesh.get_dimension();
1121 "options"_a = MeshCentroidOptions(),
1122 R
"(Compute mesh centroid.
1124:param mesh: Input mesh.
1125:param options: Centroid computation options.
1127:returns: Mesh centroid coordinates.)");
1130 "compute_mesh_centroid",
1132 std::optional<MeshCentroidOptions::WeightingType> weighting_type,
1133 std::optional<std::string_view> facet_centroid_attribute_name,
1134 std::optional<std::string_view> facet_area_attribute_name) {
1135 MeshCentroidOptions opt;
1136 if (weighting_type.has_value()) {
1137 opt.weighting_type = weighting_type.value();
1139 if (facet_centroid_attribute_name.has_value()) {
1140 opt.facet_centroid_attribute_name = facet_centroid_attribute_name.value();
1142 if (facet_area_attribute_name.has_value()) {
1143 opt.facet_area_attribute_name = facet_area_attribute_name.value();
1145 const Index dim = mesh.get_dimension();
1151 "weighting_type"_a = nb::none(),
1152 "facet_centroid_attribute_name"_a = nb::none(),
1153 "facet_area_attribute_name"_a = nb::none(),
1154 R
"(Compute mesh centroid (Pythonic API).
1156:param mesh: Input mesh.
1157:param weighting_type: Weighting type (default: Area).
1158:param facet_centroid_attribute_name: Pre-computed facet centroid attribute name.
1159:param facet_area_attribute_name: Pre-computed facet area attribute name.
1161:returns: Mesh centroid coordinates.)");
1165 [](MeshType& mesh, Tensor<Index> new_to_old) {
1166 auto [data, shape, stride] = tensor_to_span(new_to_old);
1172 R
"(Reorder vertices of a mesh in place based on a permutation.
1174:param mesh: input mesh
1175:param new_to_old: permutation vector for vertices)");
1179 [](MeshType& mesh, Tensor<Index> new_to_old) {
1180 auto [data, shape, stride] = tensor_to_span(new_to_old);
1186 R
"(Reorder facets of a mesh in place based on a permutation.
1188:param mesh: input mesh
1189:param new_to_old: permutation vector for facets)");
1191 nb::enum_<MappingPolicy>(m, "MappingPolicy",
"Mapping policy for handling collisions.")
1196 nb::class_<RemapVerticesOptions>(m,
"RemapVerticesOptions",
"Options for remapping vertices.")
1199 "collision_policy_float",
1201 "The collision policy for float attributes.")
1203 "collision_policy_integral",
1205 "The collision policy for integral attributes.");
1209 [](MeshType& mesh, Tensor<Index> old_to_new, RemapVerticesOptions opt) {
1210 auto [data, shape, stride] = tensor_to_span(old_to_new);
1216 "options"_a = RemapVerticesOptions(),
1217 R
"(Remap vertices of a mesh in place based on a permutation.
1219:param mesh: input mesh
1220:param old_to_new: permutation vector for vertices
1221:param options: options for remapping vertices)");
1226 Tensor<Index> old_to_new,
1227 std::optional<MappingPolicy> collision_policy_float,
1228 std::optional<MappingPolicy> collision_policy_integral) {
1229 RemapVerticesOptions opt;
1230 if (collision_policy_float.has_value()) {
1231 opt.collision_policy_float = collision_policy_float.value();
1233 if (collision_policy_integral.has_value()) {
1234 opt.collision_policy_integral = collision_policy_integral.value();
1236 auto [data, shape, stride] = tensor_to_span(old_to_new);
1242 "collision_policy_float"_a = nb::none(),
1243 "collision_policy_integral"_a = nb::none(),
1244 R
"(Remap vertices of a mesh in place based on a permutation (Pythonic API).
1246:param mesh: input mesh
1247:param old_to_new: permutation vector for vertices
1248:param collision_policy_float: The collision policy for float attributes.
1249:param collision_policy_integral: The collision policy for integral attributes.)");
1253 [](MeshType& mesh, std::string_view method) {
1255 if (method ==
"Lexicographic" || method ==
"lexicographic") {
1257 }
else if (method ==
"Morton" || method ==
"morton") {
1259 }
else if (method ==
"Hilbert" || method ==
"hilbert") {
1261 }
else if (method ==
"None" || method ==
"none") {
1264 throw std::runtime_error(lagrange::format(
"Invalid reordering method: {}", method));
1270 "method"_a =
"Morton",
1271 R
"(Reorder a mesh in place.
1273:param mesh: input mesh
1274:param method: reordering method, options are 'Lexicographic', 'Morton', 'Hilbert', 'None' (default is 'Morton').)",
1276 "def reorder_mesh(mesh: SurfaceMesh, "
1277 "method: typing.Literal['Lexicographic', 'Morton', 'Hilbert', 'None']) -> None"));
1280 "separate_by_facet_groups",
1282 Tensor<Index> facet_group_indices,
1283 std::string_view source_vertex_attr_name,
1284 std::string_view source_facet_attr_name,
1285 bool map_attributes) {
1286 SeparateByFacetGroupsOptions options;
1287 options.source_vertex_attr_name = source_vertex_attr_name;
1288 options.source_facet_attr_name = source_facet_attr_name;
1290 auto [data, shape, stride] = tensor_to_span(facet_group_indices);
1295 "facet_group_indices"_a,
1296 "source_vertex_attr_name"_a =
"",
1297 "source_facet_attr_name"_a =
"",
1298 "map_attributes"_a =
false,
1299 R
"(Extract a set of submeshes based on facet groups.
1301:param mesh: The source mesh.
1302:param facet_group_indices: The group index for each facet. Each group index must be in the range of [0, max(facet_group_indices)]
1303:param source_vertex_attr_name: The optional attribute name to track source vertices.
1304:param source_facet_attr_name: The optional attribute name to track source facets.
1306:returns: A list of meshes, one for each facet group.
1310 "separate_by_components",
1312 std::string_view source_vertex_attr_name,
1313 std::string_view source_facet_attr_name,
1314 bool map_attributes,
1316 SeparateByComponentsOptions options;
1317 options.source_vertex_attr_name = source_vertex_attr_name;
1318 options.source_facet_attr_name = source_facet_attr_name;
1320 options.connectivity_type = connectivity_type;
1324 "source_vertex_attr_name"_a =
"",
1325 "source_facet_attr_name"_a =
"",
1326 "map_attributes"_a =
false,
1328 R
"(Extract a set of submeshes based on connected components.
1330:param mesh: The source mesh.
1331:param source_vertex_attr_name: The optional attribute name to track source vertices.
1332:param source_facet_attr_name: The optional attribute name to track source facets.
1333:param map_attributes: Map attributes from the source to target meshes.
1334:param connectivity_type: The connectivity used for component computation.
1336:returns: A list of meshes, one for each connected component.
1342 std::variant<Tensor<Index>, nb::list> selected_facets,
1343 std::string_view source_vertex_attr_name,
1344 std::string_view source_facet_attr_name,
1345 bool map_attributes) {
1346 SubmeshOptions options;
1347 options.source_vertex_attr_name = source_vertex_attr_name;
1348 options.source_facet_attr_name = source_facet_attr_name;
1350 if (std::holds_alternative<nb::list>(selected_facets)) {
1351 auto selected_facets_list =
1352 nb::cast<std::vector<Index>>(std::get<nb::list>(selected_facets));
1353 span<const Index> data{selected_facets_list.data(), selected_facets_list.size()};
1356 auto selected_facets_tensor = std::get<Tensor<Index>>(selected_facets);
1357 auto [data, shape, stride] = tensor_to_span(selected_facets_tensor);
1363 "selected_facets"_a,
1364 "source_vertex_attr_name"_a =
"",
1365 "source_facet_attr_name"_a =
"",
1366 "map_attributes"_a =
false,
1367 R
"(Extract a submesh based on the selected facets.
1369:param mesh: The source mesh.
1370:param selected_facets: A list or tensor of facet ids to extract.
1371:param source_vertex_attr_name: The optional attribute name to track source vertices.
1372:param source_facet_attr_name: The optional attribute name to track source facets.
1373:param map_attributes: Map attributes from the source to target meshes.
1375:returns: A mesh that contains only the selected facets.
1379 "compute_dihedral_angles",
1381 std::optional<std::string_view> output_attribute_name,
1382 std::optional<std::string_view> facet_normal_attribute_name,
1383 std::optional<bool> recompute_facet_normals,
1384 std::optional<bool> keep_facet_normals) {
1385 DihedralAngleOptions options;
1386 if (output_attribute_name.has_value()) {
1387 options.output_attribute_name = output_attribute_name.value();
1389 if (facet_normal_attribute_name.has_value()) {
1390 options.facet_normal_attribute_name = facet_normal_attribute_name.value();
1392 if (recompute_facet_normals.has_value()) {
1393 options.recompute_facet_normals = recompute_facet_normals.value();
1395 if (keep_facet_normals.has_value()) {
1396 options.keep_facet_normals = keep_facet_normals.value();
1401 "output_attribute_name"_a = nb::none(),
1402 "facet_normal_attribute_name"_a = nb::none(),
1403 "recompute_facet_normals"_a = nb::none(),
1404 "keep_facet_normals"_a = nb::none(),
1405 R
"(Compute dihedral angles for each edge.
1407The dihedral angle of an edge is defined as the angle between the __normals__ of two facets adjacent
1408to the edge. The dihedral angle is always in the range [0, pi] for manifold edges. For boundary
1409edges, the dihedral angle defaults to 0. For non-manifold edges, the dihedral angle is not
1410well-defined and will be set to the special value 2 * π.
1412:param mesh: The source mesh.
1413:param output_attribute_name: The optional edge attribute name to store the dihedral angles.
1414:param facet_normal_attribute_name: The optional attribute name to store the facet normals.
1415:param recompute_facet_normals: Whether to recompute facet normals.
1416:param keep_facet_normals: Whether to keep newly computed facet normals. It has no effect on pre-existing facet normals.
1418:return: The edge attribute id of dihedral angles.)");
1421 "compute_edge_lengths",
1422 [](MeshType& mesh, std::optional<std::string_view> output_attribute_name) {
1423 EdgeLengthOptions options;
1424 if (output_attribute_name.has_value())
1425 options.output_attribute_name = output_attribute_name.value();
1429 "output_attribute_name"_a = nb::none(),
1430 R
"(Compute edge lengths.
1432:param mesh: The source mesh.
1433:param output_attribute_name: The optional edge attribute name to store the edge lengths.
1435:return: The edge attribute id of edge lengths.)");
1438 "compute_dijkstra_distance",
1441 const nb::list& barycentric_coords,
1442 std::optional<Scalar> radius,
1443 std::string_view output_attribute_name,
1444 bool output_involved_vertices) {
1445 DijkstraDistanceOptions<Scalar, Index> options;
1446 options.seed_facet = seed_facet;
1447 for (
auto val : barycentric_coords) {
1448 options.barycentric_coords.push_back(nb::cast<Scalar>(val));
1450 if (radius.has_value()) {
1451 options.radius = radius.value();
1453 options.output_attribute_name = output_attribute_name;
1454 options.output_involved_vertices = output_involved_vertices;
1459 "barycentric_coords"_a,
1460 "radius"_a = nb::none(),
1461 "output_attribute_name"_a = DijkstraDistanceOptions<Scalar, Index>{}.output_attribute_name,
1462 "output_involved_vertices"_a =
1463 DijkstraDistanceOptions<Scalar, Index>{}.output_involved_vertices,
1464 R
"(Compute Dijkstra distance from a seed facet.
1466:param mesh: The source mesh.
1467:param seed_facet: The seed facet index.
1468:param barycentric_coords: The barycentric coordinates of the seed facet.
1469:param radius: The maximum radius of the dijkstra distance.
1470:param output_attribute_name: The output attribute name to store the dijkstra distance.
1471:param output_involved_vertices: Whether to output the list of involved vertices.)");
1474 "weld_indexed_attribute",
1477 std::optional<double> epsilon_rel,
1478 std::optional<double> epsilon_abs,
1479 std::optional<double> angle_abs,
1480 std::optional<std::vector<size_t>> exclude_vertices) {
1481 WeldOptions options;
1482 options.epsilon_rel = epsilon_rel;
1483 options.epsilon_abs = epsilon_abs;
1484 options.angle_abs = angle_abs;
1485 if (exclude_vertices.has_value()) {
1486 const auto& exclude_vertices_vec = exclude_vertices.value();
1487 options.exclude_vertices = {
1488 exclude_vertices_vec.data(),
1489 exclude_vertices_vec.size()};
1495 "epsilon_rel"_a = nb::none(),
1496 "epsilon_abs"_a = nb::none(),
1497 "angle_abs"_a = nb::none(),
1498 "exclude_vertices"_a = nb::none(),
1499 R
"(Weld indexed attribute.
1501:param mesh: The source mesh to be updated in place.
1502:param attribute_id: The indexed attribute id to weld.
1503:param epsilon_rel: The relative tolerance for welding.
1504:param epsilon_abs: The absolute tolerance for welding.
1505:param angle_abs: The absolute angle tolerance for welding.
1506:param exclude_vertices: Optional list of vertex indices to exclude from welding.)");
1512 R
"(Compute the Euler characteristic.
1514:param mesh: The source mesh.
1516:return: The Euler characteristic.)");
1522 R
"(Check if the mesh is closed.
1524A mesh is considered closed if it has no boundary edges.
1526:param mesh: The source mesh.
1528:return: Whether the mesh is closed.)");
1531 "is_vertex_manifold",
1534 R
"(Check if the mesh is vertex manifold.
1536:param mesh: The source mesh.
1538:return: Whether the mesh is vertex manifold.)");
1544 R
"(Check if the mesh is edge manifold.
1546:param mesh: The source mesh.
1548:return: Whether the mesh is edge manifold.)");
1552A mesh considered as manifold if it is both vertex and edge manifold.
1554:param mesh: The source mesh.
1556:return: Whether the mesh is manifold.)");
1559 "compute_vertex_is_manifold",
1560 [](MeshType& mesh, std::string_view output_attribute_name) {
1561 VertexManifoldOptions options;
1562 options.output_attribute_name = output_attribute_name;
1566 "output_attribute_name"_a = VertexManifoldOptions().output_attribute_name,
1567 R
"(Compute whether each vertex is manifold.
1569A vertex is considered manifold if its one-ring neighborhood is homeomorphic to a disk.
1571:param mesh: The source mesh.
1572:param output_attribute_name: The output vertex attribute name.
1574:return: The attribute id of a vertex attribute indicating whether a vertex is manifold.)");
1577 "compute_edge_is_manifold",
1578 [](MeshType& mesh, std::string_view output_attribute_name) {
1579 EdgeManifoldOptions options;
1580 options.output_attribute_name = output_attribute_name;
1584 "output_attribute_name"_a = EdgeManifoldOptions().output_attribute_name,
1585 R
"(Compute whether each edge is manifold.
1587An edge is considered manifold if it is adjacent to one or two facets.
1589:param mesh: The source mesh.
1590:param output_attribute_name: The output edge attribute name.
1592:return: The attribute id of an edge attribute indicating whether an edge is manifold.)");
1598 R
"(Check if the mesh is oriented.
1600A mesh is oriented if all interior edges are oriented. An interior edge is considered as
1601oriented if it has the same number of half-edges for each edge direction. I.e. the number of
1602facets that use the edge in one direction equals the number of facets that use the edge in the
1603opposite direction. Boundary edges are always considered as oriented.
1605:param mesh: The source mesh.
1607:return: Whether the mesh is oriented.)");
1610 "compute_edge_is_oriented",
1611 [](MeshType& mesh, std::string_view output_attribute_name) {
1612 OrientationOptions options;
1613 options.output_attribute_name = output_attribute_name;
1617 "output_attribute_name"_a = OrientationOptions().output_attribute_name,
1618 R
"(Compute whether each edge is oriented.
1620An interior edge is considered as oriented if it has the same number of half-edges for each edge
1621direction. I.e. the number of facets that use the edge in one direction equals to the number of
1622facets that use the edge in the opposite direction. Boundary edges are always considered as
1625:param mesh: The source mesh.
1626:param output_attribute_name: The output edge attribute name.
1628:return: The attribute id of an edge attribute indicating whether an edge is oriented.)");
1633 StubType<Eigen::Matrix<Scalar, 4, 4>, ArrayLikeHint> affine_transform,
1634 bool normalize_normals,
1635 bool normalize_tangents_bitangents,
1637 bool in_place) -> std::optional<MeshType> {
1638 Eigen::Transform<Scalar, 3, Eigen::Affine> M(affine_transform.value);
1639 TransformOptions options;
1640 options.normalize_normals = normalize_normals;
1641 options.normalize_tangents_bitangents = normalize_tangents_bitangents;
1642 options.reorient = reorient;
1644 std::optional<MeshType> result;
1653 "affine_transform"_a,
1655 "normalize_normals"_a = TransformOptions().normalize_normals,
1656 "normalize_tangents_bitangents"_a = TransformOptions().normalize_tangents_bitangents,
1657 "reorient"_a = TransformOptions().reorient,
1658 "in_place"_a =
true,
1659 R
"(Apply affine transformation to a mesh.
1661:param mesh: Input mesh.
1662:param affine_transform: Affine transformation matrix.
1663:param normalize_normals: Whether to normalize normals.
1664:param normalize_tangents_bitangents: Whether to normalize tangents and bitangents.
1665:param reorient: If the transform has a negative determinant, flip facets and reorient attributes (normals, tangents, bitangents).
1666:param in_place: Whether to apply transformation in place.
1668:returns: Transformed mesh if in_place is False.)");
1670 nb::enum_<DistortionMetric>(m, "DistortionMetric",
"Distortion metric.")
1674 "SymmetricDirichlet",
1676 "Symmetric Dirichlet energy")
1677 .value(
"AreaRatio", DistortionMetric::AreaRatio,
"Area ratio")
1681 "compute_uv_distortion",
1683 std::string_view uv_attribute_name,
1684 std::string_view output_attribute_name,
1686 UVDistortionOptions opt;
1687 opt.uv_attribute_name = uv_attribute_name;
1688 opt.output_attribute_name = output_attribute_name;
1689 opt.metric = metric;
1693 "uv_attribute_name"_a =
"@uv",
1694 "output_attribute_name"_a =
"@uv_measure",
1696 R
"(Compute UV distortion.
1698:param mesh: Input mesh.
1699:param uv_attribute_name: UV attribute name (default: "@uv").
1700:param output_attribute_name: Output attribute name (default: "@uv_measure").
1701:param metric: Distortion metric (default: MIPS).
1703:returns: Facet attribute ID for distortion.)");
1707 [](
const MeshType& mesh,
1708 std::variant<AttributeId, std::string_view> attribute,
1711 bool keep_attributes) {
1713 if (std::holds_alternative<AttributeId>(attribute)) {
1714 opt.attribute_id = std::get<AttributeId>(attribute);
1716 opt.attribute_id = mesh.get_attribute_id(std::get<std::string_view>(attribute));
1718 opt.isovalue = isovalue;
1719 opt.keep_below = keep_below;
1720 opt.keep_attributes = keep_attributes;
1725 "isovalue"_a = IsolineOptions().isovalue,
1726 "keep_below"_a = IsolineOptions().keep_below,
1727 "keep_attributes"_a = IsolineOptions().keep_attributes,
1728 R
"(Trim a triangle mesh by an isoline.
1730:param mesh: Input triangle mesh.
1731:param attribute: Attribute ID or name of scalar field (vertex or indexed).
1732:param isovalue: Isovalue to trim with.
1733:param keep_below: Whether to keep the part below the isoline.
1734:param keep_attributes: Whether to propagate input mesh attributes to the output mesh.
1736:returns: Trimmed mesh.)");
1740 [](
const MeshType& mesh,
1741 std::variant<AttributeId, std::string_view> attribute,
1743 bool keep_attributes) {
1745 if (std::holds_alternative<AttributeId>(attribute)) {
1746 opt.attribute_id = std::get<AttributeId>(attribute);
1748 opt.attribute_id = mesh.get_attribute_id(std::get<std::string_view>(attribute));
1750 opt.isovalue = isovalue;
1751 opt.keep_attributes = keep_attributes;
1756 "isovalue"_a = IsolineOptions().isovalue,
1757 "keep_attributes"_a = IsolineOptions().keep_attributes,
1758 R
"(Extract the isoline of an implicit function defined on the mesh vertices/corners.
1760The input mesh must be a triangle mesh.
1762:param mesh: Input triangle mesh to extract the isoline from.
1763:param attribute: Attribute id or name of the scalar field to use. Can be a vertex or indexed attribute.
1764:param isovalue: Isovalue to extract.
1765:param keep_attributes: Whether to propagate input mesh attributes to the output mesh.
1767:return: A mesh whose facets is a collection of size 2 elements representing the extracted isoline.)");
1771 [](
const MeshType& mesh,
1772 std::variant<AttributeId, std::string_view> attribute,
1774 bool keep_attributes) {
1776 if (std::holds_alternative<AttributeId>(attribute)) {
1777 opt.attribute_id = std::get<AttributeId>(attribute);
1779 opt.attribute_id = mesh.get_attribute_id(std::get<std::string_view>(attribute));
1781 opt.isovalue = isovalue;
1782 opt.keep_attributes = keep_attributes;
1787 "isovalue"_a = IsolineOptions().isovalue,
1788 "keep_attributes"_a = IsolineOptions().keep_attributes,
1789 R
"(Insert the isoline of an implicit function into a triangle mesh.
1791Unlike trimming, the whole mesh is retained; facets crossed by the isoline are split so that the
1792isoline appears as a chain of edges in the output. A triangle crossed in its interior is split into
1793a triangle and a quad, so the output is in general a mixed triangle/quad mesh. When the isoline
1794passes exactly through an existing vertex (or lies along an edge), the split degenerates: the
1795triangle may instead be split into two triangles, or left unchanged.
1797:param mesh: Input triangle mesh to insert the isoline into.
1798:param attribute: Attribute id or name of the scalar field to use. Can be a vertex or indexed attribute.
1799:param isovalue: Isovalue to insert.
1800:param keep_attributes: Whether to propagate input mesh attributes to the output mesh.
1802:return: The input mesh with the isoline inserted as a chain of edges.)");
1806 "filter_attributes",
1808 std::optional<std::vector<AttributeNameOrId>> included_attributes,
1809 std::optional<std::vector<AttributeNameOrId>> excluded_attributes,
1810 StubType<std::optional<std::unordered_set<AttributeUsage>>, IterableUsageHint>
1812 StubType<std::optional<std::unordered_set<AttributeElement>>, IterableElementHint>
1813 included_element_types) {
1814 AttributeFilter filter;
1815 if (included_attributes.has_value()) {
1816 filter.included_attributes = included_attributes.value();
1818 if (excluded_attributes.has_value()) {
1819 filter.excluded_attributes = excluded_attributes.value();
1821 if (included_usages.value.has_value()) {
1822 filter.included_usages.clear_all();
1823 for (
auto usage : included_usages.value.value()) {
1824 filter.included_usages.set(usage);
1827 if (included_element_types.value.has_value()) {
1828 filter.included_element_types.clear_all();
1829 for (
auto element_type : included_element_types.value.value()) {
1830 filter.included_element_types.set(element_type);
1836 "included_attributes"_a = nb::none(),
1837 "excluded_attributes"_a = nb::none(),
1838 "included_usages"_a = nb::none(),
1839 "included_element_types"_a = nb::none(),
1840 R
"(Filters the attributes of mesh according to user specifications.
1842:param mesh: Input mesh.
1843:param included_attributes: List of attribute names or ids to include. By default, all attributes are included.
1844:param excluded_attributes: List of attribute names or ids to exclude. By default, no attribute is excluded.
1845:param included_usages: List of attribute usages to include. By default, all usages are included.
1846:param included_element_types: List of attribute element types to include. By default, all element types are included.)");
1851 std::variant<AttributeId, std::string_view> input_attribute,
1852 nb::type_object dtype,
1853 std::optional<std::string_view> output_attribute_name) {
1856 auto np = nb::module_::import_(
"numpy");
1857 if (output_attribute_name.has_value()) {
1858 auto name = output_attribute_name.value();
1859 if (dtype.is(&PyFloat_Type)) {
1862 }
else if (dtype.is(&PyLong_Type)) {
1865 }
else if (dtype.is(np.attr(
"float32"))) {
1867 }
else if (dtype.is(np.attr(
"float64"))) {
1869 }
else if (dtype.is(np.attr(
"int8"))) {
1871 }
else if (dtype.is(np.attr(
"int16"))) {
1873 }
else if (dtype.is(np.attr(
"int32"))) {
1875 }
else if (dtype.is(np.attr(
"int64"))) {
1877 }
else if (dtype.is(np.attr(
"uint8"))) {
1879 }
else if (dtype.is(np.attr(
"uint16"))) {
1881 }
else if (dtype.is(np.attr(
"uint32"))) {
1883 }
else if (dtype.is(np.attr(
"uint64"))) {
1886 throw nb::type_error(
"Unsupported `dtype`!");
1889 if (dtype.is(&PyFloat_Type)) {
1892 }
else if (dtype.is(&PyLong_Type)) {
1895 }
else if (dtype.is(np.attr(
"float32"))) {
1897 }
else if (dtype.is(np.attr(
"float64"))) {
1899 }
else if (dtype.is(np.attr(
"int8"))) {
1901 }
else if (dtype.is(np.attr(
"int16"))) {
1903 }
else if (dtype.is(np.attr(
"int32"))) {
1905 }
else if (dtype.is(np.attr(
"int64"))) {
1907 }
else if (dtype.is(np.attr(
"uint8"))) {
1909 }
else if (dtype.is(np.attr(
"uint16"))) {
1911 }
else if (dtype.is(np.attr(
"uint32"))) {
1913 }
else if (dtype.is(np.attr(
"uint64"))) {
1916 throw nb::type_error(
"Unsupported `dtype`!");
1921 if (std::holds_alternative<AttributeId>(input_attribute)) {
1922 return cast(std::get<AttributeId>(input_attribute));
1924 AttributeId id = mesh.get_attribute_id(std::get<std::string_view>(input_attribute));
1929 "input_attribute"_a,
1931 "output_attribute_name"_a = nb::none(),
1932 R
"(Cast an attribute to a new dtype.
1934:param mesh: The input mesh.
1935:param input_attribute: The input attribute id or name.
1936:param dtype: The new dtype.
1937:param output_attribute_name: The output attribute name. If none, cast will replace the input attribute.
1939:returns: The id of the new attribute.)");
1942 "get_unique_attribute_name",
1943 [](
const MeshType& mesh,
1944 std::string_view name,
1945 std::string separator,
1946 std::string postfix,
1948 bool emit_warning) {
1949 UniqueAttributeNameOptions options;
1950 options.separator = std::move(separator);
1951 options.postfix = std::move(postfix);
1952 options.max_increment = max_increment;
1953 options.emit_warning = emit_warning;
1958 "separator"_a = UniqueAttributeNameOptions().separator,
1959 "postfix"_a = UniqueAttributeNameOptions().postfix,
1960 "max_increment"_a = UniqueAttributeNameOptions().max_increment,
1961 "emit_warning"_a = UniqueAttributeNameOptions().emit_warning,
1962 R
"(Get a unique attribute name for a mesh.
1964If the desired name does not exist on the mesh it is returned as-is. If it
1965already exists, a suffix of the form ``{separator}{count}{postfix}`` is appended
1966until a unique name is found. An exception is raised if no unique name can be
1967found after ``max_increment`` attempts.
1969:param mesh: The input mesh.
1970:param name: The desired attribute name.
1971:param separator: Separator between the base name and counter (default: ".").
1972:param postfix: Postfix to append after the counter (default: "").
1973:param max_increment: Maximum number of attempts to find a unique name (default: 1000).
1974:param emit_warning: Whether to log a warning when a collision is detected (default: True).
1976:returns: A unique attribute name.)");
1979 "compute_mesh_covariance",
1981 StubType<std::array<Scalar, 3>, ArrayLikeHint> center,
1982 std::optional<std::string_view> active_facets_attribute_name)
1983 -> std::array<std::array<Scalar, 3>, 3> {
1984 MeshCovarianceOptions options;
1985 options.center = center.value;
1986 options.active_facets_attribute_name = active_facets_attribute_name;
1991 "active_facets_attribute_name"_a = nb::none(),
1992 R
"(Compute the covariance matrix of a mesh w.r.t. a center (Pythonic API).
1994:param mesh: Input mesh.
1995:param center: The center of the covariance computation.
1996:param active_facets_attribute_name: (optional) Attribute name of whether a facet should be considered in the computation.
1998:returns: The 3 by 3 covariance matrix, which should be symmetric.)");
2001 "select_facets_by_normal_similarity",
2003 Index seed_facet_id,
2004 std::optional<double> flood_error_limit,
2005 std::optional<double> flood_second_to_first_order_limit_ratio,
2006 std::optional<std::string_view> facet_normal_attribute_name,
2007 std::optional<std::string_view> is_facet_selectable_attribute_name,
2008 std::optional<std::string_view> output_attribute_name,
2009 std::optional<std::string_view> search_type,
2010 std::optional<int> num_smooth_iterations) {
2012 SelectFacetsByNormalSimilarityOptions options;
2013 if (flood_error_limit.has_value())
2014 options.flood_error_limit = flood_error_limit.value();
2015 if (flood_second_to_first_order_limit_ratio.has_value())
2016 options.flood_second_to_first_order_limit_ratio =
2017 flood_second_to_first_order_limit_ratio.value();
2018 if (facet_normal_attribute_name.has_value())
2019 options.facet_normal_attribute_name = facet_normal_attribute_name.value();
2020 if (is_facet_selectable_attribute_name.has_value()) {
2021 options.is_facet_selectable_attribute_name = is_facet_selectable_attribute_name;
2023 if (output_attribute_name.has_value())
2024 options.output_attribute_name = output_attribute_name.value();
2025 if (search_type.has_value()) {
2026 if (search_type.value() ==
"BFS")
2028 else if (search_type.value() ==
"DFS")
2031 throw std::runtime_error(
2032 lagrange::format(
"Invalid search type: {}", search_type.value()));
2034 if (num_smooth_iterations.has_value())
2035 options.num_smooth_iterations = num_smooth_iterations.value();
2041 "flood_error_limit"_a = nb::none(),
2042 "flood_second_to_first_order_limit_ratio"_a = nb::none(),
2043 "facet_normal_attribute_name"_a = nb::none(),
2044 "is_facet_selectable_attribute_name"_a = nb::none(),
2045 "output_attribute_name"_a = nb::none(),
2046 "search_type"_a = nb::none(),
2047 "num_smooth_iterations"_a = nb::none(),
2048 R
"(Select facets by normal similarity (Pythonic API).
2050:param mesh: Input mesh.
2051:param seed_facet_id: Index of the seed facet.
2052:param flood_error_limit: Tolerance for normals of the seed and the selected facets. Higher limit leads to larger selected region.
2053: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.
2054: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.
2055:param is_facet_selectable_attribute_name: If provided, this function will look for this attribute to determine if a facet is selectable.
2056:param output_attribute_name: Attribute name of whether a facet is selected.
2057:param search_type: Use 'BFS' for breadth-first search or 'DFS' for depth-first search.
2058:param num_smooth_iterations: Number of iterations to smooth the boundary of the selected region.
2060:returns: Id of the attribute on whether a facet is selected.)",
2062 "def select_facets_by_normal_similarity(mesh: SurfaceMesh, "
2063 "seed_facet_id: int, "
2064 "flood_error_limit: typing.Optional[float] = None, "
2065 "flood_second_to_first_order_limit_ratio: typing.Optional[float] = None, "
2066 "facet_normal_attribute_name: typing.Optional[str] = None, "
2067 "is_facet_selectable_attribute_name: typing.Optional[str] = None, "
2068 "output_attribute_name: typing.Optional[str] = None, "
2069 "search_type: typing.Optional[typing.Literal['BFS', 'DFS']] = None,"
2070 "num_smooth_iterations: typing.Optional[int] = None) -> int"));
2073 "select_facets_in_frustum",
2075 StubType<std::array<std::array<Scalar, 3>, 4>, ArrayLikeHint> frustum_plane_points,
2076 StubType<std::array<std::array<Scalar, 3>, 4>, ArrayLikeHint> frustum_plane_normals,
2077 std::optional<bool> greedy,
2078 std::optional<std::string_view> output_attribute_name) {
2080 Frustum<Scalar> frustum;
2081 for (
size_t i = 0; i < 4; ++i) {
2082 frustum.planes[i].point = frustum_plane_points.value[i];
2083 frustum.planes[i].normal = frustum_plane_normals.value[i];
2085 FrustumSelectionOptions options;
2086 if (greedy.has_value()) options.greedy = greedy.value();
2087 if (output_attribute_name.has_value())
2088 options.output_attribute_name = output_attribute_name.value();
2093 "frustum_plane_points"_a,
2094 "frustum_plane_normals"_a,
2095 "greedy"_a = nb::none(),
2096 "output_attribute_name"_a = nb::none(),
2097 R
"(Select facets in a frustum (Pythonic API).
2099:param mesh: Input mesh.
2100:param frustum_plane_points: Four points on each of the frustum planes.
2101:param frustum_plane_normals: Four normals of each of the frustum planes.
2102:param greedy: If true, the function returns as soon as the first facet is found.
2103:param output_attribute_name: Attribute name of whether a facet is selected.
2105:returns: Whether any facets got selected.)");
2108 "thicken_and_close_mesh",
2110 std::optional<Scalar> offset_amount,
2111 std::variant<std::monostate, std::array<double, 3>, std::string_view> direction,
2112 std::optional<double> mirror_ratio,
2113 std::optional<size_t> num_segments,
2114 std::optional<std::vector<std::string>> indexed_attributes) {
2115 ThickenAndCloseOptions options;
2117 if (
auto array_val = std::get_if<std::array<double, 3>>(&direction)) {
2118 options.direction = *array_val;
2119 }
else if (
auto string_val = std::get_if<std::string_view>(&direction)) {
2120 options.direction = *string_val;
2122 options.offset_amount = offset_amount.value_or(options.offset_amount);
2123 options.mirror_ratio = std::move(mirror_ratio);
2124 options.num_segments = num_segments.value_or(options.num_segments);
2125 options.indexed_attributes = indexed_attributes.value_or(options.indexed_attributes);
2130 "offset_amount"_a = nb::none(),
2131 "direction"_a = nb::none(),
2132 "mirror_ratio"_a = nb::none(),
2133 "num_segments"_a = nb::none(),
2134 "indexed_attributes"_a = nb::none(),
2135 R
"(Thicken a mesh by offsetting it, and close the shape into a thick 3D solid.
2137:param mesh: Input mesh.
2138:param direction: Direction of the offset. Can be an attribute name or a fixed 3D vector.
2139:param offset_amount: Amount of offset.
2140:param mirror_ratio: Ratio of the offset amount to mirror the mesh.
2141:param num_segments: Number of segments to use for the thickening.
2142:param indexed_attributes: List of indexed attributes to copy to the new mesh.
2144:returns: The thickened and closed mesh.)");
2147 "extract_boundary_loops",
2150 R
"(Extract boundary loops from a mesh.
2152:param mesh: Input mesh.
2154:returns: A list of boundary loops, each represented as a list of vertex indices.)");
2157 "extract_boundary_edges",
2158 [](MeshType& mesh) {
2159 mesh.initialize_edges();
2160 Index num_edges = mesh.get_num_edges();
2161 std::vector<Index> bd_edges;
2162 bd_edges.reserve(num_edges);
2163 for (Index ei = 0; ei < num_edges; ++ei) {
2164 if (mesh.is_boundary_edge(ei)) {
2165 bd_edges.push_back(ei);
2171 R
"(Extract boundary edges from a mesh.
2173:param mesh: Input mesh.
2175:returns: A list of boundary edge indices.)");
2178 "compute_uv_charts",
2180 std::string_view uv_attribute_name,
2181 std::string_view output_attribute_name,
2182 std::string_view connectivity_type) {
2183 UVChartOptions options;
2184 options.uv_attribute_name = uv_attribute_name;
2185 options.output_attribute_name = output_attribute_name;
2186 if (connectivity_type ==
"Vertex") {
2187 options.connectivity_type = UVChartOptions::ConnectivityType::Vertex;
2188 }
else if (connectivity_type ==
"Edge") {
2189 options.connectivity_type = UVChartOptions::ConnectivityType::Edge;
2191 throw std::runtime_error(
2192 lagrange::format(
"Invalid connectivity type: {}", connectivity_type));
2197 "uv_attribute_name"_a = UVChartOptions().uv_attribute_name,
2198 "output_attribute_name"_a = UVChartOptions().output_attribute_name,
2199 "connectivity_type"_a =
"Edge",
2200 R
"(Compute UV charts.
2202:param mesh: Input mesh.
2203:param uv_attribute_name: Name of the UV attribute.
2204:param output_attribute_name: Name of the output attribute to store the chart ids.
2205:param connectivity_type: Type of connectivity to use for chart computation. Can be "Vertex" or "Edge".
2207:returns: The number of charts.)");
2209 nb::class_<UVOrientationCount>(m, "UVOrientationCount",
"Counts of per-facet UV orientations.")
2214 "Number of CCW (positively oriented) facets.")
2218 "Number of degenerate (zero-area) facets.")
2222 "Number of CW (negatively oriented / flipped) facets.");
2225 "compute_uv_orientation",
2227 std::string_view uv_attribute_name,
2228 std::string_view output_attribute_name) {
2229 UVOrientationOptions options;
2230 options.uv_attribute_name = uv_attribute_name;
2231 options.output_attribute_name = output_attribute_name;
2235 "uv_attribute_name"_a = UVOrientationOptions().uv_attribute_name,
2236 "output_attribute_name"_a = UVOrientationOptions().output_attribute_name,
2237 R
"(Compute a per-facet orientation attribute using Shewchuk's exact ``orient2D`` predicate.
2239Each facet is assigned an ``int8`` value: ``+1`` for CCW (positively oriented), ``0`` for
2240degenerate, ``-1`` for CW (negatively oriented / flipped).
2242:param mesh: Input triangle mesh.
2243:param uv_attribute_name: Name of the UV attribute. If empty, uses the first UV attribute.
2244:param output_attribute_name: Name of the output per-facet attribute (int8).
2246:returns: A :class:`UVOrientationCount` with counts of positive, degenerate, and negative facets.)");
2251 std::string_view uv_attribute_name,
2252 std::string_view chart_id_attribute_name) {
2253 UnflipUVChartsOptions options;
2254 options.uv_attribute_name = uv_attribute_name;
2255 options.chart_id_attribute_name = chart_id_attribute_name;
2259 "uv_attribute_name"_a = UnflipUVChartsOptions().uv_attribute_name,
2260 "chart_id_attribute_name"_a = UnflipUVChartsOptions().chart_id_attribute_name,
2261 R
"(Mirror the UV positions of every UV vertex in any chart that is "flipped" by negating
2262its U coordinate. A chart is considered flipped when either its total signed UV area is negative,
2263OR every triangle in the chart is individually flipped (per :func:`compute_uv_orientation`); the
2264latter rule catches charts whose floating-point area sum is non-negative due to nearly-degenerate
2265triangles. Assumes UV vertices are not shared across charts.
2267:param mesh: Input triangle mesh. The UV attribute must be indexed.
2268:param uv_attribute_name: Name of the UV attribute. If empty, uses the first indexed UV attribute.
2269:param chart_id_attribute_name: Optional per-facet chart id attribute name. If empty, charts are
2270 computed automatically using edge connectivity on the UV mesh.
2272:returns: The number of charts that were unflipped.)");
2275 "disconnect_uv_charts",
2277 std::string_view uv_attribute_name,
2278 std::string_view chart_id_attribute_name) {
2279 DisconnectUVChartsOptions options;
2280 options.uv_attribute_name = uv_attribute_name;
2281 options.chart_id_attribute_name = chart_id_attribute_name;
2285 "uv_attribute_name"_a = DisconnectUVChartsOptions().uv_attribute_name,
2286 "chart_id_attribute_name"_a = DisconnectUVChartsOptions().chart_id_attribute_name,
2287 R
"(Disconnect UV charts by duplicating UV vertices shared across different charts.
2289After this operation, no two facets belonging to different UV charts will share a UV vertex
2290index. Without any input chart id attribute, this eliminates non-manifold UV vertices (pinch
2291points) where charts touch at a single vertex.
2293:param mesh: Input mesh. The UV attribute must be indexed.
2294:param uv_attribute_name: Name of the UV attribute. If empty, uses the first indexed UV attribute.
2295:param chart_id_attribute_name: Optional per-facet chart id attribute name. If empty, chart ids
2296 are computed automatically using edge connectivity on the UV mesh.
2298:returns: The number of UV vertices that were duplicated.)");
2302 [](
const MeshType& mesh, std::string_view uv_attribute_name) {
2303 UVMeshOptions options;
2304 options.uv_attribute_name = uv_attribute_name;
2308 "uv_attribute_name"_a = UVMeshOptions().uv_attribute_name,
2309 R
"(Extract a UV mesh view from a 3D mesh.
2311:param mesh: Input mesh.
2312:param uv_attribute_name: Name of the (indexed or vertex) UV attribute.
2314:return: A new mesh representing the UV mesh.)");
2317 [](MeshType& mesh, std::string_view uv_attribute_name) {
2318 UVMeshOptions options;
2319 options.uv_attribute_name = uv_attribute_name;
2323 "uv_attribute_name"_a = UVMeshOptions().uv_attribute_name,
2324 R
"(Extract a UV mesh reference from a 3D mesh.
2326:param mesh: Input mesh.
2327:param uv_attribute_name: Name of the (indexed or vertex) UV attribute.
2329:return: A new mesh representing the UV mesh.)");
2332 "split_facets_by_material",
2335 "material_attribute_name"_a,
2336 R
"(Split mesh facets based on a material attribute.
2338@param mesh: Input mesh on which material segmentation will be applied in place.
2339@param material_attribute_name: Name of the material attribute to use for inserting boundaries.
2341@note The material attribute should be n by k vertex attribute, where n is the number of vertices,
2342and k is the number of materials. The value at row i and column j indicates the probability of vertex
2343i belonging to material j. The function will insert boundaries between different materials based on
2344the material attribute.
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:279
void weld_indexed_attribute(SurfaceMesh< Scalar, Index > &mesh, AttributeId attr_id, const WeldOptions &options={})
Weld an indexed attribute by combining all corners around a vertex with the same attribute value.
Definition weld_indexed_attribute.cpp:320
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:292
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:265
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:609
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:429
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:24
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
std::string get_unique_attribute_name(const SurfaceMesh< Scalar, Index > &mesh, std::string_view name, const UniqueAttributeNameOptions &options={})
Returns a unique attribute name by appending a suffix if necessary.
Definition get_unique_attribute_name.cpp:23
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:24
ReorderingMethod
Mesh reordering method to apply before decimation.
Definition reorder_mesh.h:26
size_t disconnect_uv_charts(SurfaceMesh< Scalar, Index > &mesh, const DisconnectUVChartsOptions &options={})
Disconnect UV charts by duplicating UV vertices shared across different charts.
Definition disconnect_uv_charts.cpp:221
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:271
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
SurfaceMesh< Scalar, Index > insert_isoline(const SurfaceMesh< Scalar, Index > &mesh, const IsolineOptions &options={})
Insert the isoline of an implicit function into a mesh.
Definition isoline.cpp:625
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
size_t unflip_uv_charts(SurfaceMesh< Scalar, Index > &mesh, const UnflipUVChartsOptions &options={})
Mirror the UV positions of every UV vertex in any chart that is "flipped".
Definition unflip_uv_charts.cpp:159
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:407
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:137
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:533
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:146
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:137
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
UVOrientationCount compute_uv_orientation(SurfaceMesh< Scalar, Index > &mesh, const UVOrientationOptions &options={})
Compute a per-facet orientation attribute using Shewchuk's exact orient2D predicate.
Definition compute_uv_orientation.cpp:96
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:172
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:617
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:177
::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:47
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:146
bool use_signed_area
For 2D mesh only: whether the computed facet area (if any) should be signed.
Definition compute_area.h:149
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
size_t degenerate
Number of degenerate (zero-area) facets.
Definition compute_uv_orientation.h:41
size_t positive
Number of CCW (positively oriented) facets.
Definition compute_uv_orientation.h:40
size_t negative
Number of CW (negatively oriented / flipped) facets.
Definition compute_uv_orientation.h:42
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