Skip to content

Commit

Permalink
Modify ShapeSpecificaiton reification to include user data
Browse files Browse the repository at this point in the history
Support reification process which uses user data.
  • Loading branch information
SeanCurtis-TRI committed Jan 9, 2018
1 parent a21a122 commit cc60d97
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 17 deletions.
6 changes: 3 additions & 3 deletions geometry/geometry_visualization.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ class ShapeToLcm : public ShapeReifier {
return geometry_data_;
}

void ImplementGeometry(const Sphere& sphere) override {
void ImplementGeometry(const Sphere& sphere, void*) override {
geometry_data_.type = geometry_data_.SPHERE;
geometry_data_.num_float_data = 1;
geometry_data_.float_data.push_back(static_cast<float>(
sphere.get_radius()));
}

void ImplementGeometry(const Cylinder& cylinder) override {
void ImplementGeometry(const Cylinder& cylinder, void*) override {
geometry_data_.type = geometry_data_.CYLINDER;
geometry_data_.num_float_data = 2;
geometry_data_.float_data.push_back(static_cast<float>(
Expand All @@ -68,7 +68,7 @@ class ShapeToLcm : public ShapeReifier {
cylinder.get_length()));
}

void ImplementGeometry(const HalfSpace&) override {
void ImplementGeometry(const HalfSpace&, void*) override {
// Currently representing a half space as a big box. This assumes that the
// underlying box representation is centered on the origin.
geometry_data_.type = geometry_data_.BOX;
Expand Down
3 changes: 2 additions & 1 deletion geometry/shape_specification.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace geometry {

Shape::~Shape() {}

void Shape::Reify(ShapeReifier* reifier) const { reifier_(*this, reifier); }
void Shape::Reify(ShapeReifier* reifier, void* user_data) const {
reifier_(*this, reifier, user_data); }

std::unique_ptr<Shape> Shape::Clone() const { return cloner_(*this); }

Expand Down
62 changes: 53 additions & 9 deletions geometry/shape_specification.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ class Shape {
virtual ~Shape();

/** Causes this description to be reified in the given `reifier`. Each
concrete subclass must invoke the single, matching method on the reifier. */
void Reify(ShapeReifier* reifier) const;
concrete subclass must invoke the single, matching method on the reifier.
Provides optional user-data (cast as a void*) for the reifier to consume. */
void Reify(ShapeReifier* reifier, void* user_data = nullptr) const;

/** Creates a unique copy of this shape. Invokes the protected DoClone(). */
std::unique_ptr<Shape> Clone() const;
Expand Down Expand Up @@ -76,7 +77,7 @@ class Shape {

private:
std::function<std::unique_ptr<Shape>(const Shape&)> cloner_;
std::function<void(const Shape&, ShapeReifier*)> reifier_;
std::function<void(const Shape&, ShapeReifier*, void*)> reifier_;
};

/** Definition of sphere. It is centered in its canonical frame with the
Expand Down Expand Up @@ -143,13 +144,55 @@ class HalfSpace final : public Shape {
addition of a new concrete shape class requires the addition of a new
corresponding method. There should *never* be a method that accepts the Shape
base class as an argument; it should _only_ operate on concrete derived
classes. */
classes.
The expected workflow is for a class that needs to turn shape specifications
into concrete geometry instances to implement the %ShapeReifier interface
_and_ invoke the Shape::Reify() method. For example, a simple reifier that
requires no user data would look like:
```
class SimpleReifier : public ShapeReifier {
void ProcessShape(const Shape& shape) {
// Requires no user data.
shape.Reify(this);
}
...
void ImplementGeometry(const Sphere& sphere, void*) override {
// Do work to create a sphere.
}
};
```
Or a complex reifier that requires user data would look like:
```
class ComplexReifier : public ShapeReifier {
void ProcessShape(const Shape& shape) {
ImportantData data{...};
shape.Reify(this, &data);
}
...
void ImplementGeometry(const Sphere& sphere, void* data) override {
DRAKE_ASSERT(data);
ImportantData& data = *reinterpret_cast<ImportantData*>(data);
// Do work to create a sphere using the provided user data.
}
};
```
Implementing a particular shape may require more data than is strictly
encapsulated in the Shape. The Implement* interface supports passing user
data through a type-erased `void*`. Because a single class invoked
Shape::Reify() it is in a position to provide exactly the data the shape
implementations require. */
class ShapeReifier {
public:
virtual ~ShapeReifier() {}
virtual void ImplementGeometry(const Sphere& sphere) = 0;
virtual void ImplementGeometry(const Cylinder& cylinder) = 0;
virtual void ImplementGeometry(const HalfSpace& half_space) = 0;
virtual void ImplementGeometry(const Sphere& sphere, void* user_data) = 0;
virtual void ImplementGeometry(const Cylinder& cylinder, void* user_data) = 0;
virtual void ImplementGeometry(const HalfSpace& half_space,
void* user_data) = 0;
};

template <typename S>
Expand All @@ -161,10 +204,11 @@ Shape::Shape(ShapeTag<S>) {
const S& derived_shape = static_cast<const S&>(shape_arg);
return std::unique_ptr<Shape>(new S(derived_shape));
};
reifier_ = [](const Shape& shape_arg, ShapeReifier* reifier) {
reifier_ = [](const Shape& shape_arg, ShapeReifier* reifier,
void* user_data) {
DRAKE_DEMAND(typeid(shape_arg) == typeid(S));
const S& derived_shape = static_cast<const S&>(shape_arg);
reifier->ImplementGeometry(derived_shape);
reifier->ImplementGeometry(derived_shape, user_data);
};
}

Expand Down
23 changes: 19 additions & 4 deletions geometry/test/shape_specification_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,47 @@ namespace drake {
namespace geometry {
namespace {

using std::move;
using std::unique_ptr;

// Confirm correct interactions with the Reifier.

class ReifierTest : public ShapeReifier, public ::testing::Test {
public:
void ImplementGeometry(const Sphere& sphere) override {
void ImplementGeometry(const Sphere& sphere, void* data) override {
received_user_data_ = data;
sphere_made_ = true;
}
void ImplementGeometry(const Cylinder& cylinder) override {
void ImplementGeometry(const Cylinder& cylinder, void* data) override {
received_user_data_ = data;
cylinder_made_ = true;
}
void ImplementGeometry(const HalfSpace& half_space) override {
void ImplementGeometry(const HalfSpace& half_space, void* data) override {
received_user_data_ = data;
half_space_made_ = true;
}
void Reset() {
sphere_made_ = false;
half_space_made_ = false;
cylinder_made_ = false;
received_user_data_ = nullptr;
}

protected:
bool sphere_made_{false};
bool cylinder_made_{false};
bool half_space_made_{false};
void* received_user_data_{nullptr};
};

// Confirms that user data comes through intact.
TEST_F(ReifierTest, UserData) {
ASSERT_EQ(received_user_data_, nullptr);
Sphere s(1.0);
std::pair<int, double> user_data{3, 5.0};
s.Reify(this, &user_data);
ASSERT_EQ(received_user_data_, &user_data);
}

// This confirms that the shapes invoke the correct Reify method.
TEST_F(ReifierTest, ReificationDifferentiation) {
Sphere s(1.0);
Expand Down

0 comments on commit cc60d97

Please sign in to comment.