Add bindings generation for mjOption, mjVisual, and mjStatistic.

Fixes #2076.

PiperOrigin-RevId: 677789263
Change-Id: Ie1bda40af62c5ac9bbba541668a50adfc59d3f7c
This commit is contained in:
Alessio Quaglino
2024-09-23 07:41:52 -07:00
committed by Copybara-Service
parent 41ea95f077
commit b5bcc91a03
3 changed files with 48 additions and 2 deletions
@@ -43,6 +43,13 @@ def _value_binding_code(
fulltype = fulltype + '&' # plugin and orientation are not pointers
else:
fulltype = fulltype + '*'
# non-mjs structs
rawclassname = rawclassname.replace('mjOption', 'raw::MjOption')
rawclassname = rawclassname.replace('mjVisual', 'raw::MjVisual')
rawclassname = rawclassname.replace('mjStatistic', 'raw::MjStatistic')
fulltype = fulltype.replace('mjOption', 'raw::MjOption')
fulltype = fulltype.replace('mjVisual', 'raw::MjVisual')
fulltype = fulltype.replace('mjStatistic', 'raw::MjStatistic')
def_property_args = (
f'"{varname}"',
@@ -68,6 +75,9 @@ def _array_binding_code(
raise NotImplementedError()
innertype = field.inner_type.decl()
rawclassname = classname.replace('mjs', 'raw::Mjs')
rawclassname = rawclassname.replace('mjOption', 'raw::MjOption')
rawclassname = rawclassname.replace('mjVisual', 'raw::MjVisual')
rawclassname = rawclassname.replace('mjStatistic', 'raw::MjStatistic')
fullvarname = varname
if classname == 'mjSpec': # raw mjSpec has a wrapper
rawclassname = classname.replace('mjS', 'MjS')
@@ -209,12 +219,17 @@ def _ptr_binding_code(
}}
}}, py::return_value_policy::reference_internal);"""
raise NotImplementedError()
raise NotImplementedError(
'Unsupported array type: ' + vartype + ' in ' + classname
)
def _binding_code(field: ast_nodes.StructFieldDecl, key: str) -> str:
if isinstance(field.type, ast_nodes.ValueType):
return _value_binding_code(field.type, key, field.name)
elif isinstance(field.type, ast_nodes.AnonymousStructDecl):
field.type = ast_nodes.ValueType(name='mjVisual'+field.name.title())
return _value_binding_code(field.type, key, field.name)
elif isinstance(field.type, ast_nodes.PointerType):
return _ptr_binding_code(field.type, key, field.name)
elif isinstance(field.type, ast_nodes.ArrayType):
@@ -224,7 +239,10 @@ def _binding_code(field: ast_nodes.StructFieldDecl, key: str) -> str:
def generate() -> None:
for key in structs.STRUCTS.keys():
if (key.startswith('mjs') or key == 'mjSpec') and key != 'mjsElement':
if (
key.startswith('mjs')
or key in ['mjSpec', 'mjOption', 'mjVisual', 'mjStatistic']
) and key != 'mjsElement':
print('\n // ' + key)
for field in structs.STRUCTS[key].fields:
code = _binding_code(field, key)
+3
View File
@@ -109,6 +109,9 @@ PYBIND11_MODULE(_specs, m) {
py::class_<raw::MjsPlugin> mjsPlugin(m, "MjsPlugin");
py::class_<raw::MjsOrientation> mjsOrientation(m, "MjsOrientation");
py::class_<raw::MjsWrap> mjsWrap(m, "MjsWrap");
py::class_<raw::MjOption> mjOption(m, "MjOption");
py::class_<raw::MjStatistic> mjStatistic(m, "MjStatistic");
py::class_<raw::MjVisual> mjVisual(m, "MjVisual");
// ============================= MJSPEC =====================================
mjSpec.def(py::init<>());
+25
View File
@@ -717,5 +717,30 @@ class SpecsTest(absltest.TestCase):
self.assertIsNotNone(model)
self.assertEqual(model.nplugin, 0)
def test_access_option_stat_visual(self):
spec = mujoco.MjSpec()
spec.from_string(textwrap.dedent("""
<mujoco model="MuJoCo Model">
<option timestep="0.001"/>
<statistic meansize="0.05"/>
<visual>
<quality shadowsize="4096"/>
</visual>
</mujoco>
"""))
self.assertEqual(spec.option.timestep, 0.001)
self.assertEqual(spec.stat.meansize, 0.05)
self.assertEqual(spec.visual.quality.shadowsize, 4096)
spec.option.timestep = 0.002
spec.stat.meansize = 0.06
spec.visual.quality.shadowsize = 8192
model = spec.compile()
self.assertEqual(model.opt.timestep, 0.002)
self.assertEqual(model.stat.meansize, 0.06)
self.assertEqual(model.vis.quality.shadowsize, 8192)
if __name__ == '__main__':
absltest.main()