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

Support merge-include of nested models #659

Merged
merged 14 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 12 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
2 changes: 1 addition & 1 deletion doc/sdf.in
Original file line number Diff line number Diff line change
Expand Up @@ -1510,7 +1510,7 @@ SEARCH_INCLUDES = YES
# contain include files that are not input files but should be processed by
# the preprocessor.

INCLUDE_PATH = .
INCLUDE_PATH = @PROJECT_BINARY_DIR@ @PROJECT_SOURCE_DIR@/include

# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
Expand Down
13 changes: 13 additions & 0 deletions include/sdf/Error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ namespace sdf

/// \brief The provided version has been deprecated or it is pre-versioning
VERSION_DEPRECATED,

/// \brief Merge include is unspported for the type of entity being
/// included.
MERGE_INCLUDE_UNSUPPORTED,
};

class SDFORMAT_VISIBLE Error
Expand Down Expand Up @@ -188,10 +192,19 @@ namespace sdf
/// nullopt otherwise.
public: std::optional<std::string> FilePath() const;

/// \brief Sets the file path that is associated with this error.
/// \param[in] _filePath The file path that is related to this error. (e.g.
/// /tmp/test_file.sdf)
public: void SetFilePath(const std::string &_filePath);

/// \brief Get the line number associated with this error.
/// \return Returns the line number. nullopt otherwise.
public: std::optional<int> LineNumber() const;

/// \brief Sets the line number that is associated with this error.
/// \param[in] _lineNumber The line number that is related to this error.
public: void SetLineNumber(int _lineNumber);

/// \brief Get the XPath-like trace that is associated with this error.
/// \return Returns the XPath-like trace that this error is related to,
/// nullopt otherwise.
Expand Down
145 changes: 135 additions & 10 deletions include/sdf/InterfaceElements.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <memory>

#include <ignition/math/Pose3.hh>
#include <ignition/utils/ImplPtr.hh>

#include "sdf/Element.hh"
#include "sdf/InterfaceModel.hh"
Expand All @@ -40,53 +41,177 @@ inline namespace SDF_VERSION_NAMESPACE
#endif
/// \brief Contains the necessary information about an included model file
/// for custom model parsers to be able to find the file and parse it.
struct SDFORMAT_VISIBLE NestedInclude
class SDFORMAT_VISIBLE NestedInclude
{
/// \brief Constructor
public: NestedInclude();
// Defaulted copy, move constructors and destructors are needed to avoid
// deprecation warnings on memeber variables when simply instantiating this
// class.
// TODO(anyone) Remove the constructor and destructor once the depreceted
// members are removed.
SDF_SUPPRESS_DEPRECATED_BEGIN
public: NestedInclude(const NestedInclude&) = default;
public: NestedInclude(NestedInclude&&) = default;
public: NestedInclude& operator=(const NestedInclude&) = default;
public: NestedInclude& operator=(NestedInclude&&) = default;
public: ~NestedInclude() = default;
SDF_SUPPRESS_DEPRECATED_END

/// \brief Provides the URI as specified in `//include/uri`. This may or may
/// not end with a file extension (it will not end with an extension if it
/// refers to a model package).
/// \return URI of the included model
public: const std::string &Uri() const;

/// \brief Set the URI of the included model
/// \param[in] _uri URI of the included model
public: void SetUri(const std::string &_uri);

/// \brief Provides the *resolved* absolute file path from the URI.
/// It is recommended to use this in `CustomModelParser` when checking
/// predicates on filenames -- however, the predicates should generally only
/// check the file extension.
/// \return The resolved absolute file path from the URI.
public: const std::string &ResolvedFileName() const;

/// \brief Set the resolved absolute file path.
/// \param[in] _resolvedFileName The resolved absolute file path
public: void SetResolvedFileName(const std::string &_resolvedFileName);

/// \brief Name of the parent entity in absolute hierarchy.
/// Example: if the interface model's name is
/// `top_model::middle_model::my_new_model`, the absoluteParentName would be
/// `top_model::middle_model`. If the parent entity is the world, this would
/// be an empty string.
/// \return Absolute name of parent entity
public: const std::string &AbsoluteParentName() const;

/// \brief Set the absolute name of parent entity
/// \param[in] _absoluteparentname Absolute name of parent entity
public: void SetAbsoluteParentName(const std::string &_absoluteparentname);

/// \brief Name relative to immediate parent as specified in
/// `//include/name`. This is nullopt if `//include/name` is not set. Then the
/// name of the model must be determined by the custom model parser from the
/// included model file.
/// Example: `my_new_model`
/// \return The local name. nullopt if `//include/name` is not set
public: const std::optional<std::string> &LocalModelName() const;

/// \brief Set the name relative to immediate parent as specified in
/// `//include/name`
/// \param[in] _localModelName The local name
public: void SetLocalModelName(const std::string &_localModelName);

/// \brief Whether the model is static as defined by `//include/static`. This
/// is nullopt if `//include/static` is not set.
/// \return Whether the model is static. nullopt if `//include/static` is not
/// set.
public: const std::optional<bool> &IsStatic() const;

/// \brief Set whether the model is static.
/// \param[in] _isStatic True if the model is static.
public: void SetIsStatic(bool _isStatic);

/// \brief The raw pose as specified in `//include/pose`. This is nullopt if
/// `//include/pose` is not set.
/// \return The raw pose. nullopt if `//include/pose` is not set.
public: const std::optional<ignition::math::Pose3d> &IncludeRawPose() const;

/// \brief Set the raw pose as specified in `//include/pose`.
/// \param[in] _includeRawPose The raw pose
public: void SetIncludeRawPose(const ignition::math::Pose3d &_includeRawPose);

/// \brief The relative-to frame of the pose as specified in
/// `//include/pose/@relative_to`. This is nullopt if
/// `//include/pose/@relative_to` is not set.
/// \return The relative-to frame of the pose. nullopt if
/// `//include/pose/@relative_to` is not set.
public: const std::optional<std::string> &IncludePoseRelativeTo() const;

/// \brief Set the relative-to frame of the pose.
/// \param[in] _includePoseRelativeTo The relative-to frame.
public: void SetIncludePoseRelativeTo(
const std::string &_includePoseRelativeTo);

/// \brief The placement frame as specified in `//include/placement_frame`.
/// This is nullopt if `//include/placement_frame` is is not set.
/// \return The placement frame. nullopt if `//include/placement_frame` is is
/// not set.
public: const std::optional<std::string> &PlacementFrame() const;

/// \brief Set the placement frame.
/// \param[in] _placementFrame The placement frame.
public: void SetPlacementFrame(const std::string &_placementFrame);

/// This is the `//include` element. This can be used to pass custom elements
/// and attributes to the custom model parser.
/// \return The `//include` element
public: sdf::ElementPtr IncludeElement() const;

/// Set the `//include` element.
/// \param[in] _includeElement The include element
public: void SetIncludeElement(sdf::ElementPtr _includeElement);

/// \brief Provides the URI as specified in `//include/uri`. This may or may
/// not end with a file extension (it will not end with an extension if it
/// refers to a model package).
std::string uri;
/// \deprecated Use NestedInclude::Uri() instead
public: std::string uri SDF_DEPRECATED(12);

/// \brief Provides the *resolved* absolute file path from the URI.
/// It is recommended to use this in `CustomModelParser` when checking
/// predicates on filenames -- however, the predicates should generally only
/// check the file extension.
std::string resolvedFileName;
/// \deprecated Use NestedInclude::ResolvedFileName() instead
public: std::string resolvedFileName SDF_DEPRECATED(12);

/// \brief Name of the parent entity in absolute hierarchy.
/// Example: if the interface model's name is
/// `top_model::middle_model::my_new_model`, the absoluteParentName would be
/// `top_model::middle_model`. If the parent entity is the world, this would
/// be an empty string.
std::string absoluteParentName;
/// \deprecated Use NestedInclude::AbsoluteParentName() instead
public: std::string absoluteParentName SDF_DEPRECATED(12);

/// \brief Name relative to immediate parent as specified in
/// `//include/name`. This is nullopt if `//include/name` is not set. Then the
/// name of the model must be determined by the custom model parser from the
/// included model file.
/// Example: `my_new_model`
std::optional<std::string> localModelName;
/// \deprecated Use NestedInclude::LocalModelName() instead
public: std::optional<std::string> localModelName SDF_DEPRECATED(12);

/// \brief Whether the model is static as defined by `//include/static`. This
/// is nullopt if `//include/static` is not set.
std::optional<bool> isStatic;
/// \deprecated Use NestedInclude::IsStatic() instead
public: std::optional<bool> isStatic SDF_DEPRECATED(12);

/// \brief The raw pose as specified in //include/pose. This is nullopt if
/// `//include/pose` is not set.
std::optional<ignition::math::Pose3d> includeRawPose;
/// \deprecated Use NestedInclude::IncludeRawPose() instead
public: std::optional<ignition::math::Pose3d> includeRawPose
SDF_DEPRECATED(12);

/// \brief The relative-to frame of the pose as specified in
/// `//include/pose/@relative_to`. This is nullopt if
/// `//include/pose/@relative_to` is not set.
std::optional<std::string> includePoseRelativeTo;
/// \deprecated Use NestedInclude::IncludePoseRelativeTo() instead
public: std::optional<std::string> includePoseRelativeTo SDF_DEPRECATED(12);

/// \brief The placement frame as specified in `//include/placement_frame`.
/// This is nullopt if `//include/placement_frame` is is not set.
std::optional<std::string> placementFrame;
/// \deprecated Use NestedInclude::PlacementFrame() instead
public: std::optional<std::string> placementFrame SDF_DEPRECATED(12);

/// This is the `//include` element. This can be used to pass custom elements
/// and attributes to the custom model parser.
sdf::ElementPtr includeElement;
/// \deprecated Use NestedInclude::IncludeElement() instead
public: sdf::ElementPtr includeElement SDF_DEPRECATED(12);

/// \brief Private data pointer.
IGN_UTILS_IMPL_PTR(dataPtr)
};
#ifdef _MSC_VER
#pragma warning(pop)
Expand Down
2 changes: 1 addition & 1 deletion include/sdf/Model.hh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace sdf
class Joint;
class Link;
class ParserConfig;
struct NestedInclude;
class NestedInclude;
struct PoseRelativeToGraph;
struct FrameAttachedToGraph;
template <typename T> class ScopedGraph;
Expand Down
2 changes: 1 addition & 1 deletion include/sdf/World.hh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace sdf
class Model;
class ParserConfig;
class Physics;
struct NestedInclude;
class NestedInclude;
struct PoseRelativeToGraph;
struct FrameAttachedToGraph;
template <typename T> class ScopedGraph;
Expand Down
4 changes: 4 additions & 0 deletions sdf/1.9/model.sdf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<description>
Include resources from a URI. This can be used to nest models. Included resources can only contain one 'model', 'light' or 'actor' element. The URI can point to a directory or a file. If the URI is a directory, it must conform to the model database structure (see /tutorials?tut=composition&amp;cat=specification&amp;#defining-models-in-separate-files).
</description>
<attribute name="merge" type="bool" default="false" required="0">
<description>Merge the included nested model into the top model</description>
</attribute>

<element name="uri" type="string" default="__default__" required="1">
<description>URI to a resource, such as a model</description>
</element>
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ set (sources
Gui.cc
Heightmap.cc
ign.cc
InterfaceElements.cc
InterfaceFrame.cc
InterfaceJoint.cc
InterfaceLink.cc
Expand Down Expand Up @@ -114,6 +115,7 @@ if (BUILD_SDF_TEST)
Gui_TEST.cc
Heightmap_TEST.cc
Imu_TEST.cc
InterfaceElements_TEST.cc
Joint_TEST.cc
JointAxis_TEST.cc
Lidar_TEST.cc
Expand Down
11 changes: 11 additions & 0 deletions src/Error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,24 @@ std::optional<std::string> Error::FilePath() const
{
return this->dataPtr->filePath;
}
/////////////////////////////////////////////////
void Error::SetFilePath(const std::string &_filePath)
{
this->dataPtr->filePath = _filePath;
}

/////////////////////////////////////////////////
std::optional<int> Error::LineNumber() const
{
return this->dataPtr->lineNumber;
}

/////////////////////////////////////////////////
void Error::SetLineNumber(int _lineNumber)
{
this->dataPtr->lineNumber = _lineNumber;
}

/////////////////////////////////////////////////
std::optional<std::string> Error::XmlPath() const
{
Expand Down
13 changes: 13 additions & 0 deletions src/Error_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,20 @@ TEST(Error, DefaultConstruction)
EXPECT_FALSE(error.LineNumber().has_value());

if (error)
{
FAIL();
}
error.SetXmlPath("/sdf/world");
ASSERT_TRUE(error.XmlPath().has_value());
EXPECT_EQ("/sdf/world", error.XmlPath());

error.SetFilePath("/tmp/test_file.sdf");
ASSERT_TRUE(error.FilePath().has_value());
EXPECT_EQ("/tmp/test_file.sdf", error.FilePath());

error.SetLineNumber(5);
ASSERT_TRUE(error.LineNumber().has_value());
EXPECT_EQ(5, error.LineNumber());
}

/////////////////////////////////////////////////
Expand Down
16 changes: 8 additions & 8 deletions src/FrameSemantics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ Errors buildPoseRelativeToGraph(
auto relativeToId = modelFrameId;

const std::string &relativeTo =
nestedInclude->includePoseRelativeTo.value_or("");
nestedInclude->IncludePoseRelativeTo().value_or("");
if (!relativeTo.empty())
{
// look for vertex in graph that matches relative_to value
Expand All @@ -1180,11 +1180,11 @@ Errors buildPoseRelativeToGraph(
}

ignition::math::Pose3d resolvedModelPose =
nestedInclude->includeRawPose.value_or(ignition::math::Pose3d());
nestedInclude->IncludeRawPose().value_or(ignition::math::Pose3d());

sdf::Errors resolveErrors = resolveModelPoseWithPlacementFrame(
nestedInclude->includeRawPose.value_or(ignition::math::Pose3d()),
nestedInclude->placementFrame.value_or(""),
nestedInclude->IncludeRawPose().value_or(ignition::math::Pose3d()),
nestedInclude->PlacementFrame().value_or(""),
outModel.ChildModelScope(ifaceModel->Name()), resolvedModelPose);
errors.insert(errors.end(), resolveErrors.begin(), resolveErrors.end());

Expand Down Expand Up @@ -1517,7 +1517,7 @@ Errors buildPoseRelativeToGraph(
auto relativeToId = worldFrameId;

const std::string &relativeTo =
nestedInclude->includePoseRelativeTo.value_or("");
nestedInclude->IncludePoseRelativeTo().value_or("");
if (!relativeTo.empty())
{
// look for vertex in graph that matches relative_to value
Expand All @@ -1543,11 +1543,11 @@ Errors buildPoseRelativeToGraph(
}

ignition::math::Pose3d resolvedModelPose =
nestedInclude->includeRawPose.value_or(ignition::math::Pose3d());
nestedInclude->IncludeRawPose().value_or(ignition::math::Pose3d());

sdf::Errors resolveErrors = resolveModelPoseWithPlacementFrame(
nestedInclude->includeRawPose.value_or(ignition::math::Pose3d()),
nestedInclude->placementFrame.value_or(""),
nestedInclude->IncludeRawPose().value_or(ignition::math::Pose3d()),
nestedInclude->PlacementFrame().value_or(""),
_out.ChildModelScope(ifaceModel->Name()), resolvedModelPose);
errors.insert(errors.end(), resolveErrors.begin(), resolveErrors.end());

Expand Down
Loading