Add mjs_detachBody to C API.

PiperOrigin-RevId: 626947850
Change-Id: I7c897048098ecd4f2247725ef2d378dbd00ef4a0
This commit is contained in:
Alessio Quaglino
2024-04-22 01:10:13 -07:00
committed by Copybara-Service
parent 34a61ae42f
commit cc78db41f4
7 changed files with 193 additions and 13 deletions
+18
View File
@@ -110,6 +110,16 @@ const char* mjs_getError(mjSpec* s) {
// Detach body from mjSpec, return 0 if success.
int mjs_detachBody(mjSpec* s, const mjsBody* b) {
mjCModel* model = static_cast<mjCModel*>(s->element);
mjCBody* body = static_cast<mjCBody*>(b->element);
*model -= *body;
return 0;
}
// check if model has warnings // check if model has warnings
int mjs_isWarning(mjSpec* s) { int mjs_isWarning(mjSpec* s) {
mjCModel* modelC = static_cast<mjCModel*>(s->element); mjCModel* modelC = static_cast<mjCModel*>(s->element);
@@ -126,6 +136,14 @@ void mjs_deleteSpec(mjSpec* s) {
// delete body
void mjs_deleteBody(mjsBody* b) {
mjCBody* body = static_cast<mjCBody*>(b->element);
delete body;
}
// add child body to body, return child spec // add child body to body, return child spec
mjsBody* mjs_addBody(mjsBody* bodyspec, mjsDefault* defspec) { mjsBody* mjs_addBody(mjsBody* bodyspec, mjsDefault* defspec) {
mjCDef* def = defspec ? static_cast<mjCDef*>(defspec->element) : 0; mjCDef* def = defspec ? static_cast<mjCDef*>(defspec->element) : 0;
+7 -1
View File
@@ -762,10 +762,13 @@ MJAPI void mjs_deleteSpec(mjSpec* s);
MJAPI int mjs_attachBody(mjsFrame* parent, const mjsBody* child, MJAPI int mjs_attachBody(mjsFrame* parent, const mjsBody* child,
const char* prefix, const char* suffix); const char* prefix, const char* suffix);
// Attach child frame to a parent body, return 0 if success. // Attach child frame to a parent body, return 0 on success.
MJAPI int mjs_attachFrame(mjsBody* parent, const mjsFrame* child, MJAPI int mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
const char* prefix, const char* suffix); const char* prefix, const char* suffix);
// Detach body from mjSpec, remove all references, return 0 on success.
MJAPI int mjs_detachBody(mjSpec* s, const mjsBody* b);
//---------------------------------- Add tree elements --------------------------------------------- //---------------------------------- Add tree elements ---------------------------------------------
@@ -793,6 +796,9 @@ MJAPI mjsLight* mjs_addLight(mjsBody* body, mjsDefault* def);
// Add frame to body. // Add frame to body.
MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe); MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe);
// Delete body. TODO: make this a general mjs_deleteElement function
MJAPI void mjs_deleteBody(mjsBody* b);
//---------------------------------- Add non-tree elements ----------------------------------------- //---------------------------------- Add non-tree elements -----------------------------------------
+92 -7
View File
@@ -216,6 +216,16 @@ void mjCModel::CopyList(std::vector<T*>& dest,
template <class T>
static void resetlist(std::vector<T*>& list) {
for (auto element : list) {
element->id = -1;
}
list.clear();
}
mjCModel& mjCModel::operator+=(const mjCModel& other) { mjCModel& mjCModel::operator+=(const mjCModel& other) {
// create global lists // create global lists
MakeLists(bodies[0]); MakeLists(bodies[0]);
@@ -273,13 +283,85 @@ mjCModel& mjCModel::operator+=(const mjCModel& other) {
// restore to the same state as other // restore to the same state as other
if (!compiled) { if (!compiled) {
mjCBody* world = bodies[0]; mjCBody* world = bodies[0];
bodies.clear(); resetlist(bodies);
frames.clear(); resetlist(joints);
joints.clear(); resetlist(geoms);
geoms.clear(); resetlist(sites);
sites.clear(); resetlist(cameras);
cameras.clear(); resetlist(lights);
lights.clear(); resetlist(frames);
world->id = 0;
bodies.push_back(world);
}
PointToLocal();
return *this;
}
template <class T>
void mjCModel::RemoveFromList(std::vector<T*>& list, const mjCModel& other) {
int nlist = (int)list.size();
int removed = 0;
for (int i = 0; i < nlist; i++) {
T* element = list[i];
element->id -= removed;
try {
// check if the element contains an error
element->CopyFromSpec();
element->ResolveReferences(&other);
} catch (mjCError err) {
continue;
}
try {
// check if the element references something that was removed
element->ResolveReferences(this);
} catch (mjCError err) {
delete element;
list.erase(list.begin() + i);
nlist--;
i--;
removed++;
}
}
}
mjCModel& mjCModel::operator-=(const mjCBody& subtree) {
mjCModel oldmodel(*this);
oldmodel.MakeLists(oldmodel.bodies[0]);
oldmodel.CreateObjectLists();
oldmodel.ProcessLists();
// remove body from tree
*bodies[0] -= subtree;
// create global lists
MakeLists(bodies[0]);
CreateObjectLists();
ProcessLists();
// check if we have to remove anything else
RemoveFromList(pairs, oldmodel);
RemoveFromList(excludes, oldmodel);
RemoveFromList(tendons, oldmodel);
RemoveFromList(equalities, oldmodel);
RemoveFromList(actuators, oldmodel);
RemoveFromList(sensors, oldmodel);
// restore to the same state as before call
if (!compiled) {
mjCBody* world = bodies[0];
resetlist(bodies);
resetlist(joints);
resetlist(geoms);
resetlist(sites);
resetlist(cameras);
resetlist(lights);
resetlist(frames);
world->id = 0;
bodies.push_back(world); bodies.push_back(world);
} }
@@ -722,6 +804,9 @@ static T* findobject(std::string_view name, const vector<T*>& list, const mjKeyM
if (id == ids.end()) { if (id == ids.end()) {
return nullptr; return nullptr;
} }
if (id->second > (int)list.size() - 1) {
throw mjCError(0, "object not found");
}
return list[id->second]; return list[id->second];
} }
+7 -2
View File
@@ -158,12 +158,14 @@ class mjCModel : public mjCModel_, private mjSpec {
public: public:
mjCModel(); mjCModel();
mjCModel(const mjCModel& other); mjCModel(const mjCModel& other);
mjCModel& operator=(const mjCModel& other);
mjCModel& operator+=(const mjCModel& other);
~mjCModel(); ~mjCModel();
void CopyFromSpec(); // copy spec to private attributes void CopyFromSpec(); // copy spec to private attributes
void PointToLocal(); void PointToLocal();
mjCModel& operator=(const mjCModel& other); // copy other into this, if they are not the same
mjCModel& operator+=(const mjCModel& other); // add other into this, even if they are the same
mjCModel& operator-=(const mjCBody& subtree); // remove subtree and all references from model
mjSpec spec; mjSpec spec;
mjModel* Compile(const mjVFS* vfs = nullptr); // construct mjModel mjModel* Compile(const mjVFS* vfs = nullptr); // construct mjModel
@@ -196,6 +198,9 @@ class mjCModel : public mjCModel_, private mjSpec {
std::map<mjCDef*, int>& def_map, std::map<mjCDef*, int>& def_map,
const std::vector<mjCDef*>& defaults); const std::vector<mjCDef*>& defaults);
// delete from list the elements that are compatible with other but not this model
template <class T> void RemoveFromList(std::vector<T*>& list, const mjCModel& other);
// delete elements marked as discard=true // delete elements marked as discard=true
template <class T> void Delete(std::vector<T*>& elements, template <class T> void Delete(std::vector<T*>& elements,
const std::vector<bool>& discard); const std::vector<bool>& discard);
+15
View File
@@ -689,6 +689,7 @@ mjCBody& mjCBody::operator=(const mjCBody& other) {
sites.clear(); sites.clear();
cameras.clear(); cameras.clear();
lights.clear(); lights.clear();
id = other.id;
// add elements to lists // add elements to lists
*this += other; *this += other;
@@ -807,6 +808,20 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
// find and remove subtree
mjCBody& mjCBody::operator-=(const mjCBody& subtree) {
for (int i=0; i<bodies.size(); i++) {
if (bodies[i] == &subtree) {
bodies.erase(bodies.begin() + i);
break;
}
*bodies[i] -= subtree;
}
return *this;
}
void mjCBody::PointToLocal() { void mjCBody::PointToLocal() {
spec.element = static_cast<mjElement*>(this); spec.element = static_cast<mjElement*>(this);
spec.name = (mjString)&name; spec.name = (mjString)&name;
+5 -3
View File
@@ -263,6 +263,9 @@ class mjCBody : public mjCBody_, private mjsBody {
friend class mjXURDF; friend class mjXURDF;
public: public:
mjCBody(mjCModel*); // constructor
~mjCBody(); // destructor
// API for adding objects to body // API for adding objects to body
mjCBody* AddBody(mjCDef* = 0); mjCBody* AddBody(mjCDef* = 0);
mjCFrame* AddFrame(mjCFrame* = 0); mjCFrame* AddFrame(mjCFrame* = 0);
@@ -273,9 +276,10 @@ class mjCBody : public mjCBody_, private mjsBody {
mjCCamera* AddCamera(mjCDef* = 0); mjCCamera* AddCamera(mjCDef* = 0);
mjCLight* AddLight(mjCDef* = 0); mjCLight* AddLight(mjCDef* = 0);
// API for adding existing objects to body // API for adding/removing objects to body
mjCBody& operator+=(const mjCBody& other); mjCBody& operator+=(const mjCBody& other);
mjCBody& operator+=(const mjCFrame& other); mjCBody& operator+=(const mjCFrame& other);
mjCBody& operator-=(const mjCBody& subtree);
// API for accessing objects // API for accessing objects
int NumObjects(mjtObj type); int NumObjects(mjtObj type);
@@ -304,10 +308,8 @@ class mjCBody : public mjCBody_, private mjsBody {
const std::vector<double>& get_userdata() { return userdata_; } const std::vector<double>& get_userdata() { return userdata_; }
private: private:
mjCBody(mjCModel*); // constructor
mjCBody(const mjCBody& other, mjCModel* _model); // copy constructor mjCBody(const mjCBody& other, mjCModel* _model); // copy constructor
mjCBody& operator=(const mjCBody& other); // copy assignment mjCBody& operator=(const mjCBody& other); // copy assignment
~mjCBody(); // destructor
void Compile(void); // compiler void Compile(void); // compiler
void GeomFrame(void); // get inertial info from geoms void GeomFrame(void); // get inertial info from geoms
+49
View File
@@ -512,5 +512,54 @@ TEST_F(MujocoTest, AttachFrame) {
mj_deleteModel(m_expected); mj_deleteModel(m_expected);
} }
TEST_F(MujocoTest, DetachBody) {
std::array<char, 1000> er;
mjtNum tol = 0;
std::string field = "";
static constexpr char xml_result[] = R"(
<mujoco>
<worldbody>
<frame name="pframe">
<frame name="cframe">
</frame>
</frame>
<body name="ignore"/>
<frame name="frame" pos=".1 0 0" euler="0 90 0"/>
</worldbody>
<sensor>
<framepos name="ignore" objtype="body" objname="ignore"/>
</sensor>
</mujoco>)";
// model with one cylinder and a hinge
mjSpec* child = ParseSpecFromString(xml_child, er.data(), er.size());
EXPECT_THAT(child, NotNull()) << er.data();
// get subtree
mjsBody* body = mjs_findBody(child, "body");
EXPECT_THAT(body, NotNull());
// detach subtree
EXPECT_THAT(mjs_detachBody(child, body), 0);
// compile new model
mjModel* m_detached = mjs_compile(child, 0);
EXPECT_THAT(m_detached, NotNull());
// compare with expected XML
mjModel* m_expected = LoadModelFromString(xml_result, er.data(), er.size());
EXPECT_THAT(m_expected, NotNull()) << er.data();
EXPECT_LE(CompareModel(m_detached, m_expected, field), tol)
<< "Expected and attached models are different!\n"
<< "Different field: " << field << '\n';
// destroy everything
mjs_deleteSpec(child);
mjs_deleteBody(body);
mj_deleteModel(m_detached);
mj_deleteModel(m_expected);
}
} // namespace } // namespace
} // namespace mujoco } // namespace mujoco