Cosmetic improvements to src/xml/ source files.

PiperOrigin-RevId: 738010419
Change-Id: Idb90a42a374682450737684e67388329c184c777
This commit is contained in:
Yuval Tassa
2025-03-18 08:11:40 -07:00
committed by Copybara-Service
parent 205599ac1a
commit efe213a7b3
5 changed files with 743 additions and 733 deletions
+6 -4
View File
@@ -98,9 +98,11 @@ mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
char* error, int error_sz) { char* error, int error_sz) {
// parse new model // parse new model
std::unique_ptr<mjSpec, std::function<void(mjSpec*)>> spec( std::unique_ptr<mjSpec, std::function<void(mjSpec*)> > spec(
ParseXML(filename, vfs, error, error_sz), ParseXML(filename, vfs, error, error_sz),
[](mjSpec* s) { mj_deleteSpec(s); }); [](mjSpec* s) {
mj_deleteSpec(s);
});
if (!spec) { if (!spec) {
return nullptr; return nullptr;
} }
@@ -166,7 +168,7 @@ int mj_printSchema(const char* filename, char* buffer, int buffer_sz, int flg_ht
// print to stringstream // print to stringstream
mjXReader reader; mjXReader reader;
std::stringstream str; std::stringstream str;
reader.PrintSchema(str, flg_html!=0, flg_pad!=0); reader.PrintSchema(str, flg_html != 0, flg_pad != 0);
// filename given: write to file // filename given: write to file
if (filename) { if (filename) {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+74 -74
View File
@@ -125,7 +125,7 @@ void mjXURDF::Parse(
while (elem) { while (elem) {
// identify link elements // identify link elements
name = elem->Value(); name = elem->Value();
if (name=="link") { if (name == "link") {
ReadAttrTxt(elem, "name", text, true); ReadAttrTxt(elem, "name", text, true);
text = GetPrefixedName(text); text = GetPrefixedName(text);
AddBody(text); AddBody(text);
@@ -140,7 +140,7 @@ void mjXURDF::Parse(
while (elem) { while (elem) {
// identify joint elements // identify joint elements
name = elem->Value(); name = elem->Value();
if (name=="joint") { if (name == "joint") {
// find parent, get name and id // find parent, get name and id
temp = FindSubElem(elem, "parent", true); temp = FindSubElem(elem, "parent", true);
ReadAttrTxt(temp, "link", text, true); ReadAttrTxt(temp, "link", text, true);
@@ -154,12 +154,12 @@ void mjXURDF::Parse(
id_child = FindName(text, urName); id_child = FindName(text, urName);
// make sure parent and child exist // make sure parent and child exist
if (id_parent<0 || id_child<0) { if (id_parent < 0 || id_child < 0) {
throw mjXError(elem, "URDF joint parent or child missing"); throw mjXError(elem, "URDF joint parent or child missing");
} }
// check for multiple parents // check for multiple parents
if (urParent[id_child]>=0) { if (urParent[id_child] >= 0) {
throw mjXError(elem, "URDF body has multiple parents:", urName[id_child].c_str()); throw mjXError(elem, "URDF body has multiple parents:", urName[id_child].c_str());
} }
@@ -173,7 +173,7 @@ void mjXURDF::Parse(
} }
// find all top-level bodies, call recursive tree constructor // find all top-level bodies, call recursive tree constructor
for (int i=0; i<(int)urName.size(); i++) { for (int i=0; i < (int)urName.size(); i++) {
if (urParent[i] < 0) { if (urParent[i] < 0) {
AddToTree(i); AddToTree(i);
} }
@@ -184,7 +184,7 @@ void mjXURDF::Parse(
while (elem) { while (elem) {
// identify body/link elements // identify body/link elements
name = elem->Value(); name = elem->Value();
if (name=="link") { if (name == "link") {
Body(elem); Body(elem);
} }
@@ -197,7 +197,7 @@ void mjXURDF::Parse(
while (elem) { while (elem) {
// identify body/link elements // identify body/link elements
name = elem->Value(); name = elem->Value();
if (name=="joint") { if (name == "joint") {
Joint(elem); Joint(elem);
} }
@@ -297,7 +297,7 @@ void mjXURDF::Body(XMLElement* body_elem) {
name = elem->Value(); name = elem->Value();
// visual element // visual element
if (name=="visual") { if (name == "visual") {
// parse material // parse material
if ((temp = FindSubElem(elem, "material"))) { if ((temp = FindSubElem(elem, "material"))) {
// if color specified - use directly // if color specified - use directly
@@ -310,7 +310,7 @@ void mjXURDF::Body(XMLElement* body_elem) {
ReadAttrTxt(temp, "name", name, true); ReadAttrTxt(temp, "name", name, true);
name = GetPrefixedName(name); name = GetPrefixedName(name);
int imat = FindName(name, urMat); int imat = FindName(name, urMat);
if (imat>=0) { if (imat >= 0) {
std::memcpy(rgba, urRGBA[imat].val, 4*sizeof(float)); std::memcpy(rgba, urRGBA[imat].val, 4*sizeof(float));
} }
} }
@@ -320,7 +320,7 @@ void mjXURDF::Body(XMLElement* body_elem) {
pgeom = Geom(elem, pbody, false); pgeom = Geom(elem, pbody, false);
// save color // save color
if (rgba[0]>=0) { if (rgba[0] >= 0) {
std::memcpy(pgeom->rgba, rgba, 4*sizeof(float)); std::memcpy(pgeom->rgba, rgba, 4*sizeof(float));
} }
@@ -339,11 +339,11 @@ void mjXURDF::Body(XMLElement* body_elem) {
} }
// collision element // collision element
else if (name=="collision") { else if (name == "collision") {
pgeom = Geom(elem, pbody, true); pgeom = Geom(elem, pbody, true);
// use color from last visual // use color from last visual
if (rgba[0]>=0) { if (rgba[0] >= 0) {
std::memcpy(pgeom->rgba, rgba, 4*sizeof(float)); std::memcpy(pgeom->rgba, rgba, 4*sizeof(float));
} }
@@ -418,71 +418,71 @@ void mjXURDF::Joint(XMLElement* joint_elem) {
// create joint (unless fixed) // create joint (unless fixed)
double mat[9], quat[4], tmpaxis[3]; double mat[9], quat[4], tmpaxis[3];
switch (jointtype) { switch (jointtype) {
case 0: // revolute case 0: // revolute
case 1: // continuous case 1: // continuous
pjoint = mjs_addJoint(pbody, 0); pjoint = mjs_addJoint(pbody, 0);
mjs_setString(pjoint->name, jntname.c_str()); mjs_setString(pjoint->name, jntname.c_str());
pjoint->type = mjJNT_HINGE; pjoint->type = mjJNT_HINGE;
mjuu_setvec(pjoint->pos, 0, 0, 0); mjuu_setvec(pjoint->pos, 0, 0, 0);
mjuu_copyvec(pjoint->axis, axis, 3); mjuu_copyvec(pjoint->axis, axis, 3);
break; break;
case 2: // prismatic case 2: // prismatic
pjoint = mjs_addJoint(pbody, 0); pjoint = mjs_addJoint(pbody, 0);
mjs_setString(pjoint->name, jntname.c_str()); mjs_setString(pjoint->name, jntname.c_str());
pjoint->type = mjJNT_SLIDE; pjoint->type = mjJNT_SLIDE;
mjuu_setvec(pjoint->pos, 0, 0, 0); mjuu_setvec(pjoint->pos, 0, 0, 0);
mjuu_copyvec(pjoint->axis, axis, 3); mjuu_copyvec(pjoint->axis, axis, 3);
break; break;
case 3: // fixed- no joint, return case 3: // fixed- no joint, return
return; return;
case 4: // floating case 4: // floating
pjoint = mjs_addJoint(pbody, 0); pjoint = mjs_addJoint(pbody, 0);
mjs_setString(pjoint->name, jntname.c_str()); mjs_setString(pjoint->name, jntname.c_str());
pjoint->type = mjJNT_FREE; pjoint->type = mjJNT_FREE;
break; break;
case 5: // planar- construct complex joint case 5: // planar- construct complex joint
// make frame with axis = z // make frame with axis = z
mjuu_z2quat(quat, axis); mjuu_z2quat(quat, axis);
mjuu_quat2mat(mat, quat); mjuu_quat2mat(mat, quat);
// construct slider along x // construct slider along x
pjoint = mjs_addJoint(pbody, 0); pjoint = mjs_addJoint(pbody, 0);
mjs_setString(pjoint->name, (jntname + "_TX").c_str()); mjs_setString(pjoint->name, (jntname + "_TX").c_str());
pjoint->type = mjJNT_SLIDE; pjoint->type = mjJNT_SLIDE;
tmpaxis[0] = mat[0]; tmpaxis[0] = mat[0];
tmpaxis[1] = mat[3]; tmpaxis[1] = mat[3];
tmpaxis[2] = mat[6]; tmpaxis[2] = mat[6];
mjuu_setvec(pjoint->pos, 0, 0, 0); mjuu_setvec(pjoint->pos, 0, 0, 0);
mjuu_copyvec(pjoint->axis, tmpaxis, 3); mjuu_copyvec(pjoint->axis, tmpaxis, 3);
// construct slider along y // construct slider along y
pjoint1 = mjs_addJoint(pbody, 0); pjoint1 = mjs_addJoint(pbody, 0);
mjs_setString(pjoint1->name, (jntname + "_TY").c_str()); mjs_setString(pjoint1->name, (jntname + "_TY").c_str());
pjoint1->type = mjJNT_SLIDE; pjoint1->type = mjJNT_SLIDE;
tmpaxis[0] = mat[1]; tmpaxis[0] = mat[1];
tmpaxis[1] = mat[4]; tmpaxis[1] = mat[4];
tmpaxis[2] = mat[7]; tmpaxis[2] = mat[7];
mjuu_setvec(pjoint1->pos, 0, 0, 0); mjuu_setvec(pjoint1->pos, 0, 0, 0);
mjuu_copyvec(pjoint1->axis, tmpaxis, 3); mjuu_copyvec(pjoint1->axis, tmpaxis, 3);
// construct hinge around z = locaxis // construct hinge around z = locaxis
pjoint2 = mjs_addJoint(pbody, 0); pjoint2 = mjs_addJoint(pbody, 0);
mjs_setString(pjoint2->name, (jntname + "_RZ").c_str()); mjs_setString(pjoint2->name, (jntname + "_RZ").c_str());
pjoint2->type = mjJNT_HINGE; pjoint2->type = mjJNT_HINGE;
mjuu_setvec(pjoint2->pos, 0, 0, 0); mjuu_setvec(pjoint2->pos, 0, 0, 0);
mjuu_copyvec(pjoint2->axis, axis, 3); mjuu_copyvec(pjoint2->axis, axis, 3);
break; break;
case 6: // ball joint case 6: // ball joint
pjoint = mjs_addJoint(pbody, 0); pjoint = mjs_addJoint(pbody, 0);
mjs_setString(pjoint->name, jntname.c_str()); mjs_setString(pjoint->name, jntname.c_str());
pjoint->type = mjJNT_BALL; pjoint->type = mjJNT_BALL;
mjuu_setvec(pjoint->pos, 0, 0, 0); mjuu_setvec(pjoint->pos, 0, 0, 0);
mjuu_copyvec(pjoint->axis, axis, 3); mjuu_copyvec(pjoint->axis, axis, 3);
} }
// dynamics element // dynamics element
@@ -546,7 +546,7 @@ mjsGeom* mjXURDF::Geom(XMLElement* geom_elem, mjsBody* pbody, bool collision) {
// box // box
if ((temp = FindSubElem(elem, "box"))) { if ((temp = FindSubElem(elem, "box"))) {
ReadAttr(temp, "size", 3, pgeom->size, text, true, true); ReadAttr(temp, "size", 3, pgeom->size, text, true, true);
for (int i=0; i<3; i++) { for (int i=0; i < 3; i++) {
pgeom->size[i] /= 2; // MuJoCo uses half-length pgeom->size[i] /= 2; // MuJoCo uses half-length
} }
} }
@@ -673,7 +673,7 @@ void mjXURDF::Origin(XMLElement* origin_elem, double* pos, double* quat) {
// find body with given name in list, return -1 if not found // find body with given name in list, return -1 if not found
int mjXURDF::FindName(std::string name, std::vector<std::string>& list) { int mjXURDF::FindName(std::string name, std::vector<std::string>& list) {
for (unsigned int i=0; i<list.size(); i++) for (unsigned int i=0; i < list.size(); i++)
if (list[i] == name) { if (list[i] == name) {
return i; return i;
} }
@@ -686,7 +686,7 @@ int mjXURDF::FindName(std::string name, std::vector<std::string>& list) {
// add name to list, error if name already exists // add name to list, error if name already exists
void mjXURDF::AddName(std::string name, std::vector<std::string>& list) { void mjXURDF::AddName(std::string name, std::vector<std::string>& list) {
// make sure name is unique // make sure name is unique
if (FindName(name, list)>=0) { if (FindName(name, list) >= 0) {
throw mjXError(0, "repeated URDF name: ", name.c_str()); throw mjXError(0, "repeated URDF name: ", name.c_str());
} }
@@ -713,7 +713,7 @@ void mjXURDF::AddBody(std::string name) {
void mjXURDF::AddToTree(int n) { void mjXURDF::AddToTree(int n) {
// get pointer to parent in mjCModel tree // get pointer to parent in mjCModel tree
mjsBody *parent = 0, *child = 0, *world = 0; mjsBody *parent = 0, *child = 0, *world = 0;
if (urParent[n]>=0) { if (urParent[n] >= 0) {
world = mjs_findBody(spec, "world"); world = mjs_findBody(spec, "world");
parent = mjs_findChild(world, urName[urParent[n]].c_str()); parent = mjs_findChild(world, urName[urParent[n]].c_str());
@@ -731,7 +731,7 @@ void mjXURDF::AddToTree(int n) {
} }
// add children recursively // add children recursively
for (int i=0; i<(int)urChildren[n].size(); i++) { for (int i=0; i < (int)urChildren[n].size(); i++) {
AddToTree(urChildren[n][i]); AddToTree(urChildren[n][i]);
} }
} }
+41 -41
View File
@@ -112,7 +112,7 @@ FilePath ResolveFilePath(XMLElement* e, const FilePath& filename,
// error string copy // error string copy
void mjCopyError(char* dst, const char* src, int maxlen) { void mjCopyError(char* dst, const char* src, int maxlen) {
if (dst && maxlen>0) { if (dst && maxlen > 0) {
strncpy(dst, src, maxlen); strncpy(dst, src, maxlen);
dst[maxlen-1] = 0; dst[maxlen-1] = 0;
} }
@@ -243,7 +243,7 @@ std::string mjXSchema::GetError() {
// print spaces // print spaces
static void printspace(std::stringstream& str, int n, const char* space) { static void printspace(std::stringstream& str, int n, const char* space) {
for (int i=0; i<n; i++) { for (int i=0; i < n; i++) {
str << space; str << space;
} }
} }
@@ -259,14 +259,14 @@ void mjXSchema::Print(std::stringstream& str, int level) const {
printspace(str, 3*level, " "); printspace(str, 3*level, " ");
str << name1 << " (" << type_ << ")"; str << name1 << " (" << type_ << ")";
int baselen = 3*level + (int)name1.size() + 4; int baselen = 3*level + (int)name1.size() + 4;
if (baselen<30) { if (baselen < 30) {
printspace(str, 30-baselen, " "); printspace(str, 30-baselen, " ");
} }
// attributes // attributes
int cnt = std::max(baselen, 30); int cnt = std::max(baselen, 30);
for (const std::string& attr : attr_) { for (const std::string& attr : attr_) {
if (cnt>60) { if (cnt > 60) {
str << "\n"; str << "\n";
printspace(str, (cnt = std::max(30, baselen)), " "); printspace(str, (cnt = std::max(30, baselen)), " ");
} }
@@ -290,7 +290,7 @@ void mjXSchema::PrintHTML(std::stringstream& str, int level, bool pad) const {
std::string name1 = (name_ == "body" ? "(world)body" : name_); std::string name1 = (name_ == "body" ? "(world)body" : name_);
// open table // open table
if (level==0) { if (level == 0) {
str << "<table border=\"1\">\n"; str << "<table border=\"1\">\n";
} }
@@ -341,9 +341,9 @@ bool mjXSchema::NameMatch(XMLElement* elem, int level) {
// special handling of body, worldbody, and frame // special handling of body, worldbody, and frame
if (name_ == "body" && if (name_ == "body" &&
((level == 1 && !strcmp(elem->Value(), "worldbody")) || ((level == 1 && !strcmp(elem->Value(), "worldbody")) ||
(level != 1 && !strcmp(elem->Value(), "body")) || (level != 1 && !strcmp(elem->Value(), "body")) ||
(level >= 1 && !strcmp(elem->Value(), "frame")) || (level >= 1 && !strcmp(elem->Value(), "frame")) ||
(level >= 1 && !strcmp(elem->Value(), "replicate")))) { (level >= 1 && !strcmp(elem->Value(), "replicate")))) {
return true; return true;
} }
@@ -426,23 +426,23 @@ XMLElement* mjXSchema::Check(XMLElement* elem, int level) {
msg[0] = '\0'; msg[0] = '\0';
for (mjXSchema& subschema : subschema_) { for (mjXSchema& subschema : subschema_) {
switch (subschema.type_) { switch (subschema.type_) {
case '!': case '!':
if (subschema.refcnt_ > 1) if (subschema.refcnt_ > 1)
mju::sprintf_arr(msg, "unique element '%s' found %d times", mju::sprintf_arr(msg, "unique element '%s' found %d times",
subschema.name_.c_str(), subschema.refcnt_); subschema.name_.c_str(), subschema.refcnt_);
else if (subschema.refcnt_ < 1) else if (subschema.refcnt_ < 1)
mju::sprintf_arr(msg, "element '%s' is required", mju::sprintf_arr(msg, "element '%s' is required",
subschema.name_.c_str()); subschema.name_.c_str());
break; break;
case '?': case '?':
if (subschema.refcnt_ > 1) if (subschema.refcnt_ > 1)
mju::sprintf_arr(msg, "unique element '%s' found %d times", mju::sprintf_arr(msg, "unique element '%s' found %d times",
subschema.name_.c_str(), subschema.refcnt_); subschema.name_.c_str(), subschema.refcnt_);
break; break;
default: default:
break; break;
} }
} }
@@ -539,7 +539,7 @@ template bool mjXUtil::SameVector(const unsigned char* vec1, const unsigned char
// find string in map, return corresponding integer (-1: not found) // find string in map, return corresponding integer (-1: not found)
int mjXUtil::FindKey(const mjMap* map, int mapsz, std::string key) { int mjXUtil::FindKey(const mjMap* map, int mapsz, std::string key) {
for (int i=0; i<mapsz; i++) { for (int i=0; i < mapsz; i++) {
if (map[i].key == key) { if (map[i].key == key) {
return map[i].value; return map[i].value;
} }
@@ -552,7 +552,7 @@ int mjXUtil::FindKey(const mjMap* map, int mapsz, std::string key) {
// find integer in map, return corresponding string ("": not found) // find integer in map, return corresponding string ("": not found)
std::string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) { std::string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) {
for (int i=0; i<mapsz; i++) { for (int i=0; i < mapsz; i++) {
if (map[i].value == value) { if (map[i].value == value) {
return map[i].key; return map[i].key;
} }
@@ -565,8 +565,8 @@ std::string mjXUtil::FindValue(const mjMap* map, int mapsz, int value) {
// if attribute is present, return vector of numerical data // if attribute is present, return vector of numerical data
template<typename T> template<typename T>
std::optional<std::vector<T>> mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, std::optional<std::vector<T> > mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr,
bool required) { bool required) {
std::vector<T> v; std::vector<T> v;
const char* raw_cstr = elem->Attribute(attr); const char* raw_cstr = elem->Attribute(attr);
if (raw_cstr) { if (raw_cstr) {
@@ -593,13 +593,13 @@ std::optional<std::vector<T>> mjXUtil::ReadAttrVec(XMLElement* elem, const char*
return v; return v;
} }
template std::optional<std::vector<double>> template std::optional<std::vector<double> >
mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required); mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required);
template std::optional<std::vector<float>> template std::optional<std::vector<float> >
mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required); mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required);
template std::optional<std::vector<int>> template std::optional<std::vector<int> >
mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required); mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required);
template std::optional<std::vector<unsigned char>> template std::optional<std::vector<unsigned char> >
mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required); mjXUtil::ReadAttrVec(XMLElement* elem, const char* attr, bool required);
@@ -751,10 +751,10 @@ bool mjXUtil::ReadAttrInt(XMLElement* elem, const char* attr, int* data, bool re
void mjXUtil::Vector2String(std::string& txt, const std::vector<float>& vec, int ncol) { void mjXUtil::Vector2String(std::string& txt, const std::vector<float>& vec, int ncol) {
std::stringstream strm; std::stringstream strm;
for (size_t i=0; i<vec.size(); i++) { for (size_t i=0; i < vec.size(); i++) {
if (ncol && (i % ncol) == 0) { if (ncol && (i % ncol) == 0) {
strm << "\n "; strm << "\n ";
} else if (i>0) { } else if (i > 0) {
strm << " "; strm << " ";
} }
strm << vec[i]; strm << vec[i];
@@ -839,7 +839,7 @@ void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n, const T* data
bool trim) { bool trim) {
// make sure all are defined // make sure all are defined
if constexpr (std::is_floating_point_v<T>) { if constexpr (std::is_floating_point_v<T>) {
for (int i=0; i<n; i++) { for (int i=0; i < n; i++) {
if (std::isnan(data[i])) { if (std::isnan(data[i])) {
return; return;
} }
@@ -863,9 +863,9 @@ void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n, const T* data
stream.precision(mujoco::_mjPRIVATE__get_xml_precision()); stream.precision(mujoco::_mjPRIVATE__get_xml_precision());
// process all numbers // process all numbers
for (int i=0; i<n; i++) { for (int i=0; i < n; i++) {
// add space between numbers // add space between numbers
if (i>0) { if (i > 0) {
stream << " "; stream << " ";
} }
@@ -901,7 +901,7 @@ template void mjXUtil::WriteAttr(XMLElement* elem, std::string name, int n,
void mjXUtil::WriteVector(XMLElement* elem, std::string name, const std::vector<double>& vec) { void mjXUtil::WriteVector(XMLElement* elem, std::string name, const std::vector<double>& vec) {
// proceed only if non-zero found // proceed only if non-zero found
bool ok = false; bool ok = false;
for (size_t i=0; i<vec.size(); i++) { for (size_t i=0; i < vec.size(); i++) {
if (vec[i]) { if (vec[i]) {
ok = true; ok = true;
break; break;
@@ -921,8 +921,8 @@ void mjXUtil::WriteVector(XMLElement* elem, std::string name, const std::vector<
const std::vector<double>& def) { const std::vector<double>& def) {
// proceed only if non-zero found // proceed only if non-zero found
bool ok = false; bool ok = false;
for (size_t i=0; i<vec.size(); i++) { for (size_t i=0; i < vec.size(); i++) {
if (vec[i]!=def[i]) { if (vec[i] != def[i]) {
ok = true; ok = true;
break; break;
} }
@@ -952,7 +952,7 @@ void mjXUtil::WriteAttrTxt(XMLElement* elem, std::string name, std::string value
// write attribute- single int // write attribute- single int
void mjXUtil::WriteAttrInt(XMLElement* elem, std::string name, int data, int def) { void mjXUtil::WriteAttrInt(XMLElement* elem, std::string name, int data, int def) {
// skip default // skip default
if (data==def) { if (data == def) {
return; return;
} }
@@ -965,7 +965,7 @@ void mjXUtil::WriteAttrInt(XMLElement* elem, std::string name, int data, int def
void mjXUtil::WriteAttrKey(XMLElement* elem, std::string name, void mjXUtil::WriteAttrKey(XMLElement* elem, std::string name,
const mjMap* map, int mapsz, int data, int def) { const mjMap* map, int mapsz, int data, int def) {
// skip default // skip default
if (data==def) { if (data == def) {
return; return;
} }