From 3b2c57462408f6d214dc4f72f5787aa113a2dff3 Mon Sep 17 00:00:00 2001
From: Antoine Lima <antoine.lima@hds.utc.fr>
Date: Tue, 16 Feb 2021 13:16:13 +0100
Subject: [PATCH] Rewrite to separate perception display from state visuals

Hopping this will boost performance as now we are building a big
array of simple object instead of an array of complex objects (themselves
composed of simple objects)
---
 src/perceptions_display.cpp | 142 ++++++++++++++++--------------------
 src/perceptions_display.h   |  27 ++-----
 src/state_display.cpp       |  60 ++++++---------
 src/state_display.h         |  20 ++---
 src/state_visual.cpp        |  80 ++++----------------
 src/state_visual.h          |  52 +------------
 6 files changed, 115 insertions(+), 266 deletions(-)

diff --git a/src/perceptions_display.cpp b/src/perceptions_display.cpp
index 1caa158..4e5b689 100644
--- a/src/perceptions_display.cpp
+++ b/src/perceptions_display.cpp
@@ -9,6 +9,7 @@
 #include <rviz/default_plugin/covariance_property.h>
 #include <rviz/properties/int_property.h>
 #include <rviz/ogre_helpers/shape.h>
+#include <rviz/ogre_helpers/arrow.h>
 #include <rviz/ogre_helpers/movable_text.h>
 #include <rviz/ogre_helpers/billboard_line.h>
 #include <rviz/frame_manager.h>
@@ -19,8 +20,6 @@
 namespace rviz_multiception
 {
 
-// The constructor must have no arguments, so we can't give the
-// constructor the parameters it needs to fully initialize.
 PerceptionsDisplay::PerceptionsDisplay()
 {
 	color_property_ = new rviz::ColorProperty( "Color", QColor( 10, 10, 10 ),
@@ -47,55 +46,55 @@ PerceptionsDisplay::PerceptionsDisplay()
 	                                                     this, SLOT( queueRender() ));
 }
 
-// After the top-level rviz::Display::initialize() does its own setup,
-// it calls the subclass's onInitialize() function. This is where we
-// instantiate all the workings of the class. We make sure to also
-// call our immediate super-class's onInitialize() function, since it
-// does important stuff setting up the message filter.
-//
-//  Note that "MFDClass" is a typedef of
-// ``MessageFilterDisplay<message type>``, to save typing that long
-// templated class name every time you need to refer to the
-// superclass.
 void PerceptionsDisplay::onInitialize()
 {
 	MFDClass::onInitialize();
+	frame_node_ = scene_node_->createChildSceneNode();
 }
 
 PerceptionsDisplay::~PerceptionsDisplay()
 {
+	scene_manager_->destroySceneNode(frame_node_);
 }
 
 // Clear the visuals by deleting their objects.
 void PerceptionsDisplay::reset()
 {
 	MFDClass::reset();
-	state_visuals_.clear();
-	shape_visuals_.clear();
-	text_visuals_.clear();
-	trace_buffer_.clear();
-	trace_visuals_.clear();
-	for(Ogre::SceneNode* textNode: text_nodes_) context_->getSceneManager()->destroySceneNode(textNode);
-	text_nodes_.clear();
-	text_visuals_.clear();
+	resetVectors();
+}
+
+void PerceptionsDisplay::resetVectors(size_t N)
+{
+	pose_visuals_.clear();  pose_visuals_.reserve(N);
+	shape_visuals_.clear(); shape_visuals_.reserve(N);
+	cov_visuals_.clear();   cov_visuals_.reserve(N);
+	text_visuals_.clear();  text_visuals_.reserve(N);
+	for(Ogre::SceneNode* textNode: text_nodes_) scene_manager_->destroySceneNode(textNode);
+	text_nodes_.clear();    text_nodes_.reserve(N);
+
+	frame_node_->detachAllObjects();
+	frame_node_->removeAndDestroyAllChildren();
 }
 
 // Set the current color and alpha values for each visual.
 void PerceptionsDisplay::updateColorAndAlpha()
 {
-	float alpha = alpha_property_->getFloat();
-	float shape_alpha = shape_alpha_property_->getFloat();
-	Ogre::ColourValue color = color_property_->getOgreColor();
+	const float alpha = alpha_property_->getFloat();
+	const float shape_alpha = shape_alpha_property_->getFloat();
+	const Ogre::ColourValue color = color_property_->getOgreColor();
+	const Ogre::ColourValue textColor = text_color_property_->getOgreColor();
 
-	for(size_t i=0; i<state_visuals_.size(); i++)
+	for(size_t i=0; i<pose_visuals_.size(); i++)
 	{
-		state_visuals_[i]->setColor(color.r, color.g, color.b, alpha);
-		shape_visuals_[i]->setColor(color.r, color.g, color.b, shape_alpha);
+		pose_visuals_[i]->setColor(color.r, color.g, color.b, alpha);
+		shape_visuals_[i]->setColor(color.r, color.g, color.b, alpha*shape_alpha);
+// 		cov_visuals_[i]->setColor(color.r, color.g, color.b, alpha*shape_alpha);
 	}
 
-	for(size_t i=0; i<state_visuals_.size(); i++)
+	for(size_t i=0; i<text_visuals_.size(); i++)
 	{
-		trace_visuals_[i]->setColor(color.r, color.g, color.b, alpha);
+		text_visuals_[i]->setColor(textColor);
 	}
 }
 
@@ -116,79 +115,57 @@ void PerceptionsDisplay::filterTraceBuffer()
 	});
 }
 
-// This is our callback to handle an incoming message.
 void PerceptionsDisplay::processMessage(const multiception::Perceptions::ConstPtr& msg)
 {
-	// Here we call the rviz::FrameManager to get the transform from the
-	// fixed frame to the frame in the header of this message. If
-	// it fails, we can't do anything else so we return.
-	const size_t N = msg->perceptions.size();
-	if(N==0)
+	if(msg->perceptions.size()>0)
+	{
+		resetVectors(msg->perceptions.size());
+	}
+	else
 	{
 		return;
 	}
 
-	// We are keeping a vector of state visual pointers
-	state_visuals_.clear(); state_visuals_.reserve(N);
-	shape_visuals_.clear(); shape_visuals_.reserve(N);
-	for(Ogre::SceneNode* textNode: text_nodes_) context_->getSceneManager()->destroySceneNode(textNode);
-	text_nodes_.clear(); text_nodes_.reserve(N);
-	text_visuals_.clear(); text_visuals_.reserve(N);
-	
-	const float alpha = alpha_property_->getFloat();
-	const float shape_alpha = shape_alpha_property_->getFloat();
-	const Ogre::ColourValue color = color_property_->getOgreColor();
+	Ogre::Vector3 position;
+	Ogre::Quaternion orientation;
+	if(context_->getFrameManager()->getTransform(msg->header.frame_id, msg->header.stamp, position, orientation))
+	{
+		frame_node_->setPosition(position);
+		frame_node_->setOrientation(orientation);
+	}
+	else
+	{
+		ROS_DEBUG("Error transforming from frame '%s' to frame '%s'", msg->header.frame_id.c_str(), qPrintable(fixed_frame_));
+		return;
+	}
 
 	// Process the perceptions in the message
 	for(const multiception::Perception& perception : msg->perceptions)
 	{
-		Ogre::Quaternion orientation;
-		Ogre::Vector3 position;
+		boost::shared_ptr<rviz::Arrow> arrowVis { new rviz::Arrow(scene_manager_, frame_node_) };
+		boost::shared_ptr<rviz::Shape> shapeVis { new rviz::Shape(rviz::Shape::Cube, scene_manager_, frame_node_) };
+		boost::shared_ptr<rviz::CovarianceVisual> covVis = covariance_property_->createAndPushBackVisual(scene_manager_, frame_node_);
 
-		if(!context_->getFrameManager()->getTransform(perception.header.frame_id, perception.header.stamp, position, orientation))
-		{
-			ROS_DEBUG("Error transforming from frame '%s' to frame '%s'", perception.header.frame_id.c_str(), qPrintable(fixed_frame_));
-			continue;
-		}
+		computeStateObjects(perception.state, *arrowVis, *covVis);
 
-		boost::shared_ptr<StateVisual> state(new StateVisual(context_->getSceneManager(), scene_node_, covariance_property_));
-		boost::shared_ptr<rviz::Shape> shape(new rviz::Shape(rviz::Shape::Cube, context_->getSceneManager(), scene_node_));
-
-		// Set the state visual's pose and content
-		state->setContent(perception.state);
-		state->setFramePosition(position);
-		state->setFrameOrientation(orientation);
-
-		// Set the shape visual's pose and shape
+		// Process the shape
 		const float z = (perception.height>0.1) ? perception.height/2+.1 : 1;
-		Ogre::Vector3 shapePose(perception.state.x, perception.state.y, z);
-		Ogre::Quaternion shapeOrientation(Ogre::Radian(perception.state.yaw), Ogre::Vector3::UNIT_Z);
-		Ogre::Vector3 shapeScale (perception.length, perception.width, std::max(0.1f, perception.height));
-		shape->setPosition(shapePose);
-		shape->setOrientation(shapeOrientation);
-		shape->setScale(shapeScale);
-
-		state->setColor(color.r, color.g, color.b, alpha);
-		shape->setColor(color.r, color.g, color.b, shape_alpha);
+		shapeVis->setScale( Ogre::Vector3(perception.length, perception.width, std::max(0.1f, perception.height)) );
+		shapeVis->setPosition( Ogre::Vector3(perception.state.x, perception.state.y, z) );
+		shapeVis->setOrientation( Ogre::Quaternion(Ogre::Radian(perception.state.yaw), Ogre::Vector3::UNIT_Z) );
 
-		// Create a hovering debug text if one is provided
+		// Process the debug text
 		if(!perception.debug.empty())
 		{
-			Ogre::SceneNode* childNode = scene_node_->createChildSceneNode();
-			boost::shared_ptr<rviz::MovableText> debugText { new rviz::MovableText(perception.debug, "Liberation Sans", .7, text_color_property_->getOgreColor()) };
+			Ogre::SceneNode* childNode = frame_node_->createChildSceneNode();
+			boost::shared_ptr<rviz::MovableText> debugText { new rviz::MovableText(perception.debug, "Liberation Sans", .7) };
 			debugText->setTextAlignment(rviz::MovableText::H_CENTER, rviz::MovableText::V_CENTER);
-			shapePose.z = z+perception.height/2+.1+1;
-			childNode->setPosition(shapePose);
+			childNode->setPosition(Ogre::Vector3(perception.state.x, perception.state.y, z+perception.height/2+.1+1) );
 			childNode->attachObject(debugText.get());
 			text_visuals_.push_back(debugText);
 			text_nodes_.push_back(childNode);
 		}
 
-		// And add it to the vector
-		state_visuals_.push_back(state);
-		shape_visuals_.push_back(shape);
-	}
-
 	// Process the buffered trace
 	trace_buffer_.push_back(msg); filterTraceBuffer(); trace_visuals_.clear();
 
@@ -208,14 +185,17 @@ void PerceptionsDisplay::processMessage(const multiception::Perceptions::ConstPt
 				trace_visuals_[p.object_id]->addPoint(Ogre::Vector3(p.state.x, p.state.y, 0), color);
 			}
 		}
+
+		pose_visuals_.push_back(arrowVis);
+		shape_visuals_.push_back(shapeVis);
+		cov_visuals_.push_back(covVis);
 	}
 
+	updateColorAndAlpha();
 	context_->queueRender();
 }
 
 } // end namespace rviz_multiception
 
-// Tell pluginlib about this class. It is important to do this in
-// global scope, outside our package's namespace.
 #include <pluginlib/class_list_macros.h>
 PLUGINLIB_EXPORT_CLASS(rviz_multiception::PerceptionsDisplay,rviz::Display)
diff --git a/src/perceptions_display.h b/src/perceptions_display.h
index 0bc339b..c564d44 100644
--- a/src/perceptions_display.h
+++ b/src/perceptions_display.h
@@ -18,28 +18,17 @@ namespace rviz
 class ColorProperty;
 class FloatProperty;
 class IntProperty;
-class CovarianceProperty;
 class Shape;
 class BillboardLine;
 class MovableText;
+class Arrow;
+class CovarianceVisual;
+class CovarianceProperty;
 }
 
-// All the source in this plugin is in its own namespace. This is not
-// required but is good practice.
 namespace rviz_multiception
 {
 
-class StateVisual;
-
-// Here we declare our new subclass of rviz::Display. Every display
-// which can be listed in the "Displays" panel is a subclass of
-// rviz::Display.
-//
-// The PerceptionsDisplay class just implements the vector,
-// editable parameters, and Display subclass machinery. The visuals
-// themselves are represented by separate classes. The
-// idiom for the visuals is that when the objects exist, they appear
-// in the scene, and when they are deleted, they disappear.
 class PerceptionsDisplay: public rviz::MessageFilterDisplay<multiception::Perceptions>
 {
 Q_OBJECT
@@ -49,16 +38,12 @@ public:
 	PerceptionsDisplay();
 	virtual ~PerceptionsDisplay();
 
-	// Overrides of protected virtual functions from Display. As much
-	// as possible, when Displays are not enabled, they should not be
-	// subscribed to incoming data and should not show anything in the
-	// 3D view. These functions are where these connections are made
-	// and broken.
 protected:
 	virtual void onInitialize();
 
 	// A helper to clear this display back to the initial state.
 	virtual void reset();
+	void resetVectors(size_t N=0);
 
 	// Qt slots connected to signals indicating changes in the user-editable properties.
 private Q_SLOTS:
@@ -70,14 +55,16 @@ private:
 	void processMessage(const multiception::Perceptions::ConstPtr& msg);
 
 	// Storage for the list of visuals
-	std::vector<boost::shared_ptr<StateVisual>> state_visuals_;
+	std::vector<boost::shared_ptr<rviz::Arrow>> pose_visuals_;
 	std::vector<boost::shared_ptr<rviz::Shape>> shape_visuals_;
+	std::vector<boost::shared_ptr<rviz::CovarianceVisual>> cov_visuals_;
 	std::vector<boost::shared_ptr<rviz::MovableText>> text_visuals_;
 	std::map<uint8_t, boost::shared_ptr<rviz::BillboardLine>> trace_visuals_;
 	std::vector<Ogre::SceneNode*> text_nodes_;
 
 	std::vector<multiception::Perceptions::ConstPtr> trace_buffer_;
 	float trace_duration_;
+	Ogre::SceneNode* frame_node_;
 
 	// User-editable property variables.
 	rviz::ColorProperty* color_property_;
diff --git a/src/state_display.cpp b/src/state_display.cpp
index 72dcf1f..c83380b 100644
--- a/src/state_display.cpp
+++ b/src/state_display.cpp
@@ -4,6 +4,8 @@
 #include <tf/transform_listener.h>
 
 #include <rviz/visualization_manager.h>
+#include <rviz/ogre_helpers/arrow.h>
+#include <rviz/default_plugin/covariance_visual.h>
 #include <rviz/properties/color_property.h>
 #include <rviz/properties/float_property.h>
 #include <rviz/default_plugin/covariance_property.h>
@@ -16,8 +18,6 @@
 namespace rviz_multiception
 {
 
-// The constructor must have no arguments, so we can't give the
-// constructor the parameters it needs to fully initialize.
 StateDisplay::StateDisplay()
 {
 	color_property_ = new rviz::ColorProperty( "Color", QColor( 204, 51, 204 ),
@@ -32,72 +32,60 @@ StateDisplay::StateDisplay()
 	                                                     this, SLOT( queueRender() ));
 }
 
-// After the top-level rviz::Display::initialize() does its own setup,
-// it calls the subclass's onInitialize() function. This is where we
-// instantiate all the workings of the class. We make sure to also
-// call our immediate super-class's onInitialize() function, since it
-// does important stuff setting up the message filter.
-//
-//  Note that "MFDClass" is a typedef of
-// ``MessageFilterDisplay<message type>``, to save typing that long
-// templated class name every time you need to refer to the
-// superclass.
 void StateDisplay::onInitialize()
 {
 	MFDClass::onInitialize();
+	frame_node_ = scene_node_->createChildSceneNode();
 }
 
 StateDisplay::~StateDisplay()
 {
+	frame_node_->detachAllObjects();
+	frame_node_->removeAndDestroyAllChildren();
+	scene_manager_->destroySceneNode(frame_node_);
 }
 
-// Clear the visuals by deleting their objects.
 void StateDisplay::reset()
 {
 	MFDClass::reset();
-	visual_.reset();
+	pose_visual_.reset();
+	cov_visual_.reset();
 }
 
-// Set the current color and alpha values for each visual.
 void StateDisplay::updateColorAndAlpha()
 {
-	if(visual_)
+	if(pose_visual_)
 	{
-		float alpha = alpha_property_->getFloat();
-		Ogre::ColourValue color = color_property_->getOgreColor();
-		visual_->setColor(color.r, color.g, color.b, alpha);
+		const float alpha = alpha_property_->getFloat();
+		const Ogre::ColourValue color = color_property_->getOgreColor();
+
+		pose_visual_->setColor(color.r, color.g, color.b, alpha);
+// 		cov_visual_->setColor(color.r, color.g, color.b, alpha);
 	}
 }
 
-// This is our callback to handle an incoming message.
 void StateDisplay::processMessage(const multiception::StateStamped::ConstPtr& msg)
 {
-	// Here we call the rviz::FrameManager to get the transform from the
-	// fixed frame to the frame in the header of this message. If
-	// it fails, we can't do anything else so we return.
-	Ogre::Quaternion orientation;
 	Ogre::Vector3 position;
-
-	if(!context_->getFrameManager()->getTransform(msg->header.frame_id, msg->header.stamp, position, orientation))
+	Ogre::Quaternion orientation;
+	if(context_->getFrameManager()->getTransform(msg->header.frame_id, msg->header.stamp, position, orientation))
+	{
+		frame_node_->setPosition(position);
+		frame_node_->setOrientation(orientation);
+	}
+	else
 	{
 		ROS_DEBUG("Error transforming from frame '%s' to frame '%s'", msg->header.frame_id.c_str(), qPrintable(fixed_frame_));
 		return;
 	}
 
-	visual_.reset(new StateVisual(context_->getSceneManager(), scene_node_, covariance_property_));
-
-	// Now set or update the contents of the visual.
-	visual_->setContent(msg->state);
-	visual_->setFramePosition(position);
-	visual_->setFrameOrientation(orientation);
+	pose_visual_.reset( new rviz::Arrow(scene_manager_, frame_node_) );
+	cov_visual_ = covariance_property_->createAndPushBackVisual(scene_manager_, frame_node_);
 
-	updateColorAndAlpha();
-	context_->queueRender();
+	computeStateObjects(msg->state, *pose_visual_, *cov_visual_);
 }
 
 } // end namespace rviz_multiception
 
-// Tell pluginlib about this class. It is important to do this in
-// global scope, outside our package's namespace.
 #include <pluginlib/class_list_macros.h>
 PLUGINLIB_EXPORT_CLASS(rviz_multiception::StateDisplay,rviz::Display)
diff --git a/src/state_display.h b/src/state_display.h
index caa812c..d0dfa26 100644
--- a/src/state_display.h
+++ b/src/state_display.h
@@ -15,28 +15,17 @@ class SceneNode;
 
 namespace rviz
 {
+class Arrow;
+class CovarianceVisual;
 class ColorProperty;
 class FloatProperty;
 class IntProperty;
 class CovarianceProperty;
 }
 
-// All the source in this plugin is in its own namespace. This is not
-// required but is good practice.
 namespace rviz_multiception
 {
 
-class StateVisual;
-
-// Here we declare our new subclass of rviz::Display. Every display
-// which can be listed in the "Displays" panel is a subclass of
-// rviz::Display.
-//
-// The StateDisplay class just implements the vector,
-// editable parameters, and Display subclass machinery. The visuals
-// themselves are represented by a separate class, StateVisual. The
-// idiom for the visuals is that when the objects exist, they appear
-// in the scene, and when they are deleted, they disappear.
 class StateDisplay: public rviz::MessageFilterDisplay<multiception::StateStamped>
 {
 Q_OBJECT
@@ -66,7 +55,10 @@ private:
 	void processMessage(const multiception::StateStamped::ConstPtr& msg);
 
 	// Storage of the visual
-	boost::shared_ptr<StateVisual> visual_;
+	boost::shared_ptr<rviz::Arrow> pose_visual_;
+	boost::shared_ptr<rviz::CovarianceVisual> cov_visual_;
+
+	Ogre::SceneNode* frame_node_;
 
 	// User-editable property variables.
 	rviz::ColorProperty* color_property_;
diff --git a/src/state_visual.cpp b/src/state_visual.cpp
index 2d80ff6..f4d5639 100644
--- a/src/state_visual.cpp
+++ b/src/state_visual.cpp
@@ -1,11 +1,9 @@
 #include <OGRE/OgreVector3.h>
 #include <OGRE/OgreSceneNode.h>
-#include <OGRE/OgreSceneManager.h>
 
 #include <ros/console.h>
 #include <rviz/ogre_helpers/arrow.h>
 #include <rviz/default_plugin/covariance_visual.h>
-#include <rviz/default_plugin/covariance_property.h>
 #include <geometry_msgs/Vector3.h>
 
 #include <tf2/LinearMath/Quaternion.h>
@@ -16,92 +14,46 @@
 namespace rviz_multiception
 {
 
-StateVisual::StateVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node, rviz::CovarianceProperty* covariance_property)
-{
-	scene_manager_ = scene_manager;
-
-	// Ogre::SceneNode s form a tree, with each node storing the
-	// transform (position and orientation) of itself relative to its
-	// parent. Ogre does the math of combining those transforms when it
-	// is time to render.
-	//
-	// Here we create a node to store the pose of the header frame
-	// relative to the RViz fixed frame.
-	frame_node_ = parent_node->createChildSceneNode();
-
-	// We create the arrow object within the frame node so that we can
-	// set its position and direction relative to its header frame.
-	pose_arrow_.reset(new rviz::Arrow(scene_manager_, frame_node_));
-	covariance_ = covariance_property->createAndPushBackVisual(scene_manager, frame_node_);
-}
-
-StateVisual::~StateVisual()
-{
-	// Destroy the frame node since we don't need it anymore.
-	scene_manager_->destroySceneNode(frame_node_);
-}
-
-void StateVisual::setContent(const multiception::State& state)
-{
-	// Convert the geometry_msgs::Vector3 to an Ogre::Vector3.
+void computeStateObjects(const multiception::State& state, rviz::Arrow& arrowVis, rviz::CovarianceVisual& covVis) {
+	// Process the pose arrow, scaled by velocity and oriented by heading
 	Ogre::Vector3 pose(state.x, state.y, 1.5);
-	pose_arrow_->setPosition(pose);
+	arrowVis.setPosition(pose);
 
-	// Scale the arrow by the bounded velocity
+	// Special case, if yaw is 0, we consider the state to be static
+	// without orientation (e.g: pole) and displaying an orientation makes no sense
 	float length = std::min(4.0, std::max(1.0, state.v/5.0));
-	Ogre::Vector3 scale(length, 1, 1);
-	pose_arrow_->setScale(scale);
+	if(state.v<=0) arrowVis.setScale(Ogre::Vector3(0.1, 0.1, 0.1));
+	else arrowVis.setScale(Ogre::Vector3(length, 1, 1));
 
-	// Set the orientation of the arrow according to the heading.
 	Ogre::Vector3 direction(std::cos(state.yaw), std::sin(state.yaw), 0);
-	pose_arrow_->setDirection(direction);
+	arrowVis.setDirection(direction);
 
-	// Set the covariance ellipsis
+	// Process the covariance ellipsis
 	std::array<double, 36> covTot;
 	for(size_t i=0;i<covTot.size(); i++) covTot[i] = state.cov[i] + state.cov_ind[i];
 
 	geometry_msgs::PoseWithCovariance cov;
-	//cov.pose.position.x = state.x; cov.pose.position.y = state.y;
-	tf2::Quaternion q; 	q.setRPY(0.0, 0.0, state.yaw);
+	cov.pose.position.x = state.x; cov.pose.position.y = state.y;
+	tf2::Quaternion q; q.setRPY(0.0, 0.0, state.yaw);
 	cov.pose.orientation = tf2::toMsg(q);
 	cov.covariance[0*6+0] = covTot[0*6+0]; // xx
 	cov.covariance[0*6+1] = covTot[0*6+1]; // xy
 	cov.covariance[1*6+0] = covTot[1*6+0]; // yx
 	cov.covariance[1*6+1] = covTot[1*6+1]; // yy
 
-	// Special case, if yaw is 0, we consider the state to be static
-	// without orientation (e.g: pole) and displaying an orientation makes no sense
-	if(state.v==0) pose_arrow_->setScale(Ogre::Vector3(0.1, 0.1, 0.1));
-	else if(covTot[2*6+2]<M_PI)
+	if(state.v>0)
 	{
 		cov.covariance[0*6+5] = covTot[0*6+2]; // xyaw
 		cov.covariance[1*6+5] = covTot[1*6+2]; // yyaw
 		cov.covariance[5*6+0] = covTot[2*6+0]; // yawx
 		cov.covariance[5*6+1] = covTot[2*6+1]; // yawy
-		cov.covariance[5*6+5] = std::max(.0, covTot[2*6+2]); // yawyaw
+		cov.covariance[5*6+5] = std::max(0., std::min(2., covTot[2*6+2])); // yawyaw
 	}
 
-	covariance_->setCovariance(cov);
-	covariance_->setPosition(pose);
 	Ogre::Quaternion covq(Ogre::Radian(state.yaw), Ogre::Vector3::UNIT_Z);
-	covariance_->setOrientation(covq);
-}
-
-// Position and orientation are passed through to the SceneNode.
-void StateVisual::setFramePosition(const Ogre::Vector3& position)
-{
-	frame_node_->setPosition(position);
-}
-
-void StateVisual::setFrameOrientation(const Ogre::Quaternion& orientation)
-{
-	frame_node_->setOrientation(orientation);
-}
-
-// Color is passed through to the Arrow object.
-void StateVisual::setColor(float r, float g, float b, float a)
-{
-	pose_arrow_->setColor(r, g, b, a);
+	covVis.setCovariance(cov);
+	covVis.setPosition(pose);
+	covVis.setOrientation(covq);
 }
 
 } // namespace rviz_multiception
diff --git a/src/state_visual.h b/src/state_visual.h
index 42e0fd6..3c92782 100644
--- a/src/state_visual.h
+++ b/src/state_visual.h
@@ -3,66 +3,16 @@
 
 #include <multiception/Perception.h>
 
-namespace Ogre
-{
-class Vector3;
-class Quaternion;
-}
-
 namespace rviz
 {
 class Arrow;
 class CovarianceVisual;
-class CovarianceProperty;
 }
 
 namespace rviz_multiception
 {
 
-// Declare the visual class for this display.
-//
-// Each instance of StateVisual represents the visualization of a single
-// multiception::Perception message. Currently it just shows an arrow with
-// the direction and magnitude of the acceleration vector, but could
-// easily be expanded to include more of the message data.
-class StateVisual
-{
-public:
-	// Constructor. Creates the visual stuff and puts it into the
-	// scene, but in an unconfigured state.
-	StateVisual(Ogre::SceneManager* scene_manager, Ogre::SceneNode* parent_node, rviz::CovarianceProperty* covariance_property);
-
-	// Destructor. Removes the visual stuff from the scene.
-	virtual ~StateVisual();
-
-	// Configure the visual to show the data in the message.
-	void setContent(const multiception::State& state);
-
-	// Set the pose of the coordinate frame the message refers to.
-	// These could be done inside setMessage(), but that would require
-	// calls to FrameManager and error handling inside setMessage(),
-	// which doesn't seem as clean. This way PerceptionVisual is only
-	// responsible for visualization.
-	void setFramePosition(const Ogre::Vector3& position);
-	void setFrameOrientation(const Ogre::Quaternion& orientation);
-
-	// Set the color and alpha of the visual, which are user-editable
-	// parameters and therefore don't come from the message.
-	void setColor(float r, float g, float b, float a);
-
-private:
-	// The object implementing the actual arrow shape
-	boost::shared_ptr<rviz::Arrow> pose_arrow_;
-	boost::shared_ptr<rviz::CovarianceVisual> covariance_;
-
-	// A SceneNode whose pose is set to match the coordinate frame of
-	// the Imu message header.
-	Ogre::SceneNode* frame_node_;
-
-	// The SceneManager, kept here only so the destructor can ask it to
-	// destroy the ``frame_node_``.
-	Ogre::SceneManager* scene_manager_;
-};
+void computeStateObjects(const multiception::State& state, rviz::Arrow& arrowVis, rviz::CovarianceVisual& covVis);
 
 } // namespace rviz_multiception
 
-- 
GitLab