Skip to content
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

Merged
merged 14 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sdf/1.8/1_7.convert
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<convert name="sdf">
Copy link
Collaborator

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed 3e0cd5d


<convert name="world">
<convert name="model">
<unnest/>
</convert>
</convert>

</convert> <!-- End SDF -->
239 changes: 239 additions & 0 deletions src/Converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ void Converter::ConvertDescendantsImpl(tinyxml2::XMLElement *_e,
}
}


/////////////////////////////////////////////////
// TODO(jenn) delete, used for debugging
std::string PrintElement(const tinyxml2::XMLElement *_elem)
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PrintElement is now ElementToString put in XmlUtils.cc: 9ed2713

{
tinyxml2::XMLPrinter printer;
_elem->Accept(&printer);

return std::string(printer.CStr());
}

/////////////////////////////////////////////////
void Converter::ConvertImpl(tinyxml2::XMLElement *_elem,
tinyxml2::XMLElement *_convert)
Expand Down Expand Up @@ -229,13 +240,241 @@ 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");

tinyxml2::XMLDocument *doc = _elem->GetDocument();
tinyxml2::XMLNode *copy = DeepClone(doc, _elem);

// std::cout << "copy:\n" << PrintElement(copy->ToElement()) << std::endl;

tinyxml2::XMLElement *elem = _elem->FirstChildElement(), *nextElem = nullptr;
while (elem)
{
nextElem = elem->NextSiblingElement();

std::string elemName = elem->Name();

// skip element if not one of the following
if (elemName != "frame" && elemName != "joint"
&& elemName != "link" && elemName != "model")
{
elem = nextElem;
continue;
}

std::string attrName = elem->Attribute("name");

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;
_elem->InsertEndChild(elem);
break;
}

elem = nextElem;
continue;
}

std::string newModelName = attrName.substr(0, found);
tinyxml2::XMLElement *newModel = doc->NewElement("model");
newModel->SetAttribute("name", newModelName.c_str());

Converter::TupleVector deleteElems =
FindNewModelElements(doc,
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)
{
tinyxml2::XMLElement *e =
_elem->FirstChildElement(std::get<0>(deleteElem).c_str());

std::string eName = std::get<1>(deleteElem);

while (eName != std::string(e->Attribute("name")))
e = _elem->NextSiblingElement(std::get<0>(deleteElem).c_str());

if (std::string(e->Attribute("name")) == eName)
_elem->DeleteChild(e);
}

nextElem = _elem->FirstChildElement();
}

elem = nextElem;
}
}

/////////////////////////////////////////////////
Converter::TupleVector Converter::FindNewModelElements(
tinyxml2::XMLDocument *_doc,
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.find(newModelName) == std::string::npos ||
(elemName != "frame" && elemName != "joint" &&
Copy link
Collaborator

Choose a reason for hiding this comment

The 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. bool IsInterfaceElement(elem) { return elemName == "asdf" || ...; }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elemName != "link" && elemName != "model"))
{
elem = nextElem;
continue;
}

// Child attribute name w/ newModelName prefix stripped
std::string 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.find(newModelName) != std::string::npos)
{
poseRelTo = poseRelTo.substr(_newNameIdx);
poseElem->SetAttribute("relative_to", poseRelTo.c_str());
}
}

if (elemName == "frame")
{
std::string attachedTo;

if (elem->Attribute("attached_to"))
{
attachedTo = elem->Attribute("attached_to");

SDF_ASSERT(attachedTo.find(newModelName) != std::string::npos,
"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"))
{
_newModel->SetAttribute("canonical_link", attachedTo.c_str());
_newModel->InsertFirstChild(poseElem);

_copy->DeleteChild(elem);
elem = poseElem;
}
} // frame

else if (elemName == "link")
{
// TODO(jenn) //visual/pose/@relative_to & //collision/pose/@relative_to
} // link

else if (elemName == "joint")
{
// strip new model prefix from //joint/parent
tinyxml2::XMLElement *e = elem->FirstChildElement("parent");
std::string eText = e->GetText();

SDF_ASSERT(eText.find(newModelName) != std::string::npos,
"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.find(newModelName) != std::string::npos,
"Error: Joint's <child> value does not start with " + newModelName);

e->SetText(std::string(e->GetText()).substr(_newNameIdx).c_str());

// strip new model prefix from //xyz/@expressed_in
std::string axisStr = "axis";
for (int i = 0; i < 2; ++i)
{
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.find(newModelName) != std::string::npos,
"Error: <xyz>'s attribute 'expressed_in' does not start with " +
newModelName);

expressIn = expressIn.substr(_newNameIdx);

axisElem->FirstChildElement("xyz")
->SetAttribute("expressed_in", expressIn.c_str());
}
}
axisStr += "2";
}
} // joint

_newModel->InsertEndChild(elem);
deleteElems.push_back(std::make_tuple(elemName, elemAttrName));

elem = nextElem;
}

return deleteElems;
}

/////////////////////////////////////////////////
void Converter::Rename(tinyxml2::XMLElement *_elem,
tinyxml2::XMLElement *_renameElem)
Expand Down
24 changes: 24 additions & 0 deletions src/Converter.hh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <tinyxml2.h>

#include <string>
#include <vector>
#include <tuple>

#include <sdf/sdf_config.h>
#include "sdf/system_util.hh"
Expand Down Expand Up @@ -103,6 +105,28 @@ namespace sdf
private: static void Remove(tinyxml2::XMLElement *_elem,
tinyxml2::XMLElement *_removeElem);

/// \brief Unnest an element (conversion from SDFormat <= 1.7 to 1.8)
/// \param[in] _elem The element to unnest
private: static void Unnest(tinyxml2::XMLElement *_elem);

/// \brief Convenience type alias for vector of tuple<string, string>
/// where 1st string is the element name & 2nd string is attribute name
private:
using TupleVector = std::vector< std::tuple<std::string, std::string> >;

/// \brief Finds all elements related to the unnested model
/// \param[in] _doc SDF xml doc
/// \param[in] _copy Copy of the entire original element that needs unnested
/// \param[in] _newModel The new unnested model element
/// \param[in] _newNameIdx The index of the new name for child elements
/// \return Vector of tuples containing the element name & attribute name
/// to be deleted from the original element that needs unnested
private:
static TupleVector FindNewModelElements(tinyxml2::XMLDocument *_doc,
tinyxml2::XMLElement *_copy,
tinyxml2::XMLElement *_newModel,
const size_t &_newNameIdx);

private: static const char *GetValue(const char *_valueElem,
const char *_valueAttr,
tinyxml2::XMLElement *_elem);
Expand Down
Loading