14#include "PyAttribute.h"
16#include <lagrange/Attribute.h>
17#include <lagrange/AttributeFwd.h>
18#include <lagrange/AttributeTypes.h>
19#include <lagrange/IndexedAttribute.h>
20#include <lagrange/Logger.h>
21#include <lagrange/SurfaceMesh.h>
22#include <lagrange/find_matching_attributes.h>
23#include <lagrange/foreach_attribute.h>
24#include <lagrange/python/binding.h>
25#include <lagrange/python/tensor_utils.h>
26#include <lagrange/utils/BitField.h>
27#include <lagrange/utils/Error.h>
28#include <lagrange/utils/assert.h>
29#include <lagrange/utils/invalid.h>
32#include <lagrange/utils/warnoff.h>
33#include <tbb/parallel_for.h>
34#include <lagrange/utils/warnon.h>
37namespace lagrange::python {
39template <
typename Scalar,
typename Index>
40void bind_surface_mesh(nanobind::module_& m)
42 namespace nb = nanobind;
43 using namespace nb::literals;
44 using namespace lagrange;
45 using namespace lagrange::python;
47 using MeshType = SurfaceMesh<Scalar, Index>;
49 auto surface_mesh_class = nb::class_<MeshType>(m,
"SurfaceMesh",
"Surface mesh data structure");
50 surface_mesh_class.def(nb::init<Index>(), nb::arg(
"dimension") = 3);
51 surface_mesh_class.def(
53 [](MeshType& self, Tensor<Scalar> b) {
54 auto [data, shape, stride] = tensor_to_span(b);
57 self.add_vertex(data);
60 R
"(Add a vertex to the mesh.
62:param vertex: vertex coordinates)");
65 surface_mesh_class.def(
67 [](MeshType& self, nb::list b) {
69 if (self.get_dimension() == 3) {
71 {nb::cast<Scalar>(b[0]), nb::cast<Scalar>(b[1]), nb::cast<Scalar>(b[2])});
72 }
else if (self.get_dimension() == 2) {
73 self.add_vertex({nb::cast<Scalar>(b[0]), nb::cast<Scalar>(b[1])});
75 throw std::runtime_error(
"Dimension mismatch in vertex tensor");
79 R
"(Add a vertex to the mesh.
81:param vertex: vertex coordinates as a list)");
83 surface_mesh_class.def(
85 [](MeshType& self, Tensor<Scalar> b) {
86 auto [data, shape, stride] = tensor_to_span(b);
89 self.add_vertices(
static_cast<Index
>(shape[0]), data);
92 R
"(Add multiple vertices to the mesh.
94:param vertices: N x D tensor of vertex coordinates, where N is the number of vertices and D is the dimension)");
97 surface_mesh_class.def(
99 &MeshType::add_triangle,
103 R
"(Add a triangle to the mesh.
105:param v0: first vertex index
106:param v1: second vertex index
107:param v2: third vertex index
109:returns: facet index of the added triangle)");
110 surface_mesh_class.def(
112 [](MeshType& self, Tensor<Index> b) {
113 auto [data, shape, stride] = tensor_to_span(b);
116 self.add_triangles(
static_cast<Index
>(shape[0]), data);
119 R
"(Add multiple triangles to the mesh.
121:param triangles: N x 3 tensor of vertex indices, where N is the number of triangles)");
122 surface_mesh_class.def(
129 R
"(Add a quad to the mesh.
131:param v0: first vertex index
132:param v1: second vertex index
133:param v2: third vertex index
134:param v3: fourth vertex index
136:returns: facet index of the added quad)");
137 surface_mesh_class.def(
139 [](MeshType& self, Tensor<Index> b) {
140 auto [data, shape, stride] = tensor_to_span(b);
143 self.add_quads(
static_cast<Index
>(shape[0]), data);
146 R
"(Add multiple quads to the mesh.
148:param quads: N x 4 tensor of vertex indices, where N is the number of quads)");
149 surface_mesh_class.def(
151 [](MeshType& self, Tensor<Index> b) {
152 auto [data, shape, stride] = tensor_to_span(b);
155 self.add_polygon(data);
158 R
"(Add a polygon to the mesh.
160:param vertices: 1D tensor of vertex indices defining the polygon
162:returns: facet index of the added polygon)");
163 surface_mesh_class.def(
165 [](MeshType& self, Tensor<Index> b) {
166 auto [data, shape, stride] = tensor_to_span(b);
168 self.add_polygons(
static_cast<Index
>(shape[0]),
static_cast<Index
>(shape[1]), data);
171 R
"(Add multiple regular polygons to the mesh.
173:param polygons: N x K tensor of vertex indices, where N is the number of polygons and K is the number of vertices per polygon)");
174 surface_mesh_class.def(
176 [](MeshType& self, Tensor<Index> sizes, Tensor<Index> indices) {
177 auto [size_data, size_shape, size_stride] = tensor_to_span(sizes);
181 auto [index_data, index_shape, index_stride] = tensor_to_span(indices);
185 self.add_hybrid(size_data, index_data);
189 R
"(Add hybrid facets (polygons with varying number of vertices) to the mesh.
191:param sizes: 1D tensor specifying the number of vertices for each facet
192:param indices: 1D tensor of vertex indices for all facets concatenated together)");
193 surface_mesh_class.def(
195 [](MeshType& self, Tensor<Index> b) {
196 auto [data, shape, stride] = tensor_to_span(b);
199 self.remove_vertices(data);
202 R
"(Remove selected vertices from the mesh.
204:param vertices: 1D tensor of vertex indices to remove)");
205 surface_mesh_class.def(
207 [](MeshType& self, nb::list b) {
208 std::vector<Index> indices;
210 indices.push_back(nb::cast<Index>(i));
212 self.remove_vertices(indices);
215 R
"(Remove selected vertices from the mesh.
217:param vertices: list of vertex indices to remove)");
218 surface_mesh_class.def(
220 [](MeshType& self, Tensor<Index> b) {
221 auto [data, shape, stride] = tensor_to_span(b);
224 self.remove_facets(data);
227 R
"(Remove selected facets from the mesh.
229:param facets: 1D tensor of facet indices to remove)");
230 surface_mesh_class.def(
232 [](MeshType& self, nb::list b) {
233 std::vector<Index> indices;
235 indices.push_back(nb::cast<Index>(i));
237 self.remove_facets(indices);
240 R
"(Remove selected facets from the mesh.
242:param facets: list of facet indices to remove)");
243 surface_mesh_class.def(
246 auto [data, shape, stride] = tensor_to_span(b);
249 self.flip_facets(data, policy);
253 R
"(Flip the orientation of selected facets.
255:param facets: 1D tensor of facet indices to flip
256:param policy: Whether to reorient associated attributes like normals and bitangents.)");
257 surface_mesh_class.def(
260 std::vector<Index> indices;
262 indices.push_back(nb::cast<Index>(i));
264 self.flip_facets(indices, policy);
268 R
"(Flip the orientation of selected facets.
270:param facets: list of facet indices to flip
271:param policy: Whether to reorient associated attributes like normals and bitangents.)");
272 surface_mesh_class.def(
274 &MeshType::clear_vertices,
275 R
"(Remove all vertices from the mesh.)");
276 surface_mesh_class.def(
278 &MeshType::clear_facets,
279 R
"(Remove all facets from the mesh.)");
280 surface_mesh_class.def(
282 &MeshType::shrink_to_fit,
283 R
"(Shrink the internal storage to fit the current mesh size.)");
284 surface_mesh_class.def(
285 "compress_if_regular",
286 &MeshType::compress_if_regular,
287 R
"(Compress the mesh representation if it is regular (all facets have the same number of vertices).
289:returns: True if the mesh was compressed, False otherwise)");
290 surface_mesh_class.def_prop_ro("is_triangle_mesh", &MeshType::is_triangle_mesh);
291 surface_mesh_class.def_prop_ro(
"is_quad_mesh", &MeshType::is_quad_mesh);
292 surface_mesh_class.def_prop_ro(
"is_regular", &MeshType::is_regular);
293 surface_mesh_class.def_prop_ro(
"is_hybrid", &MeshType::is_hybrid);
294 surface_mesh_class.def_prop_ro(
"dimension", &MeshType::get_dimension);
295 surface_mesh_class.def_prop_ro(
"vertex_per_facet", &MeshType::get_vertex_per_facet);
296 surface_mesh_class.def_prop_ro(
"num_vertices", &MeshType::get_num_vertices);
297 surface_mesh_class.def_prop_ro(
"num_facets", &MeshType::get_num_facets);
298 surface_mesh_class.def_prop_ro(
"num_corners", &MeshType::get_num_corners);
300 surface_mesh_class.def(
302 [](MeshType& self, Index i) {
303 return span_to_tensor(self.get_position(i), nb::find(&self));
306 R
"(Get the position of a vertex.
308:param vertex_id: vertex index
310:returns: position coordinates as a tensor)");
311 surface_mesh_class.def(
313 [](MeshType& self, Index i) {
314 return span_to_tensor(self.ref_position(i), nb::find(&self));
317 R
"(Get a mutable reference to the position of a vertex.
319:param vertex_id: vertex index
321:returns: mutable position coordinates as a tensor)");
322 surface_mesh_class.def(
324 &MeshType::get_facet_size,
326 R
"(Get the number of vertices in a facet.
328:param facet_id: facet index
330:returns: number of vertices in the facet)");
331 surface_mesh_class.def(
333 &MeshType::get_facet_vertex,
336 R
"(Get a vertex index from a facet.
338:param facet_id: facet index
339:param local_vertex_id: local vertex index within the facet (0 to facet_size-1)
341:returns: global vertex index)");
342 surface_mesh_class.def(
343 "get_facet_corner_begin",
344 &MeshType::get_facet_corner_begin,
346 R
"(Get the first corner index of a facet.
348:param facet_id: facet index
350:returns: first corner index of the facet)");
351 surface_mesh_class.def(
352 "get_facet_corner_end",
353 &MeshType::get_facet_corner_end,
355 R
"(Get the end corner index of a facet (one past the last corner).
357:param facet_id: facet index
359:returns: end corner index of the facet)");
360 surface_mesh_class.def(
362 &MeshType::get_corner_vertex,
364 R
"(Get the vertex index associated with a corner.
366:param corner_id: corner index
368:returns: vertex index)");
369 surface_mesh_class.def(
371 &MeshType::get_corner_facet,
373 R
"(Get the facet index associated with a corner.
375:param corner_id: corner index
377:returns: facet index)");
378 surface_mesh_class.def(
379 "get_facet_vertices",
380 [](MeshType& self, Index f) {
381 return span_to_tensor(self.get_facet_vertices(f), nb::find(&self));
384 R
"(Get all vertex indices of a facet.
386:param facet_id: facet index
388:returns: vertex indices as a tensor)");
389 surface_mesh_class.def(
390 "ref_facet_vertices",
391 [](MeshType& self, Index f) {
392 return span_to_tensor(self.ref_facet_vertices(f), nb::find(&self));
395 R
"(Get a mutable reference to all vertex indices of a facet.
397:param facet_id: facet index
399:returns: mutable vertex indices as a tensor)");
400 surface_mesh_class.def(
402 &MeshType::get_attribute_id,
404 R
"(Get the attribute ID by name.
406:param name: attribute name
408:returns: attribute ID)");
409 surface_mesh_class.def(
410 "get_attribute_name",
411 &MeshType::get_attribute_name,
413 R
"(Get the attribute name by ID.
415:param id: attribute ID
417:returns: attribute name)");
418 surface_mesh_class.def(
421 std::string_view name,
422 std::variant<std::monostate, AttributeElement, std::string_view> element,
423 std::variant<std::monostate, AttributeUsage, std::string_view> usage,
424 std::variant<std::monostate, GenericTensor, nb::list> initial_values,
425 std::variant<std::monostate, Tensor<Index>, GenericTensor, nb::list> initial_indices,
426 std::optional<Index> num_channels,
427 std::optional<nb::type_object> dtype) {
428 const bool with_initial_values = initial_values.index() != 0;
429 const bool with_initial_indices = initial_indices.index() != 0;
433 if (num_channels.has_value()) {
434 n = num_channels.value();
435 }
else if (with_initial_values) {
436 if (initial_values.index() == 1) {
437 const auto& values = std::get<GenericTensor>(initial_values);
439 values.ndim() == 1 || values.ndim() == 2,
440 "Only vector or matrix are accepted as initial values.");
441 n = values.ndim() == 1 ? 1 :
static_cast<Index
>(values.shape(1));
442 }
else if (initial_values.index() == 2) {
446 throw nb::type_error(
"Either number of channels or initial values are required!");
453 using T = std::decay_t<
decltype(value)>;
454 if constexpr (std::is_same_v<T, AttributeElement>) {
456 }
else if (with_initial_indices) {
458 }
else if constexpr (std::is_same_v<T, std::string_view>) {
459 if (value ==
"Vertex") {
461 }
else if (value ==
"Facet") {
463 }
else if (value ==
"Edge") {
465 }
else if (value ==
"Corner") {
467 }
else if (value ==
"Value") {
469 }
else if (value ==
"Indexed") {
472 throw nb::type_error(
"Invalid element type!");
474 }
else if (with_initial_values) {
476 const Index num_vertices = self.get_num_vertices();
477 const Index num_facets = self.get_num_facets();
478 const Index num_corners = self.get_num_corners();
479 const Index num_edges =
483 if (initial_values.index() == 1) {
484 const auto& values = std::get<GenericTensor>(initial_values);
485 num_rows =
static_cast<Index
>(values.shape(0));
486 }
else if (initial_values.index() == 2) {
487 const auto& values = std::get<nb::list>(initial_values);
488 num_rows =
static_cast<Index
>(nb::len(values));
492 if (num_rows == num_vertices) {
494 num_rows != num_facets,
495 "Cannot infer attribute element due to ambiguity: vertices vs "
498 num_rows != num_edges,
499 "Cannot infer attribute element due to ambiguity: vertices vs "
502 num_rows != num_corners,
503 "Cannot infer attribute element due to ambiguity: vertices vs "
506 }
else if (num_rows == num_facets) {
508 num_rows != num_edges,
509 "Cannot infer attribute element due to ambiguity: facets vs "
512 num_rows != num_corners,
513 "Cannot infer attribute element due to ambiguity: facets vs "
516 }
else if (num_rows == num_corners) {
518 num_rows != num_edges,
519 "Cannot infer attribute element due to ambiguity: corners vs "
522 }
else if (num_rows == num_edges) {
525 throw nb::type_error(
526 "Cannot infer attribute element type from initial_values!");
529 throw nb::type_error(
"Invalid element type!");
538 using T = std::decay_t<
decltype(value)>;
539 if constexpr (std::is_same_v<T, AttributeUsage>) {
541 }
else if constexpr (std::is_same_v<T, std::string_view>) {
542 if (value ==
"Vector") {
544 }
else if (value ==
"Scalar") {
546 }
else if (value ==
"Position") {
548 }
else if (value ==
"Normal") {
550 }
else if (value ==
"Tangent") {
552 }
else if (value ==
"Bitangent") {
554 }
else if (value ==
"Color") {
556 }
else if (value ==
"UV") {
558 }
else if (value ==
"VertexIndex") {
560 }
else if (value ==
"FacetIndex") {
562 }
else if (value ==
"CornerIndex") {
564 }
else if (value ==
"EdgeIndex") {
567 throw nb::type_error(
"Invalid usage type!");
579 auto create_attribute = [&](
auto values) {
580 using ValueType =
typename std::decay_t<
decltype(values)>::element_type;
584 std::vector<Index> index_storage;
587 if (with_initial_values) {
588 init_values = values;
592 num_channels.has_value(),
593 "Number of channels is required when initial values are not provided!");
596 "dtype is required when initial values are not provided!");
600 if (
const Tensor<Index>* tensor_ptr =
601 std::get_if<Tensor<Index>>(&initial_indices)) {
603 const auto& indices = *tensor_ptr;
604 auto [index_data, index_shape, index_stride] = tensor_to_span(indices);
606 init_indices = index_data;
608 GenericTensor* generic_tensor_ptr =
609 std::get_if<GenericTensor>(&initial_indices)) {
611 auto& indices = *generic_tensor_ptr;
612 index_storage.resize(indices.size());
614#define LA_X_create_attribute_index(_, IndexType) \
615 if (indices.dtype() == nb::dtype<IndexType>()) { \
616 auto view = indices.template view<IndexType, nb::ndim<1>>(); \
617 std::copy(view.data(), view.data() + indices.size(), index_storage.begin()); \
620#undef LA_X_create_attribute_index
621 init_indices =
span<Index>(index_storage.data(), index_storage.size());
622 }
else if (
const nb::list* list_ptr = std::get_if<nb::list>(&initial_indices)) {
624 const nb::list& py_list = *list_ptr;
625 index_storage = nb::cast<std::vector<Index>>(py_list);
626 init_indices =
span<Index>(index_storage.begin(), index_storage.size());
629 return self.template create_attribute<ValueType>(
639 if (
const GenericTensor* tensor_ptr = std::get_if<GenericTensor>(&initial_values)) {
640 const auto& values = *tensor_ptr;
641#define LA_X_create_attribute(_, ValueType) \
642 if (values.dtype() == nb::dtype<ValueType>()) { \
643 Tensor<ValueType> local_values(values.handle()); \
644 auto [value_data, value_shape, value_stride] = tensor_to_span(local_values); \
645 la_runtime_assert(is_dense(value_shape, value_stride)); \
646 if (num_channels.has_value()) { \
647 Index nn = value_shape.size() == 1 ? 1 : static_cast<Index>(value_shape[1]); \
648 la_runtime_assert(nn == n, "Number of channels does not match initial_values"); \
650 return create_attribute(value_data); \
653#undef LA_X_create_attribute
654 }
else if (
const nb::list* list_ptr = std::get_if<nb::list>(&initial_values)) {
655 auto values = nb::cast<std::vector<double>>(*list_ptr);
656 return create_attribute(
span<double>(values.data(), values.size()));
657 }
else if (dtype.has_value()) {
658 const auto& t = dtype.value();
659 auto np = nb::module_::import_(
"numpy");
660 if (t.is(&PyFloat_Type)) {
663 return create_attribute(local_values);
664 }
else if (t.is(np.attr(
"float32"))) {
666 return create_attribute(local_values);
667 }
else if (t.is(np.attr(
"float64"))) {
669 return create_attribute(local_values);
670 }
else if (t.is(np.attr(
"int8"))) {
672 return create_attribute(local_values);
673 }
else if (t.is(np.attr(
"int16"))) {
675 return create_attribute(local_values);
676 }
else if (t.is(np.attr(
"int32"))) {
678 return create_attribute(local_values);
679 }
else if (t.is(np.attr(
"int64"))) {
681 return create_attribute(local_values);
682 }
else if (t.is(np.attr(
"uint8"))) {
684 return create_attribute(local_values);
685 }
else if (t.is(np.attr(
"uint16"))) {
687 return create_attribute(local_values);
688 }
else if (t.is(np.attr(
"uint32"))) {
690 return create_attribute(local_values);
691 }
else if (t.is(np.attr(
"uint64"))) {
693 return create_attribute(local_values);
696 throw nb::type_error(
"`initial_values` and `dtype` cannot both be None!");
699 "element"_a = nb::none(),
700 "usage"_a = nb::none(),
701 "initial_values"_a = nb::none(),
702 "initial_indices"_a = nb::none(),
703 "num_channels"_a = nb::none(),
704 "dtype"_a = nb::none(),
706 "def create_attribute(self, "
708 "element: typing.Union[AttributeElement, "
710 "'Vertex', 'Facet', 'Edge', 'Corner', 'Value', 'Indexed'"
712 "usage: typing.Union[AttributeUsage, "
714 "'Vector', 'Scalar', 'Position', 'Normal', 'Tangent', 'Bitangent', 'Color', 'UV', "
715 "'VertexIndex', 'FacetIndex', 'CornerIndex', 'EdgeIndex'"
717 "initial_values: typing.Union[numpy.typing.NDArray, typing.List[float], None] = None, "
718 "initial_indices: typing.Union[numpy.typing.NDArray, typing.List[int], None] = None, "
719 "num_channels: typing.Optional[int] = None, "
720 "dtype: typing.Optional[numpy.typing.DTypeLike] = None) -> int"),
721 R
"(Create an attribute.
723:param name: Name of the attribute.
724:param element: Element type of the attribute. If None, derive from the shape of initial values.
725:param usage: Usage type of the attribute. If None, derive from the shape of initial values or the number of channels.
726:param initial_values: Initial values of the attribute.
727:param initial_indices: Initial indices of the attribute (Indexed attribute only).
728:param num_channels: Number of channels of the attribute.
729:param dtype: Data type of the attribute.
731:returns: The id of the created attribute.
734 If `element` is None, it will be derived based on the cardinality of the mesh elements.
735 If there is an ambiguity, an exception will be raised.
736 In addition, explicit `element` specification is required for value attributes.
739 If `usage` is None, it will be derived based on the shape of `initial_values` or `num_channels` if specified.)");
741 surface_mesh_class.def(
742 "create_attribute_from",
743 &MeshType::template create_attribute_from<Scalar, Index>,
746 "source_name"_a =
"",
747 R
"(Shallow copy an attribute from another mesh.
749:param name: Name of the attribute.
750:param source_mesh: Source mesh.
751:param source_name: Name of the attribute in the source mesh. If empty, use the same name as `name`.
753:returns: The id of the created attribute.)");
755 surface_mesh_class.def(
758 std::string_view name,
761 GenericTensor values) {
762 auto wrap_as_attribute = [&](
auto tensor) {
763 using ValueType =
typename std::decay_t<
decltype(tensor)>
::Scalar;
764 auto [data, shape, stride] = tensor_to_span(tensor);
766 Index num_channels = shape.size() == 1 ? 1 :
static_cast<Index
>(shape[1]);
768 auto owner = std::make_shared<nb::object>(nb::find(values));
769 if constexpr (std::is_const_v<ValueType>) {
770 id = self.wrap_as_const_attribute(
777 id = self.wrap_as_attribute(
784 auto& attr = self.template ref_attribute<ValueType>(
id);
789#define LA_X_wrap_as_attribute(_, ValueType) \
790 if (values.dtype() == nb::dtype<ValueType>()) { \
791 Tensor<ValueType> local_values(values.handle()); \
792 return wrap_as_attribute(local_values); \
795#undef LA_X_wrap_as_attribute
796 throw nb::type_error(
"Unsupported value type!");
802 R
"(Wrap an existing numpy array as an attribute.
804:param name: Name of the attribute.
805:param element: Element type of the attribute.
806:param usage: Usage type of the attribute.
807:param values: Values of the attribute.
809:returns: The id of the created attribute.)");
810 surface_mesh_class.def(
811 "wrap_as_indexed_attribute",
813 std::string_view name,
815 GenericTensor values,
816 Tensor<Index> indices) {
817 auto wrap_as_indexed_attribute = [&](
auto value_tensor,
auto index_tensor) {
818 using ValueType =
typename std::decay_t<
decltype(value_tensor)>
::Scalar;
819 auto [value_data, value_shape, value_stride] = tensor_to_span(value_tensor);
820 auto [index_data, index_shape, index_stride] = tensor_to_span(index_tensor);
823 Index num_values =
static_cast<Index
>(value_shape[0]);
825 value_shape.size() == 1 ? 1 :
static_cast<Index
>(value_shape[1]);
828 auto value_owner = std::make_shared<nb::object>(nb::find(values));
829 auto index_owner = std::make_shared<nb::object>(nb::find(indices));
831 if constexpr (std::is_const_v<ValueType>) {
832 id = self.wrap_as_const_indexed_attribute(
840 id = self.wrap_as_indexed_attribute(
848 auto& attr = self.template ref_indexed_attribute<ValueType>(
id);
854#define LA_X_wrap_as_indexed_attribute(_, ValueType) \
855 if (values.dtype() == nb::dtype<ValueType>()) { \
856 Tensor<ValueType> local_values(values.handle()); \
857 return wrap_as_indexed_attribute(local_values, indices); \
860#undef LA_X_wrap_as_indexed_attribute
861 throw nb::type_error(
"Unsupported value type!");
867 R
"(Wrap an existing numpy array as an indexed attribute.
869:param name: Name of the attribute.
870:param usage: Usage type of the attribute.
871:param values: Values of the attribute.
872:param indices: Indices of the attribute.
874:returns: The id of the created attribute.)");
875 surface_mesh_class.def(
876 "duplicate_attribute",
877 &MeshType::duplicate_attribute,
880 R
"(Duplicate an attribute with a new name.
882:param old_name: name of the attribute to duplicate
883:param new_name: name for the new attribute
885:returns: attribute ID of the duplicated attribute)");
886 surface_mesh_class.def(
888 &MeshType::rename_attribute,
891 R
"(Rename an attribute.
893:param old_name: current name of the attribute
894:param new_name: new name for the attribute)");
896 surface_mesh_class.def(
899 self.delete_attribute(name, policy);
903 R
"(Delete an attribute by name.
905:param name: Name of the attribute.
906:param policy: Deletion policy for reserved attributes.)");
907 surface_mesh_class.def(
910 self.delete_attribute(
id, policy);
914 R
"(Delete an attribute by id.
916:param id: Id of the attribute.
917:param policy: Deletion policy for reserved attributes.)");
918 surface_mesh_class.def(
920 &MeshType::has_attribute,
922 R
"(Check if an attribute exists.
924:param name: attribute name
926:returns: True if the attribute exists, False otherwise)");
927 surface_mesh_class.def(
928 "is_attribute_indexed",
929 static_cast<bool (MeshType::*)(
AttributeId) const
>(&MeshType::is_attribute_indexed),
931 R
"(Check if an attribute is indexed.
933:param id: attribute ID
935:returns: True if the attribute is indexed, False otherwise)");
936 surface_mesh_class.def(
937 "is_attribute_indexed",
938 static_cast<bool (MeshType::*)(std::string_view) const
>(&MeshType::is_attribute_indexed),
940 R
"(Check if an attribute is indexed.
942:param name: attribute name
944:returns: True if the attribute is indexed, False otherwise)");
947 auto ensure_attribute_is_not_shared = [](MeshType& mesh,
AttributeId id) {
949#define LA_X_trigger_copy_on_write(_, ValueType) \
950 if (mesh.is_attribute_indexed(id)) { \
951 if (dynamic_cast<const IndexedAttribute<ValueType, Index>*>(&attr_base)) { \
952 [[maybe_unused]] auto& attr = mesh.template ref_indexed_attribute<ValueType>(id); \
955 if (dynamic_cast<const Attribute<ValueType>*>(&attr_base)) { \
956 [[maybe_unused]] auto& attr = mesh.template ref_attribute<ValueType>(id); \
960#undef LA_X_trigger_copy_on_write
963 surface_mesh_class.def(
965 [&](MeshType& self,
AttributeId id,
bool sharing) {
967 !self.is_attribute_indexed(
id),
969 "Attribute {} is indexed! Please use `indexed_attribute` property "
972 if (!sharing) ensure_attribute_is_not_shared(self,
id);
977 R
"(Get an attribute by id.
979:param id: Id of the attribute.
980:param sharing: Whether to allow sharing the attribute with other meshes.
982:returns: The attribute.)");
983 surface_mesh_class.def(
985 [&](MeshType& self, std::string_view name,
bool sharing) {
987 !self.is_attribute_indexed(name),
989 "Attribute \"{}\" is indexed! Please use `indexed_attribute` property "
992 if (!sharing) ensure_attribute_is_not_shared(self, self.get_attribute_id(name));
997 R
"(Get an attribute by name.
999:param name: Name of the attribute.
1000:param sharing: Whether to allow sharing the attribute with other meshes.
1002:return: The attribute.)");
1003 surface_mesh_class.def(
1004 "indexed_attribute",
1005 [&](MeshType& self,
AttributeId id,
bool sharing) {
1007 self.is_attribute_indexed(
id),
1009 "Attribute {} is not indexed! Please use `attribute` property instead.",
1011 if (!sharing) ensure_attribute_is_not_shared(self,
id);
1016 R
"(Get an indexed attribute by id.
1018:param id: Id of the attribute.
1019:param sharing: Whether to allow sharing the attribute with other meshes.
1021:returns: The indexed attribute.)");
1022 surface_mesh_class.def(
1023 "indexed_attribute",
1024 [&](MeshType& self, std::string_view name,
bool sharing) {
1026 self.is_attribute_indexed(name),
1028 "Attribute \"{}\" is not indexed! Please use `attribute` property instead.",
1030 if (!sharing) ensure_attribute_is_not_shared(self, self.get_attribute_id(name));
1035 R
"(Get an indexed attribute by name.
1037:param name: Name of the attribute.
1038:param sharing: Whether to allow sharing the attribute with other meshes.
1040:returns: The indexed attribute.)");
1041 surface_mesh_class.def(
1042 "__attribute_ref_count__",
1044 auto ptr = self._get_attribute_ptr(
id);
1045 return ptr.use_count();
1048 R
"(Get the reference count of an attribute (for debugging purposes).
1050:param id: attribute ID
1052:returns: reference count of the attribute)");
1053 surface_mesh_class.def_prop_rw(
1055 [](
const MeshType& self) {
1056 const auto& attr = self.get_vertex_to_position();
1057 return attribute_to_tensor(attr, nb::find(&self));
1059 [](MeshType& self, Tensor<Scalar> tensor) {
1060 auto [values, shape, stride] = tensor_to_span(tensor);
1064 size_t num_vertices = shape.size() == 1 ? 1 : shape[0];
1065 auto owner = std::make_shared<nb::object>(nb::find(tensor));
1066 auto id = self.wrap_as_vertices(
1068 static_cast<Index
>(num_vertices));
1069 auto& attr = self.template ref_attribute<Scalar>(
id);
1072 "Vertices of the mesh.");
1073 surface_mesh_class.def_prop_rw(
1075 [](
const MeshType& self) {
1076 if (self.is_regular()) {
1077 const auto& attr = self.get_corner_to_vertex();
1078 const size_t shape[2] = {
1079 static_cast<size_t>(self.get_num_facets()),
1080 static_cast<size_t>(self.get_vertex_per_facet())};
1081 return attribute_to_tensor(attr, shape, nb::find(&self));
1083 logger().warn(
"Mesh is not regular, returning the flattened facets.");
1084 const auto& attr = self.get_corner_to_vertex();
1085 return attribute_to_tensor(attr, nb::find(&self));
1088 [](MeshType& self, Tensor<Index> tensor) {
1089 auto [values, shape, stride] = tensor_to_span(tensor);
1092 const size_t num_facets = shape.size() == 1 ? 1 : shape[0];
1093 const size_t vertex_per_facet = shape.size() == 1 ? shape[0] : shape[1];
1094 auto owner = std::make_shared<nb::object>(nb::find(tensor));
1095 auto id = self.wrap_as_facets(
1097 static_cast<Index
>(num_facets),
1098 static_cast<Index
>(vertex_per_facet));
1099 auto& attr = self.template ref_attribute<Index>(
id);
1102 "Facets of the mesh.");
1103 surface_mesh_class.def_prop_ro(
1105 [](MeshType& self) {
1106 self.initialize_edges();
1108 std::vector<Index> data(num_edges * 2);
1109 tbb::parallel_for(Index{0}, num_edges, [&](Index i) {
1112 data[i * 2 + 1] = v1;
1114 nb::ndarray<Index, nb::shape<-1, 2>, nb::numpy, nb::c_contig, nb::device::cpu> edges(
1116 {static_cast<size_t>(num_edges), 2});
1117 return edges.cast();
1119 "Edges of the mesh.");
1120 surface_mesh_class.def(
1122 [](MeshType& self, Tensor<Scalar> tensor, Index num_vertices) {
1123 auto [values, shape, stride] = tensor_to_span(tensor);
1127 auto owner = std::make_shared<nb::object>(nb::find(tensor));
1128 auto id = self.wrap_as_vertices(
1131 auto& attr = self.template ref_attribute<Scalar>(
id);
1137 R
"(Wrap a tensor as vertices.
1139:param tensor: The tensor to wrap.
1140:param num_vertices: Number of vertices.
1142:return: The id of the wrapped vertices attribute.)");
1143 surface_mesh_class.def(
1145 [](MeshType& self, Tensor<Index> tensor, Index num_facets, Index vertex_per_facet) {
1146 auto [values, shape, stride] = tensor_to_span(tensor);
1149 auto owner = std::make_shared<nb::object>(nb::find(tensor));
1150 auto id = self.wrap_as_facets(
1154 auto& attr = self.template ref_attribute<Index>(
id);
1160 "vertex_per_facet"_a,
1161 R
"(Wrap a tensor as a list of regular facets.
1163:param tensor: The tensor to wrap.
1164:param num_facets: Number of facets.
1165:param vertex_per_facet: Number of vertices per facet.
1167:return: The id of the wrapped facet attribute.)");
1168 surface_mesh_class.def(
1171 Tensor<Index> offsets,
1173 Tensor<Index> facets,
1174 Index num_corners) {
1175 auto [offsets_data, offsets_shape, offsets_stride] = tensor_to_span(offsets);
1176 auto [facets_data, facets_shape, facets_stride] = tensor_to_span(facets);
1180 auto offsets_owner = std::make_shared<nb::object>(nb::find(offsets));
1181 auto facets_owner = std::make_shared<nb::object>(nb::find(facets));
1183 auto id = self.wrap_as_facets(
1188 auto& attr = self.template ref_attribute<Index>(
id);
1196 R
"(Wrap a tensor as a list of hybrid facets.
1198:param offsets: The offset indices into the facets array.
1199:param num_facets: Number of facets.
1200:param facets: The indices of the vertices of the facets.
1201:param num_corners: Number of corners.
1203:return: The id of the wrapped facet attribute.)");
1204 surface_mesh_class.def_static(
1205 "attr_name_is_reserved",
1206 &MeshType::attr_name_is_reserved,
1208 R
"(Check if an attribute name is reserved.
1210:param name: attribute name to check
1212:returns: True if the name is reserved, False otherwise)");
1213 surface_mesh_class.def_prop_ro_static(
1214 "attr_name_vertex_to_position",
1215 [](nb::handle) {
return MeshType::attr_name_vertex_to_position(); },
1216 "The name of the attribute that stores the vertex positions.");
1217 surface_mesh_class.def_prop_ro_static(
1218 "attr_name_corner_to_vertex",
1219 [](nb::handle) {
return MeshType::attr_name_corner_to_vertex(); },
1220 "The name of the attribute that stores the corner to vertex mapping.");
1221 surface_mesh_class.def_prop_ro_static(
1222 "attr_name_facet_to_first_corner",
1223 [](nb::handle) {
return MeshType::attr_name_facet_to_first_corner(); },
1224 "The name of the attribute that stores the facet to first corner mapping.");
1225 surface_mesh_class.def_prop_ro_static(
1226 "attr_name_corner_to_facet",
1227 [](nb::handle) {
return MeshType::attr_name_corner_to_facet(); },
1228 "The name of the attribute that stores the corner to facet mapping.");
1229 surface_mesh_class.def_prop_ro_static(
1230 "attr_name_corner_to_edge",
1231 [](nb::handle) {
return MeshType::attr_name_corner_to_edge(); },
1232 "The name of the attribute that stores the corner to edge mapping.");
1233 surface_mesh_class.def_prop_ro_static(
1234 "attr_name_edge_to_first_corner",
1235 [](nb::handle) {
return MeshType::attr_name_edge_to_first_corner(); },
1236 "The name of the attribute that stores the edge to first corner mapping.");
1237 surface_mesh_class.def_prop_ro_static(
1238 "attr_name_next_corner_around_edge",
1239 [](nb::handle) {
return MeshType::attr_name_next_corner_around_edge(); },
1240 "The name of the attribute that stores the next corner around edge mapping.");
1241 surface_mesh_class.def_prop_ro_static(
1242 "attr_name_vertex_to_first_corner",
1243 [](nb::handle) {
return MeshType::attr_name_vertex_to_first_corner(); },
1244 "The name of the attribute that stores the vertex to first corner mapping.");
1245 surface_mesh_class.def_prop_ro_static(
1246 "attr_name_next_corner_around_vertex",
1247 [](nb::handle) {
return MeshType::attr_name_next_corner_around_vertex(); },
1248 "The name of the attribute that stores the next corner around vertex mapping.");
1249 surface_mesh_class.def_prop_ro(
1250 "attr_id_vertex_to_position",
1251 &MeshType::attr_id_vertex_to_position);
1252 surface_mesh_class.def_prop_ro(
"attr_id_corner_to_vertex", &MeshType::attr_id_corner_to_vertex);
1253 surface_mesh_class.def_prop_ro(
1254 "attr_id_facet_to_first_corner",
1255 &MeshType::attr_id_facet_to_first_corner);
1256 surface_mesh_class.def_prop_ro(
"attr_id_corner_to_facet", &MeshType::attr_id_corner_to_facet);
1257 surface_mesh_class.def_prop_ro(
"attr_id_corner_to_edge", &MeshType::attr_id_corner_to_edge);
1258 surface_mesh_class.def_prop_ro(
1259 "attr_id_edge_to_first_corner",
1260 &MeshType::attr_id_edge_to_first_corner);
1261 surface_mesh_class.def_prop_ro(
1262 "attr_id_next_corner_around_edge",
1263 &MeshType::attr_id_next_corner_around_edge);
1264 surface_mesh_class.def_prop_ro(
1265 "attr_id_vertex_to_first_corner",
1266 &MeshType::attr_id_vertex_to_first_corner);
1267 surface_mesh_class.def_prop_ro(
1268 "attr_id_next_corner_around_vertex",
1269 &MeshType::attr_id_next_corner_around_vertex);
1270 surface_mesh_class.def(
1272 [](MeshType& self, std::optional<Tensor<Index>> tensor) {
1273 if (tensor.has_value()) {
1274 auto [edge_data, edge_shape, edge_stride] = tensor_to_span(tensor.value());
1278 "Edge tensor must be of the shape num_edges x 2");
1279 self.initialize_edges(edge_data);
1281 self.initialize_edges();
1284 "edges"_a = nb::none(),
1285 R
"(Initialize the edges.
1287The `edges` tensor provides a predefined ordering of the edges.
1288If not provided, the edges are initialized in an arbitrary order.
1290:param edges: M x 2 tensor of predefined edge vertex indices, where M is the number of edges.)");
1291 surface_mesh_class.def(
1293 &MeshType::clear_edges,
1294 R
"(Clear all edge connectivity information.)");
1295 surface_mesh_class.def_prop_ro("has_edges", &MeshType::has_edges);
1296 surface_mesh_class.def(
1301 R
"(Get the edge index associated with a local vertex of a facet.
1303:param facet_id: facet index
1304:param lv: local vertex index of the facet
1306:returns: edge index)");
1307 surface_mesh_class.def(
1309 &MeshType::get_corner_edge,
1311 R
"(Get the edge index associated with a corner.
1313:param corner_id: corner index
1315:returns: edge index)");
1316 surface_mesh_class.def(
1317 "get_edge_vertices",
1320 R
"(Get the two vertex indices of an edge.
1322:param edge_id: edge index
1324:returns: tuple of (vertex1_id, vertex2_id))");
1325 surface_mesh_class.def(
1326 "find_edge_from_vertices",
1330 R
"(Find the edge connecting two vertices.
1332:param vertex1_id: first vertex index
1333:param vertex2_id: second vertex index
1335:returns: edge index, or invalid_index if no such edge exists)");
1336 surface_mesh_class.def(
1337 "get_first_corner_around_edge",
1338 &MeshType::get_first_corner_around_edge,
1340 R
"(Get the first corner around an edge.
1342:param edge_id: edge index
1344:returns: first corner index around the edge)");
1345 surface_mesh_class.def(
1346 "get_next_corner_around_edge",
1347 &MeshType::get_next_corner_around_edge,
1349 R
"(Get the next corner around the same edge.
1351:param corner_id: current corner index
1353:returns: next corner index around the same edge)");
1354 surface_mesh_class.def(
1355 "get_first_corner_around_vertex",
1356 &MeshType::get_first_corner_around_vertex,
1358 R
"(Get the first corner around a vertex.
1360:param vertex_id: vertex index
1362:returns: first corner index around the vertex)");
1363 surface_mesh_class.def(
1364 "get_next_corner_around_vertex",
1365 &MeshType::get_next_corner_around_vertex,
1367 R
"(Get the next corner around the same vertex.
1369:param corner_id: current corner index
1371:returns: next corner index around the same vertex)");
1372 surface_mesh_class.def(
1373 "count_num_corners_around_edge",
1374 &MeshType::count_num_corners_around_edge,
1376 R
"(Count the number of corners around an edge.
1378:param edge_id: edge index
1380:returns: number of corners around the edge)");
1381 surface_mesh_class.def(
1382 "count_num_corners_around_vertex",
1383 &MeshType::count_num_corners_around_vertex,
1385 R
"(Count the number of corners around a vertex.
1387:param vertex_id: vertex index
1389:returns: number of corners around the vertex)");
1390 surface_mesh_class.def(
1391 "get_counterclockwise_corner_around_vertex",
1392 &MeshType::get_counterclockwise_corner_around_vertex,
1394 R
"(Get the counterclockwise corner around the vertex associated with the input corner.
1397 If the vertex is a non-manifold vertex, only one "umbrella" (a set of connected
1398 corners based on edge-connectivity) will be visited.
1400 If the traversal reaches a boundary or a non-manifold edge, the next adjacent corner
1401 is not well defined. It will return `invalid_index` in this case.
1403:param corner: The input corner index.
1405:returns: The counterclockwise corner index or `invalid_index` if none exists.)");
1406 surface_mesh_class.def(
1407 "get_clockwise_corner_around_vertex",
1408 &MeshType::get_clockwise_corner_around_vertex,
1410 R
"(Get the clockwise corner around the vertex associated with the input corner.
1413 If the vertex is a non-manifold vertex, only one "umbrella" (a set of connected
1414 corners based on edge-connectivity) will be visited.
1416 If the traversal reaches a boundary or a non-manifold edge, the next adjacent corner
1417 is not well defined. It will return `invalid_index` in this case.
1419:param corner: The input corner index.
1421:returns: The clockwise corner index or `invalid_index` if none exists.)");
1422 surface_mesh_class.def(
1423 "get_one_facet_around_edge",
1426 R
"(Get one facet adjacent to an edge.
1428:param edge_id: edge index
1430:returns: facet index adjacent to the edge)");
1431 surface_mesh_class.def(
1432 "get_one_corner_around_edge",
1435 R
"(Get one corner around an edge.
1437:param edge_id: edge index
1439:returns: corner index around the edge)");
1440 surface_mesh_class.def(
1441 "get_one_corner_around_vertex",
1444 R
"(Get one corner around a vertex.
1446:param vertex_id: vertex index
1448:returns: corner index around the vertex)");
1449 surface_mesh_class.def(
1453 R
"(Check if an edge is on the boundary.
1455:param edge_id: edge index
1457:returns: True if the edge is on the boundary, False otherwise)");
1459 surface_mesh_class.def(
1460 "foreach_facet_around_edge",
1461 [](MeshType& self, Index edge_id, std::function<
void(Index)>& func) {
1462 self.foreach_facet_around_edge(edge_id, func);
1466 R
"(Iterate over all facets around an edge.
1468:param edge_id: edge index
1469:param func: function to call for each facet index
1471.. code-block:: python
1473 mesh.foreach_facet_around_edge(eid, lambda fid: print(fid))
1476 surface_mesh_class.def(
1477 "foreach_facet_around_vertex",
1478 [](MeshType& self, Index vertex_id, std::function<
void(Index)>& func) {
1479 self.foreach_facet_around_vertex(vertex_id, func);
1483 R
"(Iterate over all facets around a vertex.
1485:param vertex_id: vertex index
1486:param func: function to call for each facet index
1488.. code-block:: python
1490 mesh.foreach_facet_around_vertex(vid, lambda fid: print(fid))
1493 surface_mesh_class.def(
1494 "foreach_facet_around_facet",
1495 [](MeshType& self, Index facet_id, std::function<
void(Index)>& func) {
1496 self.foreach_facet_around_facet(facet_id, func);
1500 R
"(Iterate over all adjacent facets around a facet.
1502:param facet_id: facet index
1503:param func: function to call for each adjacent facet index
1505.. code-block:: python
1507 mesh.foreach_facet_around_facet(fid, lambda afid: print(afid))
1510 surface_mesh_class.def(
1511 "foreach_corner_around_edge",
1512 [](MeshType& self, Index edge_id, std::function<
void(Index)>& func) {
1513 self.foreach_corner_around_edge(edge_id, func);
1517 R
"(Iterate over all corners around an edge.
1519:param edge_id: edge index
1520:param func: function to call for each corner index
1522.. code-block:: python
1524 mesh.foreach_corner_around_edge(eid, lambda cid: print(cid))
1527 surface_mesh_class.def(
1528 "foreach_corner_around_vertex",
1529 [](MeshType& self, Index vertex_id, std::function<
void(Index)>& func) {
1530 self.foreach_corner_around_vertex(vertex_id, func);
1534 R
"(Iterate over all corners around a vertex.
1536:param vertex_id: vertex index
1537:param func: function to call for each corner index
1539.. code-block:: python
1541 mesh.foreach_corner_around_vertex(vid, lambda cid: print(cid))
1544 surface_mesh_class.def(
1545 "foreach_edge_around_vertex",
1546 [](MeshType& self, Index vertex_id, std::function<
void(Index)>& func) {
1547 self.foreach_edge_around_vertex_with_duplicates(vertex_id, func);
1551 R
"(Iterate over all edges around a vertex.
1554 Each incident edge will be visited once for each incident facet.
1555 Thus, manifold edge will be visited exactly twice,
1556 boundary edge will be visited exactly once,
1557 non-manifold edges will be visited more than twice.
1559:param vertex_id: vertex index
1560:param func: function to call for each edge index
1562.. code-block:: python
1564 mesh.foreach_edge_around_vertex(vid, lambda eid: print(eid))
1568 surface_mesh_class.def(
1569 "facets_around_facet",
1570 [](MeshType& self, Index facet_id) {
1571 std::vector<Index> result;
1572 self.foreach_facet_around_facet(facet_id, [&](Index fid) { result.push_back(fid); });
1576 R
"(Get all adjacent facets around a facet.
1578:param facet_id: facet index
1580:returns: list of adjacent facet indices
1582.. code-block:: python
1584 facets = mesh.facets_around_facet(fid)
1589 surface_mesh_class.def(
1590 "facets_around_vertex",
1591 [](MeshType& self, Index vertex_id) {
1592 std::vector<Index> result;
1593 self.foreach_facet_around_vertex(vertex_id, [&](Index fid) { result.push_back(fid); });
1597 R
"(Get all facets around a vertex.
1599:param vertex_id: vertex index
1601:returns: list of facet indices
1603.. code-block:: python
1605 facets = mesh.facets_around_vertex(vid)
1610 surface_mesh_class.def(
1611 "facets_around_edge",
1612 [](MeshType& self, Index edge_id) {
1613 std::vector<Index> result;
1614 self.foreach_facet_around_edge(edge_id, [&](Index fid) { result.push_back(fid); });
1618 R
"(Get all facets around an edge.
1620:param edge_id: edge index
1622:returns: list of facet indices
1624.. code-block:: python
1626 facets = mesh.facets_around_edge(eid)
1631 surface_mesh_class.def(
1632 "corners_around_edge",
1633 [](MeshType& self, Index edge_id) {
1634 std::vector<Index> result;
1635 self.foreach_corner_around_edge(edge_id, [&](Index cid) { result.push_back(cid); });
1639 R
"(Get all corners around an edge.
1641:param edge_id: edge index
1643:returns: list of corner indices
1645.. code-block:: python
1647 corners = mesh.corners_around_edge(eid)
1652 surface_mesh_class.def(
1653 "corners_around_vertex",
1654 [](MeshType& self, Index vertex_id) {
1655 std::vector<Index> result;
1656 self.foreach_corner_around_vertex(vertex_id, [&](Index cid) { result.push_back(cid); });
1660 R
"(Get all corners around a vertex.
1662:param vertex_id: vertex index
1664:returns: list of corner indices
1666.. code-block:: python
1668 corners = mesh.corners_around_vertex(vid)
1673 surface_mesh_class.def(
1674 "edges_around_vertex",
1675 [](MeshType& self, Index vertex_id) {
1676 std::vector<Index> result;
1677 self.foreach_edge_around_vertex_with_duplicates(vertex_id, [&](Index eid) {
1678 result.push_back(eid);
1683 R
"(Get all edges around a vertex.
1686 Each incident edge will be visited once for each incident facet.
1687 Thus, manifold edge will be visited exactly twice,
1688 boundary edge will be visited exactly once,
1689 non-manifold edges will be visited more than twice.
1691:param vertex_id: vertex index
1693:returns: list of edge indices (with duplicates)
1695.. code-block:: python
1697 edges = mesh.edges_around_vertex(vid)
1704 MeshType* mesh =
nullptr;
1706 std::vector<AttributeId> get_metadata()
const
1709 lagrange::AttributeMatcher opts;
1710 opts.
usages = AttributeUsage::String;
1716 auto meta_data_class = nb::class_<MetaData>(m,
"MetaData",
"Metadata `dict` of the mesh");
1717 meta_data_class.def(
"__len__", [](
const MetaData& self) {
1718 auto data = self.get_metadata();
1721 meta_data_class.def(
"__getitem__", [](
const MetaData& self, std::string_view key) {
1722 return self.mesh->get_metadata(key);
1724 meta_data_class.def(
1726 [](MetaData& self, std::string_view key, std::string_view value) {
1728 if (self.mesh->has_attribute(key)) {
1729 self.mesh->set_metadata(key, value);
1731 self.mesh->create_metadata(key, value);
1734 meta_data_class.def(
"__delitem__", [](MetaData& self, std::string_view key) {
1736 self.mesh->delete_attribute(key);
1738 meta_data_class.def(
"__repr__", [](
const MetaData& self) -> std::string {
1739 auto data = self.get_metadata();
1740 if (data.empty())
return "MetaData({})";
1743 for (
auto id : data) {
1744 auto name = self.mesh->get_attribute_name(
id);
1745 auto value = self.mesh->get_metadata(
id);
1746 fmt::format_to(std::back_inserter(r),
" {}: {},\n", name, value);
1748 return fmt::format(
"MetaData(\n{})", r);
1751 surface_mesh_class.def_prop_ro(
1753 [](MeshType& self) {
1755 meta_data.mesh = &self;
1758 "Metadata of the mesh.");
1760 surface_mesh_class.def(
1761 "get_matching_attribute_ids",
1763 std::optional<AttributeElement> element,
1764 std::optional<AttributeUsage> usage,
1765 Index num_channels) {
1766 AttributeMatcher opts;
1767 if (usage.has_value()) {
1768 opts.
usages = usage.value();
1770 if (element.has_value()) {
1776 "element"_a = nb::none(),
1777 "usage"_a = nb::none(),
1778 "num_channels"_a = 0,
1779 R
"(Get all matching attribute ids with the desired element type, usage and number of channels.
1781:param element: The target element type. None matches all element types.
1782:param usage: The target usage type. None matches all usage types.
1783:param num_channels: The target number of channels. 0 matches arbitrary number of channels.
1785:returns: A list of attribute ids matching the target element, usage and number of channels.
1788 surface_mesh_class.def(
1789 "get_matching_attribute_id",
1791 std::optional<AttributeElement> element,
1792 std::optional<AttributeUsage> usage,
1793 Index num_channels) {
1794 std::optional<AttributeId> result;
1795 self.seq_foreach_attribute_id([&](
AttributeId attr_id) {
1796 if (result.has_value()) {
1799 const auto name = self.get_attribute_name(attr_id);
1800 if (self.attr_name_is_reserved(name))
return;
1801 const auto& attr = self.get_attribute_base(attr_id);
1802 if (element && attr.get_element_type() != *element)
return;
1803 if (usage && attr.get_usage() != *usage)
return;
1804 if (num_channels != 0 && attr.get_num_channels() != num_channels)
return;
1809 "element"_a = nb::none(),
1810 "usage"_a = nb::none(),
1811 "num_channels"_a = 0,
1812 R
"(Get one matching attribute id with the desired element type, usage and number of channels.
1814:param element: The target element type. None matches all element types.
1815:param usage: The target usage type. None matches all usage types.
1816:param num_channels: The target number of channels. 0 matches arbitrary number of channels.
1818:returns: An attribute id matching the target element, usage and number of channels, if found. None otherwise.
1821 surface_mesh_class.def(
1823 [](MeshType& self) -> MeshType {
1824 MeshType mesh = self;
1827 R
"(Create a shallow copy of this mesh.)");
1829 surface_mesh_class.def(
1831 [](MeshType& self, [[maybe_unused]] std::optional<nb::dict> memo) -> MeshType {
1832 MeshType mesh = self;
1838 using AttributeType = std::decay_t<
decltype(attr)>;
1839 if constexpr (AttributeType::IsIndexed) {
1840 auto& value_attr = attr.values();
1841 if (value_attr.is_external()) {
1842 value_attr.create_internal_copy();
1844 auto& index_attr = attr.indices();
1845 if (index_attr.is_external()) {
1846 index_attr.create_internal_copy();
1849 if (attr.is_external()) {
1850 attr.create_internal_copy();
1856 "memo"_a = nb::none(),
1857 R
"(Create a deep copy of this mesh.)");
1859 surface_mesh_class.def(
1861 [](MeshType& self,
bool strip) -> MeshType {
1863 return MeshType::stripped_copy(self);
1865 auto py_self = nb::find(self);
1866 return nb::cast<MeshType>(py_self.attr(
"__deepcopy__")());
1870 R
"(Create a deep copy of this mesh.
1872:param strip: If True, strip the mesh of all attributes except for the reserved attributes.)");
Index find_edge_from_vertices(Index v0, Index v1) const
Definition Mesh.h:694
bool is_boundary_edge(Index e) const
Definition Mesh.h:821
Index get_one_corner_around_edge(Index e) const
Definition Mesh.h:795
Index get_edge(Index f, Index lv) const
Definition Mesh.h:664
std::array< Index, 2 > get_edge_vertices(Index e) const
Retrieve edge endpoints.
Definition Mesh.h:730
Index get_one_facet_around_edge(Index e) const
Definition Mesh.h:782
Index get_one_corner_around_vertex(Index v) const
Definition Mesh.h:808
Index get_num_edges() const
Definition Mesh.h:650
const AttributeBase & get_attribute_base(std::string_view name) const
Gets a read-only reference to the base class of attribute given its name.
Definition SurfaceMesh.cpp:1277
Definition PyAttribute.h:24
Definition PyIndexedAttribute.h:27
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
AttributeReorientPolicy
Policy for updating attribute values when reorienting mesh facets.
Definition AttributeFwd.h:192
uint32_t AttributeId
Identified to be used to access an attribute.
Definition AttributeFwd.h:73
AttributeUsage
Usage tag indicating how the attribute should behave under mesh transformations.
Definition AttributeFwd.h:54
#define LA_ATTRIBUTE_INDEX_X(mode, data)
X Macro arguments for the Attribute<> class.
Definition AttributeTypes.h:80
AttributeElement
Type of element to which the attribute is attached.
Definition AttributeFwd.h:26
AttributeDeletePolicy
Policy for attribute deletion of reserved attribute names.
Definition AttributeFwd.h:172
#define LA_ATTRIBUTE_X(mode, data)
X Macro arguments for the Attribute<> class.
Definition AttributeTypes.h:48
@ None
Do not reorient attribute values when flipping mesh facets.
Definition AttributeFwd.h:193
@ ErrorIfReserved
Default deletion policy, throw an exception if attribute name is reserved.
Definition AttributeFwd.h:87
@ Position
Mesh attribute must have exactly dim channels.
Definition AttributeFwd.h:57
@ Tangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:59
@ Vector
Mesh attribute can have any number of channels (including 1 channel).
Definition AttributeFwd.h:55
@ CornerIndex
Single channel integer attribute indexing a mesh corner.
Definition AttributeFwd.h:65
@ VertexIndex
Single channel integer attribute indexing a mesh vertex.
Definition AttributeFwd.h:63
@ EdgeIndex
Single channel integer attribute indexing a mesh edge.
Definition AttributeFwd.h:66
@ Normal
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:58
@ FacetIndex
Single channel integer attribute indexing a mesh facet.
Definition AttributeFwd.h:64
@ Color
Mesh attribute can have 1, 2, 3 or 4 channels.
Definition AttributeFwd.h:61
@ UV
Mesh attribute must have exactly 2 channels.
Definition AttributeFwd.h:62
@ Bitangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:60
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
@ WarnAndCopy
Logs a warning and copy the buffer data if it grows beyond the buffer capacity.
Definition AttributeFwd.h:106
@ Value
Values that are not attached to a specific element.
Definition AttributeFwd.h:42
@ Edge
Per-edge mesh attributes.
Definition AttributeFwd.h:34
@ Indexed
Indexed mesh attributes.
Definition AttributeFwd.h:45
@ Facet
Per-facet mesh attributes.
Definition AttributeFwd.h:31
@ Corner
Per-corner mesh attributes.
Definition AttributeFwd.h:37
@ Vertex
Per-vertex mesh attributes.
Definition AttributeFwd.h:28
@ ErrorIfReserved
Default deletion policy, throw an exception if attribute name is reserved.
Definition AttributeFwd.h:173
void par_foreach_attribute_write(SurfaceMesh< Scalar, Index > &mesh, Visitor &&vis)
Applies a function in parallel to each attribute of a mesh.
Definition foreach_attribute.h:408
std::vector< AttributeId > find_matching_attributes(const SurfaceMesh< Scalar, Index > &mesh, const AttributeMatcher &options)
Finds all attributes with the specified usage/element type/number of channels.
Definition find_matching_attributes.cpp:80
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:194
SharedSpan< T > make_shared_span(const std::shared_ptr< Y > &r, T *element_ptr, size_t size)
Created a SharedSpan object around an internal buffer of a parent object.
Definition SharedSpan.h:101
::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
BitField< AttributeElement > element_types
List of attribute element types to include. By default, all element types are included.
Definition find_matching_attributes.h:40
BitField< AttributeUsage > usages
List of attribute usages to include. By default, all usages are included.
Definition find_matching_attributes.h:37
size_t num_channels
Number of channels to match against. Default value is 0, which disables this test.
Definition find_matching_attributes.h:43