Trim trailing default values in saved xmls.

For attributes which accept variable-length inputs. Currently used for `gainprm, biasprm, friction, solref, solimp` Example:

- Saved before: `biasprm="0 -3 -4 0 0 0 0 0 0 0"`.
- Saved after: `biasprm="0 -3 -4"`.

PiperOrigin-RevId: 607609550
Change-Id: I0501348280663867693175236872e2aabc703601
This commit is contained in:
Yuval Tassa
2024-02-16 01:35:13 -08:00
committed by Copybara-Service
parent 6a346c42e8
commit f9f7827a64
4 changed files with 65 additions and 26 deletions
+24 -19
View File
@@ -296,10 +296,12 @@ void mjXWriter::OneJoint(XMLElement* elem, mjCJoint* pjoint, mjCDef* def) {
WriteAttrInt(elem, "group", pjoint->group, def->joint.group);
WriteAttr(elem, "ref", 1, &pjoint->ref, &zero);
WriteAttr(elem, "springref", 1, &pjoint->springref, &zero);
WriteAttr(elem, "solreflimit", mjNREF, pjoint->solref_limit, def->joint.solref_limit);
WriteAttr(elem, "solimplimit", mjNIMP, pjoint->solimp_limit, def->joint.solimp_limit);
WriteAttr(elem, "solreffriction", mjNREF, pjoint->solref_friction, def->joint.solref_friction);
WriteAttr(elem, "solimpfriction", mjNIMP, pjoint->solimp_friction, def->joint.solimp_friction);
WriteAttr(elem, "solreflimit", mjNREF, pjoint->solref_limit, def->joint.solref_limit, true);
WriteAttr(elem, "solimplimit", mjNIMP, pjoint->solimp_limit, def->joint.solimp_limit, true);
WriteAttr(elem, "solreffriction", mjNREF, pjoint->solref_friction, def->joint.solref_friction,
true);
WriteAttr(elem, "solimpfriction", mjNIMP, pjoint->solimp_friction, def->joint.solimp_friction,
true);
WriteAttr(elem, "stiffness", 1, &pjoint->stiffness, &def->joint.stiffness);
WriteAttrKey(elem, "limited", TFAuto_map, 3, pjoint->limited, def->joint.limited);
WriteAttr(elem, "range", 2, pjoint->range, def->joint.range);
@@ -373,10 +375,10 @@ void mjXWriter::OneGeom(XMLElement* elem, mjCGeom* pgeom, mjCDef* def) {
WriteAttrInt(elem, "condim", pgeom->condim, def->geom.condim);
WriteAttrInt(elem, "group", pgeom->group, def->geom.group);
WriteAttrInt(elem, "priority", pgeom->priority, def->geom.priority);
WriteAttr(elem, "friction", 3, pgeom->friction, def->geom.friction);
WriteAttr(elem, "friction", 3, pgeom->friction, def->geom.friction, true);
WriteAttr(elem, "solmix", 1, &pgeom->solmix, &def->geom.solmix);
WriteAttr(elem, "solref", mjNREF, pgeom->solref, def->geom.solref);
WriteAttr(elem, "solimp", mjNIMP, pgeom->solimp, def->geom.solimp);
WriteAttr(elem, "solref", mjNREF, pgeom->solref, def->geom.solref, true);
WriteAttr(elem, "solimp", mjNIMP, pgeom->solimp, def->geom.solimp, true);
WriteAttr(elem, "margin", 1, &pgeom->margin, &def->geom.margin);
WriteAttr(elem, "gap", 1, &pgeom->gap, &def->geom.gap);
WriteAttr(elem, "gap", 1, &pgeom->gap, &def->geom.gap);
@@ -533,10 +535,11 @@ void mjXWriter::OnePair(XMLElement* elem, mjCPair* ppair, mjCDef* def) {
WriteAttrInt(elem, "condim", ppair->condim, def->pair.spec.condim);
WriteAttr(elem, "margin", 1, &ppair->margin, &def->pair.spec.margin);
WriteAttr(elem, "gap", 1, &ppair->gap, &def->pair.spec.gap);
WriteAttr(elem, "solref", mjNREF, ppair->solref, def->pair.spec.solref);
WriteAttr(elem, "solreffriction", mjNREF, ppair->solreffriction, def->pair.spec.solreffriction);
WriteAttr(elem, "solimp", mjNIMP, ppair->solimp, def->pair.spec.solimp);
WriteAttr(elem, "friction", 5, ppair->friction, def->pair.spec.friction);
WriteAttr(elem, "solref", mjNREF, ppair->solref, def->pair.spec.solref, true);
WriteAttr(elem, "solreffriction", mjNREF, ppair->solreffriction, def->pair.spec.solreffriction,
true);
WriteAttr(elem, "solimp", mjNIMP, ppair->solimp, def->pair.spec.solimp, true);
WriteAttr(elem, "friction", 5, ppair->friction, def->pair.spec.friction); // all 5 values
}
@@ -586,8 +589,8 @@ void mjXWriter::OneEquality(XMLElement* elem, mjCEquality* peq, mjCDef* def) {
// defaults and regular
WriteAttrKey(elem, "active", bool_map, 2, peq->active, def->equality.active);
WriteAttr(elem, "solref", mjNREF, peq->solref, def->equality.solref);
WriteAttr(elem, "solimp", mjNIMP, peq->solimp, def->equality.solimp);
WriteAttr(elem, "solref", mjNREF, peq->solref, def->equality.solref, true);
WriteAttr(elem, "solimp", mjNIMP, peq->solimp, def->equality.solimp, true);
}
@@ -604,10 +607,12 @@ void mjXWriter::OneTendon(XMLElement* elem, mjCTendon* pten, mjCDef* def) {
// defaults and regular
WriteAttrInt(elem, "group", pten->group, def->tendon.group);
WriteAttr(elem, "solreflimit", mjNREF, pten->solref_limit, def->tendon.solref_limit);
WriteAttr(elem, "solimplimit", mjNIMP, pten->solimp_limit, def->tendon.solimp_limit);
WriteAttr(elem, "solreffriction", mjNREF, pten->solref_friction, def->tendon.solref_friction);
WriteAttr(elem, "solimpfriction", mjNIMP, pten->solimp_friction, def->tendon.solimp_friction);
WriteAttr(elem, "solreflimit", mjNREF, pten->solref_limit, def->tendon.solref_limit, true);
WriteAttr(elem, "solimplimit", mjNIMP, pten->solimp_limit, def->tendon.solimp_limit, true);
WriteAttr(elem, "solreffriction", mjNREF, pten->solref_friction, def->tendon.solref_friction,
true);
WriteAttr(elem, "solimpfriction", mjNIMP, pten->solimp_friction, def->tendon.solimp_friction,
true);
WriteAttrKey(elem, "limited", TFAuto_map, 3, pten->limited, def->tendon.limited);
WriteAttr(elem, "range", 2, pten->range, def->tendon.range);
WriteAttr(elem, "margin", 1, &pten->margin, &def->tendon.margin);
@@ -711,8 +716,8 @@ void mjXWriter::OneActuator(XMLElement* elem, mjCActuator* pact, mjCDef* def) {
}
WriteAttrKey(elem, "gaintype", gain_map, gain_sz, pact->gaintype, def->actuator.gaintype);
WriteAttrKey(elem, "biastype", bias_map, bias_sz, pact->biastype, def->actuator.biastype);
WriteAttr(elem, "gainprm", mjNGAIN, pact->gainprm, def->actuator.gainprm);
WriteAttr(elem, "biasprm", mjNBIAS, pact->biasprm, def->actuator.biasprm);
WriteAttr(elem, "gainprm", mjNGAIN, pact->gainprm, def->actuator.gainprm, true);
WriteAttr(elem, "biasprm", mjNBIAS, pact->biasprm, def->actuator.biasprm, true);
}
// userdata
+13 -5
View File
@@ -904,7 +904,8 @@ static int Round(double x) {
// write attribute
template<typename T>
void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, const T* def) {
void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, const T* def,
bool trim) {
// make sure all are defined
if constexpr (std::is_floating_point_v<T>) {
for (int i=0; i<n; i++) {
@@ -919,6 +920,13 @@ void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, con
return;
}
// trim identical trailing default values
if (trim) {
while (n > 0 && data[n-1] == def[n-1]) {
n--;
}
}
// increase precision for testing
stringstream stream;
stream.precision(mujoco::_mjPRIVATE__get_xml_precision());
@@ -945,17 +953,17 @@ void mjXUtil::WriteAttr(XMLElement* elem, string name, int n, const T* data, con
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
const double* data, const double* def);
const double* data, const double* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
const float* data, const float* def);
const float* data, const float* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
const int* data, const int* def);
const int* data, const int* def, bool trim);
template void mjXUtil::WriteAttr(XMLElement* elem, string name, int n,
const unsigned char* data,
const unsigned char* def);
const unsigned char* def, bool trim);
// write vector<double> attribute, default = zero array
+1 -1
View File
@@ -195,7 +195,7 @@ class mjXUtil {
// write attribute- any type
template<typename T>
static void WriteAttr(tinyxml2::XMLElement* elem, std::string name, int n, const T* data,
const T* def = 0);
const T* def = 0, bool trim = false);
// write vector<double> attribute, with and without default
static void WriteVector(tinyxml2::XMLElement* elem, std::string name,
+27 -1
View File
@@ -722,7 +722,7 @@ TEST_F(XMLWriterTest, WritesActuatorDefaults) {
std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
EXPECT_THAT(saved_xml, HasSubstr(
"<general biastype=\"affine\" gainprm=\"3 0 0 0 0 0 0 0 0 0\""));
"<general biastype=\"affine\" gainprm=\"3\""));
mj_deleteModel(model);
}
@@ -999,6 +999,32 @@ TEST_F(XMLWriterTest, ActdimDefaults) {
mj_deleteModel(model);
}
TEST_F(XMLWriterTest, TrimsDefaults) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<body>
<geom size="1" friction="0.9" solref="0.1" solimp="0.1 0.2"/>
<joint name="jnt" type="slide" axis="1 0 0" range="0 2"/>
</body>
</worldbody>
<actuator>
<position joint="jnt" kp="3" kv="4"/>
<damper joint="jnt" kv="10" ctrlrange="0 1"/>
</actuator>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, NotNull());
std::string saved_xml = SaveAndReadXml(model);
EXPECT_THAT(saved_xml, HasSubstr("friction=\"0.9\""));
EXPECT_THAT(saved_xml, HasSubstr("solref=\"0.1\""));
EXPECT_THAT(saved_xml, HasSubstr("solimp=\"0.1 0.2\""));
EXPECT_THAT(saved_xml, HasSubstr("gainprm=\"3\" biasprm=\"0 -3 -4\""));
EXPECT_THAT(saved_xml, HasSubstr("gainprm=\"0 0 -10\""));
mj_deleteModel(model);
}
// check that no precision is lost when saving XMLs with FullFloatPrecision
TEST_F(XMLWriterTest, SetPrecision) {
static constexpr char xml[] = R"(