From 7dc7a349c5ba2db2d3f8ab50a367d08e2f1afbbc Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Fri, 20 Jun 2025 07:02:38 -0700 Subject: [PATCH] Move detach from mjs_delete to mjs_detach. PiperOrigin-RevId: 773686024 Change-Id: Ida2c032a6329104df80ed134cf904dde7259bd1e --- src/user/user_api.cc | 44 ++++++++++++++++++++------------------ src/user/user_model.cc | 48 +++++++++++++++++++++++++++--------------- src/user/user_model.h | 7 ++++++ 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/user/user_api.cc b/src/user/user_api.cc index a823573a..66f6a977 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -283,20 +283,6 @@ const char* mjs_getError(mjSpec* s) { -// detach body from mjSpec, return 0 on success -int mjs_detach(mjSpec* s, mjsElement* element) { - mjCModel* model = static_cast(s->element); - if (!element) { - model->SetError(mjCError(0, "Element is null.")); - return -1; - } - mjCError e(0, "Detach is not implemented yet."); - model->SetError(e); - return -1; -} - - - // check if model has warnings int mjs_isWarning(mjSpec* s) { mjCModel* modelC = static_cast(s->element); @@ -354,6 +340,28 @@ int mj_copyBack(mjSpec* s, const mjModel* m) { +// detach body from mjSpec, return 0 on success +int mjs_detach(mjSpec* s, mjsElement* element) { + mjCModel* model = static_cast(s->element); + if (!element) { + model->SetError(mjCError(0, "Element is null.")); + return -1; + } + try { + if (element->elemtype == mjOBJ_DEFAULT) { + throw mjCError(0, "Detach is not implemented for defaults."); + } else { + *model -= element; + } + return 0; + } catch (mjCError& e) { + model->SetError(e); + return -1; + } +} + + + // delete object, return 0 on success int mjs_delete(mjSpec* s, mjsElement* element) { mjCModel* model = static_cast(s->element); @@ -362,15 +370,11 @@ int mjs_delete(mjSpec* s, mjsElement* element) { return -1; } try { - if (element->elemtype == mjOBJ_BODY) { - mjCBody* body = static_cast(element); - *model -= *body; - model->DeleteElement(body); - } else if (element->elemtype == mjOBJ_DEFAULT) { + if (element->elemtype == mjOBJ_DEFAULT) { mjCDef* def = static_cast(element); *model -= *def; } else { - // it will call the appropriate destructor since ~mjCBase is virtual + mjs_detach(s, element); model->DeleteElement(element); } return 0; diff --git a/src/user/user_model.cc b/src/user/user_model.cc index cc22560e..b9bd7bc9 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -733,7 +733,6 @@ void deletefromlist(std::vector* list, mjsElement* element) { for (int j = 0; j < list->size(); ++j) { list->at(j)->id = -1; if (list->at(j) == element) { - list->at(j)->Release(); list->erase(list->begin() + j); j--; } @@ -743,20 +742,33 @@ void deletefromlist(std::vector* list, mjsElement* element) { // recursively delete all plugins in the subtree -static void deletesubtreeplugin(mjCBody* subtree, mjCModel* model) { +void mjCModel::DeleteSubtreePlugin(mjCBody* subtree) { mjsPlugin* plugin = &(subtree->spec.plugin); if (plugin->active && plugin->name->empty()) { - model->DeleteElement(plugin->element); + *this -= plugin->element; + detached_.push_back(static_cast(plugin->element)); } for (auto* body : subtree->Bodies()) { - deletesubtreeplugin(body, model); + DeleteSubtreePlugin(body); } } -// discard all invalid elements from all lists +// delete an object from the model void mjCModel::DeleteElement(mjsElement* el) { + static_cast(el)->Release(); +} + + + +// remove the element from the model +void mjCModel::operator-=(mjsElement* el) { + if (el->elemtype == mjOBJ_BODY) { + mjCBody* body = static_cast(el); + *this -= *body; + } + ResetTreeLists(); if (el->elemtype != mjOBJ_DEFAULT) { @@ -774,10 +786,7 @@ void mjCModel::DeleteElement(mjsElement* el) { { MakeTreeLists(); // rebuild lists that were reset at the beginning of the function mjCBody* subtree = static_cast(el); - if (subtree->GetRef() == 1) { - deletesubtreeplugin(subtree, this); - } - subtree->Release(); + DeleteSubtreePlugin(subtree); break; } @@ -789,8 +798,9 @@ void mjCModel::DeleteElement(mjsElement* el) { case mjOBJ_GEOM: { mjCGeom* geom = static_cast(el); - if (geom->plugin.active && geom->plugin.name->empty() && geom->GetRef() == 1) { - DeleteElement(geom->plugin.element); + if (geom->plugin.active && geom->plugin.name->empty()) { + *this -= geom->plugin.element; + detached_.push_back(static_cast(geom->plugin.element)); } deletefromlist(&(geom->body->geoms), el); break; @@ -815,8 +825,9 @@ void mjCModel::DeleteElement(mjsElement* el) { case mjOBJ_MESH: { mjCMesh* mesh = static_cast(el); - if (mesh->plugin.active && mesh->plugin.name->empty() && mesh->GetRef() == 1) { - DeleteElement(mesh->plugin.element); + if (mesh->plugin.active && mesh->plugin.name->empty()) { + *this -= mesh->plugin.element; + detached_.push_back(static_cast(mesh->plugin.element)); } deletefromlist(object_lists_[mjOBJ_MESH], el); break; @@ -825,8 +836,9 @@ void mjCModel::DeleteElement(mjsElement* el) { case mjOBJ_ACTUATOR: { mjCActuator* actuator = static_cast(el); - if (actuator->plugin.active && actuator->plugin.name->empty() && actuator->GetRef() == 1) { - DeleteElement(actuator->plugin.element); + if (actuator->plugin.active && actuator->plugin.name->empty()) { + *this -= actuator->plugin.element; + detached_.push_back(static_cast(actuator->plugin.element)); } deletefromlist(object_lists_[mjOBJ_ACTUATOR], el); break; @@ -835,8 +847,9 @@ void mjCModel::DeleteElement(mjsElement* el) { case mjOBJ_SENSOR: { mjCSensor* sensor = static_cast(el); - if (sensor->plugin.active && sensor->plugin.name->empty() && sensor->GetRef() == 1) { - DeleteElement(sensor->plugin.element); + if (sensor->plugin.active && sensor->plugin.name->empty()) { + *this -= sensor->plugin.element; + detached_.push_back(static_cast(sensor->plugin.element)); } deletefromlist(object_lists_[mjOBJ_SENSOR], el); break; @@ -946,6 +959,7 @@ mjCModel::~mjCModel() { for (int i=0; i < defaults_.size(); i++) delete defaults_[i]; for (int i=0; i < specs_.size(); i++) mj_deleteSpec(specs_[i]); for (int i=0; i < plugins_.size(); i++) plugins_[i]->Release(); + for (int i=0; i < detached_.size(); i++) detached_[i]->Release(); // clear sizes and pointer lists created in Compile Clear(); diff --git a/src/user/user_model.h b/src/user/user_model.h index e3f1d9c0..03f6c70e 100644 --- a/src/user/user_model.h +++ b/src/user/user_model.h @@ -227,6 +227,9 @@ class mjCModel : public mjCModel_, private mjSpec { template void DeleteAll(std::vector& elements); // delete object from the corresponding list + void operator-=(mjsElement* el); + + // delete object void DeleteElement(mjsElement* el); // delete default and all descendants @@ -450,11 +453,15 @@ class mjCModel : public mjCModel_, private mjSpec { template void ResolveReferences(std::vector& list, mjCBody* body = nullptr); + // delete all plugins created by the subtree + void DeleteSubtreePlugin(mjCBody* subtree); + mjListKeyMap ids; // map from object names to ids mjCError errInfo; // last error info std::vector key_pending_; // attached keyframes bool deepcopy_; // copy objects when attaching bool attached_ = false; // true if model is attached to a parent model std::unordered_map compiler2spec_; // map from compiler to spec + std::vector detached_; // list of detached objects }; #endif // MUJOCO_SRC_USER_USER_MODEL_H_