diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index 461fb4ce..b7cc39ba 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -120,16 +120,6 @@ mjCAlternative::mjCAlternative() { // used for geom, site, body and camera frames const char* mjCAlternative::Set(double* quat, double* inertia, bool degree, const char* sequence) { - // check for multiple defitions - int ndef = (int)mjuu_defined(axisangle[0]) + - (int)mjuu_defined(xyaxes[0]) + - (int)mjuu_defined(zaxis[0]) + - (int)mjuu_defined(euler[0]) + - (int)mjuu_defined(fullinertia[0]); - if (ndef>1) { - return "too many alternative definitions"; - } - // set quat using axisangle if (mjuu_defined(axisangle[0])) { // convert to radians if necessary, normalize axis diff --git a/src/xml/xml_base.cc b/src/xml/xml_base.cc index dae4d611..d95b1f8d 100644 --- a/src/xml/xml_base.cc +++ b/src/xml/xml_base.cc @@ -52,9 +52,12 @@ void mjXBase::SetModel(mjCModel* _model) { // read alternative orientation specification void mjXBase::ReadAlternative(XMLElement* elem, mjCAlternative& alt) { string text; - ReadAttr(elem, "axisangle", 4, alt.axisangle, text); - ReadAttr(elem, "xyaxes", 6, alt.xyaxes, text); - ReadAttr(elem, "zaxis", 3, alt.zaxis, text); - ReadAttr(elem, "euler", 3, alt.euler, text); - ReadAttr(elem, "fullinertia", 6, alt.fullinertia, text); + int read = (int)(elem->Attribute("quat") != 0) + + (ReadAttr(elem, "axisangle", 4, alt.axisangle, text) ? 1 : 0) + + (ReadAttr(elem, "xyaxes", 6, alt.xyaxes, text) ? 1 : 0) + + (ReadAttr(elem, "zaxis", 3, alt.zaxis, text) ? 1 : 0) + + (ReadAttr(elem, "euler", 3, alt.euler, text) ? 1 : 0) + + (ReadAttr(elem, "fullinertia", 6, alt.fullinertia, text) ? 1 : 0); + if (read > 1) + throw mjXError(elem, "multiple orientation specifiers for the same field are not allowed"); } diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 71116e3f..943c7b46 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -1120,9 +1120,9 @@ void mjXReader::OneGeom(XMLElement* elem, mjCGeom* pgeom) { ReadAttr(elem, "mass", 1, &pgeom->_mass, text); ReadAttr(elem, "density", 1, &pgeom->density, text); ReadAttr(elem, "fromto", 6, pgeom->fromto, text); - ReadAlternative(elem, pgeom->alt); ReadAttr(elem, "pos", 3, pgeom->pos, text); ReadAttr(elem, "quat", 4, pgeom->quat, text); + ReadAlternative(elem, pgeom->alt); GetXMLPos(elem, pgeom); } diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index e7689ad6..bd5c216f 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -192,6 +192,49 @@ TEST_F(UserDataTest, AllowsSpaces) { mj_deleteModel(model); } +TEST_F(UserDataTest, InvalidDoubleOrientation) { + std::string prefix = "<"; + std::string suffix = "/>"; + std::vector orientations = { + R"( quat="0 1 0 0" )", + R"( euler="1.7 2.9 0.1" )", + R"( zaxis="1.7 2.9 0.1" )", + R"( axisangle="1.7 2.9 0.1 0" )", + R"( xyaxes="1.7 2.9 0.1 0.4 1.4 0.6" )", + }; + std::vector fields = { + "geom", "body", "camera", "site" + }; + for (auto const& field : fields) { + for (auto const& orient1 : orientations) { + for (auto const& orient2 : orientations) { + if (orient1 == orient2) continue; + std::string xml = prefix + field + orient1 + orient2 + suffix; + std::array error; + mjModel* model = LoadModelFromString(xml.c_str(), error.data(), error.size()); + ASSERT_THAT(model, IsNull()); + EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field")); + } + } + } +} + +TEST_F(UserDataTest, InvalidInertialOrientation) { + static constexpr char xml[] = R"( + + + + + + + + )"; + std::array error; + mjModel* model = LoadModelFromString(xml, error.data(), error.size()); + ASSERT_THAT(model, IsNull()); + EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field")); +} + // ------------- test relative frame sensor parsing ---------------------------- using RelativeFrameSensorParsingTest = MujocoTest;