-
Notifications
You must be signed in to change notification settings - Fork 100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unflatten flattened models #504
Changes from 7 commits
a95b182
320d4b1
e128b71
a1006ad
de8a697
1435eab
8cd7f4d
cceaaa4
33dda02
dce6828
9ed2713
73cbe30
8dfcd7f
3e0cd5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
<convert name="sdf"> | ||
|
||
<convert name="world"> | ||
<convert name="model"> | ||
<unnest/> | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</convert> | ||
</convert> | ||
|
||
</convert> <!-- End SDF --> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -172,6 +172,17 @@ void Converter::ConvertDescendantsImpl(tinyxml2::XMLElement *_e, | |
} | ||
} | ||
|
||
|
||
///////////////////////////////////////////////// | ||
// TODO(jenn) delete, used for debugging | ||
std::string PrintElement(const tinyxml2::XMLElement *_elem) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit Perhaps this is useful to keep around? Perhaps implement using a trace or debug logging level? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
{ | ||
tinyxml2::XMLPrinter printer; | ||
_elem->Accept(&printer); | ||
|
||
return std::string(printer.CStr()); | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
void Converter::ConvertImpl(tinyxml2::XMLElement *_elem, | ||
tinyxml2::XMLElement *_convert) | ||
|
@@ -229,13 +240,334 @@ void Converter::ConvertImpl(tinyxml2::XMLElement *_elem, | |
{ | ||
Remove(_elem, childElem); | ||
} | ||
else if (name == "unnest") | ||
{ | ||
Unnest(_elem); | ||
// TODO(jenn) delete debug statements | ||
// std::cout << "\n\n_elem after unnest:\n" | ||
// << PrintElement(_elem) << std::endl; | ||
} | ||
else if (name != "convert") | ||
{ | ||
sdferr << "Unknown convert element[" << name << "]\n"; | ||
} | ||
} | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
void Converter::Unnest(tinyxml2::XMLElement *_elem) | ||
{ | ||
SDF_ASSERT(_elem != NULL, "SDF element is NULL"); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
tinyxml2::XMLDocument *doc = _elem->GetDocument(); | ||
tinyxml2::XMLNode *copy = DeepClone(doc, _elem); | ||
|
||
tinyxml2::XMLElement *nextElem = nullptr; | ||
for (tinyxml2::XMLElement *elem = _elem->FirstChildElement(); | ||
elem; | ||
elem = nextElem) | ||
{ | ||
nextElem = elem->NextSiblingElement(); | ||
std::string elemName = elem->Name(); | ||
|
||
// skip element if not one of the following or if missing name attribute | ||
if ((elemName != "frame" && elemName != "joint" | ||
&& elemName != "link" && elemName != "model") | ||
|| !elem->Attribute("name")) | ||
{ | ||
continue; | ||
} | ||
|
||
std::string attrName = elem->Attribute("name"); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
size_t found = attrName.find("::"); | ||
if (found == std::string::npos) | ||
{ | ||
// recursive unnest | ||
if (elemName == "model") | ||
{ | ||
// std::cout << "before recursive:\n" | ||
// << PrintElement(elem) << std::endl; | ||
Unnest(elem); | ||
|
||
// std::cout << "unnested model:\n" << PrintElement(elem) << std::endl; | ||
break; | ||
} | ||
|
||
continue; | ||
} | ||
|
||
std::string newModelName = attrName.substr(0, found); | ||
tinyxml2::XMLElement *newModel = doc->NewElement("model"); | ||
newModel->SetAttribute("name", newModelName.c_str()); | ||
|
||
Converter::TupleVector deleteElems = | ||
FindNewModelElements(copy->ToElement(), | ||
newModel, | ||
found + 2); | ||
|
||
// std::cout << "newModel:\n" << PrintElement(newModel) << std::endl; | ||
|
||
if (!deleteElems.empty()) | ||
{ | ||
_elem->InsertEndChild(newModel); | ||
|
||
// delete unnested elements | ||
for (std::tuple<std::string, std::string> deleteElem : deleteElems) | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
tinyxml2::XMLElement *e = | ||
_elem->FirstChildElement(std::get<0>(deleteElem).c_str()); | ||
|
||
if (std::get<0>(deleteElem) == "include") | ||
{ | ||
_elem->DeleteChild(e); | ||
continue; | ||
} | ||
|
||
std::string eName = std::get<1>(deleteElem); | ||
|
||
while (eName != std::string(e->Attribute("name"))) | ||
e = e->NextSiblingElement(std::get<0>(deleteElem).c_str()); | ||
|
||
if (std::string(e->Attribute("name")) == eName) | ||
_elem->DeleteChild(e); | ||
} | ||
|
||
Unnest(newModel); | ||
} | ||
} | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
Converter::TupleVector Converter::FindNewModelElements( | ||
tinyxml2::XMLElement *_copy, | ||
tinyxml2::XMLElement *_newModel, | ||
const size_t &_newNameIdx) | ||
{ | ||
Converter::TupleVector deleteElems; | ||
std::string newModelName = _newModel->Attribute("name"); | ||
|
||
tinyxml2::XMLElement *elem = _copy->FirstChildElement(), *nextElem = nullptr; | ||
while (elem) | ||
{ | ||
nextElem = elem->NextSiblingElement(); | ||
|
||
std::string elemName = elem->Name(); | ||
std::string elemAttrName; | ||
|
||
if (elem->Attribute("name")) | ||
elemAttrName = elem->Attribute("name"); | ||
|
||
if (elemAttrName.empty() || | ||
elemAttrName.compare(0, _newNameIdx - 2, newModelName) != 0 || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit Perhaps state something like this? const size_t kSeparatorLen = 2; // "::"
size_t possibleParentNameIdx = _newNameIdx - kSeparatorLen; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, you may want to consider making There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How's this? I've updated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent, thanks! |
||
(elemName != "frame" && elemName != "joint" && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit This set of if checks are repeated; intent isn't clear due to repetition. Can you give it semantics and wrap it? e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
elemName != "link" && elemName != "model")) | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
if (elemName != "include" && elemName != "gripper") | ||
{ | ||
elem = nextElem; | ||
continue; | ||
} | ||
} | ||
|
||
// Child attribute name w/ newModelName prefix stripped | ||
// except for //include & //gripper | ||
std::string childAttrName; | ||
if (elemName != "include" && elemName != "gripper") | ||
{ | ||
childAttrName = elemAttrName.substr(_newNameIdx); | ||
elem->SetAttribute("name", childAttrName.c_str()); | ||
} | ||
|
||
// strip new model prefix from //pose/@relative_to | ||
tinyxml2::XMLElement *poseElem = elem->FirstChildElement("pose"); | ||
if (poseElem != nullptr && poseElem->Attribute("relative_to")) | ||
{ | ||
std::string poseRelTo = poseElem->Attribute("relative_to"); | ||
|
||
// strip new model prefix from //pose/@relative_to | ||
if (poseRelTo.compare(0, _newNameIdx - 2, newModelName) == 0) | ||
{ | ||
poseRelTo = poseRelTo.substr(_newNameIdx); | ||
poseElem->SetAttribute("relative_to", poseRelTo.c_str()); | ||
} | ||
else if (elemName == "include") | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
// if //include/pose/@relative_to does not have new model prefix | ||
// don't add to new model | ||
elem = nextElem; | ||
continue; | ||
} | ||
} | ||
|
||
if (elemName == "frame") | ||
{ | ||
std::string attachedTo; | ||
|
||
if (elem->Attribute("attached_to")) | ||
{ | ||
attachedTo = elem->Attribute("attached_to"); | ||
|
||
SDF_ASSERT(attachedTo.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: Frame attribute 'attached_to' does not start with " + | ||
newModelName); | ||
|
||
// strip new model prefix from attached_to | ||
attachedTo = attachedTo.substr(_newNameIdx); | ||
elem->SetAttribute("attached_to", attachedTo.c_str()); | ||
} | ||
|
||
// remove frame if childAttrName == __model__ | ||
if (childAttrName == "__model__" && elem->Attribute("attached_to")) | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
_newModel->SetAttribute("canonical_link", attachedTo.c_str()); | ||
_newModel->InsertFirstChild(poseElem); | ||
|
||
_copy->DeleteChild(elem); | ||
elem = poseElem; | ||
} | ||
} // frame | ||
|
||
else if (elemName == "link") | ||
{ | ||
// find & strip new model prefix of all //link/<element>/pose/@relative_to | ||
for (tinyxml2::XMLElement *e = elem->FirstChildElement(); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
e; | ||
e = e->NextSiblingElement()) | ||
{ | ||
poseElem = e->FirstChildElement("pose"); | ||
if (poseElem != nullptr) | ||
{ | ||
std::string poseRelTo = poseElem->Attribute("relative_to"); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
SDF_ASSERT(poseRelTo.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: Pose attribute 'relative_to' does not start with " + | ||
newModelName); | ||
|
||
poseRelTo = poseRelTo.substr(_newNameIdx); | ||
poseElem->SetAttribute("relative_to", poseRelTo.c_str()); | ||
} | ||
} | ||
} // link | ||
|
||
else if (elemName == "joint") | ||
{ | ||
// strip new model prefix from //joint/parent | ||
tinyxml2::XMLElement *e = elem->FirstChildElement("parent"); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
std::string eText = e->GetText(); | ||
|
||
SDF_ASSERT(eText.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rule of 3 on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
"Error: Joint's <parent> value does not start with " + newModelName); | ||
|
||
e->SetText(eText.substr(_newNameIdx).c_str()); | ||
|
||
// strip new model prefix from //joint/child | ||
e = elem->FirstChildElement("child"); | ||
eText = e->GetText(); | ||
|
||
SDF_ASSERT(eText.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: Joint's <child> value does not start with " + newModelName); | ||
|
||
e->SetText(std::string(e->GetText()).substr(_newNameIdx).c_str()); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// strip new model prefix from //xyz/@expressed_in | ||
std::string axisStr = "axis"; | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
while (true) | ||
{ | ||
tinyxml2::XMLElement *axisElem = | ||
elem->FirstChildElement(axisStr.c_str()); | ||
if (axisElem != nullptr) | ||
{ | ||
if (axisElem->FirstChildElement("xyz")->Attribute("expressed_in")) | ||
{ | ||
std::string expressIn = | ||
axisElem->FirstChildElement("xyz")->Attribute("expressed_in"); | ||
|
||
SDF_ASSERT(expressIn.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: <xyz>'s attribute 'expressed_in' does not start with " + | ||
newModelName); | ||
|
||
expressIn = expressIn.substr(_newNameIdx); | ||
|
||
axisElem->FirstChildElement("xyz") | ||
->SetAttribute("expressed_in", expressIn.c_str()); | ||
} | ||
} | ||
|
||
if (axisStr == "axis2") break; | ||
|
||
axisStr += "2"; | ||
} | ||
|
||
// strip new model prefix from all //joint/sensor/pose/@relative_to | ||
for (e = elem->FirstChildElement("sensor"); | ||
e; | ||
e = e->NextSiblingElement("sensor")) | ||
{ | ||
poseElem = e->FirstChildElement("pose"); | ||
if (poseElem != nullptr) | ||
{ | ||
std::string poseRelTo = poseElem->Attribute("relative_to"); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
SDF_ASSERT(poseRelTo.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: Pose attribute 'relative_to' does not start with " + | ||
newModelName); | ||
|
||
poseRelTo = poseRelTo.substr(_newNameIdx); | ||
poseElem->SetAttribute("relative_to", poseRelTo.c_str()); | ||
} | ||
} | ||
} // joint | ||
|
||
else if (elemName == "gripper") | ||
{ | ||
bool hasPrefix = true; | ||
|
||
// strip prefix from all /model/gripper/gripper_link | ||
tinyxml2::XMLElement *e = elem->FirstChildElement("gripper_link"); | ||
std::string eText; | ||
while (e) | ||
{ | ||
eText = e->GetText(); | ||
azeey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (eText.compare(0, _newNameIdx - 2, newModelName) != 0) | ||
{ | ||
hasPrefix = false; | ||
break; | ||
} | ||
|
||
e->SetText(eText.substr(_newNameIdx).c_str()); | ||
e = e->NextSiblingElement("gripper_link"); | ||
} | ||
|
||
// if //model/gripper/gripper_link does not have new model prefix | ||
// don't add to new model | ||
if (!hasPrefix) | ||
{ | ||
elem = nextElem; | ||
continue; | ||
} | ||
|
||
// strip prefix from //model/gripper/palm_link | ||
e = elem->FirstChildElement("palm_link"); | ||
eText = e->GetText(); | ||
|
||
SDF_ASSERT(eText.compare(0, _newNameIdx - 2, newModelName) == 0, | ||
"Error: Gripper's <palm_link> value does not start with " | ||
+ newModelName); | ||
|
||
e->SetText(eText.substr(_newNameIdx).c_str()); | ||
} // gripper | ||
|
||
_newModel->InsertEndChild(elem); | ||
deleteElems.push_back(std::make_tuple(elemName, elemAttrName)); | ||
|
||
elem = nextElem; | ||
} | ||
|
||
return deleteElems; | ||
} | ||
|
||
///////////////////////////////////////////////// | ||
void Converter::Rename(tinyxml2::XMLElement *_elem, | ||
tinyxml2::XMLElement *_renameElem) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocking item: Should remove debug-based shims. (Just putting it here for review-based tracking)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed 3e0cd5d