14#include "PyAttribute.h"
16#include <lagrange/Attribute.h>
17#include <lagrange/AttributeValueType.h>
18#include <lagrange/Logger.h>
19#include <lagrange/internal/string_from_scalar.h>
20#include <lagrange/python/binding.h>
21#include <lagrange/python/tensor_utils.h>
22#include <lagrange/utils/Error.h>
23#include <lagrange/utils/assert.h>
24#include <lagrange/utils/fmt/format.h>
25#include <lagrange/utils/invalid.h>
27namespace lagrange::python {
29void bind_attribute(nanobind::module_& m)
31 namespace nb = nanobind;
32 using namespace nb::literals;
34 auto attr_class = nb::class_<PyAttribute>(
37 "Attribute data associated with mesh elements (vertices, facets, corners, edges).");
38 attr_class.def_prop_ro(
40 [](
PyAttribute& self) {
return self->get_element_type(); },
41 "Element type (Vertex, Facet, Corner, Edge, Value).");
42 attr_class.def_prop_ro(
44 [](
PyAttribute& self) {
return self->get_usage(); },
45 "Usage type (Position, Normal, UV, Color, etc.).");
46 attr_class.def_prop_ro(
48 [](
PyAttribute& self) {
return self->get_num_channels(); },
49 "Number of channels per element.");
51 attr_class.def_prop_rw(
54 return self.process([](
auto& attr) {
return nb::cast(attr.get_default_value()); });
57 self.process([&](
auto& attr) {
58 using ValueType =
typename std::decay_t<
decltype(attr)>::ValueType;
59 attr.set_default_value(
static_cast<ValueType
>(val));
62 "Default value for new elements.");
63 attr_class.def_prop_rw(
66 return self.process([](
auto& attr) {
return attr.get_growth_policy(); });
69 self.process([&](
auto& attr) { attr.set_growth_policy(policy); });
71 "Policy for growing the attribute when elements are added.");
72 attr_class.def_prop_rw(
75 return self.process([](
auto& attr) {
return attr.get_shrink_policy(); });
78 self.process([&](
auto& attr) { attr.set_shrink_policy(policy); });
80 "Policy for shrinking the attribute when elements are removed.");
81 attr_class.def_prop_rw(
84 return self.process([](
auto& attr) {
return attr.get_write_policy(); });
87 self.process([&](
auto& attr) { attr.set_write_policy(policy); });
89 "Policy for write operations on the attribute.");
90 attr_class.def_prop_rw(
93 return self.process([](
auto& attr) {
return attr.get_copy_policy(); });
96 self.process([&](
auto& attr) { attr.set_copy_policy(policy); });
98 "Policy for copying the attribute.");
99 attr_class.def_prop_rw(
102 return self.process([](
auto& attr) {
return attr.get_cast_policy(); });
105 self.process([&](
auto& attr) { attr.set_cast_policy(policy); });
107 "Policy for casting the attribute to different types.");
109 "create_internal_copy",
110 [](
PyAttribute& self) { self.process([](
auto& attr) { attr.create_internal_copy(); }); },
111 "Create an internal copy if the attribute wraps external data.");
114 [](
PyAttribute& self) { self.process([](
auto& attr) { attr.clear(); }); },
115 "Remove all elements from the attribute.");
119 self.process([=](
auto& attr) { attr.reserve_entries(s); });
122 R
"(Reserve enough memory for `num_entries` entries.
124:param num_entries: Number of entries to reserve. It does not need to be a multiple of `num_channels`.)");
128 self.process([=](
auto& attr) { attr.insert_elements(num_elements); });
131 R
"(Insert new elements with default value to the attribute.
133:param num_elements: Number of elements to insert.)");
137 auto insert_elements = [&](
auto& attr) {
138 using ValueType =
typename std::decay_t<
decltype(attr)>::ValueType;
139 GenericTensor tensor;
140 std::vector<ValueType> buffer;
141 if (nb::try_cast(value, tensor)) {
142 if (tensor.dtype() != nb::dtype<ValueType>()) {
143 throw nb::type_error(
145 "Tensor has a unexpected dtype. Expecting {}.",
149 Tensor<ValueType> local_tensor(tensor.handle());
150 auto [data, shape, stride] = tensor_to_span(local_tensor);
152 attr.insert_elements(data);
153 }
else if (nb::try_cast(value, buffer)) {
154 attr.insert_elements({buffer.data(), buffer.size()});
156 throw nb::type_error(
"Argument `value` must be either list or np.ndarray.");
159 self.process(insert_elements);
162 R
"(Insert new elements to the attribute.
164:param tensor: A tensor with shape (num_elements, num_channels) or (num_elements,).)");
167 [](
PyAttribute& self) {
return self.process([](
auto& attr) {
return attr.empty(); }); },
168 "Check if the attribute has no elements.");
169 attr_class.def_prop_ro(
172 return self.process([](
auto& attr) {
return attr.get_num_elements(); });
174 "Number of elements in the attribute.");
175 attr_class.def_prop_ro(
178 return self.process([](
auto& attr) {
return attr.is_external(); });
180 "Check if the attribute wraps external data.");
181 attr_class.def_prop_ro(
184 return self.process([](
auto& attr) {
return attr.is_read_only(); });
186 "Check if the attribute is read-only.");
187 attr_class.def_prop_rw(
190 return self.process([&](
auto& attr) {
191 auto tensor = attribute_to_tensor(attr, nb::find(&self));
192 return nb::cast(tensor, nb::rv_policy::reference_internal);
196 auto wrap_tensor = [&](
auto& attr) {
197 using ValueType =
typename std::decay_t<
decltype(attr)>::ValueType;
198 GenericTensor tensor;
199 std::vector<ValueType> buffer;
200 if (nb::try_cast(value, tensor)) {
201 if (tensor.dtype() != nb::dtype<ValueType>()) {
202 throw nb::type_error(
204 "Tensor has a unexpected dtype. Expecting {}.",
208 Tensor<ValueType> local_tensor(tensor.handle());
209 auto [data, shape, stride] = tensor_to_span(local_tensor);
213 shape.size() == 2 ? shape[1] == attr.get_num_channels() :
true);
214 const size_t num_elements = shape[0];
216 auto owner = std::make_shared<nb::object>(nb::find(tensor));
218 }
else if (nb::try_cast(value, buffer)) {
220 attr.insert_elements({buffer.data(), buffer.size()});
222 throw nb::type_error(
"Attribute.data must be either list or np.ndarray.");
225 self.process(wrap_tensor);
227 nb::for_getter(nb::sig(
"def data(self, /) -> numpy.typing.NDArray")),
228 "Raw data as a numpy array.");
229 attr_class.def_prop_ro(
231 [](
PyAttribute& self) -> std::optional<nb::type_object> {
232 auto np = nb::module_::import_(
"numpy");
233 switch (self.ptr()->get_value_type()) {
234 case AttributeValueType::e_int8_t:
return np.attr(
"int8");
235 case AttributeValueType::e_int16_t:
return np.attr(
"int16");
236 case AttributeValueType::e_int32_t:
return np.attr(
"int32");
237 case AttributeValueType::e_int64_t:
return np.attr(
"int64");
238 case AttributeValueType::e_uint8_t:
return np.attr(
"uint8");
239 case AttributeValueType::e_uint16_t:
return np.attr(
"uint16");
240 case AttributeValueType::e_uint32_t:
return np.attr(
"uint32");
241 case AttributeValueType::e_uint64_t:
return np.attr(
"uint64");
242 case AttributeValueType::e_float:
return np.attr(
"float32");
243 case AttributeValueType::e_double:
return np.attr(
"float64");
244 default:
logger().warn(
"Attribute has an unknown dtype.");
return std::nullopt;
247 "NumPy dtype of the attribute values.");
Definition PyAttribute.h:24
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
AttributeShrinkPolicy
Policy for shrinking external attribute buffers.
Definition AttributeFwd.h:117
AttributeGrowthPolicy
Policy for growing external attribute buffers.
Definition AttributeFwd.h:96
AttributeCopyPolicy
Policy for copying attribute that are views onto external buffers.
Definition AttributeFwd.h:161
AttributeWritePolicy
Policy for attempting to write to read-only external buffers.
Definition AttributeFwd.h:138
AttributeCastPolicy
Policy for remapping invalid values when casting to a different value type.
Definition AttributeFwd.h:182
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:175
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
std::string_view string_from_scalar()
Returns a human-readable string from any supported attribute value type.