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
int mjs_isWarning(mjSpec* s) {
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
mjsBody* mjs_addBody(mjsBody* bodyspec, mjsDefault* defspec) {
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,
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,
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 ---------------------------------------------
@@ -793,6 +796,9 @@ MJAPI mjsLight* mjs_addLight(mjsBody* body, mjsDefault* def);
// Add frame to body.
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 -----------------------------------------
+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) {
// create global lists
MakeLists(bodies[0]);
@@ -273,13 +283,85 @@ mjCModel& mjCModel::operator+=(const mjCModel& other) {
// restore to the same state as other
if (!compiled) {
mjCBody* world = bodies[0];
bodies.clear();
frames.clear();
joints.clear();
geoms.clear();
sites.clear();
cameras.clear();
lights.clear();
resetlist(bodies);
resetlist(joints);
resetlist(geoms);
resetlist(sites);
resetlist(cameras);
resetlist(lights);
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);
}
@@ -722,6 +804,9 @@ static T* findobject(std::string_view name, const vector<T*>& list, const mjKeyM
if (id == ids.end()) {
return nullptr;
}
if (id->second > (int)list.size() - 1) {
throw mjCError(0, "object not found");
}
return list[id->second];
}
+7 -2
View File
@@ -158,12 +158,14 @@ class mjCModel : public mjCModel_, private mjSpec {
public:
mjCModel();
mjCModel(const mjCModel& other);
mjCModel& operator=(const mjCModel& other);
mjCModel& operator+=(const mjCModel& other);
~mjCModel();
void CopyFromSpec(); // copy spec to private attributes
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;
mjModel* Compile(const mjVFS* vfs = nullptr); // construct mjModel
@@ -196,6 +198,9 @@ class mjCModel : public mjCModel_, private mjSpec {
std::map<mjCDef*, int>& def_map,
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
template <class T> void Delete(std::vector<T*>& elements,
const std::vector<bool>& discard);
+15
View File
@@ -689,6 +689,7 @@ mjCBody& mjCBody::operator=(const mjCBody& other) {
sites.clear();
cameras.clear();
lights.clear();
id = other.id;
// add elements to lists
*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() {
spec.element = static_cast<mjElement*>(this);
spec.name = (mjString)&name;
+5 -3
View File
@@ -263,6 +263,9 @@ class mjCBody : public mjCBody_, private mjsBody {
friend class mjXURDF;
public:
mjCBody(mjCModel*); // constructor
~mjCBody(); // destructor
// API for adding objects to body
mjCBody* AddBody(mjCDef* = 0);
mjCFrame* AddFrame(mjCFrame* = 0);
@@ -273,9 +276,10 @@ class mjCBody : public mjCBody_, private mjsBody {
mjCCamera* AddCamera(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 mjCFrame& other);
mjCBody& operator-=(const mjCBody& subtree);
// API for accessing objects
int NumObjects(mjtObj type);
@@ -304,10 +308,8 @@ class mjCBody : public mjCBody_, private mjsBody {
const std::vector<double>& get_userdata() { return userdata_; }
private:
mjCBody(mjCModel*); // constructor
mjCBody(const mjCBody& other, mjCModel* _model); // copy constructor
mjCBody& operator=(const mjCBody& other); // copy assignment
~mjCBody(); // destructor
void Compile(void); // compiler
void GeomFrame(void); // get inertial info from geoms
+49
View File
@@ -512,5 +512,54 @@ TEST_F(MujocoTest, AttachFrame) {
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 mujoco