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

Scale mode - Part2 #881

Merged
merged 23 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from 18 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
55 changes: 54 additions & 1 deletion include/ignition/gazebo/components/Model.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
#ifndef IGNITION_GAZEBO_COMPONENTS_MODEL_HH_
#define IGNITION_GAZEBO_COMPONENTS_MODEL_HH_

#include <string>

#include <sdf/Model.hh>
#include <sdf/Root.hh>

#include <ignition/gazebo/components/Factory.hh>
#include <ignition/gazebo/components/Component.hh>
Expand All @@ -29,14 +32,64 @@ namespace gazebo
{
// Inline bracket to help doxygen filtering.
inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
namespace serializers
{
class SdfModelSerializer
jennuine marked this conversation as resolved.
Show resolved Hide resolved
{
/// \brief Serialization for `sdf::Model`.
/// \param[in] _out Output stream.
/// \param[in] _time Model to stream
/// \return The stream.
public: static std::ostream &Serialize(std::ostream &_out,
const sdf::Model &_model)
{
sdf::ElementPtr modelElem = _model.Element();
if (!modelElem)
{
ignerr << "Unable to serialize sdf::Model" << std::endl;
return _out;
}

_out << "<?xml version=\"1.0\" ?>"
<< "<sdf version='" << SDF_PROTOCOL_VERSION << "'>"
<< modelElem->ToString("")
<< "</sdf>";
return _out;
}

/// \brief Deserialization for `sdf::Model`.
/// \param[in] _in Input stream.
/// \param[out] _model Model to populate
/// \return The stream.
public: static std::istream &Deserialize(std::istream &_in,
sdf::Model &_model)
{
sdf::Root root;
std::string sdf(std::istreambuf_iterator<char>(_in), {});

sdf::Errors errors = root.LoadSdfString(sdf);
if (!errors.empty())
jennuine marked this conversation as resolved.
Show resolved Hide resolved
{
ignerr << "Unable to unserialize sdf::Model" << std::endl;
return _in;
}

_model.Load(root.Element()->GetElement("model"));
return _in;
}
};
}

namespace components
{
/// \brief A component that identifies an entity as being a model.
using Model = Component<NoData, class ModelTag>;
IGN_GAZEBO_REGISTER_COMPONENT("ign_gazebo_components.Model", Model)

/// \brief A component that holds the model's SDF DOM
using ModelSdf = Component<sdf::Model, class ModelTag>;
using ModelSdf = Component<sdf::Model,
class ModelTag,
serializers::SdfModelSerializer>;
IGN_GAZEBO_REGISTER_COMPONENT("ign_gazebo_components.ModelSdf", ModelSdf)
}
}
Expand Down
6 changes: 6 additions & 0 deletions include/ignition/gazebo/rendering/RenderUtil.hh
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
/// \param[in] _active True if active.
public: void SetTransformActive(bool _active);

/// Set the scale of a given wirebox.
/// \param[in] _entityId The entity Id.
/// \param[in] _scale Scale vector in absolute coordinates.
public: void SetWireBoxScale(const Entity &_entityId,
const math::Vector3d &_scale);

/// \brief Private data pointer.
private: std::unique_ptr<RenderUtilPrivate> dataPtr;
};
Expand Down
101 changes: 99 additions & 2 deletions src/gui/plugins/scene3d/Scene3D.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <limits>
#include <map>
#include <mutex>
#include <queue>
#include <sstream>
#include <string>
#include <utility>
Expand Down Expand Up @@ -1341,6 +1342,52 @@ void IgnRenderer::XYZConstraint(math::Vector3d &_axis)
}
}

/////////////////////////////////////////////////
rendering::VisualPtr IgnRenderer::ContainsSimpleShape(
const rendering::NodePtr &_node) const
{
std::queue<rendering::NodePtr> q;

// Adding the root node.
q.push(_node);

while (!q.empty())
{
// Process the next node.
auto n = q.front();
q.pop();
if (n)
{
auto v = std::dynamic_pointer_cast<ignition::rendering::Visual>(n);
if (v)
{
try
{
int userData = std::get<int>(v->UserData("geometry-type"));

/// \TODO(anyone) Consider sdf::GeometryType::CAPSULE and
/// sdf::GeometryType::ELLIPSOID in Edifice.
if (userData == static_cast<int>(sdf::GeometryType::BOX) ||
userData == static_cast<int>(sdf::GeometryType::CYLINDER) ||
userData == static_cast<int>(sdf::GeometryType::SPHERE))
{
return v;
}
}
catch (const std::bad_variant_access& ex)
{
}
}

// Add all children to the queue to be considered.
for (auto i = 0u; i < n->ChildCount(); ++i)
q.push(n->ChildByIndex(i));
}
}

return nullptr;
}

/////////////////////////////////////////////////
void IgnRenderer::HandleMouseTransformControl()
{
Expand Down Expand Up @@ -1607,10 +1654,20 @@ void IgnRenderer::HandleMouseTransformControl()
else if (this->dataPtr->transformControl.Mode() ==
rendering::TransformMode::TM_SCALE)
{
// Check if the model that we're trying to scale looks like a simple shape
auto node = this->dataPtr->transformControl.Node();
auto v = std::dynamic_pointer_cast<ignition::rendering::Visual>(node);
rendering::VisualPtr visualSimple = this->ContainsSimpleShape(node);
if (!visualSimple)
return;

int userData = std::get<int>(v->UserData("geometry-type"));
sdf::GeometryType geomType = static_cast<sdf::GeometryType>(userData);

this->XYZConstraint(axis);
// note: scaling is limited to local space
math::Vector3d scale =
this->dataPtr->transformControl.ScaleFrom2d(axis, start, end);
this->dataPtr->transformControl.ScaleFrom2d(axis, start, end);
if (this->dataPtr->keyEvent.Control())
{
math::Vector3d snapVals = this->ScaleSnap();
Expand All @@ -1624,14 +1681,54 @@ void IgnRenderer::HandleMouseTransformControl()

SnapPoint(scale, snapVals);
}

// Apply geometry constaints to the scaling.
if (geomType == sdf::GeometryType::SPHERE)
{
if (axis.X() > 0)
{
scale.Y(scale.X());
scale.Z(scale.X());
}
else if (axis.Y() > 0)
{
scale.X(scale.Y());
scale.Z(scale.Y());
}
else if (axis.Z() > 0)
{
scale.X(scale.Z());
scale.Y(scale.Z());
}
}
else if (geomType == sdf::GeometryType::CYLINDER)
{
if (axis.X() > 0)
{
scale.Y(scale.X());
}
else if (axis.Y() > 0)
{
scale.X(scale.Y());
}
}

// Scale.
this->dataPtr->transformControl.Scale(scale);

// Update the bounding box.
if (v)
{
Entity entityId = std::get<int>(v->UserData("gazebo-entity"));
auto s = this->dataPtr->transformControl.Node()->LocalScale();
this->dataPtr->renderUtil.SetWireBoxScale(entityId, s);
}
}
this->dataPtr->drag = 0;
this->dataPtr->mouseDirty = false;
}
}


/////////////////////////////////////////////////
void IgnRenderer::HandleMouseViewControl()
{
Expand Down
9 changes: 9 additions & 0 deletions src/gui/plugins/scene3d/Scene3D.hh
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
/// when the deselection is initiated from this plugin.
private: void DeselectAllEntities(bool _sendEvent);

/// \brief Helper function that traverses the node tree looking for any
/// child with the "geometry-type" user data. If found, the function also
/// checks if the value is a simple shape (box, cylinder or sphere).
/// \param[in] _node Root node.
/// \return The visual pointer to the simple shape if found or nullptr
/// otherwise.
private: rendering::VisualPtr ContainsSimpleShape(
const rendering::NodePtr &_node) const;

/// \brief Signal fired when context menu event is triggered
signals: void ContextMenuRequested(QString _entity);

Expand Down
4 changes: 4 additions & 0 deletions src/gui/plugins/transform_control/TransformControl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ bool TransformControl::eventFilter(QObject *_obj, QEvent *_event)
{
this->activateRotate();
}
else if (keyEvent->key() == Qt::Key_S)
{
this->activateScale();
}
}
else if (_event->type() == QEvent::KeyRelease)
{
Expand Down
3 changes: 3 additions & 0 deletions src/gui/plugins/transform_control/TransformControl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ namespace gazebo
/// \brief Notify that rotation has been activated
signals: void activateRotate();

/// \brief Notify that scale has been activated
signals: void activateScale();

/// \internal
/// \brief Pointer to private data.
private: std::unique_ptr<TransformControlPrivate> dataPtr;
Expand Down
18 changes: 9 additions & 9 deletions src/gui/plugins/transform_control/TransformControl.qml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ ToolBar {
Layout.minimumHeight: 100

// TODO(anyone) enable scale button when support is added in ign-physics
// function activateScale() {
// scale.checked = true;
// TransformControl.OnMode("scale");
// }
function activateScale() {
scale.checked = true;
TransformControl.OnMode("scale");
}

property color snapTitle: (Material.theme == Material.Light) ?
Material.color(Material.Grey, Material.Shade900) :
Expand Down Expand Up @@ -106,6 +106,11 @@ ToolBar {
onActivateRotate: activateRotate();
}

Connections {
target: TransformControl
onActivateScale: activateScale();
}

RowLayout {
spacing: 2
ToolButton {
Expand Down Expand Up @@ -219,10 +224,6 @@ ToolBar {
TransformControl.OnMode("rotate")
}
}
// TODO(anyone) enable scale snap values below when support is added in ign-physics
// Also be sure to replace the placeholder 0's in all of the `OnSnapUpdate` calls in
// this file to xScaleEntry.value, yScaleEntry.value, and zScaleEntry.value, respectively
/*
ToolButton {
id: scale
text: "S"
Expand Down Expand Up @@ -260,7 +261,6 @@ ToolBar {
TransformControl.OnMode("scale")
}
}
*/

ToolButton {
id: snap
Expand Down
33 changes: 33 additions & 0 deletions src/rendering/RenderUtil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,39 @@ void RenderUtilPrivate::LowlightNode(const rendering::NodePtr &_node)
}
}

/////////////////////////////////////////////////
void RenderUtil::SetWireBoxScale(const Entity &_entityId,
const math::Vector3d &_scale)
{
/// \TODO(anyone) Consider to use this->dataPtr->sceneManager.NodeById()
/// when WireBoxes are tracked in the scene manager.
if (this->dataPtr->wireBoxes.find(_entityId) ==
this->dataPtr->wireBoxes.end())
{
ignerr << "Trying to scale a wirebox with unknown id [" << _entityId
<< "]" << std::endl;
return;
}

ignition::rendering::WireBoxPtr wireBox = this->dataPtr->wireBoxes[_entityId];
if (!wireBox)
{
ignerr << "Null wirebox associated to id [" << _entityId
<< "]" << std::endl;
return;
}

auto visParent = wireBox->Parent();
if (!visParent)
{
ignerr << "Trying to scale a wirebox with null parent [" << _entityId
<< "]" << std::endl;
return;
}

visParent->SetLocalScale(_scale);
}

/////////////////////////////////////////////////
void RenderUtil::ViewCollisions(const Entity &_entity)
{
Expand Down
5 changes: 5 additions & 0 deletions src/rendering/SceneManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ rendering::VisualPtr SceneManager::CreateVisual(Entity _id,
rendering::VisualPtr visualVis = this->dataPtr->scene->CreateVisual(name);
visualVis->SetUserData("gazebo-entity", static_cast<int>(_id));
visualVis->SetUserData("pause-update", static_cast<int>(0));

auto geometry = _visual.Geom();
if (geometry)
visualVis->SetUserData("geometry-type", static_cast<int>(geometry->Type()));

visualVis->SetLocalPose(_visual.RawPose());

if (_visual.HasLaserRetro())
Expand Down
Loading