Replace mjs_detachBody and mjs_detachDefault with mjs_detach.

In a follow-up, mjs_detach will be changed to only remove without deleting an element.

PiperOrigin-RevId: 773637351
Change-Id: I9ee4d3584ba88ccc225591337cf036bab806e25c
This commit is contained in:
Alessio Quaglino
2025-06-20 04:11:14 -07:00
committed by Copybara-Service
parent 4ce6293217
commit 0488d9f4eb
15 changed files with 177 additions and 179 deletions
+5 -14
View File
@@ -3783,23 +3783,14 @@ Attachment
Attach child to a parent, return the attached element if success or NULL otherwise. Attach child to a parent, return the attached element if success or NULL otherwise.
.. _mjs_detachBody: .. _mjs_detach:
`mjs_detachBody <#mjs_detachBody>`__ `mjs_detach <#mjs_detach>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_detachBody .. mujoco-include:: mjs_detach
Delete body and descendants from mjSpec, remove all references, return 0 on success. Detach but not delete object corresponding to the given element, return 0 on success.
.. _mjs_detachDefault:
`mjs_detachDefault <#mjs_detachDefault>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjs_detachDefault
Delete default class and descendants from mjSpec, remove all references, return 0 on success.
.. _AddTreeElements: .. _AddTreeElements:
+7
View File
@@ -5,6 +5,13 @@ Changelog
Upcoming version (not yet released) Upcoming version (not yet released)
----------------------------------- -----------------------------------
.. admonition:: Breaking API changes
:class: attention
- The functions ``mjs_detachBody`` and ``mjs_detachDefault`` have been replaced by :ref:`mjs_delete`. Additionally,
`:ref:mjs_detach` was added in order to remove an element from the spec without deleting it.
- The Python functions ``element.delete`` have been replaced by ``spec.delete(element)``.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
- Inverse dynamics were not being computed correctly when :ref:`tendon armature<tendon-spatial-armature>` was present, - Inverse dynamics were not being computed correctly when :ref:`tendon armature<tendon-spatial-armature>` was present,
+2 -3
View File
@@ -3410,8 +3410,7 @@ void mju_defaultTask(mjTask* task);
void mju_taskJoin(mjTask* task); void mju_taskJoin(mjTask* task);
mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child, mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); const char* prefix, const char* suffix);
int mjs_detachBody(mjSpec* s, mjsBody* b); int mjs_detach(mjSpec* spec, mjsElement* element);
int mjs_detachDefault(mjSpec* s, mjsDefault* d);
mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def); mjsBody* mjs_addBody(mjsBody* body, const mjsDefault* def);
mjsSite* mjs_addSite(mjsBody* body, const mjsDefault* def); mjsSite* mjs_addSite(mjsBody* body, const mjsDefault* def);
mjsJoint* mjs_addJoint(mjsBody* body, const mjsDefault* def); mjsJoint* mjs_addJoint(mjsBody* body, const mjsDefault* def);
@@ -3420,7 +3419,7 @@ mjsGeom* mjs_addGeom(mjsBody* body, const mjsDefault* def);
mjsCamera* mjs_addCamera(mjsBody* body, const mjsDefault* def); mjsCamera* mjs_addCamera(mjsBody* body, const mjsDefault* def);
mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def); mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def);
mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe); mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
int mjs_delete(mjsElement* element); int mjs_delete(mjSpec* spec, mjsElement* element);
mjsActuator* mjs_addActuator(mjSpec* s, const mjsDefault* def); mjsActuator* mjs_addActuator(mjSpec* s, const mjsDefault* def);
mjsSensor* mjs_addSensor(mjSpec* s); mjsSensor* mjs_addSensor(mjSpec* s);
mjsFlex* mjs_addFlex(mjSpec* s); mjsFlex* mjs_addFlex(mjSpec* s);
+1 -1
View File
@@ -607,7 +607,7 @@ For elements that can have children (bodies and defaults), the methods ``spec.de
``spec.detach_default(def)`` remove, respectively, ``body`` and ``def`` from the spec, together with all of their ``spec.detach_default(def)`` remove, respectively, ``body`` and ``def`` from the spec, together with all of their
children. When detaching body subtrees, all elements which reference elements in the subtree, will also be removed. For children. When detaching body subtrees, all elements which reference elements in the subtree, will also be removed. For
all other elements, the method ``delete()`` removes the corresponding element from the spec, e.g. all other elements, the method ``delete()`` removes the corresponding element from the spec, e.g.
``spec.geom('my_geom').delete()`` will remove the geom named "my_geom" and all of the elements that reference it. ``spec.delete(spec.geom('my_geom'))`` will remove the geom named "my_geom" and all of the elements that reference it.
Tree traversal Tree traversal
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
+3 -6
View File
@@ -1411,11 +1411,8 @@ MJAPI void mju_taskJoin(mjTask* task);
MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child, MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); const char* prefix, const char* suffix);
// Delete body and descendants from mjSpec, remove all references, return 0 on success. // Detach but not delete object corresponding to the given element, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b); MJAPI int mjs_detach(mjSpec* spec, mjsElement* element);
// Delete default class and descendants from mjSpec, remove all references, return 0 on success.
MJAPI int mjs_detachDefault(mjSpec* s, mjsDefault* d);
//---------------------------------- Tree elements ------------------------------------------------- //---------------------------------- Tree elements -------------------------------------------------
@@ -1444,7 +1441,7 @@ MJAPI mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def);
MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe); MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
// Delete object corresponding to the given element, return 0 on success. // Delete object corresponding to the given element, return 0 on success.
MJAPI int mjs_delete(mjsElement* element); MJAPI int mjs_delete(mjSpec* spec, mjsElement* element);
//---------------------------------- Non-tree elements --------------------------------------------- //---------------------------------- Non-tree elements ---------------------------------------------
+2 -2
View File
@@ -2000,7 +2000,7 @@
"\n", "\n",
"# Delete all key frames to avoid name conflicts\n", "# Delete all key frames to avoid name conflicts\n",
"while humanoid.keys:\n", "while humanoid.keys:\n",
" humanoid.keys[-1].delete()\n", " humanoid.delete(keys[-1])\n",
"\n", "\n",
"# Create a grid of humanoids by attaching humanoid to spec multiple times\n", "# Create a grid of humanoids by attaching humanoid to spec multiple times\n",
"for i in range(4):\n", "for i in range(4):\n",
@@ -2335,7 +2335,7 @@
"fly = mj.MjSpec.from_file(fly_file)\n", "fly = mj.MjSpec.from_file(fly_file)\n",
"# Remove lights from fly so they are not duplicated in line-up\n", "# Remove lights from fly so they are not duplicated in line-up\n",
"for light in fly.lights:\n", "for light in fly.lights:\n",
" light.delete()\n", " fly.delete(light)\n",
"\n", "\n",
"small_fly = scale_spec(fly, 1.25)\n", "small_fly = scale_spec(fly, 1.25)\n",
"large_fly = scale_spec(fly, 2)\n", "large_fly = scale_spec(fly, 2)\n",
+12 -26
View File
@@ -8987,45 +8987,25 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
), ),
doc='Attach child to a parent, return the attached element if success or NULL otherwise.', # pylint: disable=line-too-long doc='Attach child to a parent, return the attached element if success or NULL otherwise.', # pylint: disable=line-too-long
)), )),
('mjs_detachBody', ('mjs_detach',
FunctionDecl( FunctionDecl(
name='mjs_detachBody', name='mjs_detach',
return_type=ValueType(name='int'), return_type=ValueType(name='int'),
parameters=( parameters=(
FunctionParameterDecl( FunctionParameterDecl(
name='s', name='spec',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjSpec'), inner_type=ValueType(name='mjSpec'),
), ),
), ),
FunctionParameterDecl( FunctionParameterDecl(
name='b', name='element',
type=PointerType( type=PointerType(
inner_type=ValueType(name='mjsBody'), inner_type=ValueType(name='mjsElement'),
), ),
), ),
), ),
doc='Delete body and descendants from mjSpec, remove all references, return 0 on success.', # pylint: disable=line-too-long doc='Detach but not delete object corresponding to the given element, return 0 on success.', # pylint: disable=line-too-long
)),
('mjs_detachDefault',
FunctionDecl(
name='mjs_detachDefault',
return_type=ValueType(name='int'),
parameters=(
FunctionParameterDecl(
name='s',
type=PointerType(
inner_type=ValueType(name='mjSpec'),
),
),
FunctionParameterDecl(
name='d',
type=PointerType(
inner_type=ValueType(name='mjsDefault'),
),
),
),
doc='Delete default class and descendants from mjSpec, remove all references, return 0 on success.', # pylint: disable=line-too-long
)), )),
('mjs_addBody', ('mjs_addBody',
FunctionDecl( FunctionDecl(
@@ -9202,6 +9182,12 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
name='mjs_delete', name='mjs_delete',
return_type=ValueType(name='int'), return_type=ValueType(name='int'),
parameters=( parameters=(
FunctionParameterDecl(
name='spec',
type=PointerType(
inner_type=ValueType(name='mjSpec'),
),
),
FunctionParameterDecl( FunctionParameterDecl(
name='element', name='element',
type=PointerType( type=PointerType(
+71 -36
View File
@@ -397,7 +397,7 @@ PYBIND11_MODULE(_specs, m) {
}, },
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
mjSpec.def("detach_default", [](MjSpec& self, raw::MjsDefault& def) { mjSpec.def("detach_default", [](MjSpec& self, raw::MjsDefault& def) {
if (mjs_detachDefault(self.ptr, &def) != 0) { if (mjs_delete(self.ptr, def.element) != 0) {
throw pybind11::value_error(mjs_getError(self.ptr)); throw pybind11::value_error(mjs_getError(self.ptr));
} }
}); });
@@ -408,7 +408,7 @@ PYBIND11_MODULE(_specs, m) {
}, },
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
mjSpec.def("detach_body", [](MjSpec& self, raw::MjsBody& body) { mjSpec.def("detach_body", [](MjSpec& self, raw::MjsBody& body) {
mjs_detachBody(self.ptr, &body); mjs_delete(self.ptr, body.element);
}); });
mjSpec.def( mjSpec.def(
"attach", "attach",
@@ -759,7 +759,9 @@ PYBIND11_MODULE(_specs, m) {
// ============================= MJSFRAME ==================================== // ============================= MJSFRAME ====================================
mjsFrame.def("delete", [](raw::MjsFrame& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsFrame& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsFrame.def("set_frame", [](raw::MjsFrame& self, raw::MjsFrame& frame) { mjsFrame.def("set_frame", [](raw::MjsFrame& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -796,7 +798,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSGEOM ===================================== // ============================= MJSGEOM =====================================
mjsGeom.def("delete", [](raw::MjsGeom& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsGeom& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsGeom.def("set_frame", [](raw::MjsGeom& self, raw::MjsFrame& frame) { mjsGeom.def("set_frame", [](raw::MjsGeom& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -824,7 +828,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSJOINT ==================================== // ============================= MJSJOINT ====================================
mjsJoint.def("delete", [](raw::MjsJoint& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsJoint& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsJoint.def("set_frame", [](raw::MjsJoint& self, raw::MjsFrame& frame) { mjsJoint.def("set_frame", [](raw::MjsJoint& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -852,7 +858,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSSITE ===================================== // ============================= MJSSITE =====================================
mjsSite.def("delete", [](raw::MjsSite& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsSite& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsSite.def("set_frame", [](raw::MjsSite& self, raw::MjsFrame& frame) { mjsSite.def("set_frame", [](raw::MjsSite& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -897,8 +905,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSCAMERA =================================== // ============================= MJSCAMERA ===================================
mjsCamera.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsCamera& obj) {
[](raw::MjsCamera& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsCamera.def("set_frame", [](raw::MjsCamera& self, raw::MjsFrame& frame) { mjsCamera.def("set_frame", [](raw::MjsCamera& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -926,7 +935,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSLIGHT ==================================== // ============================= MJSLIGHT ====================================
mjsLight.def("delete", [](raw::MjsLight& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsLight& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsLight.def("set_frame", [](raw::MjsLight& self, raw::MjsFrame& frame) { mjsLight.def("set_frame", [](raw::MjsLight& self, raw::MjsFrame& frame) {
if (mjs_setFrame(self.element, &frame) != 0) { if (mjs_setFrame(self.element, &frame) != 0) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element))); throw pybind11::value_error(mjs_getError(mjs_getSpec(self.element)));
@@ -954,8 +965,9 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSMATERIAL ================================= // ============================= MJSMATERIAL =================================
mjsMaterial.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsMaterial& obj) {
[](raw::MjsMaterial& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsMaterial.def_property( mjsMaterial.def_property(
"classname", "classname",
[](raw::MjsMaterial& self) -> raw::MjsDefault* { [](raw::MjsMaterial& self) -> raw::MjsDefault* {
@@ -966,7 +978,9 @@ PYBIND11_MODULE(_specs, m) {
}); });
// ============================= MJSMESH ===================================== // ============================= MJSMESH =====================================
mjsMesh.def("delete", [](raw::MjsMesh& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsMesh& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsMesh.def_property( mjsMesh.def_property(
"classname", "classname",
[](raw::MjsMesh& self) -> raw::MjsDefault* { [](raw::MjsMesh& self) -> raw::MjsDefault* {
@@ -977,7 +991,9 @@ PYBIND11_MODULE(_specs, m) {
}); });
// ============================= MJSPAIR ===================================== // ============================= MJSPAIR =====================================
mjsPair.def("delete", [](raw::MjsPair& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsPair& obj) {
mjs_delete(self.ptr, obj.element);
});
mjsPair.def_property( mjsPair.def_property(
"classname", "classname",
[](raw::MjsPair& self) -> raw::MjsDefault* { [](raw::MjsPair& self) -> raw::MjsDefault* {
@@ -988,8 +1004,9 @@ PYBIND11_MODULE(_specs, m) {
}); });
// ============================= MJSEQUAL ==================================== // ============================= MJSEQUAL ====================================
mjsEquality.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsEquality& obj) {
[](raw::MjsEquality& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsEquality.def_property( mjsEquality.def_property(
"classname", "classname",
[](raw::MjsEquality& self) -> raw::MjsDefault* { [](raw::MjsEquality& self) -> raw::MjsDefault* {
@@ -1000,8 +1017,9 @@ PYBIND11_MODULE(_specs, m) {
}); });
// ============================= MJSACTUATOR ================================= // ============================= MJSACTUATOR =================================
mjsActuator.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsActuator& obj) {
[](raw::MjsActuator& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsActuator.def_property( mjsActuator.def_property(
"classname", "classname",
[](raw::MjsActuator& self) -> raw::MjsDefault* { [](raw::MjsActuator& self) -> raw::MjsDefault* {
@@ -1101,8 +1119,9 @@ PYBIND11_MODULE(_specs, m) {
py::arg("gain")); py::arg("gain"));
// ============================= MJSTENDON =================================== // ============================= MJSTENDON ===================================
mjsTendon.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsTendon& obj) {
[](raw::MjsTendon& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsTendon.def( mjsTendon.def(
"default", "default",
[](raw::MjsTendon& self) -> raw::MjsDefault* { [](raw::MjsTendon& self) -> raw::MjsDefault* {
@@ -1135,39 +1154,54 @@ PYBIND11_MODULE(_specs, m) {
py::return_value_policy::reference_internal); py::return_value_policy::reference_internal);
// ============================= MJSSENSOR =================================== // ============================= MJSSENSOR ===================================
mjsSensor.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsSensor& obj) {
[](raw::MjsSensor& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
// ============================= MJSFLEX ===================================== // ============================= MJSFLEX =====================================
mjsFlex.def("delete", [](raw::MjsFlex& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsFlex& obj) {
mjs_delete(self.ptr, obj.element);
});
// ============================= MJSHFIELD =================================== // ============================= MJSHFIELD ===================================
mjsHField.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsHField& obj) {
[](raw::MjsHField& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
// ============================= MJSSKIN ===================================== // ============================= MJSSKIN =====================================
mjsSkin.def("delete", [](raw::MjsSkin& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsSkin& obj) {
mjs_delete(self.ptr, obj.element);
});
// ============================= MJSTEXTURE ================================== // ============================= MJSTEXTURE ==================================
mjsTexture.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsTexture& obj) {
[](raw::MjsTexture& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
// ============================= MJSKEY ====================================== // ============================= MJSKEY ======================================
mjsKey.def("delete", [](raw::MjsKey& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsKey& obj) {
mjs_delete(self.ptr, obj.element);
});
// ============================= MJSTEXT ===================================== // ============================= MJSTEXT =====================================
mjsText.def("delete", [](raw::MjsText& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsText& obj) {
mjs_delete(self.ptr, obj.element);
});
// ============================= MJSNUMERIC ================================== // ============================= MJSNUMERIC ==================================
mjsNumeric.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsNumeric& obj) {
[](raw::MjsNumeric& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
// ============================= MJSEXCLUDE ================================== // ============================= MJSEXCLUDE ==================================
mjsExclude.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsExclude& obj) {
[](raw::MjsExclude& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
// ============================= MJSTUPLE ==================================== // ============================= MJSTUPLE ====================================
mjsTuple.def("delete", [](raw::MjsTuple& self) { mjs_delete(self.element); }); mjSpec.def("delete", [](MjSpec& self, raw::MjsTuple& obj) {
mjs_delete(self.ptr, obj.element);
});
// ============================= MJSPLUGIN =================================== // ============================= MJSPLUGIN ===================================
mjsPlugin.def_property( mjsPlugin.def_property(
@@ -1176,8 +1210,9 @@ PYBIND11_MODULE(_specs, m) {
[](raw::MjsPlugin& self, raw::MjsPlugin* other) { [](raw::MjsPlugin& self, raw::MjsPlugin* other) {
self.element = other->element; self.element = other->element;
}); });
mjsPlugin.def("delete", mjSpec.def("delete", [](MjSpec& self, raw::MjsPlugin& obj) {
[](raw::MjsPlugin& self) { mjs_delete(self.element); }); mjs_delete(self.ptr, obj.element);
});
mjsPlugin.def_property( mjsPlugin.def_property(
"config", "config",
[](raw::MjsPlugin& self) -> py::dict { [](raw::MjsPlugin& self) -> py::dict {
+4 -4
View File
@@ -838,9 +838,9 @@ class SpecsTest(absltest.TestCase):
self.assertIsNotNone(site) self.assertIsNotNone(site)
self.assertEqual(site, spec.site('head')) self.assertEqual(site, spec.site('head'))
site.delete() spec.delete(site)
spec.sensors[-1].delete() spec.delete(spec.sensors[-1])
spec.sensors[-1].delete() spec.delete(spec.sensors[-1])
model = spec.compile() model = spec.compile()
self.assertIsNotNone(model) self.assertIsNotNone(model)
@@ -930,7 +930,7 @@ class SpecsTest(absltest.TestCase):
""") """)
plugin = spec.plugins[0] plugin = spec.plugins[0]
self.assertIsNotNone(plugin) self.assertIsNotNone(plugin)
plugin.delete() spec.delete(plugin)
model = spec.compile() model = spec.compile()
self.assertIsNotNone(model) self.assertIsNotNone(model)
+25 -33
View File
@@ -284,35 +284,18 @@ const char* mjs_getError(mjSpec* s) {
// detach body from mjSpec, return 0 on success // detach body from mjSpec, return 0 on success
int mjs_detachBody(mjSpec* s, mjsBody* b) { int mjs_detach(mjSpec* s, mjsElement* element) {
mjCModel* model = static_cast<mjCModel*>(s->element); mjCModel* model = static_cast<mjCModel*>(s->element);
mjCBody* body = static_cast<mjCBody*>(b->element); if (!element) {
try { model->SetError(mjCError(0, "Element is null."));
*model -= *body;
} catch (mjCError& e) {
model->SetError(e);
return -1; return -1;
} }
model->Detach(body); mjCError e(0, "Detach is not implemented yet.");
return 0; model->SetError(e);
return -1;
} }
// detach default from mjSpec, return 0 on success
int mjs_detachDefault(mjSpec* s, mjsDefault* def) {
mjCModel* modelC = static_cast<mjCModel*>(s->element);
if (!def) {
modelC->SetError(mjCError(0, "Cannot detach, default is null"));
return -1;
}
mjCDef* defC = static_cast<mjCDef*>(def->element);
try {
*modelC -= *defC;
} catch (mjCError& e) {
modelC->SetError(e);
return -1;
}
return 0;
}
// check if model has warnings // check if model has warnings
int mjs_isWarning(mjSpec* s) { int mjs_isWarning(mjSpec* s) {
@@ -372,15 +355,24 @@ int mj_copyBack(mjSpec* s, const mjModel* m) {
// delete object, return 0 on success // delete object, return 0 on success
int mjs_delete(mjsElement* element) { int mjs_delete(mjSpec* s, mjsElement* element) {
mjCModel* model; mjCModel* model = static_cast<mjCModel*>(s->element);
if (element->elemtype == mjOBJ_DEFAULT) if (!element) {
model = static_cast<mjCDef*>(element)->model; model->SetError(mjCError(0, "Element is null."));
else return -1;
model = static_cast<mjCBase*>(element)->model; }
try { try {
// it will call the appropriate destructor since ~mjCBase is virtual if (element->elemtype == mjOBJ_BODY) {
model->DeleteElement(element); mjCBody* body = static_cast<mjCBody*>(element);
*model -= *body;
model->DeleteElement(body);
} else if (element->elemtype == mjOBJ_DEFAULT) {
mjCDef* def = static_cast<mjCDef*>(element);
*model -= *def;
} else {
// it will call the appropriate destructor since ~mjCBase is virtual
model->DeleteElement(element);
}
return 0; return 0;
} catch (mjCError& e) { } catch (mjCError& e) {
model->SetError(e); model->SetError(e);
@@ -1035,7 +1027,7 @@ const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const char* s
mjsFrame* mjs_bodyToFrame(mjsBody** body) { mjsFrame* mjs_bodyToFrame(mjsBody** body) {
mjCBody* bodyC = static_cast<mjCBody*>((*body)->element); mjCBody* bodyC = static_cast<mjCBody*>((*body)->element);
mjCFrame* frameC = bodyC->ToFrame(); mjCFrame* frameC = bodyC->ToFrame();
bodyC->model->Detach(bodyC); bodyC->model->DeleteElement((*body)->element);
*body = nullptr; *body = nullptr;
return &frameC->spec; return &frameC->spec;
} }
+3 -6
View File
@@ -76,11 +76,8 @@ MJAPI int mj_copyBack(mjSpec* s, const mjModel* m);
MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child, MJAPI mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
const char* prefix, const char* suffix); const char* prefix, const char* suffix);
// Detach body from mjSpec, remove all references and delete the body, return 0 on success. // Detach but not delete object corresponding to the given element, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b); MJAPI int mjs_detach(mjSpec* s, mjsElement* element);
// Detach default from mjSpec, remove all references and delete the default, return 0 on success.
MJAPI int mjs_detachDefault(mjSpec* s, mjsDefault* d);
//---------------------------------- Add tree elements --------------------------------------------- //---------------------------------- Add tree elements ---------------------------------------------
@@ -109,7 +106,7 @@ MJAPI mjsLight* mjs_addLight(mjsBody* body, const mjsDefault* def);
MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe); MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
// Delete object corresponding to the given element, return 0 on success. // Delete object corresponding to the given element, return 0 on success.
MJAPI int mjs_delete(mjsElement* element); MJAPI int mjs_delete(mjSpec* s, mjsElement* element);
//---------------------------------- Add non-tree elements ----------------------------------------- //---------------------------------- Add non-tree elements -----------------------------------------
+30 -24
View File
@@ -742,15 +742,44 @@ void deletefromlist(std::vector<T*>* list, mjsElement* element) {
// recursively delete all plugins in the subtree
static void deletesubtreeplugin(mjCBody* subtree, mjCModel* model) {
mjsPlugin* plugin = &(subtree->spec.plugin);
if (plugin->active && plugin->name->empty()) {
model->DeleteElement(plugin->element);
}
for (auto* body : subtree->Bodies()) {
deletesubtreeplugin(body, model);
}
}
// discard all invalid elements from all lists // discard all invalid elements from all lists
void mjCModel::DeleteElement(mjsElement* el) { void mjCModel::DeleteElement(mjsElement* el) {
ResetTreeLists(); ResetTreeLists();
if (el->elemtype != mjOBJ_DEFAULT) {
if (static_cast<mjCBase*>(el)->model != this) {
throw mjCError(nullptr, "element is not in this model");
}
} else {
if (static_cast<mjCDef*>(el)->model != this) {
throw mjCError(nullptr, "default is not in this model");
}
}
switch (el->elemtype) { switch (el->elemtype) {
case mjOBJ_BODY: case mjOBJ_BODY:
{
MakeTreeLists(); // rebuild lists that were reset at the beginning of the function MakeTreeLists(); // rebuild lists that were reset at the beginning of the function
throw mjCError(nullptr, "bodies cannot be deleted, use detach instead"); mjCBody* subtree = static_cast<mjCBody*>(el);
if (subtree->GetRef() == 1) {
deletesubtreeplugin(subtree, this);
}
subtree->Release();
break; break;
}
case mjOBJ_DEFAULT: case mjOBJ_DEFAULT:
MakeTreeLists(); // rebuild lists that were reset at the beginning of the function MakeTreeLists(); // rebuild lists that were reset at the beginning of the function
@@ -828,29 +857,6 @@ void mjCModel::DeleteElement(mjsElement* el) {
// recursively delete all plugins in the subtree
void deletesubtreeplugin(mjCBody* subtree, mjCModel* model) {
mjsPlugin* plugin = &(subtree->spec.plugin);
if (plugin->active && plugin->name->empty()) {
model->DeleteElement(plugin->element);
}
for (auto* body : subtree->Bodies()) {
deletesubtreeplugin(body, model);
}
}
// deletes all plugins in the subtree and then the subtree itself
void mjCModel::Detach(mjCBody* subtree) {
if (subtree->GetRef() == 1) {
deletesubtreeplugin(subtree, this);
}
subtree->Release();
}
// TODO: we should not use C-type casting with multiple C++ inheritance // TODO: we should not use C-type casting with multiple C++ inheritance
void mjCModel::CreateObjectLists() { void mjCModel::CreateObjectLists() {
for (int i = 0; i < mjNOBJECT; ++i) { for (int i = 0; i < mjNOBJECT; ++i) {
-3
View File
@@ -232,9 +232,6 @@ class mjCModel : public mjCModel_, private mjSpec {
// delete default and all descendants // delete default and all descendants
void RemoveDefault(mjCDef* def); void RemoveDefault(mjCDef* def);
// detach subtree from model
void Detach(mjCBody* subtree);
// API for access to model elements (outside tree) // API for access to model elements (outside tree)
int NumObjects(mjtObj type); // number of objects in specified list int NumObjects(mjtObj type); // number of objects in specified list
mjCBase* GetObject(mjtObj type, int id); // pointer to specified object mjCBase* GetObject(mjtObj type, int id); // pointer to specified object
+1 -1
View File
@@ -3554,7 +3554,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
} }
// delete subtree // delete subtree
if (mjs_detachBody(spec, subtree)) { if (mjs_delete(spec, subtree->element)) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, mjs_getError(spec));
} }
} }
+11 -20
View File
@@ -219,7 +219,7 @@ TEST_F(PluginTest, DeletePlugin) {
EXPECT_THAT(model->actuator_plugin[0], 0); EXPECT_THAT(model->actuator_plugin[0], 0);
// delete actuator // delete actuator
mjs_delete(actuator->element); mjs_delete(spec, actuator->element);
// recompile and check that the plugin is not present // recompile and check that the plugin is not present
mjModel* newmodel = mj_compile(spec, NULL); mjModel* newmodel = mj_compile(spec, NULL);
@@ -326,7 +326,7 @@ TEST_F(PluginTest, DetachPlugin) {
// detach the body and compile // detach the body and compile
mjsBody* body_to_detach = mjs_findBody(parent, "child-body"); mjsBody* body_to_detach = mjs_findBody(parent, "child-body");
EXPECT_THAT(body_to_detach, NotNull()); EXPECT_THAT(body_to_detach, NotNull());
EXPECT_THAT(mjs_detachBody(parent, body_to_detach), 0); EXPECT_THAT(mjs_delete(parent, body_to_detach->element), 0);
mjModel* model = mj_compile(parent, nullptr); mjModel* model = mj_compile(parent, nullptr);
EXPECT_THAT(model, NotNull()); EXPECT_THAT(model, NotNull());
EXPECT_THAT(model->nbody, 2); EXPECT_THAT(model->nbody, 2);
@@ -1368,12 +1368,8 @@ void TestDetachBody(bool compile) {
mjsBody* body = mjs_findBody(child, "body"); mjsBody* body = mjs_findBody(child, "body");
EXPECT_THAT(body, NotNull()); EXPECT_THAT(body, NotNull());
// get an error if trying to delete the body // delete subtree
EXPECT_EQ(mjs_delete(body->element), -1); EXPECT_THAT(mjs_delete(child, body->element), 0);
EXPECT_THAT(mjs_getError(child), HasSubstr("use detach instead"));
// detach subtree
EXPECT_THAT(mjs_detachBody(child, body), 0);
// try saving to XML before compiling again // try saving to XML before compiling again
std::array<char, 1024> e; std::array<char, 1024> e;
@@ -1898,12 +1894,12 @@ TEST_F(MujocoTest, PreserveState) {
// detach subtree // detach subtree
mjsBody* body = mjs_findBody(spec, "detachable"); mjsBody* body = mjs_findBody(spec, "detachable");
EXPECT_THAT(body, NotNull()); EXPECT_THAT(body, NotNull());
EXPECT_THAT(mjs_detachBody(spec, body), 0); EXPECT_THAT(mjs_delete(spec, body->element), 0);
// detach mocap // detach mocap
mjsBody* mocap_body = mjs_findBody(spec, "mocap_detach"); mjsBody* mocap_body = mjs_findBody(spec, "mocap_detach");
EXPECT_THAT(mocap_body, NotNull()); EXPECT_THAT(mocap_body, NotNull());
EXPECT_THAT(mjs_detachBody(spec, mocap_body), 0); EXPECT_THAT(mjs_delete(spec, mocap_body->element), 0);
// add body // add body
mjsBody* newbody = mjs_addBody(mjs_findBody(spec, "world"), 0); mjsBody* newbody = mjs_addBody(mjs_findBody(spec, "world"), 0);
@@ -2787,25 +2783,20 @@ TEST_F(MujocoTest, DetachDefault) {
mjsDefault* child = mjs_findDefault(spec, "child1"); mjsDefault* child = mjs_findDefault(spec, "child1");
EXPECT_THAT(child, NotNull()); EXPECT_THAT(child, NotNull());
// try using mjs_delete to remove default, should fail // delete default
EXPECT_EQ(mjs_delete(child->element), -1); EXPECT_EQ(mjs_delete(spec, child->element), 0);
// detach default
EXPECT_EQ(mjs_detachDefault(spec, child), 0);
child = mjs_findDefault(spec, "child1"); child = mjs_findDefault(spec, "child1");
EXPECT_THAT(child, IsNull()); EXPECT_THAT(child, IsNull());
// try and detach previously detached default, should fail // try and detach previously detached default, should fail
EXPECT_EQ(mjs_detachDefault(spec, child), -1); EXPECT_EQ(mjs_delete(spec, nullptr), -1);
child = mjs_findDefault(spec, "child1"); child = mjs_findDefault(spec, "child1");
EXPECT_THAT(child, IsNull()); EXPECT_THAT(child, IsNull());
EXPECT_THAT(mjs_getError(spec),
HasSubstr("Cannot detach, default is null"));
// detach parent // detach parent
mjsDefault* parent = mjs_findDefault(spec, "parent"); mjsDefault* parent = mjs_findDefault(spec, "parent");
EXPECT_THAT(parent, NotNull()); EXPECT_THAT(parent, NotNull());
mjs_detachDefault(spec, parent); mjs_delete(spec, parent->element);
// both parent and remaining child should be removed // both parent and remaining child should be removed
parent = mjs_findDefault(spec, "parent"); parent = mjs_findDefault(spec, "parent");
@@ -2816,7 +2807,7 @@ TEST_F(MujocoTest, DetachDefault) {
// error when trying to detach the 'main' default // error when trying to detach the 'main' default
mjsDefault* main = mjs_findDefault(spec, "main"); mjsDefault* main = mjs_findDefault(spec, "main");
EXPECT_THAT(main, NotNull()); EXPECT_THAT(main, NotNull());
EXPECT_EQ(mjs_detachDefault(spec, main), -1); EXPECT_EQ(mjs_delete(spec, main->element), -1);
EXPECT_THAT(mjs_getError(spec), EXPECT_THAT(mjs_getError(spec),
HasSubstr("cannot remove the global default ('main')")); HasSubstr("cannot remove the global default ('main')"));