diff --git a/doc/python.rst b/doc/python.rst index 2cfa609e..9fe5af85 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -656,10 +656,8 @@ The :ref:`mjsMesh` object includes convenience methods for model creation with n Texture editing ^^^^^^^^^^^^^^^ -The :ref:`mjsTexture` buffer option stores the texture bytes in the ``data`` attribute. This attribute is stored as -bytes but its elements can be read and modified as ``int``\s via index access and read as Python's built-in ``bytes`` or -``bytearray`` objects. See `specs_test.py -`__. +The :ref:`mjsTexture` buffer option stores the texture bytes in the ``data`` attribute. This attribute can be read and +modified, for example: .. code-block:: python diff --git a/python/mujoco/codegen/generate_spec_bindings.py b/python/mujoco/codegen/generate_spec_bindings.py index 2d13667b..ca8e8bdc 100644 --- a/python/mujoco/codegen/generate_spec_bindings.py +++ b/python/mujoco/codegen/generate_spec_bindings.py @@ -226,17 +226,17 @@ def _ptr_binding_code( return f"""\ {classname}.def_property( "{varname}", - []({rawclassname}& self) -> MjTypeVec {{ - return MjTypeVec(self.{fullvarname}->data(), - self.{fullvarname}->size()); - }}, - []({rawclassname}& self, py::bytes& rhs) {{ - self.{fullvarname}->clear(); - self.{fullvarname}->reserve(py::len(rhs)); - std::string_view rhs_view = py::cast(rhs); - for (auto val : rhs_view) {{ - self.{fullvarname}->push_back(static_cast(val)); - }} + []({rawclassname}& self) -> py::bytes {{ + return py::bytes(reinterpret_cast(self.{fullvarname}->data()), + self.{fullvarname}->size()); + }}, + []({rawclassname}& self, py::bytes rhs) {{ + self.{fullvarname}->clear(); + std::string_view rhs_view = py::cast(rhs); + self.{fullvarname}->reserve(rhs_view.length()); + for (char val : rhs_view) {{ + self.{fullvarname}->push_back(static_cast(val)); + }} }}, py::return_value_policy::move);""" elif vartype == 'mjStringVec': return f"""\ diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc index 309a349b..e3bfc7ef 100644 --- a/python/mujoco/specs.cc +++ b/python/mujoco/specs.cc @@ -134,41 +134,6 @@ void DefineArray(py::module& m, const std::string& typestr) { }, py::keep_alive<0, 1>(), py::return_value_policy::reference_internal); }; -// Specialization for std::byte to convert to int for Python iteration -template <> -void DefineArray(py::module& m, const std::string& typestr) { - using Class = MjTypeVec; - py::class_(m, typestr.c_str()) - .def( - py::init([](std::byte* data, int size) { return Class(data, size); })) - .def("__getitem__", - [](Class& v, int i) -> int { - if (i < 0 || i >= v.size) { - throw py::index_error("Index out of range."); - } - return static_cast(v.ptr[i]); - }) - .def("__setitem__", - [](Class& v, int i, int c) { - if (i < 0 || i >= v.size) { - throw py::index_error("Index out of range."); - } - if (c < 0 || c > 255) { - throw py::value_error("Value out of range [0, 255]."); - } - v.ptr[i] = static_cast(c); - }) - .def("__len__", [](Class& v) { return v.size; }) - .def( - "__iter__", - [](Class& v) { - return py::make_iterator( - reinterpret_cast(v.ptr), - reinterpret_cast(v.ptr + v.size)); - }, - py::keep_alive<0, 1>(), py::return_value_policy::reference_internal); -}; - py::list FindAllImpl(raw::MjsBody& body, mjtObj objtype, bool recursive) { py::list list; raw::MjsElement* el = mjs_firstChild(&body, objtype, recursive); diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py index 337650d0..312ff668 100644 --- a/python/mujoco/specs_test.py +++ b/python/mujoco/specs_test.py @@ -1040,7 +1040,7 @@ class SpecsTest(absltest.TestCase): spec = mujoco.MjSpec() texture = spec.add_texture(name='texture', height=1, width=2, nchannel=3) texture.data = bytes([1, 2, 3, 4, 5, 6]) - read_bytes = bytes(texture.data) + read_bytes = texture.data self.assertEqual(read_bytes, bytes([1, 2, 3, 4, 5, 6])) def test_modify_texture(self): @@ -1048,16 +1048,18 @@ class SpecsTest(absltest.TestCase): spec = mujoco.MjSpec() texture = spec.add_texture(name='texture', height=1, width=3, nchannel=3) texture.data = bytes([255, 0, 0, 0, 255, 0, 0, 0, 255]) - texture.data[1] = 255 + data_array = bytearray(texture.data) + data_array[1] = 255 + texture.data = bytes(data_array) self.assertEqual( - bytes(texture.data), bytes([255, 255, 0, 0, 255, 0, 0, 0, 255]) + texture.data, bytes([255, 255, 0, 0, 255, 0, 0, 0, 255]) ) # Assigning values outside the range [0, 255] should raise an error. with self.assertRaises(ValueError): - texture.data[3] = 256 + data_array[0] = 256 with self.assertRaises(ValueError): - texture.data[3] = -1 + data_array[0] = -1 def test_find_unnamed_asset(self): spec = mujoco.MjSpec()