Program Listing for File patharray_display.cpp
↰ Return to documentation for file (rviz_path_array/src/patharray_display.cpp)
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2020 LAAS/CNRS
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the institute nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Phani Teja Singamaneni (email:ptsingaman@laas.fr)
*********************************************************************/
#include "patharray_display.h"
#include <OgreBillboardSet.h>
#include <OgreManualObject.h>
#include <OgreMatrix4.h>
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <boost/bind.hpp>
#include "rviz/display_context.h"
#include "rviz/frame_manager.h"
#include "rviz/ogre_helpers/billboard_line.h"
#include "rviz/properties/color_property.h"
#include "rviz/properties/enum_property.h"
#include "rviz/properties/float_property.h"
#include "rviz/properties/int_property.h"
#include "rviz/properties/vector_property.h"
#include "rviz/validate_floats.h"
#include "rviz/validate_quaternions.h"
namespace rviz_path_array {
AgentPathArrayDisplay::AgentPathArrayDisplay() {
style_property_ = new rviz::EnumProperty("Line Style", "Lines", "The rendering operation to use to draw the grid lines.", this, SLOT(updateStyle()));
style_property_->addOption("Lines", LINES);
style_property_->addOption("Billboards", BILLBOARDS);
line_width_property_ = new rviz::FloatProperty("Line Width", 0.03,
"The width, in meters, of each path line."
"Only works with the 'Billboards' style.",
this, SLOT(updateLineWidth()), this);
line_width_property_->setMin(0.001);
line_width_property_->hide();
color_property_ = new rviz::ColorProperty("Color", QColor(25, 255, 0), "Color to draw the path.", this);
alpha_property_ = new rviz::FloatProperty("Alpha", 1.0, "Amount of transparency to apply to the path.", this);
buffer_length_property_ = new rviz::IntProperty("Buffer Length", 1, "Number of paths to display.", this, SLOT(updateBufferLength()));
buffer_length_property_->setMin(1);
offset_property_ = new rviz::VectorProperty("Offset", Ogre::Vector3::ZERO, "Allows you to offset the path from the origin of the reference frame. In meters.", this, SLOT(updateOffset()));
pose_style_property_ = new rviz::EnumProperty("Pose Style", "None", "Shape to display the pose as.", this, SLOT(updatePoseStyle()));
pose_style_property_->addOption("None", NONE);
pose_style_property_->addOption("Axes", AXES);
pose_style_property_->addOption("Arrows", ARROWS);
pose_axes_length_property_ = new rviz::FloatProperty("Length", 0.3, "Length of the axes.", this, SLOT(updatePoseAxisGeometry()));
pose_axes_radius_property_ = new rviz::FloatProperty("Radius", 0.03, "Radius of the axes.", this, SLOT(updatePoseAxisGeometry()));
pose_arrow_color_property_ = new rviz::ColorProperty("Pose Color", QColor(255, 85, 255), "Color to draw the poses.", this, SLOT(updatePoseArrowColor()));
pose_arrow_shaft_length_property_ = new rviz::FloatProperty("Shaft Length", 0.1, "Length of the arrow shaft.", this, SLOT(updatePoseArrowGeometry()));
pose_arrow_head_length_property_ = new rviz::FloatProperty("Head Length", 0.2, "Length of the arrow head.", this, SLOT(updatePoseArrowGeometry()));
pose_arrow_shaft_diameter_property_ = new rviz::FloatProperty("Shaft Diameter", 0.1, "Diameter of the arrow shaft.", this, SLOT(updatePoseArrowGeometry()));
pose_arrow_head_diameter_property_ = new rviz::FloatProperty("Head Diameter", 0.3, "Diameter of the arrow head.", this, SLOT(updatePoseArrowGeometry()));
pose_axes_length_property_->hide();
pose_axes_radius_property_->hide();
pose_arrow_color_property_->hide();
pose_arrow_shaft_length_property_->hide();
pose_arrow_head_length_property_->hide();
pose_arrow_shaft_diameter_property_->hide();
pose_arrow_head_diameter_property_->hide();
}
AgentPathArrayDisplay::~AgentPathArrayDisplay() {
destroyObjects();
destroyPoseAxesChain();
destroyPoseArrowChain();
}
void AgentPathArrayDisplay::onInitialize() {
MFDClass::onInitialize();
updateBufferLength();
}
void AgentPathArrayDisplay::reset() {
MFDClass::reset();
updateBufferLength();
}
void AgentPathArrayDisplay::allocateAxesVector(std::vector<rviz::Axes*>& axes_vect, int num) {
if (num > axes_vect.size()) {
for (size_t i = axes_vect.size(); i < num; i++) {
auto* axes = new rviz::Axes(scene_manager_, scene_node_, pose_axes_length_property_->getFloat(), pose_axes_radius_property_->getFloat());
axes_vect.push_back(axes);
}
} else if (num < axes_vect.size()) {
for (int i = axes_vect.size() - 1; num <= i; i--) {
delete axes_vect[i];
}
axes_vect.resize(num);
}
}
void AgentPathArrayDisplay::allocateArrowVector(std::vector<rviz::Arrow*>& arrow_vect, int num) {
if (num > arrow_vect.size()) {
for (size_t i = arrow_vect.size(); i < num; i++) {
auto* arrow = new rviz::Arrow(scene_manager_, scene_node_);
arrow_vect.push_back(arrow);
}
} else if (num < arrow_vect.size()) {
for (int i = arrow_vect.size() - 1; num <= i; i--) {
delete arrow_vect[i];
}
arrow_vect.resize(num);
}
}
void AgentPathArrayDisplay::destroyPoseAxesChain() {
for (auto& i : axes_chain_) {
allocateAxesVector(i, 0);
}
axes_chain_.resize(0);
}
void AgentPathArrayDisplay::destroyPoseArrowChain() {
for (auto& i : arrow_chain_) {
allocateArrowVector(i, 0);
}
arrow_chain_.resize(0);
}
void AgentPathArrayDisplay::updateStyle() {
auto style = static_cast<LineStyle>(style_property_->getOptionInt());
switch (style) {
case LINES:
default:
line_width_property_->hide();
break;
case BILLBOARDS:
line_width_property_->show();
break;
}
updateBufferLength();
}
void AgentPathArrayDisplay::updateLineWidth() {
auto style = static_cast<LineStyle>(style_property_->getOptionInt());
float line_width = line_width_property_->getFloat();
if (style == BILLBOARDS) {
for (auto* billboard_line : billboard_lines_) {
if (billboard_line) billboard_line->setLineWidth(line_width);
}
}
context_->queueRender();
}
void AgentPathArrayDisplay::updateOffset() {
scene_node_->setPosition(offset_property_->getVector());
context_->queueRender();
}
void AgentPathArrayDisplay::updatePoseStyle() {
auto pose_style = static_cast<PoseStyle>(pose_style_property_->getOptionInt());
switch (pose_style) {
case AXES:
pose_axes_length_property_->show();
pose_axes_radius_property_->show();
pose_arrow_color_property_->hide();
pose_arrow_shaft_length_property_->hide();
pose_arrow_head_length_property_->hide();
pose_arrow_shaft_diameter_property_->hide();
pose_arrow_head_diameter_property_->hide();
break;
case ARROWS:
pose_axes_length_property_->hide();
pose_axes_radius_property_->hide();
pose_arrow_color_property_->show();
pose_arrow_shaft_length_property_->show();
pose_arrow_head_length_property_->show();
pose_arrow_shaft_diameter_property_->show();
pose_arrow_head_diameter_property_->show();
break;
default:
pose_axes_length_property_->hide();
pose_axes_radius_property_->hide();
pose_arrow_color_property_->hide();
pose_arrow_shaft_length_property_->hide();
pose_arrow_head_length_property_->hide();
pose_arrow_shaft_diameter_property_->hide();
pose_arrow_head_diameter_property_->hide();
}
updateBufferLength();
}
void AgentPathArrayDisplay::updatePoseAxisGeometry() {
for (auto& axes_vect : axes_chain_) {
for (auto& j : axes_vect) {
j->set(pose_axes_length_property_->getFloat(), pose_axes_radius_property_->getFloat());
}
}
context_->queueRender();
}
void AgentPathArrayDisplay::updatePoseArrowColor() {
QColor color = pose_arrow_color_property_->getColor();
for (auto& arrow_vect : arrow_chain_) {
for (auto& j : arrow_vect) {
j->setColor(color.redF(), color.greenF(), color.blueF(), 1.0f);
}
}
context_->queueRender();
}
void AgentPathArrayDisplay::updatePoseArrowGeometry() {
for (auto& arrow_vect : arrow_chain_) {
for (auto& j : arrow_vect) {
j->set(pose_arrow_shaft_length_property_->getFloat(), pose_arrow_shaft_diameter_property_->getFloat(), pose_arrow_head_length_property_->getFloat(),
pose_arrow_head_diameter_property_->getFloat());
}
}
context_->queueRender();
}
void AgentPathArrayDisplay::destroyObjects() {
// Destroy all simple lines, if any
for (auto& manual_object : manual_objects_) {
if (manual_object) {
manual_object->clear();
scene_manager_->destroyManualObject(manual_object);
manual_object = nullptr; // ensure it doesn't get destroyed again
}
}
// Destroy all billboards, if any
for (auto& billboard_line : billboard_lines_) {
if (billboard_line) {
delete billboard_line; // also destroys the corresponding scene node
billboard_line = nullptr; // ensure it doesn't get destroyed again
}
}
}
void AgentPathArrayDisplay::updateBufferLength() {
// Delete old path objects
destroyObjects();
// Destroy all axes and arrows
destroyPoseAxesChain();
destroyPoseArrowChain();
// Read options
int buffer_length = buffer_length_property_->getInt();
auto style = static_cast<LineStyle>(style_property_->getOptionInt());
// Create new path objects
switch (style) {
case LINES: // simple lines with fixed width of 1px
manual_objects_.resize(buffer_length);
for (auto& i : manual_objects_) {
Ogre::ManualObject* manual_object = scene_manager_->createManualObject();
manual_object->setDynamic(true);
scene_node_->attachObject(manual_object);
i = manual_object;
}
break;
case BILLBOARDS: // billboards with configurable width
billboard_lines_.resize(buffer_length);
for (auto& i : billboard_lines_) {
auto* billboard_line = new rviz::BillboardLine(scene_manager_, scene_node_);
i = billboard_line;
}
break;
}
axes_chain_.resize(buffer_length);
arrow_chain_.resize(buffer_length);
}
bool validateFloats(const cohan_msgs::AgentPathArray& msg) {
bool valid = true;
for (const auto& path : msg.paths) {
valid = valid && rviz::validateFloats(path.path.poses);
}
return valid;
}
void AgentPathArrayDisplay::processMessage(const cohan_msgs::AgentPathArray::ConstPtr& msg) {
// Calculate index of oldest element in cyclic buffer
size_t buffer_index = messages_received_ % buffer_length_property_->getInt();
auto style = static_cast<LineStyle>(style_property_->getOptionInt());
Ogre::ManualObject* manual_object = nullptr;
rviz::BillboardLine* billboard_line = nullptr;
// Delete oldest element
switch (style) {
case LINES:
manual_object = manual_objects_[buffer_index];
manual_object->clear();
break;
case BILLBOARDS:
billboard_line = billboard_lines_[buffer_index];
billboard_line->clear();
break;
}
// Check if path contains invalid coordinate values
if (!validateFloats(*msg)) {
setStatus(rviz::StatusProperty::Error, "Topic", "Message contained invalid floating point values (nans or infs)");
return;
}
bool val_quat = true;
for (const auto& path : msg->paths) {
val_quat = val_quat && rviz::validateQuaternions(path.path.poses);
}
if (!val_quat) {
ROS_WARN_ONCE_NAMED("quaternions",
"Path '%s' contains unnormalized quaternions. "
"This warning will only be output once but may be true for others; "
"enable DEBUG messages for ros.rviz.quaternions to see more details.",
qPrintable(getName()));
ROS_DEBUG_NAMED("quaternions", "Path '%s' contains unnormalized quaternions.", qPrintable(getName()));
}
// Lookup transform into fixed frame
Ogre::Vector3 position;
Ogre::Quaternion orientation;
if (!context_->getFrameManager()->getTransform(msg->header, position, orientation)) {
ROS_DEBUG("Error transforming from frame '%s' to frame '%s'", msg->header.frame_id.c_str(), qPrintable(fixed_frame_));
}
Ogre::Matrix4 transform(orientation);
transform.setTrans(position);
// scene_node_->setPosition( position );
// scene_node_->setOrientation( orientation );
Ogre::ColourValue color = color_property_->getOgreColor();
color.a = alpha_property_->getFloat();
float line_width = line_width_property_->getFloat();
for (const auto& path : msg->paths) {
uint32_t num_points = path.path.poses.size();
switch (style) {
case LINES:
manual_object->estimateVertexCount(num_points);
manual_object->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP);
for (uint32_t i = 0; i < num_points; ++i) {
const geometry_msgs::Point& pos = path.path.poses[i].pose.position;
Ogre::Vector3 xpos = transform * Ogre::Vector3(pos.x, pos.y, pos.z);
manual_object->position(xpos.x, xpos.y, xpos.z);
manual_object->colour(color);
}
manual_object->end();
break;
case BILLBOARDS:
billboard_line->setNumLines(1);
billboard_line->setMaxPointsPerLine(num_points);
billboard_line->setLineWidth(line_width);
for (uint32_t i = 0; i < num_points; ++i) {
const geometry_msgs::Point& pos = path.path.poses[i].pose.position;
Ogre::Vector3 xpos = transform * Ogre::Vector3(pos.x, pos.y, pos.z);
billboard_line->addPoint(xpos, color);
}
break;
}
// process pose markers
auto pose_style = static_cast<PoseStyle>(pose_style_property_->getOptionInt());
std::vector<rviz::Arrow*>& arrow_vect = arrow_chain_[buffer_index];
std::vector<rviz::Axes*>& axes_vect = axes_chain_[buffer_index];
switch (pose_style) {
case AXES:
allocateAxesVector(axes_vect, num_points);
for (uint32_t i = 0; i < num_points; ++i) {
const geometry_msgs::Point& pos = path.path.poses[i].pose.position;
const geometry_msgs::Quaternion& quat = path.path.poses[i].pose.orientation;
Ogre::Vector3 xpos = transform * Ogre::Vector3(pos.x, pos.y, pos.z);
Ogre::Quaternion xquat = orientation * Ogre::Quaternion(quat.w, quat.x, quat.y, quat.z);
axes_vect[i]->setPosition(xpos);
axes_vect[i]->setOrientation(xquat);
}
break;
case ARROWS:
allocateArrowVector(arrow_vect, num_points);
for (uint32_t i = 0; i < num_points; ++i) {
const geometry_msgs::Point& pos = path.path.poses[i].pose.position;
const geometry_msgs::Quaternion& quat = path.path.poses[i].pose.orientation;
Ogre::Vector3 xpos = transform * Ogre::Vector3(pos.x, pos.y, pos.z);
Ogre::Quaternion xquat = orientation * Ogre::Quaternion(quat.w, quat.x, quat.y, quat.z);
QColor color = pose_arrow_color_property_->getColor();
arrow_vect[i]->setColor(color.redF(), color.greenF(), color.blueF(), 1.0f);
arrow_vect[i]->set(pose_arrow_shaft_length_property_->getFloat(), pose_arrow_shaft_diameter_property_->getFloat(), pose_arrow_head_length_property_->getFloat(),
pose_arrow_head_diameter_property_->getFloat());
arrow_vect[i]->setPosition(xpos);
arrow_vect[i]->setDirection(xquat * Ogre::Vector3(1, 0, 0));
}
break;
default:
break;
}
context_->queueRender();
}
}
} // namespace rviz_path_array
#include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(rviz_path_array::AgentPathArrayDisplay, rviz::Display)