diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index fbec781a..0d58d0f9 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -4126,6 +4126,15 @@ mjs_getSpec Get spec from body. +.. _mjs_getSpecFromFrame: + +mjs_getSpecFromFrame +~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getSpecFromFrame + +Get spec from frame. + .. _mjs_findBody: mjs_findBody diff --git a/doc/includes/references.h b/doc/includes/references.h index 19ea8a36..dee3c074 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -3590,6 +3590,7 @@ mjsSkin* mjs_addSkin(mjSpec* s); mjsTexture* mjs_addTexture(mjSpec* s); mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def); mjSpec* mjs_getSpec(mjsBody* body); +mjSpec* mjs_getSpecFromFrame(mjsFrame* frame); mjsBody* mjs_findBody(mjSpec* s, const char* name); mjsElement* mjs_findElement(mjSpec* s, mjtObj type, const char* name); mjsBody* mjs_findChild(mjsBody* body, const char* name); diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index 0df9b7e9..771209f1 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -1526,6 +1526,9 @@ MJAPI mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def); // Get spec from body. MJAPI mjSpec* mjs_getSpec(mjsBody* body); +// Get spec from frame. +MJAPI mjSpec* mjs_getSpecFromFrame(mjsFrame* frame); + // Find body in spec by name. MJAPI mjsBody* mjs_findBody(mjSpec* s, const char* name); diff --git a/introspect/functions.py b/introspect/functions.py index 25f21010..7f7d6133 100644 --- a/introspect/functions.py +++ b/introspect/functions.py @@ -9682,6 +9682,22 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Get spec from body.', )), + ('mjs_getSpecFromFrame', + FunctionDecl( + name='mjs_getSpecFromFrame', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='frame', + type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + ), + ), + doc='Get spec from frame.', + )), ('mjs_findBody', FunctionDecl( name='mjs_findBody', diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc index ad8f767a..cb11562f 100644 --- a/python/mujoco/specs.cc +++ b/python/mujoco/specs.cc @@ -563,7 +563,12 @@ PYBIND11_MODULE(_specs, m) { "attach_frame", [](raw::MjsBody& self, raw::MjsFrame& frame, std::string& prefix, std::string& suffix) -> raw::MjsFrame* { - return mjs_attachFrame(&self, &frame, prefix.c_str(), suffix.c_str()); + auto new_frame = + mjs_attachFrame(&self, &frame, prefix.c_str(), suffix.c_str()); + if (!new_frame) { + throw pybind11::value_error(mjs_getError(mjs_getSpec(&self))); + } + return new_frame; }, py::return_value_policy::reference_internal); @@ -578,7 +583,13 @@ PYBIND11_MODULE(_specs, m) { "attach_body", [](raw::MjsFrame& self, raw::MjsBody& body, std::string& prefix, std::string& suffix) -> raw::MjsBody* { - return mjs_attachBody(&self, &body, prefix.c_str(), suffix.c_str()); + auto new_body = + mjs_attachBody(&self, &body, prefix.c_str(), suffix.c_str()); + if (!new_body) { + throw pybind11::value_error( + mjs_getError(mjs_getSpecFromFrame(&self))); + } + return new_body; }, py::return_value_policy::reference_internal); diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py index 2bc0b599..f56f009c 100644 --- a/python/mujoco/specs_test.py +++ b/python/mujoco/specs_test.py @@ -846,5 +846,20 @@ class SpecsTest(absltest.TestCase): with self.assertRaises(IndexError): material.textures[-1] = 'x' + def test_attach_error(self): + child = mujoco.MjSpec() + parent = mujoco.MjSpec() + parent.degree = not child.degree + body = parent.worldbody.add_body() + frame = child.worldbody.add_frame() + with self.assertRaises(ValueError) as cm: + body.attach_frame(frame, '', '') + self.assertEqual( + str(cm.exception), + 'Error: cannot attach mjSpecs with incompatible compiler/angle' + ' attribute', + ) + + if __name__ == '__main__': absltest.main() diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 55472226..d6ada1d9 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -521,13 +521,20 @@ mjsDefault* mjs_addDefault(mjSpec* s, const char* classname, const mjsDefault* p -// get objects +// get spec from body mjSpec* mjs_getSpec(mjsBody* body) { return &(static_cast(body->element)->model->spec); } +// get spec from frame +mjSpec* mjs_getSpecFromFrame(mjsFrame* frame) { + return &(static_cast(frame->element)->model->spec); +} + + + // find spec (model asset) by name mjSpec* mjs_findSpec(mjSpec* s, const char* name) { mjCModel* model = static_cast(s->element); diff --git a/src/user/user_api.h b/src/user/user_api.h index 8c55c0ff..162097b4 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -188,6 +188,9 @@ MJAPI mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def); // Get spec from body. MJAPI mjSpec* mjs_getSpec(mjsBody* body); +// Get spec from frame. +MJAPI mjSpec* mjs_getSpecFromFrame(mjsFrame* frame); + // Find spec (model asset) by name. MJAPI mjSpec* mjs_findSpec(mjSpec* spec, const char* name);