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

Add arrow to selected lanes #223

Merged
merged 3 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions delphyne-gui/visualizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ add_library(${maliput_viewer_widget} SHARED
maliput_viewer_model.cc
orbit_view_control.cc
render_maliput_widget.cc
arrow_mesh.cc
${${maliput_viewer_widget}_MOC}
${LayerSelectionWidget_MOC}
${RenderMaliputWidget_MOC}
Expand Down
85 changes: 85 additions & 0 deletions delphyne-gui/visualizer/arrow_mesh.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2018 Toyota Research Institute

#include "arrow_mesh.hh"
#include <ignition/common/MeshManager.hh>
#include <ignition/math/Helpers.hh>
#include <ignition/rendering/Material.hh>
#include <ignition/rendering/Mesh.hh>
#include <ignition/rendering/Scene.hh>

namespace delphyne {
namespace gui {

ArrowMesh::ArrowMesh(ignition::rendering::ScenePtr& _scene, double _zOffset, double _scaleFactor)
: zOffset(_zOffset),
scaleFactor(_scaleFactor),
distanceToMove(zOffset / 4.0),
step(0.005),
totalTicks(distanceToMove / step),
currentTick(0),
currentDirection(-1) {
ignition::rendering::MaterialPtr material = _scene->CreateMaterial();
material->SetDiffuse(255.0, 0.0, 0.0, 1.0);
material->SetAmbient(255.0, 0.0, 0.0, 1.0);
this->arrow = _scene->CreateVisual();
this->arrow->AddGeometry(_scene->CreateCone());
this->arrow->SetMaterial(material);
this->arrow->SetVisible(false);
this->arrow->SetWorldPosition(0., 0., 0.);
this->arrow->SetWorldRotation(0, IGN_PI, 0.);
_scene->RootVisual()->AddChild(this->arrow);
this->CreateCylinder(_scene);
ignition::common::MeshManager* meshManager = ignition::common::MeshManager::Instance();
const ignition::common::Mesh* unitConeMesh = meshManager->MeshByName("unit_cone");
minArrowBoundingBox = unitConeMesh->Min();
}

void ArrowMesh::SelectAt(double _distanceFromCamera, const ignition::math::Vector3d& _worldPosition) {
const double scaleIncrement = 1.0 + scaleFactor * _distanceFromCamera;
const double newMinArrowBBZAxis = scaleIncrement * minArrowBoundingBox.Z();
this->arrow->SetWorldScale(scaleIncrement, scaleIncrement, scaleIncrement);
Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi I see what you're doing here, but does it scale appropriately if you zoom in/out? Should the scaling code be separate and called upon zoom?

Copy link
Author

Choose a reason for hiding this comment

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

In practice it looks fine to me. I wanted to avoid user interaction with the arrow's scale directly

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I'll take a look locally.

this->arrow->SetWorldPosition(_worldPosition.X(), _worldPosition.Y(),
_worldPosition.Z() + std::abs(newMinArrowBBZAxis) + zOffset);
this->cylinder->SetWorldPosition(_worldPosition.X(), _worldPosition.Y(),
_worldPosition.Z() + std::abs(minCylinderBoundingBox.Z()));
currentDirection = -1;
currentTick = 0;
}

void ArrowMesh::SetVisibility(bool _visible) {
this->arrow->SetVisible(_visible);
this->cylinder->SetVisible(_visible);
}

void ArrowMesh::Update() {
ignition::math::Vector3d worldPosition = this->arrow->WorldPosition();
this->arrow->SetWorldPosition(worldPosition.X(), worldPosition.Y(), worldPosition.Z() + currentDirection * step);
++currentTick;
if (currentTick == totalTicks) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi nit: last two lines could be collapsed into one.

currentDirection = currentDirection * -1;
currentTick = 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi haha you did it. Consider turning moveDown into a an integer that can be either -1 or 1 and multiples step before summation. It'll simplify the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi nit: can we call moveDown something else e.g. currentDirection ?

}

void ArrowMesh::CreateCylinder(ignition::rendering::ScenePtr& _scene) {
ignition::rendering::MaterialPtr material = _scene->CreateMaterial();
material->SetDiffuse(255.0, 0.0, 0.0, 1.0);
material->SetAmbient(255.0, 0.0, 0.0, 1.0);
this->cylinder = _scene->CreateVisual();
this->cylinder->AddGeometry(_scene->CreateCylinder());
this->cylinder->SetMaterial(material);
this->cylinder->SetVisible(false);
this->cylinder->SetWorldPosition(0., 0., 0.);
// This size is enough for visualization.
this->cylinder->SetWorldScale(0.5, 0.5, 0.2);
Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi as I told you offline, this is likely to run into some issues when dealing with very small lanes. Unless we somehow bound its size by the lane's area, which is going to be tough. Maybe once we get an outline we don't need this one anymore?

Copy link
Author

Choose a reason for hiding this comment

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

The arrow is useful but I think that with the outliner, the cylinder isn't necessary

_scene->RootVisual()->AddChild(this->cylinder);
ignition::common::MeshManager* meshManager = ignition::common::MeshManager::Instance();
const ignition::common::Mesh* unitCylinderMesh = meshManager->MeshByName("unit_cylinder");
minCylinderBoundingBox = unitCylinderMesh->Min();
minCylinderBoundingBox.X() = minCylinderBoundingBox.X() * 0.5;
minCylinderBoundingBox.Y() = minCylinderBoundingBox.Y() * 0.5;
minCylinderBoundingBox.Z() = minCylinderBoundingBox.Z() * 0.2;
}

} // namespace gui
} // namespace delphyne
71 changes: 71 additions & 0 deletions delphyne-gui/visualizer/arrow_mesh.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2018 Toyota Research Institute

#ifndef DELPHYNE_GUI_ARROW_MESH_HH
#define DELPHYNE_GUI_ARROW_MESH_HH

#include <ignition/math/Vector3.hh>
#include <ignition/rendering/Scene.hh>

namespace delphyne {
namespace gui {

/// \brief Renders a cone which will act as a pointing arrow when the user clicks over a lane
class ArrowMesh {
public:
/// \brief Creates a cone and adds it as a child to the RootVisual of the scene, which will move
/// upwards and downwards for a fixed amount of ticks.
/// \param[in] _scene Scene pointer to create the cone.
/// \param[in] _zOffset Units above the pointed object that the cone tip should be.
/// \param[in] _scaleFactor Factor to increase/decrease the scale of the arrow based on the distance from the camera
/// to the clicked position.
ArrowMesh(ignition::rendering::ScenePtr& _scene, double _zOffset = 2.0, double _scaleFactor = 0.025);
/// \brief Destructor.
~ArrowMesh() = default;

/// \brief Moves the arrow to a given world position and resets the downwards movement.
/// \param[in] _distanceFromCamera How far the camera is from the clicked point.
/// \param[in] _worldPosition World position of the cursor ray cast click.
void SelectAt(double _distanceFromCamera, const ignition::math::Vector3d& _worldPosition);

/// \brief Toggles the visibility of the arrow.
Copy link
Contributor

Choose a reason for hiding this comment

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

@BMarchi nit: add one extra blank line here.

/// \param[in] _visible Boolean that determines if the arrow should be visible or not.
void SetVisibility(bool _visible);

/// \brief Updates the position of the arrow moving slightly the z axis.
void Update();

private:
/// \brief Creates a red cylinder that will be below the arrow to emphasize the click position.
void CreateCylinder(ignition::rendering::ScenePtr& _scene);

/// \brief Bounding box of the cone in construction time.
ignition::math::Vector3d minArrowBoundingBox;

/// \brief Bounding box of the cylinder in construction time
ignition::math::Vector3d minCylinderBoundingBox;

/// \brief Visual pointer of the created cone.
ignition::rendering::VisualPtr arrow;

/// \brief Small cylinder that will be below the arrow to emphasize the click position.
ignition::rendering::VisualPtr cylinder;

/// \brief Units that the tip of the cone should be from the clicked position.
const double zOffset;
/// \brief Factor to increase/decrease the size of the cone.
const double scaleFactor;
/// \brief Distance to move downwards/upwards.
const double distanceToMove;
/// \brief How much units should the arrow move downwards/upwards per tick.
const double step;
/// \brief How much ticks required to move the arrow from down to up or up to down.
const int totalTicks;
/// \brief How many ticks passed since it started moving.
int currentTick;
/// \brief Tells the arrow if it should move upwards (1) or downwards (-1).
int currentDirection;
};
} // namespace gui
} // namespace delphyne

#endif // DELPHYNE_GUI_ARROW_MESH_HH
5 changes: 5 additions & 0 deletions delphyne-gui/visualizer/maliput_viewer_widget.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath) {
this->rulesVisualizerWiget->AddLaneId(lane_ids[i]);
}

this->renderWidget->RenderArrow();
this->renderWidget->RenderRoadMeshes(this->model->Meshes());
this->renderWidget->RenderLabels(this->model->Labels());

Expand All @@ -93,6 +94,10 @@ void MaliputViewerWidget::OnVisualClicked(ignition::rendering::RayQueryResult ra
const std::string& lane_id = lane->id().string();
ignmsg << "Clicked lane ID: " << lane_id << "\n";
OnRulesForLaneRequested(QString(lane_id.c_str()));
this->renderWidget->PutArrowAt(rayResult.distance, rayResult.point);
this->renderWidget->SetArrowVisibility(true);
} else {
this->renderWidget->SetArrowVisibility(false);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions delphyne-gui/visualizer/maliput_viewer_widget.hh
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class MaliputViewerWidget : public ignition::gui::Plugin {
/// \brief Builds the widgets of the GUI.
void BuildGUI();

void RenderArrow();

/// \brief Widget to hold and modify the visualization status of each layer.
LayerSelectionWidget* layerSelectionWidget{nullptr};

Expand Down
33 changes: 32 additions & 1 deletion delphyne-gui/visualizer/render_maliput_widget.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@

#include <maliput-utilities/generate_obj.h>

#include <memory>

#include "arrow_mesh.hh"

using namespace delphyne;
using namespace gui;

Expand Down Expand Up @@ -57,6 +61,7 @@ RenderMaliputWidget::~RenderMaliputWidget() {
// For right now, disable this, but we should debug this and re-enable this
// cleanup.
// this->engine->Fini();
this->arrow.reset();
this->orbitViewControl.reset();
this->camera->RemoveChildren();
this->camera.reset();
Expand Down Expand Up @@ -417,6 +422,23 @@ void RenderMaliputWidget::RenderLabels(const std::map<MaliputLabelType, std::vec
}
}

/////////////////////////////////////////////////
void RenderMaliputWidget::RenderArrow() {
if (arrow == nullptr) {
arrow = std::make_unique<ArrowMesh>(this->scene, 0.5);
}
}

void RenderMaliputWidget::PutArrowAt(double _distance, const ignition::math::Vector3d& _worldPosition) {
DELPHYNE_DEMAND(this->arrow != nullptr);
this->arrow->SelectAt(_distance, _worldPosition);
}

void RenderMaliputWidget::SetArrowVisibility(bool _visible) {
DELPHYNE_DEMAND(arrow != nullptr);
arrow->SetVisibility(_visible);
}

/////////////////////////////////////////////////
void RenderMaliputWidget::Clear() {
// Clears the text labels.
Expand Down Expand Up @@ -460,6 +482,9 @@ void RenderMaliputWidget::paintEvent(QPaintEvent* _e) {
if (this->renderWindow && this->camera) {
this->camera->Update();
}
if (arrow) {
arrow->Update();
}

_e->accept();
}
Expand Down Expand Up @@ -500,9 +525,10 @@ void RenderMaliputWidget::mousePressEvent(QMouseEvent* _e) {
const ignition::rendering::RayQueryResult& rayResult = this->orbitViewControl->GetQueryResult();
if (rayResult.distance > 0 && this->camera->Scene()->VisualById(rayResult.objectId) != nullptr) {
emit VisualClicked(rayResult);
} else {
SetArrowVisibility(false);
}
}

this->UpdateViewport();
}

Expand Down Expand Up @@ -535,6 +561,11 @@ void RenderMaliputWidget::wheelEvent(QWheelEvent* _e) {
}

this->orbitViewControl->OnMouseWheel(_e);
const ignition::rendering::RayQueryResult& rayResult = this->orbitViewControl->GetQueryResult();
if (rayResult.distance > 0 && this->camera->Scene()->VisualById(rayResult.objectId) != nullptr) {
const double distance = this->camera->WorldPosition().Distance(this->orbitViewControl->GetQueryResult().point);
this->PutArrowAt(distance, this->orbitViewControl->GetQueryResult().point);
}

this->UpdateViewport();
}
Expand Down
16 changes: 16 additions & 0 deletions delphyne-gui/visualizer/render_maliput_widget.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <QtWidgets/QWidget>

#include "arrow_mesh.hh"
#include "maliput_viewer_model.hh"
#include "orbit_view_control.hh"

Expand Down Expand Up @@ -71,6 +72,18 @@ class RenderMaliputWidget : public QWidget {
// this method to properly set the transparency.
void RenderLabels(const std::map<MaliputLabelType, std::vector<MaliputLabel>>& _labels);

/// \brief Create and render an arrow that will be positioned slightly above the selected road.
void RenderArrow();

/// \brief Move the arrow based on the distance travelled by the camera's ray distance.
/// \param[in] _distance Distance travelled from the camera's world position to the clicked object.
/// \param[in] _worldPosition Position where the camera's ray hit.
void PutArrowAt(double _distance, const ignition::math::Vector3d& _worldPosition);

/// \brief Change visibility of the arrow mesh.
/// \param[in] _visible Visibility of the arrow.
void SetArrowVisibility(bool _visible);

/// \brief Clears all the references to text labels, meshes and the scene.
void Clear();

Expand Down Expand Up @@ -189,6 +202,9 @@ class RenderMaliputWidget : public QWidget {
/// \brief Controls the view of the scene.
std::unique_ptr<OrbitViewControl> orbitViewControl;

/// \brief Arrow that points the location clicked in the visualizer.
std::unique_ptr<ArrowMesh> arrow;

/// \brief A pointer to the rendering engine
ignition::rendering::RenderEngine* engine;

Expand Down