marker_utils.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, Willow Garage, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "marker_utils.h"
40 #include <rviz/display_context.h>
46 #include <rviz/validate_floats.h>
47 #include <rviz/windows_compat.h>
48 
49 
50 namespace rviz
51 {
52 MarkerBase*
53 createMarker(int marker_type, MarkerDisplay* owner, DisplayContext* context, Ogre::SceneNode* parent_node)
54 {
55  switch (marker_type)
56  {
57  case visualization_msgs::Marker::CUBE:
58  case visualization_msgs::Marker::CYLINDER:
59  case visualization_msgs::Marker::SPHERE:
60  return new rviz::ShapeMarker(owner, context, parent_node);
61 
62  case visualization_msgs::Marker::ARROW:
63  return new rviz::ArrowMarker(owner, context, parent_node);
64 
65  case visualization_msgs::Marker::LINE_STRIP:
66  return new rviz::LineStripMarker(owner, context, parent_node);
67 
68  case visualization_msgs::Marker::LINE_LIST:
69  return new rviz::LineListMarker(owner, context, parent_node);
70 
71  case visualization_msgs::Marker::SPHERE_LIST:
72  case visualization_msgs::Marker::CUBE_LIST:
73  case visualization_msgs::Marker::POINTS:
74  return new rviz::PointsMarker(owner, context, parent_node);
75 
76  case visualization_msgs::Marker::TEXT_VIEW_FACING:
77  return new rviz::TextViewFacingMarker(owner, context, parent_node);
78 
79  case visualization_msgs::Marker::MESH_RESOURCE:
80  return new rviz::MeshResourceMarker(owner, context, parent_node);
81 
82  case visualization_msgs::Marker::TRIANGLE_LIST:
83  return new rviz::TriangleListMarker(owner, context, parent_node);
84 
85  default:
86  return nullptr;
87  }
88 }
89 
90 QString getMarkerTypeName(unsigned int type)
91 {
92  switch (type)
93  {
94  case visualization_msgs::Marker::ARROW:
95  return "Arrow";
96  case visualization_msgs::Marker::CUBE:
97  return "Cube";
98  case visualization_msgs::Marker::CUBE_LIST:
99  return "Cube List";
100  case visualization_msgs::Marker::TRIANGLE_LIST:
101  return "Triangle List";
102  case visualization_msgs::Marker::SPHERE:
103  return "Sphere";
104  case visualization_msgs::Marker::SPHERE_LIST:
105  return "Sphere List";
106  case visualization_msgs::Marker::CYLINDER:
107  return "Cylinder";
108  case visualization_msgs::Marker::LINE_STRIP:
109  return "Line Strip";
110  case visualization_msgs::Marker::LINE_LIST:
111  return "Line List";
112  case visualization_msgs::Marker::POINTS:
113  return "Points";
114  case visualization_msgs::Marker::TEXT_VIEW_FACING:
115  return "Text View Facing";
116  case visualization_msgs::Marker::MESH_RESOURCE:
117  return "Mesh";
118  default:
119  return "Unknown";
120  }
121 }
122 
123 namespace
124 {
125 void addSeparatorIfRequired(std::stringstream& ss)
126 {
127  if (ss.tellp() != 0) // check if string is not empty
128  {
129  ss << " \n";
130  }
131 }
132 
133 void increaseLevel(::ros::console::levels::Level new_status,
134  ::ros::console::levels::Level& current_status)
135 {
136  if (new_status > current_status)
137  current_status = new_status;
138 }
139 
140 template <typename T>
141 constexpr const char* fieldName();
142 template <>
143 constexpr const char* fieldName<::geometry_msgs::Point>()
144 {
145  return "Position";
146 }
147 template <>
148 constexpr const char* fieldName<::geometry_msgs::Quaternion>()
149 {
150  return "Orientation";
151 }
152 template <>
153 constexpr const char* fieldName<::geometry_msgs::Vector3>()
154 {
155  return "Scale";
156 }
157 template <>
158 constexpr const char* fieldName<::std_msgs::ColorRGBA>()
159 {
160  return "Color";
161 }
162 
163 template <typename T>
164 void checkFloats(const T& t, std::stringstream& ss, ::ros::console::levels::Level& level)
165 {
166  if (!validateFloats(t))
167  {
168  addSeparatorIfRequired(ss);
169  ss << fieldName<T>() << " contains invalid floating point values (nans or infs)";
170  increaseLevel(::ros::console::levels::Error, level);
171  }
172 }
173 
174 void checkQuaternion(const visualization_msgs::Marker& marker,
175  std::stringstream& ss,
177 {
178  checkFloats(marker.pose.orientation, ss, level);
179  if (marker.pose.orientation.x == 0.0 && marker.pose.orientation.y == 0.0 &&
180  marker.pose.orientation.z == 0.0 && marker.pose.orientation.w == 0.0)
181  {
182  addSeparatorIfRequired(ss);
183  ss << "Uninitialized quaternion, assuming identity.";
184  increaseLevel(::ros::console::levels::Info, level);
185  }
186  else if (!validateQuaternions(marker.pose))
187  {
188  addSeparatorIfRequired(ss);
189  ss << "Unnormalized quaternion in marker message.";
190  increaseLevel(::ros::console::levels::Warn, level);
191  }
192 }
193 
194 void checkScale(const visualization_msgs::Marker& marker,
195  std::stringstream& ss,
197 {
198  checkFloats(marker.scale, ss, level);
199  // for ARROW markers, scale.z is the optional head length
200  if (marker.type == visualization_msgs::Marker::ARROW && marker.scale.x != 0.0 && marker.scale.y != 0.0)
201  return;
202 
203  if (marker.scale.x == 0.0 || marker.scale.y == 0.0 || marker.scale.z == 0.0)
204  {
205  addSeparatorIfRequired(ss);
206  ss << "Scale contains 0.0 in x, y or z.";
207  increaseLevel(::ros::console::levels::Warn, level);
208  }
209 }
210 
211 void checkScaleLineStripAndList(const visualization_msgs::Marker& marker,
212  std::stringstream& ss,
214 {
215  if (marker.scale.x == 0.0)
216  {
217  addSeparatorIfRequired(ss);
218  ss << "Width LINE_LIST or LINE_STRIP is 0.0 (scale.x).";
219  increaseLevel(::ros::console::levels::Warn, level);
220  }
221 }
222 
223 void checkScalePoints(const visualization_msgs::Marker& marker,
224  std::stringstream& ss,
226 {
227  if (marker.scale.x == 0.0 || marker.scale.y == 0.0)
228  {
229  addSeparatorIfRequired(ss);
230  ss << "Width and/or height of POINTS is 0.0 (scale.x, scale.y).";
231  increaseLevel(::ros::console::levels::Warn, level);
232  }
233 }
234 
235 void checkScaleText(const visualization_msgs::Marker& marker,
236  std::stringstream& ss,
238 {
239  if (marker.scale.z == 0.0)
240  {
241  addSeparatorIfRequired(ss);
242  ss << "Text height of TEXT_VIEW_FACING is 0.0 (scale.z).";
243  increaseLevel(::ros::console::levels::Warn, level);
244  }
245 }
246 
247 void checkColor(const visualization_msgs::Marker& marker,
248  std::stringstream& ss,
250 {
251  checkFloats(marker.color, ss, level);
252  if (marker.color.a == 0.0 &&
253  // Mesh markers use a color of (0,0,0,0) to indicate that the original color of the mesh should be
254  // used as is
255  !(marker.type == visualization_msgs::Marker::MESH_RESOURCE && marker.mesh_use_embedded_materials &&
256  marker.color.r == 0.0 && marker.color.g == 0.0 && marker.color.b == 0.0))
257  {
258  addSeparatorIfRequired(ss);
259  ss << "Marker is fully transparent (color.a is 0.0).";
260  increaseLevel(::ros::console::levels::Info, level);
261  }
262 }
263 
264 void checkPointsArrow(const visualization_msgs::Marker& marker,
265  std::stringstream& ss,
267 {
268  if (!marker.points.empty() && marker.points.size() != 2)
269  {
270  addSeparatorIfRequired(ss);
271  ss << "Number of points for an ARROW marker should be either 0 or 2.";
272  increaseLevel(::ros::console::levels::Error, level);
273  return;
274  }
275  for (const auto& p : marker.points)
276  checkFloats(p, ss, level);
277 }
278 
279 void checkPointsNotEmpty(const visualization_msgs::Marker& marker,
280  std::stringstream& ss,
282 {
283  if (marker.points.empty())
284  {
285  addSeparatorIfRequired(ss);
286  ss << "Points should not be empty for specified marker type.";
287  increaseLevel(::ros::console::levels::Error, level);
288  }
289  switch (marker.type)
290  {
291  case visualization_msgs::Marker::TRIANGLE_LIST:
292  if (marker.points.size() % 3 != 0)
293  {
294  addSeparatorIfRequired(ss);
295  ss << "Number of points should be a multiple of 3 for TRIANGLE_LIST marker.";
296  increaseLevel(::ros::console::levels::Error, level);
297  }
298  break;
299  case visualization_msgs::Marker::LINE_LIST:
300  if (marker.points.size() % 2 != 0)
301  {
302  addSeparatorIfRequired(ss);
303  ss << "Number of points should be a multiple of 2 for LINE_LIST marker.";
304  increaseLevel(::ros::console::levels::Error, level);
305  }
306  break;
307  case visualization_msgs::Marker::LINE_STRIP:
308  if (marker.points.size() <= 1)
309  {
310  addSeparatorIfRequired(ss);
311  ss << "At least two points are required for a LINE_STRIP marker.";
312  increaseLevel(::ros::console::levels::Error, level);
313  }
314  break;
315  default:
316  break;
317  }
318 }
319 
320 void checkPointsEmpty(const visualization_msgs::Marker& marker,
321  std::stringstream& ss,
323 {
324  if (!marker.points.empty())
325  {
326  addSeparatorIfRequired(ss);
327  ss << "Non-empty points array is ignored.";
328  increaseLevel(::ros::console::levels::Warn, level);
329  }
330 }
331 
332 void checkColors(const visualization_msgs::Marker& marker,
333  std::stringstream& ss,
335  unsigned int multiple)
336 {
337  if (marker.colors.empty())
338  {
339  checkColor(marker, ss, level);
340  return;
341  }
342  if (marker.colors.size() != marker.points.size() &&
343  (multiple == 1 || multiple * marker.colors.size() != marker.points.size()))
344  {
345  addSeparatorIfRequired(ss);
346  ss << "Number of colors doesn't match number of points.";
347  increaseLevel(::ros::console::levels::Error, level);
348  }
349 }
350 
351 void checkColorsEmpty(const visualization_msgs::Marker& marker,
352  std::stringstream& ss,
354 {
355  if (!marker.colors.empty())
356  {
357  addSeparatorIfRequired(ss);
358  ss << "Non-empty colors array is ignored.";
359  increaseLevel(::ros::console::levels::Warn, level);
360  }
361 }
362 
363 void checkTextNotEmptyOrWhitespace(const visualization_msgs::Marker& marker,
364  std::stringstream& ss,
366 {
367  if (marker.text.find_first_not_of(" \t\n\v\f\r") == std::string::npos)
368  {
369  addSeparatorIfRequired(ss);
370  ss << "Text is empty or only consists of whitespace.";
371  increaseLevel(::ros::console::levels::Error, level);
372  }
373 }
374 
375 void checkTextEmpty(const visualization_msgs::Marker& marker,
376  std::stringstream& ss,
378 {
379  if (!marker.text.empty())
380  {
381  addSeparatorIfRequired(ss);
382  ss << "Non-empty text is ignored.";
383  increaseLevel(::ros::console::levels::Info, level);
384  }
385 }
386 
387 void checkMesh(const visualization_msgs::Marker& marker,
388  std::stringstream& ss,
390 {
391  if (marker.mesh_resource.empty())
392  {
393  addSeparatorIfRequired(ss);
394  ss << "Empty mesh_resource path.";
395  increaseLevel(::ros::console::levels::Error, level);
396  }
397 }
398 
399 void checkMeshEmpty(const visualization_msgs::Marker& marker,
400  std::stringstream& ss,
402 {
403  if (!marker.mesh_resource.empty())
404  {
405  addSeparatorIfRequired(ss);
406  ss << "Non-empty mesh_resource is ignored.";
407  increaseLevel(::ros::console::levels::Info, level);
408  }
409  if (marker.mesh_use_embedded_materials)
410  {
411  addSeparatorIfRequired(ss);
412  ss << "mesh_use_embedded_materials is ignored.";
413  increaseLevel(::ros::console::levels::Info, level);
414  }
415 }
416 
417 } // namespace
418 
419 bool checkMarkerMsg(const visualization_msgs::Marker& marker, MarkerDisplay* owner)
420 {
421  if (marker.action != visualization_msgs::Marker::ADD)
422  return true;
423 
424  std::stringstream ss;
425  ::ros::console::levels::Level level = ::ros::console::levels::Debug;
426  checkFloats(marker.pose.position, ss, level);
427 
428  switch (marker.type)
429  {
430  case visualization_msgs::Marker::ARROW:
431  checkQuaternion(marker, ss, level);
432  checkScale(marker, ss, level);
433  checkColor(marker, ss, level);
434  checkPointsArrow(marker, ss, level);
435  checkColorsEmpty(marker, ss, level);
436  checkTextEmpty(marker, ss, level);
437  checkMeshEmpty(marker, ss, level);
438  break;
439 
440  case visualization_msgs::Marker::CUBE:
441  case visualization_msgs::Marker::CYLINDER:
442  case visualization_msgs::Marker::SPHERE:
443  checkQuaternion(marker, ss, level);
444  checkScale(marker, ss, level);
445  checkColor(marker, ss, level);
446  checkPointsEmpty(marker, ss, level);
447  checkColorsEmpty(marker, ss, level);
448  checkTextEmpty(marker, ss, level);
449  checkMeshEmpty(marker, ss, level);
450  break;
451 
452 
453  case visualization_msgs::Marker::LINE_STRIP:
454  case visualization_msgs::Marker::LINE_LIST:
455  checkQuaternion(marker, ss, level);
456  checkScaleLineStripAndList(marker, ss, level);
457  checkPointsNotEmpty(marker, ss, level);
458  checkColors(marker, ss, level, 1);
459  checkTextEmpty(marker, ss, level);
460  checkMeshEmpty(marker, ss, level);
461  break;
462 
463  case visualization_msgs::Marker::SPHERE_LIST:
464  case visualization_msgs::Marker::CUBE_LIST:
465  case visualization_msgs::Marker::TRIANGLE_LIST:
466  checkQuaternion(marker, ss, level);
467  checkScale(marker, ss, level);
468  checkPointsNotEmpty(marker, ss, level);
469  checkColors(marker, ss, level, marker.type == visualization_msgs::Marker::TRIANGLE_LIST ? 3 : 1);
470  checkTextEmpty(marker, ss, level);
471  checkMeshEmpty(marker, ss, level);
472  break;
473 
474  case visualization_msgs::Marker::POINTS:
475  checkScalePoints(marker, ss, level);
476  checkPointsNotEmpty(marker, ss, level);
477  checkColors(marker, ss, level, 1);
478  checkTextEmpty(marker, ss, level);
479  checkMeshEmpty(marker, ss, level);
480  break;
481 
482  case visualization_msgs::Marker::TEXT_VIEW_FACING:
483  checkColor(marker, ss, level);
484  checkScaleText(marker, ss, level);
485  checkTextNotEmptyOrWhitespace(marker, ss, level);
486  checkPointsEmpty(marker, ss, level);
487  checkColorsEmpty(marker, ss, level);
488  checkMeshEmpty(marker, ss, level);
489  break;
490 
491  case visualization_msgs::Marker::MESH_RESOURCE:
492  checkQuaternion(marker, ss, level);
493  checkColor(marker, ss, level);
494  checkScale(marker, ss, level);
495  checkPointsEmpty(marker, ss, level);
496  checkColorsEmpty(marker, ss, level);
497  checkTextEmpty(marker, ss, level);
498  checkMesh(marker, ss, level);
499  break;
500 
501  default:
502  ss << "Unknown marker type: " << marker.type << '.';
503  level = ::ros::console::levels::Error;
504  }
505 
506  if (ss.tellp() != 0) // stringstream is not empty
507  {
508  std::string warning = ss.str();
509 
510  bool fatal = level >= ::ros::console::levels::Error;
511  if (owner)
512  owner->setMarkerStatus(MarkerID(marker.ns, marker.id),
513  fatal ? StatusProperty::Error : StatusProperty::Warn, warning);
514  else
515  ROS_LOG(level, ROSCONSOLE_DEFAULT_NAME ".marker", "Marker '%s/%d': %s", marker.ns.c_str(),
516  marker.id, warning.c_str());
517 
518  return !fatal;
519  }
520 
521  if (owner)
522  owner->deleteMarkerStatus(MarkerID(marker.ns, marker.id));
523  return true;
524 }
525 
526 bool checkMarkerArrayMsg(const visualization_msgs::MarkerArray& array, MarkerDisplay* owner)
527 {
528  std::vector<MarkerID> marker_ids;
529  marker_ids.reserve(array.markers.size());
530 
531  ::ros::console::levels::Level level = ::ros::console::levels::Debug;
532  std::stringstream ss;
533 
534  for (const visualization_msgs::Marker& marker : array.markers)
535  {
536  if (marker.action == visualization_msgs::Marker::DELETEALL)
537  {
538  if (!marker_ids.empty())
539  {
540  addSeparatorIfRequired(ss);
541  ss << "DELETEALL after having markers added. These will never show.";
542  increaseLevel(::ros::console::levels::Warn, level);
543  }
544  marker_ids.clear();
545  continue;
546  }
547 
548  MarkerID current_id(marker.ns, marker.id);
549  std::vector<MarkerID>::iterator search =
550  std::lower_bound(marker_ids.begin(), marker_ids.end(), current_id);
551  if (search != marker_ids.end() && *search == current_id)
552  {
553  addSeparatorIfRequired(ss);
554  increaseLevel(::ros::console::levels::Warn, level);
555  if (marker.action == visualization_msgs::Marker::DELETE)
556  {
557  ss << "Deleting just added marker '" << marker.ns.c_str() << "/" << marker.id << "'.";
558  marker_ids.erase(search);
559  }
560  else
561  ss << "Adding marker '" << marker.ns.c_str() << "/" << marker.id << "' multiple times.";
562  }
563  else
564  {
565  marker_ids.insert(search, current_id);
566  }
567  }
568 
569  if (ss.tellp() != 0) // stringstream is not empty
570  {
571  std::string warning = ss.str();
572 
573  bool fatal = level >= ::ros::console::levels::Error;
574  if (owner)
575  owner->setStatusStd(fatal ? StatusProperty::Error : StatusProperty::Warn, "marker_array", warning);
576  else
577  ROS_LOG(level, ROSCONSOLE_DEFAULT_NAME ".marker", "MarkerArray: %s", warning.c_str());
578 
579  return !fatal;
580  }
581 
582  if (owner)
583  owner->deleteStatusStd("marker_array");
584  return true;
585 }
586 
587 } // namespace rviz
rviz::ShapeMarker
Definition: shape_marker.h:42
points_marker.h
windows_compat.h
validate_floats.h
rviz::MarkerDisplay
Displays "markers" sent in by other ROS nodes on the "visualization_marker" topic.
Definition: marker_display.h:70
ros::console::levels::Error
Error
triangle_list_marker.h
rviz::TextViewFacingMarker
Definition: text_view_facing_marker.h:47
rviz::StatusProperty::Error
@ Error
Definition: status_property.h:46
text_view_facing_marker.h
status_list.h
property.h
rviz::createMarker
MarkerBase * createMarker(int marker_type, MarkerDisplay *owner, DisplayContext *context, Ogre::SceneNode *parent_node)
Definition: marker_utils.cpp:53
marker_display.h
rviz::validateFloats
bool validateFloats(const sensor_msgs::CameraInfo &msg)
Definition: camera_display.cpp:72
status_property.h
validate_quaternions.h
rviz::Display::deleteStatusStd
void deleteStatusStd(const std::string &name)
Delete the status entry with the given std::string name. This is thread-safe.
Definition: display.h:172
line_list_marker.h
rviz
Definition: add_display_dialog.cpp:54
rviz::validateQuaternions
bool validateQuaternions(const visualization_msgs::InteractiveMarker &marker)
Definition: interactive_marker_display.cpp:63
rviz::StatusProperty::Warn
@ Warn
Definition: status_property.h:45
rviz::LineStripMarker
Definition: line_strip_marker.h:42
rviz::MarkerDisplay::deleteMarkerStatus
void deleteMarkerStatus(const MarkerID &id)
Definition: marker_display.cpp:245
search
ROSCPP_DECL bool search(const std::string &key, std::string &result)
rviz::checkMarkerMsg
bool checkMarkerMsg(const visualization_msgs::Marker &marker, MarkerDisplay *owner)
Definition: marker_utils.cpp:419
property_tree_model.h
rviz::DisplayContext
Pure-virtual base class for objects which give Display subclasses context in which to work.
Definition: display_context.h:81
ROS_LOG
#define ROS_LOG(level, name,...)
ros::console::levels::Level
Level
ros::console::levels::Info
Info
rviz::ArrowMarker
Definition: arrow_marker.h:45
rviz::Display::setStatusStd
void setStatusStd(StatusProperty::Level level, const std::string &name, const std::string &text)
Show status level and text, using a std::string. Convenience function which converts std::string to Q...
Definition: display.h:163
arrow_marker.h
rviz::MarkerID
std::pair< std::string, int32_t > MarkerID
Definition: interactive_marker_display.h:58
rviz::checkMarkerArrayMsg
bool checkMarkerArrayMsg(const visualization_msgs::MarkerArray &array, MarkerDisplay *owner)
Definition: marker_utils.cpp:526
marker_utils.h
shape_marker.h
mesh_resource_marker.h
ros::console::levels::Warn
Warn
display_context.h
line_strip_marker.h
rviz::getMarkerTypeName
QString getMarkerTypeName(unsigned int type)
Definition: marker_utils.cpp:90
ROSCONSOLE_DEFAULT_NAME
#define ROSCONSOLE_DEFAULT_NAME
rviz::TriangleListMarker
Definition: triangle_list_marker.h:46
rviz::PointsMarker
Definition: points_marker.h:47
rviz::MeshResourceMarker
Definition: mesh_resource_marker.h:47
rviz::LineListMarker
Definition: line_list_marker.h:42
rviz::MarkerDisplay::setMarkerStatus
void setMarkerStatus(const MarkerID &id, StatusLevel level, const std::string &text)
Definition: marker_display.cpp:237


rviz
Author(s): Dave Hershberger, David Gossow, Josh Faust, William Woodall
autogenerated on Fri Aug 2 2024 08:43:09