Skip to content

Commit 3c19b63

Browse files
okmatijacopybara-github
authored andcommitted
Refactor texture.data to be handled using py::bytes
PiperOrigin-RevId: 843226742 Change-Id: I7ab920da3c387467b1fced27f557c6edd1af4bce
1 parent 87d333c commit 3c19b63

File tree

4 files changed

+20
-55
lines changed

4 files changed

+20
-55
lines changed

doc/python.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,8 @@ The :ref:`mjsMesh` object includes convenience methods for model creation with n
656656
657657
Texture editing
658658
^^^^^^^^^^^^^^^
659-
The :ref:`mjsTexture` buffer option stores the texture bytes in the ``data`` attribute. This attribute is stored as
660-
bytes but its elements can be read and modified as ``int``\s via index access and read as Python's built-in ``bytes`` or
661-
``bytearray`` objects. See `specs_test.py
662-
<https://github.com/google-deepmind/mujoco/blob/main/python/mujoco/specs_test.py>`__.
659+
The :ref:`mjsTexture` buffer option stores the texture bytes in the ``data`` attribute. This attribute can be read and
660+
modified, for example:
663661

664662
.. code-block:: python
665663

python/mujoco/codegen/generate_spec_bindings.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -226,17 +226,17 @@ def _ptr_binding_code(
226226
return f"""\
227227
{classname}.def_property(
228228
"{varname}",
229-
[]({rawclassname}& self) -> MjTypeVec<std::byte> {{
230-
return MjTypeVec<std::byte>(self.{fullvarname}->data(),
231-
self.{fullvarname}->size());
232-
}},
233-
[]({rawclassname}& self, py::bytes& rhs) {{
234-
self.{fullvarname}->clear();
235-
self.{fullvarname}->reserve(py::len(rhs));
236-
std::string_view rhs_view = py::cast<std::string_view>(rhs);
237-
for (auto val : rhs_view) {{
238-
self.{fullvarname}->push_back(static_cast<std::byte>(val));
239-
}}
229+
[]({rawclassname}& self) -> py::bytes {{
230+
return py::bytes(reinterpret_cast<const char*>(self.{fullvarname}->data()),
231+
self.{fullvarname}->size());
232+
}},
233+
[]({rawclassname}& self, py::bytes rhs) {{
234+
self.{fullvarname}->clear();
235+
std::string_view rhs_view = py::cast<std::string_view>(rhs);
236+
self.{fullvarname}->reserve(rhs_view.length());
237+
for (char val : rhs_view) {{
238+
self.{fullvarname}->push_back(static_cast<std::byte>(val));
239+
}}
240240
}}, py::return_value_policy::move);"""
241241
elif vartype == 'mjStringVec':
242242
return f"""\

python/mujoco/specs.cc

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -134,41 +134,6 @@ void DefineArray(py::module& m, const std::string& typestr) {
134134
}, py::keep_alive<0, 1>(), py::return_value_policy::reference_internal);
135135
};
136136

137-
// Specialization for std::byte to convert to int for Python iteration
138-
template <>
139-
void DefineArray<std::byte>(py::module& m, const std::string& typestr) {
140-
using Class = MjTypeVec<std::byte>;
141-
py::class_<Class>(m, typestr.c_str())
142-
.def(
143-
py::init([](std::byte* data, int size) { return Class(data, size); }))
144-
.def("__getitem__",
145-
[](Class& v, int i) -> int {
146-
if (i < 0 || i >= v.size) {
147-
throw py::index_error("Index out of range.");
148-
}
149-
return static_cast<int>(v.ptr[i]);
150-
})
151-
.def("__setitem__",
152-
[](Class& v, int i, int c) {
153-
if (i < 0 || i >= v.size) {
154-
throw py::index_error("Index out of range.");
155-
}
156-
if (c < 0 || c > 255) {
157-
throw py::value_error("Value out of range [0, 255].");
158-
}
159-
v.ptr[i] = static_cast<std::byte>(c);
160-
})
161-
.def("__len__", [](Class& v) { return v.size; })
162-
.def(
163-
"__iter__",
164-
[](Class& v) {
165-
return py::make_iterator(
166-
reinterpret_cast<unsigned char*>(v.ptr),
167-
reinterpret_cast<unsigned char*>(v.ptr + v.size));
168-
},
169-
py::keep_alive<0, 1>(), py::return_value_policy::reference_internal);
170-
};
171-
172137
py::list FindAllImpl(raw::MjsBody& body, mjtObj objtype, bool recursive) {
173138
py::list list;
174139
raw::MjsElement* el = mjs_firstChild(&body, objtype, recursive);

python/mujoco/specs_test.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,24 +1040,26 @@ def test_read_texture(self):
10401040
spec = mujoco.MjSpec()
10411041
texture = spec.add_texture(name='texture', height=1, width=2, nchannel=3)
10421042
texture.data = bytes([1, 2, 3, 4, 5, 6])
1043-
read_bytes = bytes(texture.data)
1043+
read_bytes = texture.data
10441044
self.assertEqual(read_bytes, bytes([1, 2, 3, 4, 5, 6]))
10451045

10461046
def test_modify_texture(self):
10471047
# Assign red, green and blue pixels, then make the first pixel yellow.
10481048
spec = mujoco.MjSpec()
10491049
texture = spec.add_texture(name='texture', height=1, width=3, nchannel=3)
10501050
texture.data = bytes([255, 0, 0, 0, 255, 0, 0, 0, 255])
1051-
texture.data[1] = 255
1051+
data_array = bytearray(texture.data)
1052+
data_array[1] = 255
1053+
texture.data = bytes(data_array)
10521054
self.assertEqual(
1053-
bytes(texture.data), bytes([255, 255, 0, 0, 255, 0, 0, 0, 255])
1055+
texture.data, bytes([255, 255, 0, 0, 255, 0, 0, 0, 255])
10541056
)
10551057

10561058
# Assigning values outside the range [0, 255] should raise an error.
10571059
with self.assertRaises(ValueError):
1058-
texture.data[3] = 256
1060+
data_array[0] = 256
10591061
with self.assertRaises(ValueError):
1060-
texture.data[3] = -1
1062+
data_array[0] = -1
10611063

10621064
def test_find_unnamed_asset(self):
10631065
spec = mujoco.MjSpec()

0 commit comments

Comments
 (0)