Fix MjSpec.add_texture gridlayout argument to accept strings and character lists

PiperOrigin-RevId: 859599919
Change-Id: If785f36010a7437b354ec0ce7db30c2b7ec69fe6
This commit is contained in:
Taylor Howell
2026-01-22 07:27:29 -08:00
committed by Copybara-Service
parent bab468703c
commit 5ae6b5fe31
2 changed files with 66 additions and 0 deletions
@@ -463,6 +463,19 @@ def generate_add() -> None:
f'py::arg("{f.name}") = py::none()',
)
elif isinstance(f.type, ast_nodes.ArrayType):
inner_type = f.type.inner_type.decl()
if inner_type == 'char':
return (
(
f'set_char_array(out->{f.name}, {f.name},'
f' {f.type.extents[0]}, "{f.name}");'
),
'char_array',
f.name,
'str | list[str]',
f'py::object& {f.name}',
f'py::arg("{f.name}") = py::none()',
)
if f.name == 'size' and f.type.extents[0] == 3:
return (
f'set_array_size(out->{f.name}, {f.name});',
@@ -759,6 +772,38 @@ def generate_add() -> None:
}
};
"""
elif t == 'char_array':
code += """\n
auto set_char_array = [](auto&& des, py::object& obj, int size, const char* name) {
if (obj.is_none()) {
return;
}
std::string chars;
if (py::isinstance<py::str>(obj)) {
chars = py::cast<std::string>(obj);
} else if (py::isinstance<py::list>(obj)) {
py::list list = py::cast<py::list>(obj);
chars.reserve(py::len(list));
for (auto item : list) {
std::string s = py::cast<std::string>(item);
if (s.size() != 1) {
throw pybind11::value_error(std::string(name) + " list elements must be single characters.");
}
chars.push_back(s[0]);
}
} else {
throw pybind11::type_error(std::string(name) + " must be a string or a list of single-character strings.");
}
if (chars.size() != size) {
std::string msg = std::string(name) + " should have length " + std::to_string(size) + ", got " + std::to_string(chars.size()) + ".";
throw pybind11::value_error(msg);
}
int idx = 0;
for (char val : chars) {
des[idx++] = val;
}
};
"""
elif t == 'array_size':
code += """\n
auto set_array_size = [](auto&& des, const std::optional<std::vector<double>>& array) {
+21
View File
@@ -1120,6 +1120,27 @@ class SpecsTest(absltest.TestCase):
self.assertIsNone(spec.texture('none'))
self.assertIsNone(spec.mesh('none'))
def test_texture_gridlayout(self):
spec = mujoco.MjSpec()
texture = spec.add_texture(name='test', gridlayout='.U..LFRB.D..')
self.assertEqual(list(texture.gridlayout), list('.U..LFRB.D..'))
texture2 = spec.add_texture(name='test2', gridlayout=list('.U..LFRB.D..'))
self.assertEqual(list(texture2.gridlayout), list('.U..LFRB.D..'))
with self.assertRaises(ValueError) as cm:
spec.add_texture(name='test3', gridlayout='.U..')
self.assertIn('should have length 12', str(cm.exception))
with self.assertRaises(ValueError) as cm:
spec.add_texture(name='test4', gridlayout=['.', 'U', '.', '.'])
self.assertIn('should have length 12', str(cm.exception))
with self.assertRaises(ValueError) as cm:
spec.add_texture(name='test5', gridlayout=['..', 'U'] + ['.'] * 10)
self.assertIn('list elements must be single characters', str(cm.exception))
def test_attach_units(self):
child = mujoco.MjSpec()
parent = mujoco.MjSpec()