diff --git a/.gitignore b/.gitignore index 7a3633634..618178ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ # OS X .DS_Store -grid_map_pcl/data/ +grid_map_pcl/data/* +!grid_map_pcl/data/input_cloud.pcd diff --git a/README.md b/README.md index 839c3bd6a..23a5ea791 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Features: * **Based on Eigen:** Grid map data is stored as [Eigen] data types. Users can apply available Eigen algorithms directly to the map data for versatile and efficient data manipulation. * **Convenience functions:** Several helper methods allow for convenient and memory safe cell data access. For example, iterator functions for rectangular, circular, polygonal regions and lines are implemented. * **ROS interface:** Grid maps can be directly converted to and from ROS message types such as PointCloud2, OccupancyGrid, GridCells, and our custom GridMap message. Conversion packages provide compatibility with [costmap_2d], [PCL], and [OctoMap] data types. + * Note: Currently, PointCloud2 can only be converted one-way; see this [issue](https://github.com/ANYbotics/grid_map/issues/158) for context. * **OpenCV interface:** Grid maps can be seamlessly converted from and to [OpenCV] image types to make use of the tools provided by [OpenCV]. * **Visualizations:** The *grid_map_rviz_plugin* renders grid maps as 3d surface plots (height maps) in [RViz]. Additionally, the *grid_map_visualization* package helps to visualize grid maps as point clouds, occupancy grids, grid cells etc. * **Filters:** The *grid_map_filters* provides are range of filters to process grid maps as a sequence of filters. Parsing of mathematical expressions allows to flexibly setup powerful computations such as thresholding, normal vectors, smoothening, variance, inpainting, and matrix kernel convolutions. @@ -21,8 +22,8 @@ The source code is released under a [BSD 3-Clause license](LICENSE). **Author: Péter Fankhauser
Affiliation: [ANYbotics](https://www.anybotics.com/)
-Maintainer: Péter Fankhauser, pfankhauser@anybotics.com
** -With contributions by: Tanja Baumann, Jeff Delmerico, Remo Diethelm, Perry Franklin, Dominic Jud, Ralph Kaestner, Philipp Krüsi, Alex Millane, Daniel Stonier, Elena Stumm, Martin Wermelinger, Christos Zalidis, Edo Jelavic, Ruben Grandia, Simone Arreghini, Magnus Gärtner +Maintainer: Maximilian Wulf, mwulf@anybotics.com, Magnus Gärtner, mgaertner@anybotics.com
** +With contributions by: Simone Arreghini, Tanja Baumann, Jeff Delmerico, Remo Diethelm, Perry Franklin, Magnus Gärtner, Ruben Grandia, Edo Jelavic, Dominic Jud, Ralph Kaestner, Philipp Krüsi, Alex Millane, Daniel Stonier, Elena Stumm, Martin Wermelinger, Christos Zalidis This projected was initially developed at ETH Zurich (Autonomous Systems Lab & Robotic Systems Lab). @@ -52,6 +53,20 @@ If you use this work in an academic context, please cite the following publicati url = {http://www.springer.com/de/book/9783319260525} } +## Branches + +These branches are currently maintained: +* ROS 1 + * [master](https://github.com/ANYbotics/grid_map) +* ROS 2 + * [humble](https://github.com/ANYbotics/grid_map/tree/humble) + * [iron](https://github.com/ANYbotics/grid_map/tree/iron) + * [jazzy](https://github.com/ANYbotics/grid_map/tree/jazzy) + * [rolling](https://github.com/ANYbotics/grid_map/tree/rolling) + +Pull requests for ROS 1 should target `master`. +Pull requests for ROS 2 should target `rolling` and will be backported if they do not break ABI. + ## Documentation An introduction to the grid map library including a tutorial is given in [this book chapter](http://www.researchgate.net/publication/284415855). @@ -108,6 +123,7 @@ This repository consists of following packages: * ***grid_map_filters*** builds on the [ROS Filters] package to process grid maps as a sequence of filters. * ***grid_map_msgs*** holds the [ROS] message and service definitions around the [grid_map_msg/GridMap] message type. * ***grid_map_rviz_plugin*** is an [RViz] plugin to visualize grid maps as 3d surface plots (height maps). +* ***grid_map_sdf*** provides an algorithm to convert an elevation map into a 3D signed distance field. * ***grid_map_visualization*** contains a node written to convert GridMap messages to other [ROS] message types for example for visualization in [RViz]. Additional conversion packages: @@ -258,11 +274,21 @@ Beware that while iterators are convenient, it is often the cleanest and most ef There are two different methods to change the position of the map: * `setPosition(...)`: Changes the position of the map without changing data stored in the map. This changes the corresponce between the data and the map frame. -* `move(...)`: Relocates the grid map such that the corresponce between data and the map frame does not change. Data in the overlapping region before and after the position change remains stored. Data that falls outside of the map at its new position is discarded. Cells that cover previously unknown regions are emptied (set to nan). The data storage is implemented as two-dimensional circular buffer to minimize computational effort. - -`setPosition(...)` | `move(...)` -:---: | :---: -![Grid map iterator](grid_map_core/doc/setposition_method.gif) | ![Submap iterator](grid_map_core/doc/move_method.gif)| +* `move(...)`: + Relocates the region captured by grid map w.r.t. to the static grid map frame. Use this to move the grid map boundaries + without relocating the grid map data. Takes care of all the data handling, such that the grid map data is stationary in the grid map + frame. + - Data in the overlapping region before and after the position change remains stored. + - Data that falls outside the map at its new position is discarded. + - Cells that cover previously unknown regions are emptied (set to nan). + The data storage is implemented as two-dimensional circular buffer to minimize computational effort. + + **Note**: Due to the circular buffer structure, neighbouring indices might not fall close in the map frame. + This assumption only holds for indices obtained by getUnwrappedIndex(). + + `setPosition(...)` | `move(...)` + :---: | :---: + ![Grid map iterator](grid_map_core/doc/setposition_method.gif) | ![Submap iterator](grid_map_core/doc/move_method.gif)| ## Packages @@ -273,6 +299,11 @@ This [RViz] plugin visualizes a grid map layer as 3d surface plot (height map). ![Grid map visualization in RViz](grid_map_rviz_plugin/doc/grid_map_rviz_plugin.png) +### grid_map_sdf + +This package provides an efficient algorithm to convert an elevation map into a dense 3D signed distance field. Each point in the 3D grid contains the distance to the closest point in the map together with the gradient. + +![ANYmal SDF demo](grid_map_sdf/doc/anymal_sdf_demo.gif) ### grid_map_visualization @@ -376,12 +407,13 @@ Several basic filters are provided in the *grid_map_filters* package: * **`gridMapFilters/ThresholdFilter`** - Set values below/above a threshold to a specified value. + Set values in the output layer to a specified value _if_ the condition_layer is exceeding either the upper or lower threshold (only one threshold at a time). name: lower_threshold type: gridMapFilters/ThresholdFilter params: - layer: layer_name + condition_layer: layer_name + output_layer: layer_name lower_threshold: 0.0 # alternative: upper_threshold set_to: 0.0 # # Other uses: .nan, .inf @@ -399,6 +431,7 @@ Several basic filters are provided in the *grid_map_filters* package: Compute for each _NaN_ cell of a layer the median (of finites) inside a patch with radius. Optionally, apply median calculations for values that are already finite, the patch radius for these points is given by existing_value_radius. + Note that the fill computation is only performed if the fill_mask is valid for that point. name: median type: gridMapFilters/MedianFillFilter @@ -408,6 +441,9 @@ Several basic filters are provided in the *grid_map_filters* package: fill_hole_radius: 0.11 # in m. filter_existing_values: false # Default is false. If enabled it also does a median computation for existing values. existing_value_radius: 0.2 # in m. Note that this option only has an effect if filter_existing_values is set true. + fill_mask_layer: fill_mask # A layer that is used to compute which areas to fill. If not present in the input it is automatically computed. + debug: false # If enabled, the additional debug_infill_mask_layer is published. + debug_infill_mask_layer: infill_mask # Layer used to visualize the intermediate, sparse-outlier removed fill mask. Only published if debug is enabled. * **`gridMapFilters/NormalVectorsFilter`** @@ -497,29 +533,31 @@ Additionally, the *grid_map_cv* package provides the following filters: ### Devel Job Status -| | Indigo | Kinetic | Lunar | Melodic | -| --- | --- | --- | --- | --- | -| grid_map | [![Build Status](http://build.ros.org/buildStatus/icon?job=Idev__grid_map__ubuntu_trusty_amd64)](http://build.ros.org/job/Idev__grid_map__ubuntu_trusty_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kdev__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Kdev__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ldev__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Ldev__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdev__grid_map__ubuntu_bionic_amd64)](http://build.ros.org/job/Mdev__grid_map__ubuntu_bionic_amd64/) | -| doc | [![Build Status](http://build.ros.org/buildStatus/icon?job=Idoc__grid_map__ubuntu_trusty_amd64)](http://build.ros.org/job/Idoc__grid_map__ubuntu_trusty_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kdoc__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Kdoc__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ldoc__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Ldoc__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdoc__grid_map__ubuntu_bionic_amd64)](http://build.ros.org/job/Mdoc__grid_map__ubuntu_bionic_amd64/) | +| | Kinetic | Melodic | Noetic | +| --- | --- | --- | --- | +| grid_map | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kdev__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Kdev__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdev__grid_map__ubuntu_bionic_amd64)](http://build.ros.org/job/Mdev__grid_map__ubuntu_bionic_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdev__grid_map__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Mdev__grid_map__ubuntu_focal_armhf__binary/) | +| doc | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kdoc__grid_map__ubuntu_xenial_amd64)](http://build.ros.org/job/Kdoc__grid_map__ubuntu_xenial_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdoc__grid_map__ubuntu_bionic_amd64)](http://build.ros.org/job/Mdoc__grid_map__ubuntu_bionic_amd64/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mdoc__grid_map__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Mdoc__grid_map__ubuntu_focal_armhf__binary/) | ### Release Job Status -| | Indigo | Kinetic | Lunar | Melodic | -| --- | --- | --- | --- | --- | -| grid_map | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map__ubuntu_bionic_amd64__binary/) | -| grid_map_core | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_core__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_core__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_core__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_core__ubuntu_bionic_amd64__binary/) | -| grid_map_costmap_2d | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_costmap_2d__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_costmap_2d__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_costmap_2d__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_costmap_2d__ubuntu_bionic_amd64__binary/) | -| grid_map_cv | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_cv__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_cv__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_cv__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_cv__ubuntu_bionic_amd64__binary/) | -| grid_map_demos | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_demos__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_demos__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_demos__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_demos__ubuntu_bionic_amd64__binary/) | -| grid_map_filters | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_filters__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_filters__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_filters__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_filters__ubuntu_bionic_amd64__binary/) | -| grid_map_loader | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_loader__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_loader__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_loader__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_loader__ubuntu_bionic_amd64__binary/) | -| grid_map_msgs | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_msgs__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_msgs__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_msgs__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_msgs__ubuntu_bionic_amd64__binary/) | -| grid_map_octomap | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_octomap__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_octomap__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_octomap__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_octomap__ubuntu_bionic_amd64__binary/) | -| grid_map_pcl | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_pcl__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_pcl__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_pcl__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_pcl__ubuntu_bionic_amd64__binary/) | -| grid_map_ros | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_ros__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_ros__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_ros__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_ros__ubuntu_bionic_amd64__binary/) | -| grid_map_rviz_plugin | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_rviz_plugin__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_rviz_plugin__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_rviz_plugin__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_rviz_plugin__ubuntu_bionic_amd64__binary/) | -| grid_map_sdf | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_sdf__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_sdf__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_sdf__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_sdf__ubuntu_bionic_amd64__binary/) | -| grid_map_visualization | [![Build Status](http://build.ros.org/buildStatus/icon?job=Ibin_uT64__grid_map_visualization__ubuntu_trusty_amd64__binary)](http://build.ros.org/job/Ibin_uT64__grid_map_visualization__ubuntu_trusty_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Lbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Lbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_visualization__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_visualization__ubuntu_bionic_amd64__binary/) | +| | Kinetic | Melodic | Noetic | +| --- | --- | --- | --- | +| grid_map | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map__ubuntu_focal_armhf__binary/) | +| grid_map_core | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_core__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_core__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_core__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_core__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_core__ubuntu_focal_armhf__binary/) | +| grid_map_costmap_2d | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_costmap_2d__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_costmap_2d__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_costmap_2d__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_costmap_2d__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_costmap_2d__ubuntu_focal_armhf__binary/) | +| grid_map_cv | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_cv__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_cv__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_cv__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_cv__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_cv__ubuntu_focal_armhf__binary/) | +| grid_map_demos | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_demos__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_demos__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_demos__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_demos__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_demos__ubuntu_focal_armhf__binary/) | +| grid_map_filters | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_filters__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_filters__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_filters__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_filters__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_filters__ubuntu_focal_armhf__binary/) | +| grid_map_loader | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_loader__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_loader__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_loader__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_loader__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_loader__ubuntu_focal_armhf__binary/) | +| grid_map_msgs | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_msgs__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_msgs__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_msgs__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_msgs__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_msgs__ubuntu_focal_armhf__binary/) | +| grid_map_octomap | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_octomap__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_octomap__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_octomap__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_octomap__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_octomap__ubuntu_focal_armhf__binary/) | +| grid_map_pcl | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_pcl__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_pcl__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_pcl__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_pcl__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_pcl__ubuntu_focal_armhf__binary/) | +| grid_map_ros | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_ros__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_ros__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_ros__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_ros__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_ros__ubuntu_focal_armhf__binary/) | +| grid_map_rviz_plugin | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_rviz_plugin__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_rviz_plugin__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_rviz_plugin__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_rviz_plugin__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_rviz_plugin__ubuntu_focal_armhf__binary/) | +| grid_map_sdf | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_sdf__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_sdf__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_sdf__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_sdf__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_sdf__ubuntu_focal_armhf__binary/) | +| grid_map_visualization | [![Build Status](http://build.ros.org/buildStatus/icon?job=Kbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary)](http://build.ros.org/job/Kbin_uX64__grid_map_visualization__ubuntu_xenial_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Mbin_uB64__grid_map_visualization__ubuntu_bionic_amd64__binary)](http://build.ros.org/job/Mbin_uB64__grid_map_visualization__ubuntu_bionic_amd64__binary/) | [![Build Status](http://build.ros.org/buildStatus/icon?job=Nbin_ufhf_uFhf__grid_map_visualization__ubuntu_focal_armhf__binary)](http://build.ros.org/job/Nbin_ufhf_uFhf__grid_map_visualization__ubuntu_focal_armhf__binary/) | + + ## Bugs & Feature Requests diff --git a/grid_map/CHANGELOG.rst b/grid_map/CHANGELOG.rst index bf72135e9..139fe8762 100644 --- a/grid_map/CHANGELOG.rst +++ b/grid_map/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map/package.xml b/grid_map/package.xml index a150c1d10..2e9e139dd 100644 --- a/grid_map/package.xml +++ b/grid_map/package.xml @@ -1,10 +1,10 @@ grid_map - 1.6.2 + 1.7.18 Meta-package for the universal grid map library. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_core/CHANGELOG.rst b/grid_map_core/CHANGELOG.rst index 413502356..7a4acaefd 100644 --- a/grid_map_core/CHANGELOG.rst +++ b/grid_map_core/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_core ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ * Implements a grid map transformation from one map frame to another map frame given the transform between the frames. diff --git a/grid_map_core/CMakeLists.txt b/grid_map_core/CMakeLists.txt index e11f8f39e..b290bbd70 100644 --- a/grid_map_core/CMakeLists.txt +++ b/grid_map_core/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_core) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries diff --git a/grid_map_core/include/grid_map_core/BufferRegion.hpp b/grid_map_core/include/grid_map_core/BufferRegion.hpp index bbe95c776..7821c2d65 100644 --- a/grid_map_core/include/grid_map_core/BufferRegion.hpp +++ b/grid_map_core/include/grid_map_core/BufferRegion.hpp @@ -35,8 +35,8 @@ class BufferRegion constexpr static unsigned int nQuadrants = 4; BufferRegion(); - BufferRegion(const Index& startIndex, const Size& size, const BufferRegion::Quadrant& quadrant); - virtual ~BufferRegion(); + BufferRegion(Index startIndex, Size size, BufferRegion::Quadrant quadrant); + virtual ~BufferRegion() = default; const Index& getStartIndex() const; void setStartIndex(const Index& startIndex); @@ -48,7 +48,7 @@ class BufferRegion private: //! Start index (typically top-left) of the buffer region. - Index staretIndex_; + Index startIndex_; //! Size of the buffer region. Size size_; diff --git a/grid_map_core/include/grid_map_core/CubicInterpolation.hpp b/grid_map_core/include/grid_map_core/CubicInterpolation.hpp index 7d6c6e6ec..360e38442 100644 --- a/grid_map_core/include/grid_map_core/CubicInterpolation.hpp +++ b/grid_map_core/include/grid_map_core/CubicInterpolation.hpp @@ -35,7 +35,7 @@ class GridMap; * necessary for interpolation. These are either 16 * function values in the case of bicubic convolution interpolation * or function values and their derivatives for the case - * of standard bicubic inteprolation. + * of standard bicubic interpolation. */ using FunctionValueMatrix = Eigen::Matrix4d; @@ -46,7 +46,7 @@ using FunctionValueMatrix = Eigen::Matrix4d; * @param[in] nElem - number of elements in the container * @return index that is within [0, nElem-1]. */ -unsigned int bindIndexToRange(int idReq, unsigned int nElem); +unsigned int bindIndexToRange(unsigned int idReq, unsigned int nElem); /*! @@ -74,7 +74,7 @@ static const Eigen::Matrix4d cubicInterpolationConvolutionMatrix { -1.0, 3.0, -3.0, 1.0).finished() }; /* - * Index of the middle knot for bicubic inteprolation. This is + * Index of the middle knot for bicubic interpolation. This is * the function value with subscripts (0,0), i.e. f00 in * https://en.wikipedia.org/wiki/Bicubic_interpolation * In the grid map it corresponds to the grid map point closest to the @@ -100,7 +100,7 @@ bool getNormalizedCoordinates(const GridMap &gridMap, const Position &queriedPos Position *position); /* - * Queries the grid map for function values at the coordiantes which are neccesary for + * Queries the grid map for function values at the coordinates which are necessary for * performing the interpolation. The right function values are then assembled * in a matrix. * @param[in] gridMap - grid map with the data @@ -119,7 +119,7 @@ bool assembleFunctionValueMatrix(const GridMap &gridMap, const std::string &laye * Performs convolution in 1D. the function requires 4 function values * to compute the convolution. The result is interpolated data in 1D. * @param[in] t - normalized coordinate (x or y) - * @param[in] functionValues - vector of 4 function values neccessary to perform + * @param[in] functionValues - vector of 4 function values necessary to perform * interpolation in 1 dimension. * @return - interpolated value at normalized coordinate t */ @@ -177,7 +177,7 @@ struct DataMatrix /* * Interpolation is performed on a unit square. - * Hence we need to compute 4 corners of that unit square, + * Hence, we need to compute 4 corners of that unit square, * and find their indices in the grid map. IndicesMatrix * is a container that stores those indices. Each index * contains two numbers (row number, column number) in the @@ -313,7 +313,7 @@ double mixedSecondOrderDerivativeAt(const Matrix &layerData, const Index &index, double evaluatePolynomial(const FunctionValueMatrix &functionValues, double tx, double ty); /* - * Assemble function value matrix from small submatrices containing function values + * Assemble function value matrix from small sub-matrices containing function values * or derivative values at the corners of the unit square. * See https://en.wikipedia.org/wiki/Bicubic_interpolation for details. * diff --git a/grid_map_core/include/grid_map_core/GridMap.hpp b/grid_map_core/include/grid_map_core/GridMap.hpp index 9bc1d78f2..74a62b701 100644 --- a/grid_map_core/include/grid_map_core/GridMap.hpp +++ b/grid_map_core/include/grid_map_core/GridMap.hpp @@ -8,13 +8,13 @@ #pragma once -#include "grid_map_core/TypeDefs.hpp" -#include "grid_map_core/SubmapGeometry.hpp" #include "grid_map_core/BufferRegion.hpp" +#include "grid_map_core/SubmapGeometry.hpp" +#include "grid_map_core/TypeDefs.hpp" // STL -#include #include +#include // Eigen #include @@ -37,8 +37,7 @@ class SubmapGeometry; * - "surface_normal_x", "surface_normal_y", "surface_normal_z" * etc. */ -class GridMap -{ +class GridMap { public: // Type traits for use with template methods/classes using GridMap as a template parameter. typedef grid_map::DataType DataType; @@ -48,7 +47,7 @@ class GridMap * Constructor. * @param layers a vector of strings containing the definition/description of the data layer. */ - GridMap(const std::vector& layers); + explicit GridMap(const std::vector& layers); /*! * Emtpy constructor. @@ -74,8 +73,7 @@ class GridMap * @param resolution the cell size in [m/cell]. * @param position the 2d position of the grid map in the grid map frame [m]. */ - void setGeometry(const Length& length, const double resolution, - const Position& position = Position::Zero()); + void setGeometry(const Length& length, const double resolution, const Position& position = Position::Zero()); /*! * Set the geometry of the grid map from submap geometry information. @@ -88,7 +86,7 @@ class GridMap * @param layer the name of the layer. * @value value the value to initialize the cells with. */ - void add(const std::string& layer, const double value = NAN); + void add(const std::string& layer, const float value = NAN); /*! * Add a new data layer (if the layer already exists, overwrite its data, otherwise add layer and data). @@ -127,7 +125,7 @@ class GridMap * @return grid map data as matrix. * @throw std::out_of_range if no map layer with name `layer` is present. */ - const Matrix& operator [](const std::string& layer) const; + const Matrix& operator[](const std::string& layer) const; /*! * Returns the grid map data for a layer as non-const. Use this method @@ -136,7 +134,7 @@ class GridMap * @return grid map data. * @throw std::out_of_range if no map layer with name `layer` is present. */ - Matrix& operator [](const std::string& layer); + Matrix& operator[](const std::string& layer); /*! * Removes a layer from the grid map. @@ -283,8 +281,7 @@ class GridMap * @param vector the vector with the values of the data type. * @return true if successful, false if no valid data available. */ - bool getVector(const std::string& layerPrefix, const Index& index, - Eigen::Vector3d& vector) const; + bool getVector(const std::string& layerPrefix, const Index& index, Eigen::Vector3d& vector) const; /*! * Gets a submap from the map. The requested submap is specified with the requested @@ -309,12 +306,12 @@ class GridMap * @param[out] isSuccess true if successful, false otherwise. * @return submap (is empty if success is false). */ - GridMap getSubmap(const Position& position, const Length& length, Index& indexInSubmap, - bool& isSuccess) const; + GridMap getSubmap(const Position& position, const Length& length, Index& indexInSubmap, bool& isSuccess) const; /*! * Apply isometric transformation (rotation + offset) to grid map and returns the transformed map. * Note: The returned map may not have the same length since it's geometric description contains + * Note: The transformation will only be applied to the height layer of the grid map, other layers will remain untouched. * the original map. * @param[in] transform the requested transformation to apply. * @param[in] heightLayerName the height layer of the map. @@ -325,26 +322,33 @@ class GridMap * @return transformed map. * @throw std::out_of_range if no map layer with name `heightLayerName` is present. */ - GridMap getTransformedMap(const Eigen::Isometry3d& transform, const std::string& heightLayerName, - const std::string& newFrameId, + GridMap getTransformedMap(const Eigen::Isometry3d& transform, const std::string& heightLayerName, const std::string& newFrameId, const double sampleRatio = 0.0) const; - /*! - * Set the position of the grid map. - * Note: This method does not change the data stored in the grid map and - * is complementary to the `move(...)` method. For a comparison between - * the `setPosition` and the `move` method, see the `move_demo_node.cpp` - * file of the `grid_map_demos` package. - * @param position the 2d position of the grid map in the grid map frame [m]. - */ - void setPosition(const Position& position); - /*! - * Move the grid map w.r.t. to the grid map frame. Use this to move the grid map - * boundaries without moving the grid map data. Takes care of all the data handling, - * such that the grid map data is stationary in the grid map frame. - * Note: For a comparison between the `setPosition` and the `move` method, - * see the `move_demo_node.cpp` file of the `grid_map_demos` package. + * Set the position of the grid map. + * Note: This method does not change the data stored in the grid map and + * is complementary to the `move(...)` method. For a comparison between + * the `setPosition` and the `move` method, see the `move_demo_node.cpp` + * file of the `grid_map_demos` package. + * @param position the 2d position of the grid map in the grid map frame [m]. + */ + void setPosition(const Position& position); + + /*! + * Relocates the region captured by grid map w.r.t. to the static grid map frame. Use this to move the grid map boundaries + * without relocating the grid map data. Takes care of all the data handling, such that the grid map data is stationary in the grid map + * frame. + * - Data in the overlapping region before and after the position change remains stored. + * - Data that falls outside the map at its new position is discarded. + * - Cells that cover previously unknown regions are emptied (set to nan). + * The data storage is implemented as two-dimensional circular buffer to minimize computational effort. + * + * Note: Due to the circular buffer structure, neighbouring indices might not fall close in the map frame. + * This assumption only holds for indices obtained by getUnwrappedIndex(). + * + * Note: For a comparison between the `setPosition` and the `move` method, see the `move_demo_node.cpp` file of the `grid_map_demos` package. + * * @param position the new location of the grid map in the map frame. * @param newRegions the regions of the newly covered / previously uncovered regions of the buffer. * @return true if map has been moved, false otherwise. @@ -369,8 +373,7 @@ class GridMap * @param layers the layers that are copied if not all layers are used. * @return true if successful. */ - bool addDataFrom(const GridMap& other, bool extendMap, - bool overwriteData, bool copyAllLayers, + bool addDataFrom(const GridMap& other, bool extendMap, bool overwriteData, bool copyAllLayers, std::vector layers = std::vector()); /*! @@ -515,7 +518,6 @@ class GridMap */ bool atPositionLinearInterpolated(const std::string& layer, const Position& position, float& value) const; - /*! * Get cell data at requested position, cubic convolution * interpolated from 4x4 cells. At the edge of the map, @@ -527,8 +529,7 @@ class GridMap * @param[out] value the data of the cell. * @return true if bicubic convolution interpolation was successful. */ - bool atPositionBicubicConvolutionInterpolated(const std::string& layer, const Position& position, - float& value) const; + bool atPositionBicubicConvolutionInterpolated(const std::string& layer, const Position& position, float& value) const; /*! * Get cell data at requested position, cubic interpolated @@ -541,9 +542,7 @@ class GridMap * @param[out] value the data of the cell. * @return true if bicubic interpolation was successful. */ - bool atPositionBicubicInterpolated(const std::string& layer, const Position& position, - float& value) const; - + bool atPositionBicubicInterpolated(const std::string& layer, const Position& position, float& value) const; /*! * Resize the buffer. @@ -580,11 +579,11 @@ class GridMap //! Size of the buffer (rows and cols of the data structure). Size size_; - //! Circular buffer start indeces. + //! Circular buffer start indices. Index startIndex_; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_core/include/grid_map_core/GridMapMath.hpp b/grid_map_core/include/grid_map_core/GridMapMath.hpp index b45f44aaa..fb1a38eb3 100644 --- a/grid_map_core/include/grid_map_core/GridMapMath.hpp +++ b/grid_map_core/include/grid_map_core/GridMapMath.hpp @@ -17,6 +17,12 @@ namespace grid_map { +union Color +{ + unsigned long longColor_; + float floatColor_; +}; + /*! * Gets the position of a cell specified by its index in the map frame. * @param[out] position the position of the center of the cell in the map frame. @@ -77,7 +83,7 @@ void getPositionOfDataStructureOrigin(const Position& position, Position& positionOfOrigin); /*! - * Computes how many cells/indeces the map is moved based on a position shift in + * Computes how many cells/indices the map is moved based on a position shift in * the grid map frame. Use this function if you are moving the grid map * and want to ensure that the cells match before and after. * @param[out] indexShift the corresponding shift of the indices. @@ -94,7 +100,7 @@ bool getIndexShiftFromPositionShift(Index& indexShift, * if you are moving the grid map and want to ensure that the cells match * before and after. * @param[out] positionShift the corresponding shift in position in the grid map frame. - * @param[in] indexShift the desired shift of the indeces. + * @param[in] indexShift the desired shift of the indices. * @param[in] resolution the resolution of the map. * @return true if successful. */ @@ -114,7 +120,7 @@ bool checkIfIndexInRange(const Index& index, const Size& bufferSize); * Bounds an index that runs out of the range of the buffer. * This means that an index that overflows is stopped at the last valid index. * This is the 2d version of boundIndexToRange(int&, const int&). - * @param[in/out] index the indeces that will be bounded to the valid region of the buffer. + * @param[in/out] index the indices that will be bounded to the valid region of the buffer. * @param[in] bufferSize the size of the buffer. */ void boundIndexToRange(Index& index, const Size& bufferSize); @@ -131,7 +137,7 @@ void boundIndexToRange(int& index, const int& bufferSize); * Wraps an index that runs out of the range of the buffer back into allowed the region. * This means that an index that overflows is reset to zero. * This is the 2d version of wrapIndexToRange(int&, const int&). - * @param[in/out] index the indeces that will be wrapped into the valid region of the buffer. + * @param[in/out] index the indices that will be wrapped into the valid region of the buffer. * @param[in] bufferSize the size of the buffer. */ void wrapIndexToRange(Index& index, const Size& bufferSize); @@ -158,7 +164,7 @@ void boundPositionToRange(Position& position, const Length& mapLength, const Pos * and the map frame (x/y-coordinate). * @return the alignment transformation. */ -const Eigen::Matrix2i getBufferOrderToMapFrameAlignment(); +Eigen::Matrix2i getBufferOrderToMapFrameAlignment(); /*! * Given a map and a desired submap (defined by position and size), this function computes @@ -198,7 +204,7 @@ bool getSubmapInformation(Index& submapTopLeftIndex, * @param bottomRightIndex the bottom right index in the map. * @return buffer size for the submap. */ -Size getSubmapSizeFromCornerIndeces(const Index& topLeftIndex, const Index& bottomRightIndex, +Size getSubmapSizeFromCornerIndices(const Index& topLeftIndex, const Index& bottomRightIndex, const Size& bufferSize, const Index& bufferStartIndex); /*! @@ -224,7 +230,7 @@ bool getBufferRegionsForSubmap(std::vector& submapBufferRegions, * @param[in/out] index the index in the map that is incremented (corrected for the circular buffer). * @param[in] bufferSize the map buffer size. * @param[in] bufferStartIndex the map buffer start index. - * @return true if successfully incremented indeces, false if end of iteration limits are reached. + * @return true if successfully incremented indices, false if end of iteration limits are reached. */ bool incrementIndex(Index& index, const Size& bufferSize, const Index& bufferStartIndex = Index::Zero()); @@ -243,7 +249,7 @@ bool incrementIndex(Index& index, const Size& bufferSize, * @param[in] submapBufferSize the submap buffer size. * @param[in] bufferSize the map buffer size. * @param[in] bufferStartIndex the map buffer start index. - * @return true if successfully incremented indeces, false if end of iteration limits are reached. + * @return true if successfully incremented indices, false if end of iteration limits are reached. */ bool incrementIndexForSubmap(Index& submapIndex, Index& index, const Index& submapTopLeftIndex, const Size& submapBufferSize, const Size& bufferSize, @@ -278,7 +284,7 @@ Index getBufferIndexFromIndex(const Index& index, const Size& bufferSize, const * @param[in] (optional) rowMajor if the linear index is generated for row-major format. * @return the linear 1d index. */ -size_t getLinearIndexFromIndex(const Index& index, const Size& bufferSize, const bool rowMajor = false); +size_t getLinearIndexFromIndex(const Index& index, const Size& bufferSize, bool rowMajor = false); /*! * Returns the regular index (2-dim.) corresponding to the linear index (1-dim.) for a given buffer size. @@ -287,26 +293,7 @@ size_t getLinearIndexFromIndex(const Index& index, const Size& bufferSize, const * @param[in] (optional) rowMajor if the linear index is generated for row-major format. * @return the regular 2d index. */ -Index getIndexFromLinearIndex(const size_t linearIndex, const Size& bufferSize, const bool rowMajor = false); - -/*! - * Generates a list of indices for a region in the map. - * @param regionIndex the region top-left index. - * @param regionSize the region size. - * @param indices the list of indices of the region. - */ -void getIndicesForRegion(const Index& regionIndex, const Size& regionSize, - std::vector indices); - -/*! - * Generates a list of indices for multiple regions in the map. - * This method makes sure every index is only once contained in the list. - * @param regionIndeces the regions' top-left index. - * @param regionSizes the regions' sizes. - * @param indices the list of indices of the regions. - */ -void getIndicesForRegions(const std::vector& regionIndeces, const Size& regionSizes, - std::vector indices); +Index getIndexFromLinearIndex(size_t linearIndex, const Size& bufferSize, bool rowMajor = false); /*! * Transforms an int color value (concatenated RGB values) to an int color vector (RGB from 0-255). @@ -354,4 +341,4 @@ void colorVectorToValue(const Eigen::Vector3i& colorVector, float& colorValue); */ void colorVectorToValue(const Eigen::Vector3f& colorVector, float& colorValue); -} // namespace +} // namespace grid_map diff --git a/grid_map_core/include/grid_map_core/Polygon.hpp b/grid_map_core/include/grid_map_core/Polygon.hpp index cf486fbd8..d7720fb55 100644 --- a/grid_map_core/include/grid_map_core/Polygon.hpp +++ b/grid_map_core/include/grid_map_core/Polygon.hpp @@ -37,11 +37,6 @@ class Polygon */ Polygon(std::vector vertices); - /*! - * Destructor. - */ - virtual ~Polygon(); - /*! * Check if point is inside polygon. * @param point the point to be checked. @@ -60,7 +55,7 @@ class Polygon * @param index the index of the requested vertex. * @return the requested vertex. */ - const Position& getVertex(const size_t index) const; + const Position& getVertex(size_t index) const; /*! * Removes all vertices from the polygon. @@ -72,7 +67,7 @@ class Polygon * @param index the index of the requested vertex. * @return the requested vertex. */ - const Position& operator [](const size_t index) const; + const Position& operator [](size_t index) const; /*! * Returns the vertices of the polygon. @@ -90,7 +85,7 @@ class Polygon * Set the timestamp of the polygon. * @param timestamp the timestamp to set (in nanoseconds). */ - void setTimestamp(const uint64_t timestamp); + void setTimestamp(uint64_t timestamp); /*! * Get the timestamp of the polygon. @@ -156,15 +151,15 @@ class Polygon * @param margin the margin to offset the polygon by (in [m]). * @return true if successful, false otherwise. */ - bool offsetInward(const double margin); + bool offsetInward(double margin); /*! - * If only two verices are given, this methods generates a + * If only two vertices are given, this methods generates a * `thickened` line polygon with four vertices. * @param thickness the desired thickness of the line. * @return true if successful, false otherwise. */ - bool thickenLine(const double thickness); + bool thickenLine(double thickness); /*! * Return a triangulated version of the polygon. @@ -179,8 +174,8 @@ class Polygon * @param[in] nVertices number of vertices of the approximation polygon. Default = 20. * @return circle as polygon. */ - static Polygon fromCircle(const Position center, const double radius, - const int nVertices = 20); + static Polygon fromCircle(Position center, double radius, + int nVertices = 20); /*! * Approximates two circles with a convex hull and returns it as polygon. @@ -190,10 +185,10 @@ class Polygon * @param[in] nVertices number of vertices of the approximation polygon. Default = 20. * @return convex hull of the two circles as polygon. */ - static Polygon convexHullOfTwoCircles(const Position center1, - const Position center2, - const double radius, - const int nVertices = 20); + static Polygon convexHullOfTwoCircles(Position center1, + Position center2, + double radius, + int nVertices = 20); /*! * Computes the convex hull of two polygons and returns it as polygon. @@ -237,7 +232,7 @@ class Polygon static double vectorsMakeClockwiseTurn(const Eigen::Vector2d& pointO, const Eigen::Vector2d& pointA, const Eigen::Vector2d& pointB); - + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) //! Frame id of the polygon. std::string frameId_; @@ -246,7 +241,7 @@ class Polygon //! Vertices of the polygon. std::vector vertices_; - + // NOLINTEND(misc-non-private-member-variables-in-classes) public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; diff --git a/grid_map_core/include/grid_map_core/SubmapGeometry.hpp b/grid_map_core/include/grid_map_core/SubmapGeometry.hpp index 02143b5f0..3e7b0487d 100644 --- a/grid_map_core/include/grid_map_core/SubmapGeometry.hpp +++ b/grid_map_core/include/grid_map_core/SubmapGeometry.hpp @@ -35,7 +35,7 @@ class SubmapGeometry SubmapGeometry(const GridMap& gridMap, const Position& position, const Length& length, bool& isSuccess); - virtual ~SubmapGeometry(); + virtual ~SubmapGeometry() = default; const GridMap& getGridMap() const; const Length& getLength() const; diff --git a/grid_map_core/include/grid_map_core/TypeDefs.hpp b/grid_map_core/include/grid_map_core/TypeDefs.hpp index 7e19c63fc..04a7a36ad 100644 --- a/grid_map_core/include/grid_map_core/TypeDefs.hpp +++ b/grid_map_core/include/grid_map_core/TypeDefs.hpp @@ -7,22 +7,22 @@ */ // Eigen -#include - #pragma once +#include + namespace grid_map { - typedef Eigen::MatrixXf Matrix; - typedef Matrix::Scalar DataType; - typedef Eigen::Vector2d Position; - typedef Eigen::Vector2d Vector; - typedef Eigen::Vector3d Position3; - typedef Eigen::Vector3d Vector3; - typedef Eigen::Array2i Index; - typedef Eigen::Array2i Size; - typedef Eigen::Array2d Length; - typedef uint64_t Time; + using Matrix = Eigen::MatrixXf; + using DataType = Matrix::Scalar; + using Position = Eigen::Vector2d; + using Vector = Eigen::Vector2d; + using Position3 = Eigen::Vector3d; + using Vector3 = Eigen::Vector3d; + using Index = Eigen::Array2i; + using Size = Eigen::Array2i; + using Length = Eigen::Array2d; + using Time = uint64_t; /* * Interpolations are ordered in the order @@ -43,4 +43,5 @@ namespace grid_map { INTER_CUBIC // standard bicubic interpolation }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/eigen_plugins/DenseBasePlugin.hpp b/grid_map_core/include/grid_map_core/eigen_plugins/DenseBasePlugin.hpp index 8b3dd6f9b..118a6e368 100644 --- a/grid_map_core/include/grid_map_core/eigen_plugins/DenseBasePlugin.hpp +++ b/grid_map_core/include/grid_map_core/eigen_plugins/DenseBasePlugin.hpp @@ -1,12 +1,18 @@ +#pragma once + Scalar numberOfFinites() const { - if (SizeAtCompileTime==0 || (SizeAtCompileTime==Dynamic && size()==0)) return Scalar(0); + if (SizeAtCompileTime==0 || (SizeAtCompileTime==Dynamic && size()==0)) { + return Scalar(0); + } return Scalar((derived().array() == derived().array()).count()); } Scalar sumOfFinites() const { - if (SizeAtCompileTime==0 || (SizeAtCompileTime==Dynamic && size()==0)) return Scalar(0); + if (SizeAtCompileTime==0 || (SizeAtCompileTime==Dynamic && size()==0)) { + return Scalar(0); + } return Scalar(this->redux(Eigen::internal::scalar_sum_of_finites_op())); } diff --git a/grid_map_core/include/grid_map_core/eigen_plugins/Functors.hpp b/grid_map_core/include/grid_map_core/eigen_plugins/Functors.hpp index c4963c0fa..1f9f6658e 100644 --- a/grid_map_core/include/grid_map_core/eigen_plugins/Functors.hpp +++ b/grid_map_core/include/grid_map_core/eigen_plugins/Functors.hpp @@ -18,11 +18,11 @@ struct Clamp max_(max) { } - const Scalar operator()(const Scalar& x) const + Scalar operator()(const Scalar& x) const { return x < min_ ? min_ : (x > max_ ? max_ : x); } Scalar min_, max_; }; -} +} // namespace grid_map diff --git a/grid_map_core/include/grid_map_core/eigen_plugins/FunctorsPlugin.hpp b/grid_map_core/include/grid_map_core/eigen_plugins/FunctorsPlugin.hpp index 79dcdd665..77aa03902 100644 --- a/grid_map_core/include/grid_map_core/eigen_plugins/FunctorsPlugin.hpp +++ b/grid_map_core/include/grid_map_core/eigen_plugins/FunctorsPlugin.hpp @@ -1,4 +1,4 @@ -#include +#include template struct scalar_sum_of_finites_op { EIGEN_EMPTY_STRUCT_CTOR(scalar_sum_of_finites_op) diff --git a/grid_map_core/include/grid_map_core/gtest_eigen.hpp b/grid_map_core/include/grid_map_core/gtest_eigen.hpp index c061fb103..8872ab8f2 100644 --- a/grid_map_core/include/grid_map_core/gtest_eigen.hpp +++ b/grid_map_core/include/grid_map_core/gtest_eigen.hpp @@ -96,8 +96,9 @@ namespace grid_map { } template - void assertFinite(const M1 & A, std::string const & message = "") + void assertFinite(const M1 & A, std::string const & /*message*/ = "") { + std::cout << ("test") << std::endl; for(int r = 0; r < A.rows(); r++) { for(int c = 0; c < A.cols(); c++) @@ -107,58 +108,58 @@ namespace grid_map { } } - inline bool compareRelative(double a, double b, double percentTolerance, double * percentError = NULL) - { - // \todo: does anyone have a better idea? + inline bool compareRelative(double a, double b, double percentTolerance, double* percentError = nullptr) { + // \todo: does anyone have a better idea? double fa = fabs(a); double fb = fabs(b); - if( (fa < 1e-15 && fb < 1e-15) || // Both zero. - (fa == 0.0 && fb < 1e-6) || // One exactly zero and the other small - (fb == 0.0 && fa < 1e-6) ) // ditto - return true; + if ((fa < 1e-15 && fb < 1e-15) || // Both zero. + (fa == 0.0 && fb < 1e-6) || // One exactly zero and the other small + (fb == 0.0 && fa < 1e-6)) { // ditto + return true; + } - double diff = fabs(a - b)/std::max(fa,fb); - if(diff > percentTolerance * 1e-2) - { - if(percentError) - *percentError = diff * 100.0; - return false; - } + double diff = fabs(a - b) / std::max(fa, fb); + if (diff > percentTolerance * 1e-2) { + if (percentError != nullptr) { + *percentError = diff * 100.0; + } + return false; + } return true; } #define ASSERT_DOUBLE_MX_EQ(A, B, PERCENT_TOLERANCE, MSG) \ - ASSERT_EQ((size_t)(A).rows(), (size_t)(B).rows()) << MSG << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B << "\nare not the same size"; \ - ASSERT_EQ((size_t)(A).cols(), (size_t)(B).cols()) << MSG << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B << "\nare not the same size"; \ + ASSERT_EQ((size_t)(A).rows(), (size_t)(B).rows()) << (MSG) << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B) << "\nare not the same size"; \ + ASSERT_EQ((size_t)(A).cols(), (size_t)(B).cols()) << (MSG) << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B) << "\nare not the same size"; \ for(int r = 0; r < (A).rows(); r++) \ { \ for(int c = 0; c < (A).cols(); c++) \ { \ double percentError = 0.0; \ ASSERT_TRUE(grid_map::compareRelative( (A)(r,c), (B)(r,c), PERCENT_TOLERANCE, &percentError)) \ - << MSG << "\nComparing:\n" \ + << (MSG) << "\nComparing:\n" \ << #A << "(" << r << "," << c << ") = " << (A)(r,c) << std::endl \ << #B << "(" << r << "," << c << ") = " << (B)(r,c) << std::endl \ - << "Error was " << percentError << "% > " << PERCENT_TOLERANCE << "%\n" \ - << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B; \ + << "Error was " << percentError << "% > " << (PERCENT_TOLERANCE) << "%\n" \ + << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B); \ } \ } #define ASSERT_DOUBLE_SPARSE_MX_EQ(A, B, PERCENT_TOLERANCE, MSG) \ - ASSERT_EQ((size_t)(A).rows(), (size_t)(B).rows()) << MSG << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B << "\nare not the same size"; \ - ASSERT_EQ((size_t)(A).cols(), (size_t)(B).cols()) << MSG << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B << "\nare not the same size"; \ + ASSERT_EQ((size_t)(A).rows(), (size_t)(B).rows()) << (MSG) << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B) << "\nare not the same size"; \ + ASSERT_EQ((size_t)(A).cols(), (size_t)(B).cols()) << (MSG) << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B) << "\nare not the same size"; \ for(int r = 0; r < (A).rows(); r++) \ { \ for(int c = 0; c < (A).cols(); c++) \ { \ double percentError = 0.0; \ ASSERT_TRUE(grid_map::compareRelative( (A).coeff(r,c), (B).coeff(r,c), PERCENT_TOLERANCE, &percentError)) \ - << MSG << "\nComparing:\n" \ + << (MSG) << "\nComparing:\n" \ << #A << "(" << r << "," << c << ") = " << (A).coeff(r,c) << std::endl \ << #B << "(" << r << "," << c << ") = " << (B).coeff(r,c) << std::endl \ - << "Error was " << percentError << "% > " << PERCENT_TOLERANCE << "%\n" \ - << "\nMatrix " << #A << ":\n" << A << "\nand matrix " << #B << "\n" << B; \ + << "Error was " << percentError << "% > " << (PERCENT_TOLERANCE) << "%\n" \ + << "\nMatrix " << #A << ":\n" << (A) << "\nand matrix " << #B << "\n" << (B); \ } \ } -} // namespace +} // namespace grid_map diff --git a/grid_map_core/include/grid_map_core/iterators/CircleIterator.hpp b/grid_map_core/include/grid_map_core/iterators/CircleIterator.hpp index 054819ba6..14f271344 100644 --- a/grid_map_core/include/grid_map_core/iterators/CircleIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/CircleIterator.hpp @@ -30,14 +30,7 @@ class CircleIterator * @param center the position of the circle center. * @param radius the radius of the circle. */ - CircleIterator(const GridMap& gridMap, const Position& center, const double radius); - - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - CircleIterator& operator =(const CircleIterator& other); + CircleIterator(const GridMap& gridMap, const Position& center, double radius); /*! * Compare to another iterator. @@ -78,7 +71,7 @@ class CircleIterator * @param[out] startIndex the start index of the submap. * @param[out] bufferSize the buffer size of the submap. */ - void findSubmapParameters(const Position& center, const double radius, + void findSubmapParameters(const Position& center, double radius, Index& startIndex, Size& bufferSize) const; //! Position of the circle center; @@ -104,4 +97,5 @@ class CircleIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/EllipseIterator.hpp b/grid_map_core/include/grid_map_core/iterators/EllipseIterator.hpp index 2506e4ac9..06be4d480 100644 --- a/grid_map_core/include/grid_map_core/iterators/EllipseIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/EllipseIterator.hpp @@ -32,14 +32,7 @@ class EllipseIterator * @param length the length of the main axis. * @param angle the rotation angle of the ellipse (in [rad]). */ - EllipseIterator(const GridMap& gridMap, const Position& center, const Length& length, const double rotation = 0.0); - - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - EllipseIterator& operator =(const EllipseIterator& other); + EllipseIterator(const GridMap& gridMap, const Position& center, const Length& length, double rotation = 0.0); /*! * Compare to another iterator. @@ -87,7 +80,7 @@ class EllipseIterator * @param[out] startIndex the start index of the submap. * @param[out] bufferSize the buffer size of the submap. */ - void findSubmapParameters(const Position& center, const Length& length, const double rotation, + void findSubmapParameters(const Position& center, const Length& length, double rotation, Index& startIndex, Size& bufferSize) const; //! Position of the circle center; @@ -113,4 +106,5 @@ class EllipseIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/GridMapIterator.hpp b/grid_map_core/include/grid_map_core/iterators/GridMapIterator.hpp index 2beeca89a..3a3443b15 100644 --- a/grid_map_core/include/grid_map_core/iterators/GridMapIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/GridMapIterator.hpp @@ -34,13 +34,6 @@ class GridMapIterator */ GridMapIterator(const GridMapIterator* other); - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - GridMapIterator& operator =(const GridMapIterator& other); - /*! * Compare to another iterator. * @return whether the current iterator points to a different address than the other one. @@ -52,7 +45,7 @@ class GridMapIterator * to which the iterator is pointing at. * @return the regular index (2-dim.) of the cell on which the iterator is pointing. */ - const Index operator *() const; + Index operator *() const; /*! * Returns the the linear (1-dim.) index of the cell the iterator is pointing at. @@ -66,7 +59,7 @@ class GridMapIterator * Retrieve the index as unwrapped index, i.e., as the corresponding index of a * grid map with no circular buffer offset. */ - const Index getUnwrappedIndex() const; + Index getUnwrappedIndex() const; /*! * Increase the iterator to the next element. @@ -86,8 +79,8 @@ class GridMapIterator */ bool isPastEnd() const; -protected: - + protected: + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) //! Size of the buffer. Size size_; @@ -102,9 +95,11 @@ class GridMapIterator //! Is iterator out of scope. bool isPastEnd_; + // NOLINTEND(misc-non-private-member-variables-in-classes) public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/LineIterator.hpp b/grid_map_core/include/grid_map_core/iterators/LineIterator.hpp index d3f23f188..ffbdc9ce1 100644 --- a/grid_map_core/include/grid_map_core/iterators/LineIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/LineIterator.hpp @@ -40,13 +40,6 @@ class LineIterator */ LineIterator(const grid_map::GridMap& gridMap, const Index& start, const Index& end); - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - LineIterator& operator =(const LineIterator& other); - /*! * Compare to another iterator. * @return whether the current iterator points to a different address than the other one. @@ -96,7 +89,7 @@ class LineIterator * @param[out] index the index of the moved start position. * @return true if successful, false otherwise. */ - bool getIndexLimitedToMapRange(const grid_map::GridMap& gridMap, const Position& start, + static bool getIndexLimitedToMapRange(const grid_map::GridMap& gridMap, const Position& start, const Position& end, Index& index); //! Current index. @@ -116,12 +109,12 @@ class LineIterator //! Helper variables for Bresenham Line Drawing algorithm. Size increment1_, increment2_; - int denominator_, numerator_, numeratorAdd_; + int denominator_{0}, numerator_{0}, numeratorAdd_{0}; //! Map information needed to get position from iterator. Length mapLength_; Position mapPosition_; - double resolution_; + double resolution_{NAN}; Size bufferSize_; Index bufferStartIndex_; @@ -129,4 +122,5 @@ class LineIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/PolygonIterator.hpp b/grid_map_core/include/grid_map_core/iterators/PolygonIterator.hpp index da68fd65c..6027670ab 100644 --- a/grid_map_core/include/grid_map_core/iterators/PolygonIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/PolygonIterator.hpp @@ -30,13 +30,6 @@ class PolygonIterator */ PolygonIterator(const grid_map::GridMap& gridMap, const grid_map::Polygon& polygon); - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - PolygonIterator& operator =(const PolygonIterator& other); - /*! * Compare to another iterator. * @return whether the current iterator points to a different address than the other one. @@ -94,4 +87,5 @@ class PolygonIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/SlidingWindowIterator.hpp b/grid_map_core/include/grid_map_core/iterators/SlidingWindowIterator.hpp index c581f8fbf..42b2a95a2 100644 --- a/grid_map_core/include/grid_map_core/iterators/SlidingWindowIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/SlidingWindowIterator.hpp @@ -41,32 +41,32 @@ class SlidingWindowIterator : public GridMapIterator */ SlidingWindowIterator(const GridMap& gridMap, const std::string& layer, const EdgeHandling& edgeHandling = EdgeHandling::CROP, - const size_t windowSize = 3); + size_t windowSize = 3); /*! * Copy constructor. * @param other the object to copy. */ - SlidingWindowIterator(const SlidingWindowIterator* other); + explicit SlidingWindowIterator(const SlidingWindowIterator* other); /*! * Set the side length of the moving window (in m). * @param gridMap the grid map to iterate on. * @param windowLength the side length of the window (in m). */ - void setWindowLength(const GridMap& gridMap, const double windowLength); + void setWindowLength(const GridMap& gridMap, double windowLength); /*! * Increase the iterator to the next element. * @return a reference to the updated iterator. */ - SlidingWindowIterator& operator ++(); + SlidingWindowIterator& operator ++() override; /*! * Return the data of the sliding window. * @return the data of the sliding window. */ - const Matrix getData() const; + Matrix getData() const; private: //! Setup members. @@ -85,10 +85,11 @@ class SlidingWindowIterator : public GridMapIterator size_t windowSize_; //! Size of the border of the window around the center cell. - size_t windowMargin_; + size_t windowMargin_{0}; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/SpiralIterator.hpp b/grid_map_core/include/grid_map_core/iterators/SpiralIterator.hpp index b3715cb3d..f1c3966a8 100644 --- a/grid_map_core/include/grid_map_core/iterators/SpiralIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/SpiralIterator.hpp @@ -29,14 +29,7 @@ class SpiralIterator * @param center the position of the circle center. * @param radius the radius of the circle. */ - SpiralIterator(const grid_map::GridMap& gridMap, const Eigen::Vector2d& center, const double radius); - - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - SpiralIterator& operator =(const SpiralIterator& other); + SpiralIterator(const grid_map::GridMap& gridMap, Eigen::Vector2d center, double radius); /*! * Compare to another iterator. @@ -74,7 +67,7 @@ class SpiralIterator * Check if index is inside the circle. * @return true if inside, false otherwise. */ - bool isInside(const Index index) const; + bool isInside(const Index& index) const; /*! * Uses the current distance to get the points of a ring @@ -82,8 +75,8 @@ class SpiralIterator */ void generateRing(); - int signum(const int val) { - return (0 < val) - (val < 0); + static int signum(const int val) { + return static_cast(0 < val) - static_cast(val < 0); } //! Position of the circle center; @@ -112,4 +105,5 @@ class SpiralIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/iterators/SubmapIterator.hpp b/grid_map_core/include/grid_map_core/iterators/SubmapIterator.hpp index 9ca7706bd..3cf1a1a48 100644 --- a/grid_map_core/include/grid_map_core/iterators/SubmapIterator.hpp +++ b/grid_map_core/include/grid_map_core/iterators/SubmapIterator.hpp @@ -52,13 +52,6 @@ class SubmapIterator */ SubmapIterator(const SubmapIterator* other); - /*! - * Assignment operator. - * @param iterator the iterator to copy data from. - * @return a reference to *this. - */ - SubmapIterator& operator =(const SubmapIterator& other); - /*! * Compare to another iterator. * @return whether the current iterator points to a different address than the other one. @@ -122,4 +115,5 @@ class SubmapIterator EIGEN_MAKE_ALIGNED_OPERATOR_NEW }; -} /* namespace */ +} // namespace grid_map + diff --git a/grid_map_core/include/grid_map_core/utils/testing.hpp b/grid_map_core/include/grid_map_core/utils/testing.hpp new file mode 100644 index 000000000..e997c2f86 --- /dev/null +++ b/grid_map_core/include/grid_map_core/utils/testing.hpp @@ -0,0 +1,37 @@ +/** + * Copied from ANYbotics/eigen_utils, to avoid testing dependency. + */ + +#pragma once + +#include +#include + +#define ASSERT_MATRICES_EQ_WITH_NAN(first, second) assertMatrixesEqualWithNan((first), #first, (second), #second, __LINE__) +static void assertMatrixesEqualWithNan(Eigen::Ref first, std::string firstName, + Eigen::Ref second, std::string secondName, int line) { + ASSERT_EQ(first.rows(), second.rows()); + ASSERT_EQ(first.cols(), second.cols()); + + bool matricesAreEqual = true; + for (Eigen::Index row = 0; row < first.rows() && matricesAreEqual; ++row) { + for (Eigen::Index col = 0; col < first.cols() && matricesAreEqual; ++col) { + bool ifRealThenValid = first.block<1, 1>(row, col).isApprox(second.block<1, 1>(row, col)); + bool bothNaN = std::isnan(first(row, col)) && std::isnan(second(row, col)); + if (ifRealThenValid || bothNaN) { + continue; + } else { + matricesAreEqual = false; + } + } + } + + Eigen::IOFormat compactFormat(2, 0, ",", "\n", "[", "]"); + ASSERT_TRUE(matricesAreEqual) // NO LINT + << "L. " << std::to_string(line) << ": Matrices are not equal" // NO LINT + << "\n" // NO LINT + << firstName << "\n" // NO LINT + << first.format(compactFormat) << "\n" // NO LINT + << secondName << "\n" // NO LINT + << second.format(compactFormat) << "\n"; // NO LINT +} diff --git a/grid_map_core/package.xml b/grid_map_core/package.xml index cfeacb11f..445ef048e 100644 --- a/grid_map_core/package.xml +++ b/grid_map_core/package.xml @@ -1,10 +1,10 @@ grid_map_core - 1.6.2 + 1.7.18 Universal grid map library to manage two-dimensional grid maps with multiple data layers. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_core/src/BufferRegion.cpp b/grid_map_core/src/BufferRegion.cpp index bc4782b80..4c1e8e616 100644 --- a/grid_map_core/src/BufferRegion.cpp +++ b/grid_map_core/src/BufferRegion.cpp @@ -9,32 +9,26 @@ namespace grid_map { -BufferRegion::BufferRegion() : - staretIndex_(Index::Zero()), +BufferRegion::BufferRegion() : startIndex_(Index::Zero()), size_(Size::Zero()), quadrant_(BufferRegion::Quadrant::Undefined) { } -BufferRegion::BufferRegion(const Index& index, const Size& size, const BufferRegion::Quadrant& quadrant) : - staretIndex_(index), - size_(size), - quadrant_(quadrant) -{ -} - -BufferRegion::~BufferRegion() +BufferRegion::BufferRegion(Index index, Size size, BufferRegion::Quadrant quadrant) : startIndex_(std::move(index)), + size_(std::move(size)), + quadrant_(std::move(quadrant)) { } const Index& BufferRegion::getStartIndex() const { - return staretIndex_; + return startIndex_; } -void BufferRegion::setStartIndex(const Index& staretIndex) +void BufferRegion::setStartIndex(const Index& startIndex) { - staretIndex_ = staretIndex; + startIndex_ = startIndex; } const Size& BufferRegion::getSize() const diff --git a/grid_map_core/src/CubicInterpolation.cpp b/grid_map_core/src/CubicInterpolation.cpp index 89ce6b32f..d9eb2fc54 100644 --- a/grid_map_core/src/CubicInterpolation.cpp +++ b/grid_map_core/src/CubicInterpolation.cpp @@ -9,25 +9,21 @@ #include "grid_map_core/CubicInterpolation.hpp" #include "grid_map_core/GridMap.hpp" -#include "grid_map_core/GridMapMath.hpp" namespace grid_map { -unsigned int bindIndexToRange(int idReq, unsigned int nElem) +unsigned int bindIndexToRange(unsigned int idReq, unsigned int nElem) { - if (idReq < 0) { - return 0; - } if (idReq >= nElem) { - return static_cast(nElem - 1); + return (nElem - 1); } - return static_cast(idReq); + return idReq; } -double getLayerValue(const Matrix &layerMat, int rowReq, int colReq) +double getLayerValue(const Matrix &layerMat, unsigned int rowReq, unsigned int colReq) { - const auto numCol = layerMat.cols(); - const auto numRow = layerMat.rows(); + const auto numCol = static_cast(layerMat.cols()); + const auto numRow = static_cast(layerMat.rows()); const unsigned int iBoundToRange = bindIndexToRange(rowReq, numRow); const unsigned int jBoundToRange = bindIndexToRange(colReq, numCol); return layerMat(iBoundToRange, jBoundToRange); @@ -35,7 +31,7 @@ double getLayerValue(const Matrix &layerMat, int rowReq, int colReq) /** - * BICUBIC CONVLUTION INTERPOLATION ALGORITHM + * BICUBIC CONVOLUTION INTERPOLATION ALGORITHM * also known as piecewise bicubic interpolation, * it does not ensure continuity of the first derivatives. * see: @@ -92,7 +88,7 @@ bool assembleFunctionValueMatrix(const GridMap &gridMap, const std::string &laye } const Matrix &layerMatrix = gridMap.get(layer); - auto f = [&layerMatrix](int rowReq, int colReq) { + auto f = [&layerMatrix](unsigned int rowReq, unsigned int colReq) { double retVal = getLayerValue(layerMatrix, rowReq, colReq); return retVal; }; @@ -135,11 +131,7 @@ bool getNormalizedCoordinates(const GridMap &gridMap, const Position &queriedPos bool getIndicesOfMiddleKnot(const GridMap &gridMap, const Position &queriedPosition, Index *index) { - - if (!gridMap.getIndex(queriedPosition, *index)) { - return false; - } - return true; + return gridMap.getIndex(queriedPosition, *index); } } /* namespace bicubic_conv */ @@ -195,7 +187,7 @@ bool evaluateBicubicInterpolation(const GridMap &gridMap, const std::string &lay FunctionValueMatrix functionValues; assembleFunctionValueMatrix(f, dfx, dfy, ddfxy, &functionValues); - // get normalized coordiantes + // get normalized coordinates Position normalizedCoordinates; if (!computeNormalizedCoordinates(gridMap, unitSquareCornerIndices.bottomLeft_, queriedPosition, &normalizedCoordinates)) { @@ -264,11 +256,7 @@ bool getUnitSquareCornerIndices(const GridMap &gridMap, const Position &queriedP bool getClosestPointIndices(const GridMap &gridMap, const Position &queriedPosition, Index *index) { - - if (!gridMap.getIndex(queriedPosition, *index)) { - return false; - } - return true; + return gridMap.getIndex(queriedPosition, *index); } bool computeNormalizedCoordinates(const GridMap &gridMap, const Index &originIndex, @@ -298,8 +286,8 @@ bool getFunctionValues(const Matrix &layerData, const IndicesMatrix &indices, Da void bindIndicesToRange(const GridMap &gridMap, IndicesMatrix *indices) { - const int numCol = gridMap.getSize().y(); - const int numRow = gridMap.getSize().x(); + const unsigned int numCol = gridMap.getSize().y(); + const unsigned int numRow = gridMap.getSize().x(); //top left { @@ -346,10 +334,11 @@ bool getFirstOrderDerivatives(const Matrix &layerData, const IndicesMatrix &indi double firstOrderDerivativeAt(const Matrix &layerData, const Index &index, Dim2D dim, double resolution) { - const int numCol = layerData.cols(); - const int numRow = layerData.rows(); + const auto numCol{static_cast(layerData.cols())}; + const auto numRow{static_cast(layerData.rows())}; - double left, right; + double left; + double right; switch (dim) { case Dim2D::X: { left = layerData(bindIndexToRange(index.x() + 1, numRow), index.y()); @@ -376,13 +365,12 @@ double firstOrderDerivativeAt(const Matrix &layerData, const Index &index, Dim2D double mixedSecondOrderDerivativeAt(const Matrix &layerData, const Index &index, double resolution) { /* - * no need for dimensions since the we have to differentiate w.r.t. x and y + * no need for dimensions since we have to differentiate w.r.t. x and y * the order doesn't matter. Derivative values are the same. * Taken from https://www.mathematik.uni-dortmund.de/~kuzmin/cfdintro/lecture4.pdf */ - - const int numCol = layerData.cols(); - const int numRow = layerData.rows(); + const auto numCol{static_cast(layerData.cols())}; + const auto numRow{static_cast(layerData.rows())}; const double f11 = layerData(bindIndexToRange(index.x() - 1, numRow), bindIndexToRange(index.y() - 1, numCol)); diff --git a/grid_map_core/src/GridMap.cpp b/grid_map_core/src/GridMap.cpp index 71695a4aa..b72ea4c2a 100644 --- a/grid_map_core/src/GridMap.cpp +++ b/grid_map_core/src/GridMap.cpp @@ -13,16 +13,15 @@ #include "grid_map_core/SubmapGeometry.hpp" #include "grid_map_core/iterators/GridMapIterator.hpp" -#include - -#include +#include #include #include #include #include -using namespace std; -using namespace grid_map; +using std::cout; +using std::endl; +using std::isfinite; namespace grid_map { @@ -57,8 +56,6 @@ void GridMap::setGeometry(const Length& length, const double resolution, const P length_ = (size_.cast() * resolution_).matrix(); position_ = position; startIndex_.setZero(); - - return; } void GridMap::setGeometry(const SubmapGeometry& geometry) { @@ -74,19 +71,15 @@ const std::vector& GridMap::getBasicLayers() const { } bool GridMap::hasBasicLayers() const { - return basicLayers_.size() > 0; + return !basicLayers_.empty(); } bool GridMap::hasSameLayers(const GridMap& other) const { - for (const auto& layer : layers_) { - if (!other.exists(layer)) { - return false; - } - } - return true; + return std::all_of(layers_.begin(), layers_.end(), + [&](const std::string& layer){return other.exists(layer);}); } -void GridMap::add(const std::string& layer, const double value) { +void GridMap::add(const std::string& layer, const float value) { add(layer, Matrix::Constant(size_(0), size_(1), value)); } @@ -177,6 +170,7 @@ float GridMap::atPosition(const std::string& layer, const Position& position, In interpolationMethod = InterpolationMethods::INTER_LINEAR; skipNextSwitchCase = true; } + [[fallthrough]]; } case InterpolationMethods::INTER_CUBIC: { if (!skipNextSwitchCase) { @@ -187,20 +181,25 @@ float GridMap::atPosition(const std::string& layer, const Position& position, In interpolationMethod = InterpolationMethods::INTER_LINEAR; } } + [[fallthrough]]; } case InterpolationMethods::INTER_LINEAR: { float value; - if (atPositionLinearInterpolated(layer, position, value)) + if (atPositionLinearInterpolated(layer, position, value)){ return value; - else + } + else { interpolationMethod = InterpolationMethods::INTER_NEAREST; + } + [[fallthrough]]; } case InterpolationMethods::INTER_NEAREST: { Index index; if (getIndex(position, index)) { return at(layer, index); - } else + } else { throw std::out_of_range("GridMap::atPosition(...) : Position is out of range."); + } break; } default: @@ -254,12 +253,8 @@ bool GridMap::isValid(const Index& index, const std::vector& layers if (layers.empty()) { return false; } - for (const auto& layer : layers) { - if (!isValid(index, layer)) { - return false; - } - } - return true; + return std::all_of(layers.begin(), layers.end(), + [&](const std::string& layer){return isValid(index, layer);}); } bool GridMap::getPosition3(const std::string& layer, const Index& index, Position3& position) const { @@ -276,7 +271,7 @@ bool GridMap::getPosition3(const std::string& layer, const Index& index, Positio bool GridMap::getVector(const std::string& layerPrefix, const Index& index, Eigen::Vector3d& vector) const { Eigen::Vector3d temp{at(layerPrefix + "x", index), at(layerPrefix + "y", index), at(layerPrefix + "z", index)}; - if (!isValid(temp[0]) || !isValid(temp[1]) || !isValid(temp[2])) { + if (!isValid(temp[0]) || !isValid(temp[1]) || !isValid(temp[2])) { // NOLINT (implicit-float-conversion) return false; } else { vector = temp; @@ -289,8 +284,8 @@ GridMap GridMap::getSubmap(const Position& position, const Length& length, bool& return getSubmap(position, length, index, isSuccess); } -GridMap GridMap::getSubmap(const Position& position, const Length& length, Index& indexInSubmap, bool& isSuccess) const { - // Submap the generate. +GridMap GridMap::getSubmap(const Position& position, const Length& length, Index& /*indexInSubmap*/, bool& isSuccess) const { + // Submap to generate. GridMap submap(layers_); submap.setBasicLayers(basicLayers_); submap.setTimestamp(timestamp_); @@ -299,7 +294,7 @@ GridMap GridMap::getSubmap(const Position& position, const Length& length, Index // Get submap geometric information. SubmapGeometry submapInformation(*this, position, length, isSuccess); if (!isSuccess) { - return GridMap(layers_); + return GridMap{layers_}; } submap.setGeometry(submapInformation); submap.startIndex_.setZero(); // Because of the way we copy the data below. @@ -310,7 +305,7 @@ GridMap GridMap::getSubmap(const Position& position, const Length& length, Index if (!getBufferRegionsForSubmap(bufferRegions, submapInformation.getStartIndex(), submap.getSize(), size_, startIndex_)) { cout << "Cannot access submap of this size." << endl; isSuccess = false; - return GridMap(layers_); + return GridMap{layers_}; } for (const auto& data : data_) { @@ -399,10 +394,10 @@ GridMap GridMap::getTransformedMap(const Eigen::Isometry3d& transform, const std if (sampleRatio > 0.0) { positionSamples.reserve(5); positionSamples.push_back(center); - positionSamples.push_back(Position3(center.x() - sampleLength, center.y(), center.z())); - positionSamples.push_back(Position3(center.x() + sampleLength, center.y(), center.z())); - positionSamples.push_back(Position3(center.x(), center.y() - sampleLength, center.z())); - positionSamples.push_back(Position3(center.x(), center.y() + sampleLength, center.z())); + positionSamples.emplace_back(center.x() - sampleLength, center.y(), center.z()); + positionSamples.emplace_back(center.x() + sampleLength, center.y(), center.z()); + positionSamples.emplace_back(center.x(), center.y() - sampleLength, center.z()); + positionSamples.emplace_back(center.x(), center.y() + sampleLength, center.z()); } else { positionSamples.push_back(center); } @@ -428,7 +423,7 @@ GridMap GridMap::getTransformedMap(const Eigen::Isometry3d& transform, const std const auto currentValueInOldGrid = at(layer, *iterator); auto& newValue = newMap.at(layer, newIndex); if (layer == heightLayerName) { - newValue = transformedPosition.z(); + newValue = static_cast(transformedPosition.z()); } // adjust height else { newValue = currentValueInOldGrid; @@ -457,7 +452,7 @@ bool GridMap::move(const Position& position, std::vector& newRegio if (abs(indexShift(i)) >= getSize()(i)) { // Entire map is dropped. clearAll(); - newRegions.push_back(BufferRegion(Index(0, 0), getSize(), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(0, 0), getSize(), BufferRegion::Quadrant::Undefined); } else { // Drop cells out of map. int sign = (indexShift(i) > 0 ? 1 : -1); @@ -471,10 +466,10 @@ bool GridMap::move(const Position& position, std::vector& newRegio // One region to drop. if (i == 0) { clearRows(index, nCells); - newRegions.push_back(BufferRegion(Index(index, 0), Size(nCells, getSize()(1)), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(index, 0), Size(nCells, getSize()(1)), BufferRegion::Quadrant::Undefined); } else if (i == 1) { clearCols(index, nCells); - newRegions.push_back(BufferRegion(Index(0, index), Size(getSize()(0), nCells), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(0, index), Size(getSize()(0), nCells), BufferRegion::Quadrant::Undefined); } } else { // Two regions to drop. @@ -482,20 +477,20 @@ bool GridMap::move(const Position& position, std::vector& newRegio int firstNCells = getSize()(i) - firstIndex; if (i == 0) { clearRows(firstIndex, firstNCells); - newRegions.push_back(BufferRegion(Index(firstIndex, 0), Size(firstNCells, getSize()(1)), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(firstIndex, 0), Size(firstNCells, getSize()(1)), BufferRegion::Quadrant::Undefined); } else if (i == 1) { clearCols(firstIndex, firstNCells); - newRegions.push_back(BufferRegion(Index(0, firstIndex), Size(getSize()(0), firstNCells), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(0, firstIndex), Size(getSize()(0), firstNCells), BufferRegion::Quadrant::Undefined); } int secondIndex = 0; int secondNCells = nCells - firstNCells; if (i == 0) { clearRows(secondIndex, secondNCells); - newRegions.push_back(BufferRegion(Index(secondIndex, 0), Size(secondNCells, getSize()(1)), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(secondIndex, 0), Size(secondNCells, getSize()(1)), BufferRegion::Quadrant::Undefined); } else if (i == 1) { clearCols(secondIndex, secondNCells); - newRegions.push_back(BufferRegion(Index(0, secondIndex), Size(getSize()(0), secondNCells), BufferRegion::Quadrant::Undefined)); + newRegions.emplace_back(Index(0, secondIndex), Size(getSize()(0), secondNCells), BufferRegion::Quadrant::Undefined); } } } @@ -508,7 +503,7 @@ bool GridMap::move(const Position& position, std::vector& newRegio position_ += alignedPositionShift; // Check if map has been moved at all. - return (indexShift.any() != 0); + return indexShift.any(); } bool GridMap::move(const Position& position) { @@ -518,10 +513,14 @@ bool GridMap::move(const Position& position) { bool GridMap::addDataFrom(const GridMap& other, bool extendMap, bool overwriteData, bool copyAllLayers, std::vector layers) { // Set the layers to copy. - if (copyAllLayers) layers = other.getLayers(); + if (copyAllLayers) { + layers = other.getLayers(); + } // Resize map. - if (extendMap) extendToInclude(other); + if (extendMap) { + extendToInclude(other); + } // Check if all layers to copy exist and add missing layers. for (const auto& layer : layers) { @@ -531,14 +530,20 @@ bool GridMap::addDataFrom(const GridMap& other, bool extendMap, bool overwriteDa } // Copy data. for (GridMapIterator iterator(*this); !iterator.isPastEnd(); ++iterator) { - if (isValid(*iterator) && !overwriteData) continue; + if (isValid(*iterator) && !overwriteData) { + continue; + } Position position; getPosition(*iterator, position); Index index; - if (!other.isInside(position)) continue; + if (!other.isInside(position)) { + continue; + } other.getIndex(position, index); for (const auto& layer : layers) { - if (!other.isValid(index, layer)) continue; + if (!other.isValid(index, layer)) { + continue; + } at(layer, *iterator) = other.at(layer, index); } } @@ -603,11 +608,15 @@ bool GridMap::extendToInclude(const GridMap& other) { } // Copy data. for (GridMapIterator iterator(*this); !iterator.isPastEnd(); ++iterator) { - if (isValid(*iterator)) continue; + if (isValid(*iterator)) { + continue; + } Position position; getPosition(*iterator, position); Index index; - if (!mapCopy.isInside(position)) continue; + if (!mapCopy.isInside(position)) { + continue; + } mapCopy.getIndex(position, index); for (const auto& layer : layers_) { at(layer, *iterator) = mapCopy.at(layer, index); @@ -666,7 +675,9 @@ bool GridMap::isDefaultStartIndex() const { } void GridMap::convertToDefaultStartIndex() { - if (isDefaultStartIndex()) return; + if (isDefaultStartIndex()) { + return; + } std::vector bufferRegions; if (!getBufferRegionsForSubmap(bufferRegions, startIndex_, size_, size_, startIndex_)) { @@ -696,7 +707,7 @@ void GridMap::convertToDefaultStartIndex() { } Position GridMap::getClosestPositionInMap(const Position& position) const { - if (getSize().x() < 1u || getSize().y() < 1u) { + if (getSize().x() < 1 || getSize().y() < 1) { return position_; } @@ -720,12 +731,13 @@ Position GridMap::getClosestPositionInMap(const Position& position) const { const double maxY = bottomLeftCorner.y(); const double minY = bottomRightCorner.y(); - // Clip to box constraints. - positionInMap.x() = std::fmin(positionInMap.x(), maxX); - positionInMap.y() = std::fmin(positionInMap.y(), maxY); + // Clip to box constraints and correct for indexing precision. + // Points on the border can lead to invalid indices because the cells represent half open intervals, i.e. [...). + positionInMap.x() = std::fmin(positionInMap.x(), maxX - std::numeric_limits::epsilon()); + positionInMap.y() = std::fmin(positionInMap.y(), maxY - std::numeric_limits::epsilon()); - positionInMap.x() = std::fmax(positionInMap.x(), minX); - positionInMap.y() = std::fmax(positionInMap.y(), minY); + positionInMap.x() = std::fmax(positionInMap.x(), minX + std::numeric_limits::epsilon()); + positionInMap.y() = std::fmax(positionInMap.y(), minY + std::numeric_limits::epsilon()); return positionInMap; } @@ -751,23 +763,13 @@ void GridMap::clearAll() { } void GridMap::clearRows(unsigned int index, unsigned int nRows) { - std::vector layersToClear; - if (basicLayers_.size() > 0) - layersToClear = basicLayers_; - else - layersToClear = layers_; - for (auto& layer : layersToClear) { + for (auto& layer : layers_) { data_.at(layer).block(index, 0, nRows, getSize()(1)).setConstant(NAN); } } void GridMap::clearCols(unsigned int index, unsigned int nCols) { - std::vector layersToClear; - if (basicLayers_.size() > 0) - layersToClear = basicLayers_; - else - layersToClear = layers_; - for (auto& layer : layersToClear) { + for (auto& layer : layers_) { data_.at(layer).block(0, index, getSize()(0), nCols).setConstant(NAN); } } @@ -828,7 +830,9 @@ bool GridMap::atPositionLinearInterpolated(const std::string& layer, const Posit for (size_t i = 0; i < 4; ++i) { const size_t indexLin = getLinearIndexFromIndex(indices[idxShift[i]], mapSize); - if ((indexLin < startIndexLin) || (indexLin > endIndexLin)) return false; + if ((indexLin < startIndexLin) || (indexLin > endIndexLin)) { + return false; + } f[i] = layerMat(indexLin); } @@ -836,8 +840,9 @@ bool GridMap::atPositionLinearInterpolated(const std::string& layer, const Posit const Position positionRed = (position - point) / resolution_; const Position positionRedFlip = Position(1., 1.) - positionRed; - value = f[0] * positionRedFlip.x() * positionRedFlip.y() + f[1] * positionRed.x() * positionRedFlip.y() + - f[2] * positionRedFlip.x() * positionRed.y() + f[3] * positionRed.x() * positionRed.y(); + + value = f[0] * positionRedFlip.x() * positionRedFlip.y() + f[1] * positionRed.x() * positionRedFlip.y() + // NOLINT (implicit-float-conversion) + f[2] * positionRedFlip.x() * positionRed.y() + f[3] * positionRed.x() * positionRed.y(); // NOLINT (implicit-float-conversion) return true; } @@ -860,7 +865,7 @@ bool GridMap::atPositionBicubicConvolutionInterpolated(const std::string& layer, if (!std::isfinite(interpolatedValue)) { return false; } - value = interpolatedValue; + value = static_cast(interpolatedValue); return true; } @@ -876,7 +881,7 @@ bool GridMap::atPositionBicubicInterpolated(const std::string& layer, const Posi if (!std::isfinite(interpolatedValue)) { return false; } - value = interpolatedValue; + value = static_cast(interpolatedValue); return true; diff --git a/grid_map_core/src/GridMapMath.cpp b/grid_map_core/src/GridMapMath.cpp index 1e9ec2c39..b56d618fd 100644 --- a/grid_map_core/src/GridMapMath.cpp +++ b/grid_map_core/src/GridMapMath.cpp @@ -14,7 +14,7 @@ // Limits #include -using namespace std; +using std::numeric_limits; namespace grid_map { @@ -98,18 +98,34 @@ inline Index getIndexFromIndexVector( return getBufferIndexFromIndex(index, bufferSize, bufferStartIndex); } -inline BufferRegion::Quadrant getQuadrant(const Index& index, const Index& bufferStartIndex) -{ - if (index[0] >= bufferStartIndex[0] && index[1] >= bufferStartIndex[1]) return BufferRegion::Quadrant::TopLeft; - if (index[0] >= bufferStartIndex[0] && index[1] < bufferStartIndex[1]) return BufferRegion::Quadrant::TopRight; - if (index[0] < bufferStartIndex[0] && index[1] >= bufferStartIndex[1]) return BufferRegion::Quadrant::BottomLeft; - if (index[0] < bufferStartIndex[0] && index[1] < bufferStartIndex[1]) return BufferRegion::Quadrant::BottomRight; +inline BufferRegion::Quadrant getQuadrant(const Index& index, const Index& bufferStartIndex) { + if (index[0] >= bufferStartIndex[0] && index[1] >= bufferStartIndex[1]) { + return BufferRegion::Quadrant::TopLeft; + } + if (index[0] >= bufferStartIndex[0] && index[1] < bufferStartIndex[1]) { + return BufferRegion::Quadrant::TopRight; + } + if (index[0] < bufferStartIndex[0] && index[1] >= bufferStartIndex[1]) { + return BufferRegion::Quadrant::BottomLeft; + } + if (index[0] < bufferStartIndex[0] && index[1] < bufferStartIndex[1]) { + return BufferRegion::Quadrant::BottomRight; + } return BufferRegion::Quadrant::Undefined; } -} // namespace +} // namespace internal -using namespace internal; +using internal::checkIfStartIndexAtDefaultPosition; +using internal::getBufferOrderToMapFrameTransformation; +using internal::getIndexFromIndexVector; +using internal::getIndexVectorFromIndex; +using internal::getMapFrameToBufferOrderTransformation; +using internal::getQuadrant; +using internal::getVectorToFirstCell; +using internal::getVectorToOrigin; +using internal::transformBufferOrderToMapFrame; +using internal::transformMapFrameToBufferOrder; bool getPositionFromIndex(Position& position, const Index& index, @@ -119,7 +135,9 @@ bool getPositionFromIndex(Position& position, const Size& bufferSize, const Index& bufferStartIndex) { - if (!checkIfIndexInRange(index, bufferSize)) return false; + if (!checkIfIndexInRange(index, bufferSize)) { + return false; + } Vector offset; getVectorToFirstCell(offset, mapLength, resolution); position = mapPosition + offset + resolution * getIndexVectorFromIndex(index, bufferSize, bufferStartIndex); @@ -149,11 +167,8 @@ bool checkIfPositionWithinMap(const Position& position, getVectorToOrigin(offset, mapLength); Position positionTransformed = getMapFrameToBufferOrderTransformation().cast() * (position - mapPosition - offset); - if (positionTransformed.x() >= 0.0 && positionTransformed.y() >= 0.0 - && positionTransformed.x() < mapLength(0) && positionTransformed.y() < mapLength(1)) { - return true; - } - return false; + return positionTransformed.x() >= 0.0 && positionTransformed.y() >= 0.0 + && positionTransformed.x() < mapLength(0) && positionTransformed.y() < mapLength(1); } void getPositionOfDataStructureOrigin(const Position& position, @@ -190,11 +205,7 @@ bool getPositionShiftFromIndexShift(Vector& positionShift, bool checkIfIndexInRange(const Index& index, const Size& bufferSize) { - if (index[0] >= 0 && index[1] >= 0 && index[0] < bufferSize[0] && index[1] < bufferSize[1]) - { - return true; - } - return false; + return index[0] >= 0 && index[1] >= 0 && index[0] < bufferSize[0] && index[1] < bufferSize[1]; } void boundIndexToRange(Index& index, const Size& bufferSize) @@ -206,8 +217,11 @@ void boundIndexToRange(Index& index, const Size& bufferSize) void boundIndexToRange(int& index, const int& bufferSize) { - if (index < 0) index = 0; - else if (index >= bufferSize) index = bufferSize - 1; + if (index < 0) { + index = 0; + } else if (index >= bufferSize) { + index = bufferSize - 1; + } } void wrapIndexToRange(Index& index, const Size& bufferSize) @@ -248,7 +262,9 @@ void boundPositionToRange(Position& position, const Length& mapLength, const Pos for (int i = 0; i < positionShifted.size(); i++) { double epsilon = 10.0 * numeric_limits::epsilon(); // TODO Why is the factor 10 necessary. - if (std::fabs(position(i)) > 1.0) epsilon *= std::fabs(position(i)); + if (std::fabs(position(i)) > 1.0) { + epsilon *= std::fabs(position(i)); + } if (positionShifted(i) <= 0) { positionShifted(i) = epsilon; @@ -263,7 +279,7 @@ void boundPositionToRange(Position& position, const Length& mapLength, const Pos position = positionShifted + mapPosition - vectorToOrigin; } -const Eigen::Matrix2i getBufferOrderToMapFrameAlignment() +Eigen::Matrix2i getBufferOrderToMapFrameAlignment() { return getBufferOrderToMapFrameTransformation().array().abs().matrix(); } @@ -287,19 +303,25 @@ bool getSubmapInformation(Index& submapTopLeftIndex, // Corners of submap. Position topLeftPosition = requestedSubmapPosition - halfTransform * requestedSubmapLength.matrix(); boundPositionToRange(topLeftPosition, mapLength, mapPosition); - if(!getIndexFromPosition(submapTopLeftIndex, topLeftPosition, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) return false; + if (!getIndexFromPosition(submapTopLeftIndex, topLeftPosition, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) { + return false; + } Index topLeftIndex; topLeftIndex = getIndexFromBufferIndex(submapTopLeftIndex, bufferSize, bufferStartIndex); Position bottomRightPosition = requestedSubmapPosition + halfTransform * requestedSubmapLength.matrix(); boundPositionToRange(bottomRightPosition, mapLength, mapPosition); Index bottomRightIndex; - if(!getIndexFromPosition(bottomRightIndex, bottomRightPosition, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) return false; + if (!getIndexFromPosition(bottomRightIndex, bottomRightPosition, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) { + return false; + } bottomRightIndex = getIndexFromBufferIndex(bottomRightIndex, bufferSize, bufferStartIndex); // Get the position of the top left corner of the generated submap. Position topLeftCorner; - if(!getPositionFromIndex(topLeftCorner, submapTopLeftIndex, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) return false; + if (!getPositionFromIndex(topLeftCorner, submapTopLeftIndex, mapLength, mapPosition, resolution, bufferSize, bufferStartIndex)) { + return false; + } topLeftCorner -= halfTransform * Position::Constant(resolution); // Size of submap. @@ -318,7 +340,7 @@ bool getSubmapInformation(Index& submapTopLeftIndex, return getIndexFromPosition(requestedIndexInSubmap, requestedSubmapPosition, submapLength, submapPosition, resolution, submapBufferSize); } -Size getSubmapSizeFromCornerIndeces(const Index& topLeftIndex, const Index& bottomRightIndex, +Size getSubmapSizeFromCornerIndices(const Index& topLeftIndex, const Index& bottomRightIndex, const Size& bufferSize, const Index& bufferStartIndex) { const Index unwrappedTopLeftIndex = getIndexFromBufferIndex(topLeftIndex, bufferSize, bufferStartIndex); @@ -332,7 +354,9 @@ bool getBufferRegionsForSubmap(std::vector& submapBufferRegions, const Size& bufferSize, const Index& bufferStartIndex) { - if ((getIndexFromBufferIndex(submapIndex, bufferSize, bufferStartIndex) + submapBufferSize > bufferSize).any()) return false; + if ((getIndexFromBufferIndex(submapIndex, bufferSize, bufferStartIndex) + submapBufferSize > bufferSize).any()) { + return false; + } submapBufferRegions.clear(); @@ -345,87 +369,87 @@ bool getBufferRegionsForSubmap(std::vector& submapBufferRegions, if (quadrantOfTopLeft == BufferRegion::Quadrant::TopLeft) { if (quadrantOfBottomRight == BufferRegion::Quadrant::TopLeft) { - submapBufferRegions.push_back(BufferRegion(submapIndex, submapBufferSize, BufferRegion::Quadrant::TopLeft)); + submapBufferRegions.emplace_back(submapIndex, submapBufferSize, BufferRegion::Quadrant::TopLeft); return true; } if (quadrantOfBottomRight == BufferRegion::Quadrant::TopRight) { Size topLeftSize(submapBufferSize(0), bufferSize(1) - submapIndex(1)); - submapBufferRegions.push_back(BufferRegion(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft)); + submapBufferRegions.emplace_back(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft); Index topRightIndex(submapIndex(0), 0); Size topRightSize(submapBufferSize(0), submapBufferSize(1) - topLeftSize(1)); - submapBufferRegions.push_back(BufferRegion(topRightIndex, topRightSize, BufferRegion::Quadrant::TopRight)); + submapBufferRegions.emplace_back(topRightIndex, topRightSize, BufferRegion::Quadrant::TopRight); return true; } if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomLeft) { Size topLeftSize(bufferSize(0) - submapIndex(0), submapBufferSize(1)); - submapBufferRegions.push_back(BufferRegion(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft)); + submapBufferRegions.emplace_back(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft); Index bottomLeftIndex(0, submapIndex(1)); Size bottomLeftSize(submapBufferSize(0) - topLeftSize(0), submapBufferSize(1)); - submapBufferRegions.push_back(BufferRegion(bottomLeftIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft)); + submapBufferRegions.emplace_back(bottomLeftIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft); return true; } if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomRight) { Size topLeftSize(bufferSize(0) - submapIndex(0), bufferSize(1) - submapIndex(1)); - submapBufferRegions.push_back(BufferRegion(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft)); + submapBufferRegions.emplace_back(submapIndex, topLeftSize, BufferRegion::Quadrant::TopLeft); Index topRightIndex(submapIndex(0), 0); Size topRightSize(bufferSize(0) - submapIndex(0), submapBufferSize(1) - topLeftSize(1)); - submapBufferRegions.push_back(BufferRegion(topRightIndex, topRightSize, BufferRegion::Quadrant::TopRight)); + submapBufferRegions.emplace_back(topRightIndex, topRightSize, BufferRegion::Quadrant::TopRight); Index bottomLeftIndex(0, submapIndex(1)); Size bottomLeftSize(submapBufferSize(0) - topLeftSize(0), bufferSize(1) - submapIndex(1)); - submapBufferRegions.push_back(BufferRegion(bottomLeftIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft)); + submapBufferRegions.emplace_back(bottomLeftIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft); Index bottomRightIndex = Index::Zero(); Size bottomRightSize(bottomLeftSize(0), topRightSize(1)); - submapBufferRegions.push_back(BufferRegion(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight)); + submapBufferRegions.emplace_back(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight); return true; } } else if (quadrantOfTopLeft == BufferRegion::Quadrant::TopRight) { if (quadrantOfBottomRight == BufferRegion::Quadrant::TopRight) { - submapBufferRegions.push_back(BufferRegion(submapIndex, submapBufferSize, BufferRegion::Quadrant::TopRight)); + submapBufferRegions.emplace_back(submapIndex, submapBufferSize, BufferRegion::Quadrant::TopRight); return true; } if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomRight) { Size topRightSize(bufferSize(0) - submapIndex(0), submapBufferSize(1)); - submapBufferRegions.push_back(BufferRegion(submapIndex, topRightSize, BufferRegion::Quadrant::TopRight)); + submapBufferRegions.emplace_back(submapIndex, topRightSize, BufferRegion::Quadrant::TopRight); Index bottomRightIndex(0, submapIndex(1)); Size bottomRightSize(submapBufferSize(0) - topRightSize(0), submapBufferSize(1)); - submapBufferRegions.push_back(BufferRegion(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight)); + submapBufferRegions.emplace_back(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight); return true; } } else if (quadrantOfTopLeft == BufferRegion::Quadrant::BottomLeft) { if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomLeft) { - submapBufferRegions.push_back(BufferRegion(submapIndex, submapBufferSize, BufferRegion::Quadrant::BottomLeft)); + submapBufferRegions.emplace_back(submapIndex, submapBufferSize, BufferRegion::Quadrant::BottomLeft); return true; } if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomRight) { Size bottomLeftSize(submapBufferSize(0), bufferSize(1) - submapIndex(1)); - submapBufferRegions.push_back(BufferRegion(submapIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft)); + submapBufferRegions.emplace_back(submapIndex, bottomLeftSize, BufferRegion::Quadrant::BottomLeft); Index bottomRightIndex(submapIndex(0), 0); Size bottomRightSize(submapBufferSize(0), submapBufferSize(1) - bottomLeftSize(1)); - submapBufferRegions.push_back(BufferRegion(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight)); + submapBufferRegions.emplace_back(bottomRightIndex, bottomRightSize, BufferRegion::Quadrant::BottomRight); return true; } } else if (quadrantOfTopLeft == BufferRegion::Quadrant::BottomRight) { if (quadrantOfBottomRight == BufferRegion::Quadrant::BottomRight) { - submapBufferRegions.push_back(BufferRegion(submapIndex, submapBufferSize, BufferRegion::Quadrant::BottomRight)); + submapBufferRegions.emplace_back(submapIndex, submapBufferSize, BufferRegion::Quadrant::BottomRight); return true; } @@ -449,7 +473,9 @@ bool incrementIndex(Index& index, const Size& bufferSize, const Index& bufferSta } // End of iterations reached. - if (!checkIfIndexInRange(unwrappedIndex, bufferSize)) return false; + if (!checkIfIndexInRange(unwrappedIndex, bufferSize)) { + return false; + } // Return true iterated index. index = getBufferIndexFromIndex(unwrappedIndex, bufferSize, bufferStartIndex); @@ -475,7 +501,9 @@ bool incrementIndexForSubmap(Index& submapIndex, Index& index, const Index& subm } // End of iterations reached. - if (!checkIfIndexInRange(tempSubmapIndex, submapBufferSize)) return false; + if (!checkIfIndexInRange(tempSubmapIndex, submapBufferSize)) { + return false; + } // Get corresponding index in map. Index unwrappedSubmapTopLeftIndex = getIndexFromBufferIndex(submapTopLeftIndex, bufferSize, bufferStartIndex); @@ -489,7 +517,9 @@ bool incrementIndexForSubmap(Index& submapIndex, Index& index, const Index& subm Index getIndexFromBufferIndex(const Index& bufferIndex, const Size& bufferSize, const Index& bufferStartIndex) { - if (checkIfStartIndexAtDefaultPosition(bufferStartIndex)) return bufferIndex; + if (checkIfStartIndexAtDefaultPosition(bufferStartIndex)) { + return bufferIndex; + } Index index = bufferIndex - bufferStartIndex; wrapIndexToRange(index, bufferSize); @@ -498,7 +528,9 @@ Index getIndexFromBufferIndex(const Index& bufferIndex, const Size& bufferSize, Index getBufferIndexFromIndex(const Index& index, const Size& bufferSize, const Index& bufferStartIndex) { - if (checkIfStartIndexAtDefaultPosition(bufferStartIndex)) return index; + if (checkIfStartIndexAtDefaultPosition(bufferStartIndex)) { + return index; + } Index bufferIndex = index + bufferStartIndex; wrapIndexToRange(bufferIndex, bufferSize); @@ -507,31 +539,20 @@ Index getBufferIndexFromIndex(const Index& index, const Size& bufferSize, const size_t getLinearIndexFromIndex(const Index& index, const Size& bufferSize, const bool rowMajor) { - if (!rowMajor) return index(1) * bufferSize(0) + index(0); + if (!rowMajor) { + return index(1) * bufferSize(0) + index(0); + } return index(0) * bufferSize(1) + index(1); } Index getIndexFromLinearIndex(const size_t linearIndex, const Size& bufferSize, const bool rowMajor) { - if (!rowMajor) return Index((int)linearIndex % bufferSize(0), (int)linearIndex / bufferSize(0)); + if (!rowMajor) { + return Index((int)linearIndex % bufferSize(0), (int)linearIndex / bufferSize(0)); + } return Index((int)linearIndex / bufferSize(1), (int)linearIndex % bufferSize(1)); } -void getIndicesForRegion(const Index& regionIndex, const Size& regionSize, - std::vector indices) -{ -// for (int i = line.index_; col < line.endIndex(); col++) { -// for (int i = 0; i < getSize()(0); i++) { -// -// } -// } -} - -void getIndicesForRegions(const std::vector& regionIndeces, const Size& regionSizes, - std::vector indices) -{ -} - bool colorValueToVector(const unsigned long& colorValue, Eigen::Vector3i& colorVector) { colorVector(0) = (colorValue >> 16) & 0x0000ff; @@ -564,9 +585,9 @@ bool colorVectorToValue(const Eigen::Vector3i& colorVector, unsigned long& color void colorVectorToValue(const Eigen::Vector3i& colorVector, float& colorValue) { - unsigned long color = (colorVector(0) << 16) + (colorVector(1) << 8) + colorVector(2); - // cppcheck-suppress invalidPointerCast - colorValue = *reinterpret_cast(&color); + Color colors; + colors.longColor_ = (colorVector(0) << 16) + (colorVector(1) << 8) + colorVector(2); + colorValue = colors.floatColor_; } void colorVectorToValue(const Eigen::Vector3f& colorVector, float& colorValue) @@ -575,5 +596,5 @@ void colorVectorToValue(const Eigen::Vector3f& colorVector, float& colorValue) colorVectorToValue(tempColorVector, colorValue); } -} // namespace +} // namespace grid_map diff --git a/grid_map_core/src/Polygon.cpp b/grid_map_core/src/Polygon.cpp index 715aec4c6..3ab234c1a 100644 --- a/grid_map_core/src/Polygon.cpp +++ b/grid_map_core/src/Polygon.cpp @@ -27,12 +27,10 @@ Polygon::Polygon(std::vector vertices) vertices_ = vertices; } -Polygon::~Polygon() {} - bool Polygon::isInside(const Position& point) const { int cross = 0; - for (int i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { + for (size_t i = 0, j = vertices_.size() - 1; i < vertices_.size(); j = i++) { if ( ((vertices_[i].y() > point.y()) != (vertices_[j].y() > point.y())) && (point.x() < (vertices_[j].x() - vertices_[i].x()) * (point.y() - vertices_[i].y()) / (vertices_[j].y() - vertices_[i].y()) + vertices_[i].x()) ) @@ -102,7 +100,7 @@ double Polygon::getArea() const { double area = 0.0; int j = vertices_.size() - 1; - for (int i = 0; i < vertices_.size(); i++) { + for (size_t i = 0; i < vertices_.size(); i++) { area += (vertices_.at(j).x() + vertices_.at(i).x()) * (vertices_.at(j).y() - vertices_.at(i).y()); j = i; @@ -116,7 +114,7 @@ Position Polygon::getCentroid() const std::vector vertices = getVertices(); vertices.push_back(vertices.at(0)); double area = 0.0; - for (int i = 0; i < vertices.size() - 1; i++) { + for (size_t i = 0; i < vertices.size() - 1; i++) { const double a = vertices[i].x() * vertices[i+1].y() - vertices[i+1].x() * vertices[i].y(); area += a; centroid.x() += a * (vertices[i].x() + vertices[i+1].x()); @@ -220,7 +218,7 @@ bool Polygon::offsetInward(const double margin) return true; } -std::vector Polygon::triangulate(const TriangulationMethods& method) const +std::vector Polygon::triangulate(const TriangulationMethods& /*method*/) const { // TODO Add more triangulation methods. // https://en.wikipedia.org/wiki/Polygon_triangulation @@ -311,7 +309,7 @@ Polygon Polygon::monotoneChainConvexHullOfPoints(const std::vector& po int k = 0; // Build lower hull - for (int i = 0; i < sortedPoints.size(); ++i) { + for (size_t i = 0; i < sortedPoints.size(); ++i) { while (k >= 2 && vectorsMakeClockwiseTurn(pointsConvexHull.at(k - 2), pointsConvexHull.at(k - 1), sortedPoints.at(i))) { k--; } diff --git a/grid_map_core/src/SubmapGeometry.cpp b/grid_map_core/src/SubmapGeometry.cpp index df2a41eee..065adac29 100644 --- a/grid_map_core/src/SubmapGeometry.cpp +++ b/grid_map_core/src/SubmapGeometry.cpp @@ -21,10 +21,6 @@ SubmapGeometry::SubmapGeometry(const GridMap& gridMap, const Position& position, gridMap_.getSize(), gridMap_.getStartIndex()); } -SubmapGeometry::~SubmapGeometry() -{ -} - const GridMap& SubmapGeometry::getGridMap() const { return gridMap_; diff --git a/grid_map_core/src/iterators/CircleIterator.cpp b/grid_map_core/src/iterators/CircleIterator.cpp index ecdc5fe15..b681b33ac 100644 --- a/grid_map_core/src/iterators/CircleIterator.cpp +++ b/grid_map_core/src/iterators/CircleIterator.cpp @@ -1,5 +1,5 @@ /* - * Circleterator.hpp + * CircleIterator.hpp * * Created on: Nov 13, 2014 * Author: Péter Fankhauser @@ -7,9 +7,9 @@ */ #include "grid_map_core/iterators/CircleIterator.hpp" -#include "grid_map_core/GridMapMath.hpp" -using namespace std; +#include +#include "grid_map_core/GridMapMath.hpp" namespace grid_map { @@ -26,22 +26,10 @@ CircleIterator::CircleIterator(const GridMap& gridMap, const Position& center, c Index submapStartIndex; Index submapBufferSize; findSubmapParameters(center, radius, submapStartIndex, submapBufferSize); - internalIterator_ = std::shared_ptr(new SubmapIterator(gridMap, submapStartIndex, submapBufferSize)); - if(!isInside()) ++(*this); -} - -CircleIterator& CircleIterator::operator =(const CircleIterator& other) -{ - center_ = other.center_; - radius_ = other.radius_; - radiusSquare_ = other.radiusSquare_; - internalIterator_ = other.internalIterator_; - mapLength_ = other.mapLength_; - mapPosition_ = other.mapPosition_; - resolution_ = other.resolution_; - bufferSize_ = other.bufferSize_; - bufferStartIndex_ = other.bufferStartIndex_; - return *this; + internalIterator_ = std::make_shared(gridMap, submapStartIndex, submapBufferSize); + if(!isInside()) { + ++(*this); + } } bool CircleIterator::operator !=(const CircleIterator& other) const @@ -57,10 +45,14 @@ const Index& CircleIterator::operator *() const CircleIterator& CircleIterator::operator ++() { ++(*internalIterator_); - if (internalIterator_->isPastEnd()) return *this; + if (internalIterator_->isPastEnd()) { + return *this; + } for ( ; !internalIterator_->isPastEnd(); ++(*internalIterator_)) { - if (isInside()) break; + if (isInside()) { + break; + } } return *this; @@ -89,7 +81,7 @@ void CircleIterator::findSubmapParameters(const Position& center, const double r getIndexFromPosition(startIndex, topLeft, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); Index endIndex; getIndexFromPosition(endIndex, bottomRight, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); - bufferSize = getSubmapSizeFromCornerIndeces(startIndex, endIndex, bufferSize_, bufferStartIndex_); + bufferSize = getSubmapSizeFromCornerIndices(startIndex, endIndex, bufferSize_, bufferStartIndex_); } } /* namespace grid_map */ diff --git a/grid_map_core/src/iterators/EllipseIterator.cpp b/grid_map_core/src/iterators/EllipseIterator.cpp index 6463c509c..7fe7632bd 100644 --- a/grid_map_core/src/iterators/EllipseIterator.cpp +++ b/grid_map_core/src/iterators/EllipseIterator.cpp @@ -9,19 +9,17 @@ #include "grid_map_core/iterators/EllipseIterator.hpp" #include "grid_map_core/GridMapMath.hpp" -#include +#include #include -using namespace std; - namespace grid_map { EllipseIterator::EllipseIterator(const GridMap& gridMap, const Position& center, const Length& length, const double rotation) : center_(center) { semiAxisSquare_ = (0.5 * length).square(); - double sinRotation = sin(rotation); - double cosRotation = cos(rotation); + double sinRotation = std::sin(rotation); + double cosRotation = std::cos(rotation); transformMatrix_ << cosRotation, sinRotation, sinRotation, -cosRotation; mapLength_ = gridMap.getLength(); mapPosition_ = gridMap.getPosition(); @@ -31,22 +29,10 @@ EllipseIterator::EllipseIterator(const GridMap& gridMap, const Position& center, Index submapStartIndex; Index submapBufferSize; findSubmapParameters(center, length, rotation, submapStartIndex, submapBufferSize); - internalIterator_ = std::shared_ptr(new SubmapIterator(gridMap, submapStartIndex, submapBufferSize)); - if(!isInside()) ++(*this); -} - -EllipseIterator& EllipseIterator::operator =(const EllipseIterator& other) -{ - center_ = other.center_; - semiAxisSquare_ = other.semiAxisSquare_; - transformMatrix_ = other.transformMatrix_; - internalIterator_ = other.internalIterator_; - mapLength_ = other.mapLength_; - mapPosition_ = other.mapPosition_; - resolution_ = other.resolution_; - bufferSize_ = other.bufferSize_; - bufferStartIndex_ = other.bufferStartIndex_; - return *this; + internalIterator_ = std::make_shared(gridMap, submapStartIndex, submapBufferSize); + if (!isInside()) { + ++(*this); + } } bool EllipseIterator::operator !=(const EllipseIterator& other) const @@ -62,10 +48,14 @@ const Eigen::Array2i& EllipseIterator::operator *() const EllipseIterator& EllipseIterator::operator ++() { ++(*internalIterator_); - if (internalIterator_->isPastEnd()) return *this; + if (internalIterator_->isPastEnd()) { + return *this; + } for ( ; !internalIterator_->isPastEnd(); ++(*internalIterator_)) { - if (isInside()) break; + if (isInside()) { + break; + } } return *this; @@ -103,7 +93,7 @@ void EllipseIterator::findSubmapParameters(const Position& center, const Length& getIndexFromPosition(startIndex, topLeft, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); Index endIndex; getIndexFromPosition(endIndex, bottomRight, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); - bufferSize = getSubmapSizeFromCornerIndeces(startIndex, endIndex, bufferSize_, bufferStartIndex_); + bufferSize = getSubmapSizeFromCornerIndices(startIndex, endIndex, bufferSize_, bufferStartIndex_); } } /* namespace grid_map */ diff --git a/grid_map_core/src/iterators/GridMapIterator.cpp b/grid_map_core/src/iterators/GridMapIterator.cpp index 68ae57b73..756c103e0 100644 --- a/grid_map_core/src/iterators/GridMapIterator.cpp +++ b/grid_map_core/src/iterators/GridMapIterator.cpp @@ -29,22 +29,12 @@ GridMapIterator::GridMapIterator(const GridMapIterator* other) isPastEnd_ = other->isPastEnd_; } -GridMapIterator& GridMapIterator::operator =(const GridMapIterator& other) -{ - size_ = other.size_; - startIndex_ = other.startIndex_; - linearSize_ = other.linearSize_; - linearIndex_ = other.linearIndex_; - isPastEnd_ = other.isPastEnd_; - return *this; -} - bool GridMapIterator::operator !=(const GridMapIterator& other) const { return linearIndex_ != other.linearIndex_; } -const Index GridMapIterator::operator *() const +Index GridMapIterator::operator *() const { return getIndexFromLinearIndex(linearIndex_, size_); } @@ -54,7 +44,7 @@ const size_t& GridMapIterator::getLinearIndex() const return linearIndex_; } -const Index GridMapIterator::getUnwrappedIndex() const +Index GridMapIterator::getUnwrappedIndex() const { return getIndexFromBufferIndex(*(*this), size_, startIndex_); } diff --git a/grid_map_core/src/iterators/LineIterator.cpp b/grid_map_core/src/iterators/LineIterator.cpp index 0afc4eb97..2c6337d28 100644 --- a/grid_map_core/src/iterators/LineIterator.cpp +++ b/grid_map_core/src/iterators/LineIterator.cpp @@ -9,14 +9,13 @@ #include "grid_map_core/iterators/LineIterator.hpp" #include "grid_map_core/GridMapMath.hpp" -using namespace std; - namespace grid_map { LineIterator::LineIterator(const grid_map::GridMap& gridMap, const Position& start, const Position& end) { - Index startIndex, endIndex; + Index startIndex; + Index endIndex; if (getIndexLimitedToMapRange(gridMap, start, end, startIndex) && getIndexLimitedToMapRange(gridMap, end, start, endIndex)) { initialize(gridMap, startIndex, endIndex); @@ -31,26 +30,6 @@ LineIterator::LineIterator(const grid_map::GridMap& gridMap, const Index& start, initialize(gridMap, start, end); } -LineIterator& LineIterator::operator =(const LineIterator& other) -{ - index_ = other.index_; - start_ = other.start_; - end_ = other.end_; - iCell_ = other.iCell_; - nCells_ = other.nCells_; - increment1_ = other.increment1_; - increment2_ = other.increment2_; - denominator_ = other.denominator_; - numerator_ = other.numerator_; - numeratorAdd_ = other.numeratorAdd_; - mapLength_ = other.mapLength_; - mapPosition_ = other.mapPosition_; - resolution_ = other.resolution_; - bufferSize_ = other.bufferSize_; - bufferStartIndex_ = other.bufferStartIndex_; - return *this; -} - bool LineIterator::operator !=(const LineIterator& other) const { return (index_ != other.index_).any(); @@ -101,8 +80,9 @@ bool LineIterator::getIndexLimitedToMapRange(const grid_map::GridMap& gridMap, Vector direction = (end - start).normalized(); while (!gridMap.getIndex(newStart, index)) { newStart += (gridMap.getResolution() - std::numeric_limits::epsilon()) * direction; - if ((end - newStart).norm() < gridMap.getResolution() - std::numeric_limits::epsilon()) + if ((end - newStart).norm() < gridMap.getResolution() - std::numeric_limits::epsilon()) { return false; + } } return true; } diff --git a/grid_map_core/src/iterators/PolygonIterator.cpp b/grid_map_core/src/iterators/PolygonIterator.cpp index e20849ff9..733a8421e 100644 --- a/grid_map_core/src/iterators/PolygonIterator.cpp +++ b/grid_map_core/src/iterators/PolygonIterator.cpp @@ -6,11 +6,11 @@ * Institute: ETH Zurich, ANYbotics */ +#include + #include "grid_map_core/iterators/PolygonIterator.hpp" #include "grid_map_core/GridMapMath.hpp" -using namespace std; - namespace grid_map { PolygonIterator::PolygonIterator(const grid_map::GridMap& gridMap, const grid_map::Polygon& polygon) @@ -24,20 +24,10 @@ PolygonIterator::PolygonIterator(const grid_map::GridMap& gridMap, const grid_ma Index submapStartIndex; Size submapBufferSize; findSubmapParameters(polygon, submapStartIndex, submapBufferSize); - internalIterator_ = std::shared_ptr(new SubmapIterator(gridMap, submapStartIndex, submapBufferSize)); - if(!isInside()) ++(*this); -} - -PolygonIterator& PolygonIterator::operator =(const PolygonIterator& other) -{ - polygon_ = other.polygon_; - internalIterator_ = other.internalIterator_; - mapLength_ = other.mapLength_; - mapPosition_ = other.mapPosition_; - resolution_ = other.resolution_; - bufferSize_ = other.bufferSize_; - bufferStartIndex_ = other.bufferStartIndex_; - return *this; + internalIterator_ = std::make_shared(gridMap, submapStartIndex, submapBufferSize); + if (!isInside()) { + ++(*this); + } } bool PolygonIterator::operator !=(const PolygonIterator& other) const @@ -53,10 +43,14 @@ const Index& PolygonIterator::operator *() const PolygonIterator& PolygonIterator::operator ++() { ++(*internalIterator_); - if (internalIterator_->isPastEnd()) return *this; + if (internalIterator_->isPastEnd()) { + return *this; + } - for ( ; !internalIterator_->isPastEnd(); ++(*internalIterator_)) { - if (isInside()) break; + for (; !internalIterator_->isPastEnd(); ++(*internalIterator_)) { + if (isInside()) { + break; + } } return *this; @@ -74,7 +68,7 @@ bool PolygonIterator::isInside() const return polygon_.isInside(position); } -void PolygonIterator::findSubmapParameters(const grid_map::Polygon& polygon, Index& startIndex, Size& bufferSize) const +void PolygonIterator::findSubmapParameters(const grid_map::Polygon& /*polygon*/, Index& startIndex, Size& bufferSize) const { Position topLeft = polygon_.getVertices()[0]; Position bottomRight = topLeft; @@ -87,7 +81,7 @@ void PolygonIterator::findSubmapParameters(const grid_map::Polygon& polygon, Ind getIndexFromPosition(startIndex, topLeft, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); Index endIndex; getIndexFromPosition(endIndex, bottomRight, mapLength_, mapPosition_, resolution_, bufferSize_, bufferStartIndex_); - bufferSize = getSubmapSizeFromCornerIndeces(startIndex, endIndex, bufferSize_, bufferStartIndex_); + bufferSize = getSubmapSizeFromCornerIndices(startIndex, endIndex, bufferSize_, bufferStartIndex_); } } /* namespace grid_map */ diff --git a/grid_map_core/src/iterators/SlidingWindowIterator.cpp b/grid_map_core/src/iterators/SlidingWindowIterator.cpp index 6ade5a4b3..c1ba2a43a 100644 --- a/grid_map_core/src/iterators/SlidingWindowIterator.cpp +++ b/grid_map_core/src/iterators/SlidingWindowIterator.cpp @@ -34,8 +34,10 @@ SlidingWindowIterator::SlidingWindowIterator(const SlidingWindowIterator* other) void SlidingWindowIterator::setWindowLength(const GridMap& gridMap, const double windowLength) { - windowSize_ = std::round(windowLength / gridMap.getResolution()); - if (windowSize_ % 2 != 1) ++windowSize_; + windowSize_ = static_cast(std::round(windowLength / gridMap.getResolution())); + if (windowSize_ % 2 != 1) { + ++windowSize_; + } setup(gridMap); } @@ -44,7 +46,9 @@ SlidingWindowIterator& SlidingWindowIterator::operator ++() if (edgeHandling_ == EdgeHandling::INSIDE) { while (!isPastEnd()) { GridMapIterator::operator++(); - if (dataInsideMap()) break; + if (dataInsideMap()) { + break; + } } } else { GridMapIterator::operator++(); @@ -52,10 +56,10 @@ SlidingWindowIterator& SlidingWindowIterator::operator ++() return *this; } -const Matrix SlidingWindowIterator::getData() const +Matrix SlidingWindowIterator::getData() const { const Index centerIndex(*(*this)); - const Index windowMargin(Index::Constant(windowMargin_)); + const Index windowMargin(Index::Constant(static_cast(windowMargin_))); const Index originalTopLeftIndex(centerIndex - windowMargin); Index topLeftIndex(originalTopLeftIndex); boundIndexToRange(topLeftIndex, size_); @@ -71,8 +75,11 @@ const Matrix SlidingWindowIterator::getData() const case EdgeHandling::MEAN: const Matrix data = data_.block(topLeftIndex(0), topLeftIndex(1), adjustedWindowSize(0), adjustedWindowSize(1)); Matrix returnData(windowSize_, windowSize_); - if (edgeHandling_ == EdgeHandling::EMPTY) returnData.setConstant(NAN); - else if (edgeHandling_ == EdgeHandling::MEAN) returnData.setConstant(data.meanOfFinites()); + if (edgeHandling_ == EdgeHandling::EMPTY) { + returnData.setConstant(NAN); + } else if (edgeHandling_ == EdgeHandling::MEAN) { + returnData.setConstant(data.meanOfFinites()); + } const Index topLeftIndexShift(topLeftIndex - originalTopLeftIndex); returnData.block(topLeftIndexShift(0), topLeftIndexShift(1), adjustedWindowSize(0), adjustedWindowSize(1)) = data_.block(topLeftIndex(0), topLeftIndex(1), adjustedWindowSize(0), adjustedWindowSize(1)); @@ -83,10 +90,12 @@ const Matrix SlidingWindowIterator::getData() const void SlidingWindowIterator::setup(const GridMap& gridMap) { - if (!gridMap.isDefaultStartIndex()) throw std::runtime_error( - "SlidingWindowIterator cannot be used with grid maps that don't have a default buffer start index."); - if (windowSize_ % 2 == 0) throw std::runtime_error( - "SlidingWindowIterator has a wrong window size!"); + if (!gridMap.isDefaultStartIndex()) { + throw std::runtime_error("SlidingWindowIterator cannot be used with grid maps that don't have a default buffer start index."); + } + if (windowSize_ % 2 == 0) { + throw std::runtime_error("SlidingWindowIterator has a wrong window size!"); + } windowMargin_ = (windowSize_ - 1) / 2; if (edgeHandling_ == EdgeHandling::INSIDE) { @@ -99,7 +108,7 @@ void SlidingWindowIterator::setup(const GridMap& gridMap) bool SlidingWindowIterator::dataInsideMap() const { const Index centerIndex(*(*this)); - const Index windowMargin(Index::Constant(windowMargin_)); + const Index windowMargin(Index::Constant(static_cast(windowMargin_))); const Index topLeftIndex(centerIndex - windowMargin); const Index bottomRightIndex(centerIndex + windowMargin); return checkIfIndexInRange(topLeftIndex, size_) && checkIfIndexInRange(bottomRightIndex, size_); diff --git a/grid_map_core/src/iterators/SpiralIterator.cpp b/grid_map_core/src/iterators/SpiralIterator.cpp index 2f8899e6e..9ee5b3cd9 100644 --- a/grid_map_core/src/iterators/SpiralIterator.cpp +++ b/grid_map_core/src/iterators/SpiralIterator.cpp @@ -10,14 +10,14 @@ #include "grid_map_core/GridMapMath.hpp" #include +#include -using namespace std; namespace grid_map { -SpiralIterator::SpiralIterator(const grid_map::GridMap& gridMap, const Eigen::Vector2d& center, +SpiralIterator::SpiralIterator(const grid_map::GridMap& gridMap, Eigen::Vector2d center, const double radius) - : center_(center), + : center_(std::move(center)), radius_(radius), distance_(0) { @@ -27,31 +27,17 @@ SpiralIterator::SpiralIterator(const grid_map::GridMap& gridMap, const Eigen::Ve resolution_ = gridMap.getResolution(); bufferSize_ = gridMap.getSize(); gridMap.getIndex(center_, indexCenter_); - nRings_ = std::ceil(radius_ / resolution_); - if (checkIfIndexInRange(indexCenter_, bufferSize_)) + nRings_ = static_cast(std::ceil(radius_ / resolution_)); + if (checkIfIndexInRange(indexCenter_, bufferSize_)) { pointsRing_.push_back(indexCenter_); - else - while(pointsRing_.empty() && !isPastEnd()) + } else { + while (pointsRing_.empty() && !isPastEnd()) { generateRing(); + } + } } -SpiralIterator& SpiralIterator::operator =(const SpiralIterator& other) -{ - center_ = other.center_; - indexCenter_ = other.indexCenter_; - radius_ = other.radius_; - radiusSquare_ = other.radiusSquare_; - nRings_ = other.nRings_; - distance_ = other.distance_; - pointsRing_ = other.pointsRing_; - mapLength_ = other.mapLength_; - mapPosition_ = other.mapPosition_; - resolution_ = other.resolution_; - bufferSize_ = other.bufferSize_; - return *this; -} - -bool SpiralIterator::operator !=(const SpiralIterator& other) const +bool SpiralIterator::operator !=(const SpiralIterator& /*other*/) const { return (pointsRing_.back() != pointsRing_.back()).any(); } @@ -64,7 +50,9 @@ const Eigen::Array2i& SpiralIterator::operator *() const SpiralIterator& SpiralIterator::operator ++() { pointsRing_.pop_back(); - if (pointsRing_.empty() && !isPastEnd()) generateRing(); + if (pointsRing_.empty() && !isPastEnd()) { + generateRing(); + } return *this; } @@ -73,7 +61,7 @@ bool SpiralIterator::isPastEnd() const return (distance_ == nRings_ && pointsRing_.empty()); } -bool SpiralIterator::isInside(const Index index) const +bool SpiralIterator::isInside(const Index& index) const { Eigen::Vector2d position; getPositionFromIndex(position, index, mapLength_, mapPosition_, resolution_, bufferSize_); @@ -92,25 +80,24 @@ void SpiralIterator::generateRing() pointInMap.y() = point.y() + indexCenter_.y(); if (checkIfIndexInRange(pointInMap, bufferSize_)) { if (distance_ == nRings_ || distance_ == nRings_ - 1) { - if (isInside(pointInMap)) + if (isInside(pointInMap)) { pointsRing_.push_back(pointInMap); + } } else { pointsRing_.push_back(pointInMap); } } normal.x() = -signum(point.y()); normal.y() = signum(point.x()); - if (normal.x() != 0 - && (int) Vector(point.x() + normal.x(), point.y()).norm() == distance_) + if (normal.x() != 0 && static_cast(Vector(point.x() + normal.x(), point.y()).norm()) == distance_) { point.x() += normal.x(); - else if (normal.y() != 0 - && (int) Vector(point.x(), point.y() + normal.y()).norm() == distance_) + } else if (normal.y() != 0 && static_cast(Vector(point.x(), point.y() + normal.y()).norm()) == distance_) { point.y() += normal.y(); - else { + } else { point.x() += normal.x(); point.y() += normal.y(); } - } while (point.x() != distance_ || point.y() != 0); + } while (static_cast(point.x()) != distance_ || point.y() != 0); } double SpiralIterator::getCurrentRadius() const diff --git a/grid_map_core/src/iterators/SubmapIterator.cpp b/grid_map_core/src/iterators/SubmapIterator.cpp index bf1232547..12fb120b3 100644 --- a/grid_map_core/src/iterators/SubmapIterator.cpp +++ b/grid_map_core/src/iterators/SubmapIterator.cpp @@ -9,7 +9,7 @@ #include "grid_map_core/iterators/SubmapIterator.hpp" #include "grid_map_core/GridMapMath.hpp" -using namespace std; + namespace grid_map { @@ -48,18 +48,6 @@ SubmapIterator::SubmapIterator(const SubmapIterator* other) isPastEnd_ = other->isPastEnd_; } -SubmapIterator& SubmapIterator::operator =(const SubmapIterator& other) -{ - size_ = other.size_; - startIndex_ = other.startIndex_; - submapSize_ = other.submapSize_; - submapStartIndex_ = other.submapStartIndex_; - index_ = other.index_; - submapIndex_ = other.submapIndex_; - isPastEnd_ = other.isPastEnd_; - return *this; -} - bool SubmapIterator::operator !=(const SubmapIterator& other) const { return (index_ != other.index_).any(); diff --git a/grid_map_core/test/EigenPluginsTest.cpp b/grid_map_core/test/EigenPluginsTest.cpp index 0ec5cf652..cdbd1e810 100644 --- a/grid_map_core/test/EigenPluginsTest.cpp +++ b/grid_map_core/test/EigenPluginsTest.cpp @@ -14,8 +14,7 @@ // Eigen #include -using namespace std; -using namespace Eigen; +using Eigen::Matrix; TEST(EigenMatrixBaseAddons, numberOfFinites) { @@ -70,7 +69,8 @@ TEST(EigenMatrixBaseAddons, minCoeffOfFinites) double min = matrix.minCoeff(); EXPECT_NEAR(min, matrix.minCoeffOfFinites(), 1e-10); - int i, j; + int i; + int j; matrix.maxCoeff(&i, &j); matrix(i, j) = NAN; EXPECT_NEAR(min, matrix.minCoeffOfFinites(), 1e-10); @@ -88,7 +88,8 @@ TEST(EigenMatrixBaseAddons, maxCoeffOfFinites) double max = matrix.maxCoeff(); EXPECT_NEAR(max, matrix.maxCoeffOfFinites(), 1e-10); - int i, j; + int i; + int j; matrix.minCoeff(&i, &j); matrix(i, j) = NAN; EXPECT_NEAR(max, matrix.maxCoeffOfFinites(), 1e-10); diff --git a/grid_map_core/test/EllipseIteratorTest.cpp b/grid_map_core/test/EllipseIteratorTest.cpp index 801cd9552..f77fee01e 100644 --- a/grid_map_core/test/EllipseIteratorTest.cpp +++ b/grid_map_core/test/EllipseIteratorTest.cpp @@ -9,21 +9,16 @@ #include "grid_map_core/iterators/EllipseIterator.hpp" #include "grid_map_core/GridMap.hpp" -// Eigen -#include - // gtest #include -// Limits -#include - // Vector #include -using namespace std; -using namespace Eigen; -using namespace grid_map; +using grid_map::GridMap; +using grid_map::Length; +using grid_map::Position; +using grid_map::EllipseIterator; TEST(EllipseIterator, OneCellWideEllipse) { diff --git a/grid_map_core/test/GridMapIteratorTest.cpp b/grid_map_core/test/GridMapIteratorTest.cpp index c04e472d5..33c1ac4f7 100644 --- a/grid_map_core/test/GridMapIteratorTest.cpp +++ b/grid_map_core/test/GridMapIteratorTest.cpp @@ -9,20 +9,16 @@ #include "grid_map_core/iterators/GridMapIterator.hpp" #include "grid_map_core/GridMap.hpp" -// Eigen -#include - // gtest #include -// Limits -#include - // Vector #include -using namespace std; -using namespace grid_map; +using grid_map::GridMap; +using grid_map::Length; +using grid_map::Position; +using grid_map::GridMapIterator; TEST(GridMapIterator, Simple) { @@ -52,7 +48,7 @@ TEST(GridMapIterator, LinearIndex) auto& data = map["layer"]; unsigned int i = 0; for (; !iterator.isPastEnd(); ++iterator, ++i) { - data(iterator.getLinearIndex()) = 1.0; + data(static_cast(iterator.getLinearIndex())) = 1.0; EXPECT_EQ(i, iterator.getLinearIndex()); EXPECT_FALSE(iterator.isPastEnd()); } diff --git a/grid_map_core/test/GridMapMathTest.cpp b/grid_map_core/test/GridMapMathTest.cpp index a8c188606..1382f7651 100644 --- a/grid_map_core/test/GridMapMathTest.cpp +++ b/grid_map_core/test/GridMapMathTest.cpp @@ -8,9 +8,6 @@ #include "grid_map_core/GridMapMath.hpp" -// Eigen -#include - // gtest #include @@ -20,8 +17,9 @@ // Vector #include -using namespace std; -using namespace grid_map; +using std::numeric_limits; + +namespace grid_map{ TEST(PositionFromIndex, Simple) { @@ -303,10 +301,9 @@ TEST(checkIfIndexInRange, All) TEST(boundIndexToRange, All) { - int index; int bufferSize = 10; - index = 0; + int index = 0; boundIndexToRange(index, bufferSize); EXPECT_EQ(0, index); @@ -337,10 +334,9 @@ TEST(boundIndexToRange, All) TEST(wrapIndexToRange, All) { - int index; int bufferSize = 10; - index = 0; + int index = 0; wrapIndexToRange(index, bufferSize); EXPECT_EQ(0, index); @@ -501,7 +497,7 @@ TEST(getSubmapInformation, Simple) Position requestedSubmapPosition; Position requestedSubmapLength; - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Index submapSize; Position submapPosition; @@ -536,7 +532,7 @@ TEST(getSubmapInformation, Zero) Position requestedSubmapPosition; Length requestedSubmapLength; - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Index submapSize; Position submapPosition; @@ -572,7 +568,7 @@ TEST(getSubmapInformation, ExceedingBoundaries) Position requestedSubmapPosition; Length requestedSubmapLength; - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Size submapSize; Position submapPosition; @@ -626,7 +622,7 @@ TEST(getSubmapInformation, CircularBuffer) Position requestedSubmapPosition; Length requestedSubmapLength; - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Size submapSize; Position submapPosition; @@ -696,7 +692,7 @@ TEST(getSubmapInformation, Debug1) Position requestedSubmapPosition(-7.44, -3.42); Length requestedSubmapLength(0.12, 0.12); - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Size submapSize; Position submapPosition; @@ -725,7 +721,7 @@ TEST(getSubmapInformation, Debug2) Position requestedSubmapPosition(0.24, -26.82); Length requestedSubmapLength(0.624614, 0.462276); - // The returned submap indeces + // The returned submap indices Index submapTopLeftIndex; Size submapSize; Position submapPosition; @@ -1039,3 +1035,5 @@ TEST(getIndexFromLinearIndex, Simple) EXPECT_TRUE((Index(0, 1) == getIndexFromLinearIndex(8, Size(8, 5), false)).all()); EXPECT_TRUE((Index(7, 4) == getIndexFromLinearIndex(39, Size(8, 5), false)).all()); } + +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_core/test/GridMapTest.cpp b/grid_map_core/test/GridMapTest.cpp index 0acf5e08b..64b15f88b 100644 --- a/grid_map_core/test/GridMapTest.cpp +++ b/grid_map_core/test/GridMapTest.cpp @@ -11,14 +11,9 @@ // gtest #include -// Math -#include +namespace grid_map { -using namespace std; -using namespace grid_map; - -TEST(GridMap, CopyConstructor) -{ +TEST(GridMap, CopyConstructor) { GridMap map({"layer_a", "layer_b"}); map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.1, 0.2)); map["layer_a"].setConstant(1.0); @@ -66,12 +61,21 @@ TEST(GridMap, Move) EXPECT_EQ(3, startIndex(0)); EXPECT_EQ(2, startIndex(1)); - - EXPECT_FALSE(map.isValid(Index(0, 0))); // TODO Check entire map. - EXPECT_TRUE(map.isValid(Index(3, 2))); - EXPECT_FALSE(map.isValid(Index(2, 2))); - EXPECT_FALSE(map.isValid(Index(3, 1))); - EXPECT_TRUE(map.isValid(Index(7, 4))); + + Eigen::Matrix isValidExpected; + isValidExpected << false, false, false, false, false, // clang-format off + false, false, false, false, false, + false, false, false, false, false, + false, false, true, true, true, + false, false, true, true, true, + false, false, true, true, true, + false, false, true, true, true, + false, false, true, true, true; // clang-format on + for(int row{0}; row < 8; row++){ + for(int col{0}; col < 5; col++){ + EXPECT_EQ(map.isValid(Index(row, col)), isValidExpected(row, col)) << "Value of map.isValid at ["< stringVector; - stringVector.push_back("nan"); + stringVector.emplace_back("nan"); map1.addDataFrom(map2, true, false, false, stringVector); Index index; map1.getIndex(Position(-2, -2), index); @@ -208,7 +418,8 @@ TEST(AddDataFrom, ExtendMapNotAligned) TEST(AddDataFrom, CopyData) { - GridMap map1, map2; + GridMap map1; + GridMap map2; map1.setGeometry(Length(5.1, 5.1), 1.0, Position(0.0, 0.0)); // bufferSize(5, 5) map1.add("zero", 0.0); map1.add("one"); @@ -249,9 +460,7 @@ TEST(ValueAtPosition, NearestNeighbor) map.at("types", Index(2,1)) = 2.0; map.at("types", Index(2,2)) = 2.0; - double value; - - value = map.atPosition("types", Position(1.35,-0.4)); + double value = map.atPosition("types", Position(1.35,-0.4)); EXPECT_DOUBLE_EQ((float)3.8, value); value = map.atPosition("types", Position(-0.3,0.0)); @@ -273,10 +482,8 @@ TEST(ValueAtPosition, LinearInterpolated) map.at("types", Index(2,1)) = 2.0; map.at("types", Index(2,2)) = 2.0; - double value; - // Close to the border -> reverting to INTER_NEAREST. - value = map.atPosition("types", Position(-0.5,-1.2), InterpolationMethods::INTER_LINEAR); + double value = map.atPosition("types", Position(-0.5,-1.2), InterpolationMethods::INTER_LINEAR); EXPECT_DOUBLE_EQ(2.0, value); // In between 1.0 and 2.0 field. value = map.atPosition("types", Position(-0.5,0.0), InterpolationMethods::INTER_LINEAR); @@ -285,3 +492,5 @@ TEST(ValueAtPosition, LinearInterpolated) value = map.atPosition("types", Position(0.69,0.38), InterpolationMethods::INTER_LINEAR); EXPECT_NEAR(2.1963200, value, 0.0000001); } + +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_core/test/LineIteratorTest.cpp b/grid_map_core/test/LineIteratorTest.cpp index 664267bef..2086fc141 100644 --- a/grid_map_core/test/LineIteratorTest.cpp +++ b/grid_map_core/test/LineIteratorTest.cpp @@ -12,13 +12,9 @@ // gtest #include -// Limits -#include +namespace grid_map { -using namespace grid_map; - -TEST(LineIterator, StartOutsideMap) -{ +TEST(LineIterator, StartOutsideMap) { GridMap map( { "types" }); map.setGeometry(Length(8.0, 5.0), 1.0, Position(0.0, 0.0)); @@ -178,3 +174,5 @@ TEST(LineIterator, StartAndEndOutsideMovedMap) ++iterator; EXPECT_TRUE(iterator.isPastEnd()); } + +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_core/test/PolygonIteratorTest.cpp b/grid_map_core/test/PolygonIteratorTest.cpp index 4bbc5d6c2..ef73ae6e2 100644 --- a/grid_map_core/test/PolygonIteratorTest.cpp +++ b/grid_map_core/test/PolygonIteratorTest.cpp @@ -10,26 +10,21 @@ #include "grid_map_core/GridMap.hpp" #include "grid_map_core/Polygon.hpp" -// Eigen -#include - // gtest #include -// Limits -#include - // Vector #include -using namespace std; -using namespace Eigen; -using namespace grid_map; +using grid_map::GridMap; +using grid_map::Length; +using grid_map::Polygon; +using grid_map::PolygonIterator; +using grid_map::Position; -TEST(PolygonIterator, FullCover) -{ - vector types; - types.push_back("type"); +TEST(PolygonIterator, FullCover) { + std::vector types; + types.emplace_back("type"); GridMap map(types); map.setGeometry(Length(8.0, 5.0), 1.0, Position(0.0, 0.0)); // bufferSize(8, 5) @@ -55,7 +50,9 @@ TEST(PolygonIterator, FullCover) EXPECT_EQ(0, (*iterator)(0)); EXPECT_EQ(2, (*iterator)(1)); - for (int i = 0; i < 37; ++i) ++iterator; + for (int i = 0; i < 37; ++i) { + ++iterator; + } EXPECT_FALSE(iterator.isPastEnd()); EXPECT_EQ(7, (*iterator)(0)); @@ -173,7 +170,9 @@ TEST(PolygonIterator, MoveMap) EXPECT_EQ(6, (*iterator)(0)); EXPECT_EQ(2, (*iterator)(1)); - for (int i = 0; i < 4; ++i) ++iterator; + for (int i = 0; i < 4; ++i) { + ++iterator; + } EXPECT_FALSE(iterator.isPastEnd()); EXPECT_EQ(7, (*iterator)(0)); @@ -184,7 +183,9 @@ TEST(PolygonIterator, MoveMap) EXPECT_EQ(0, (*iterator)(0)); EXPECT_EQ(1, (*iterator)(1)); - for (int i = 0; i < 8; ++i) ++iterator; + for (int i = 0; i < 8; ++i) { + ++iterator; + } EXPECT_FALSE(iterator.isPastEnd()); EXPECT_EQ(2, (*iterator)(0)); diff --git a/grid_map_core/test/PolygonTest.cpp b/grid_map_core/test/PolygonTest.cpp index 5540377ca..0974a0514 100644 --- a/grid_map_core/test/PolygonTest.cpp +++ b/grid_map_core/test/PolygonTest.cpp @@ -13,11 +13,14 @@ // Eigen #include -#include -using namespace std; -using namespace Eigen; -using namespace grid_map; +using Eigen::MatrixXd; +using Eigen::Vector2d; +using Eigen::VectorXd; + +using grid_map::Length; +using grid_map::Polygon; +using grid_map::Position; TEST(Polygon, getCentroidTriangle) { @@ -71,10 +74,10 @@ TEST(Polygon, convexHullPoints) { // Test that points which already create a convex shape (square) can be used to create a convex polygon. std::vector points1; - points1.push_back(Vector2d(0.0, 0.0)); - points1.push_back(Vector2d(1.0, 0.0)); - points1.push_back(Vector2d(1.0, 1.0)); - points1.push_back(Vector2d(0.0, 1.0)); + points1.emplace_back(0.0, 0.0); + points1.emplace_back(1.0, 0.0); + points1.emplace_back(1.0, 1.0); + points1.emplace_back(0.0, 1.0); Polygon polygon1 = Polygon::monotoneChainConvexHullOfPoints(points1); EXPECT_EQ(4, polygon1.nVertices()); EXPECT_TRUE(polygon1.isInside(Vector2d(0.5, 0.5))); @@ -82,14 +85,14 @@ TEST(Polygon, convexHullPoints) // Test that a random set of points can be used to create a convex polygon. std::vector points2; - points2.push_back(Vector2d(0.0, 0.0)); - points2.push_back(Vector2d(1.0, 0.0)); - points2.push_back(Vector2d(2.0, 1.0)); - points2.push_back(Vector2d(1.0, 2.0)); - points2.push_back(Vector2d(-1.0, 2.0)); - points2.push_back(Vector2d(-1.0, -2.0)); - points2.push_back(Vector2d(0.0, 1.0)); - points2.push_back(Vector2d(1.0, 1.0)); + points2.emplace_back(0.0, 0.0); + points2.emplace_back(1.0, 0.0); + points2.emplace_back(2.0, 1.0); + points2.emplace_back(1.0, 2.0); + points2.emplace_back(-1.0, 2.0); + points2.emplace_back(-1.0, -2.0); + points2.emplace_back(0.0, 1.0); + points2.emplace_back(1.0, 1.0); Polygon polygon2 = Polygon::monotoneChainConvexHullOfPoints(points2); EXPECT_EQ(4, polygon2.nVertices()); EXPECT_TRUE(polygon2.isInside(Vector2d(0.5, 0.5))); diff --git a/grid_map_core/test/SlidingWindowIteratorTest.cpp b/grid_map_core/test/SlidingWindowIteratorTest.cpp index b1ff23b01..902144a69 100644 --- a/grid_map_core/test/SlidingWindowIteratorTest.cpp +++ b/grid_map_core/test/SlidingWindowIteratorTest.cpp @@ -9,13 +9,14 @@ #include "grid_map_core/iterators/SlidingWindowIterator.hpp" #include "grid_map_core/GridMap.hpp" -#include -#include #include #include -using namespace std; -using namespace grid_map; +using grid_map::GridMap; +using grid_map::Index; +using grid_map::Length; +using grid_map::Position; +using grid_map::SlidingWindowIterator; TEST(SlidingWindowIterator, WindowSize3Cutoff) { @@ -41,7 +42,9 @@ TEST(SlidingWindowIterator, WindowSize3Cutoff) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(3, 2)).all()) break; + if ((*iterator == Index(3, 2)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 3); @@ -50,7 +53,9 @@ TEST(SlidingWindowIterator, WindowSize3Cutoff) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(7, 4)).all()) break; + if ((*iterator == Index(7, 4)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 2); @@ -85,7 +90,9 @@ TEST(SlidingWindowIterator, WindowSize5) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(3, 2)).all()) break; + if ((*iterator == Index(3, 2)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 5); @@ -94,7 +101,9 @@ TEST(SlidingWindowIterator, WindowSize5) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(7, 4)).all()) break; + if ((*iterator == Index(7, 4)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 3); @@ -119,7 +128,9 @@ TEST(SlidingWindowIterator, WindowSize3Inside) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(3, 2)).all()) break; + if ((*iterator == Index(3, 2)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 3); @@ -128,7 +139,9 @@ TEST(SlidingWindowIterator, WindowSize3Inside) for (; !iterator.isPastEnd(); ++iterator) { EXPECT_FALSE(iterator.isPastEnd()); - if ((*iterator == Index(6, 3)).all()) break; + if ((*iterator == Index(6, 3)).all()) { + break; + } } EXPECT_EQ(iterator.getData().rows(), 3); diff --git a/grid_map_core/test/SpiralIteratorTest.cpp b/grid_map_core/test/SpiralIteratorTest.cpp index a43dd2a8b..f206dfc91 100644 --- a/grid_map_core/test/SpiralIteratorTest.cpp +++ b/grid_map_core/test/SpiralIteratorTest.cpp @@ -9,21 +9,16 @@ #include "grid_map_core/iterators/SpiralIterator.hpp" #include "grid_map_core/GridMap.hpp" -// Eigen -#include - // gtest #include -// Limits -#include - // Vector #include -using namespace std; -using namespace Eigen; -using namespace grid_map; +using grid_map::GridMap; +using grid_map::Length; +using grid_map::Position; +using grid_map::SpiralIterator; TEST(SpiralIterator, CenterOutOfMap) { diff --git a/grid_map_core/test/SubmapIteratorTest.cpp b/grid_map_core/test/SubmapIteratorTest.cpp index 9134b5651..3c838ea62 100644 --- a/grid_map_core/test/SubmapIteratorTest.cpp +++ b/grid_map_core/test/SubmapIteratorTest.cpp @@ -15,15 +15,13 @@ // gtest #include -// Limits -#include - // Vector #include -using namespace std; -using namespace Eigen; -using namespace grid_map; +using std::vector; +using std::string; + +namespace grid_map{ TEST(SubmapIterator, Simple) { Eigen::Array2i submapTopLeftIndex(3, 1); @@ -32,9 +30,9 @@ TEST(SubmapIterator, Simple) { Eigen::Array2i submapIndex; vector types; - types.push_back("type"); + types.emplace_back("type"); GridMap map(types); - map.setGeometry(Array2d(8.1, 5.1), 1.0, Vector2d(0.0, 0.0)); // bufferSize(8, 5) + map.setGeometry(Eigen::Array2d(8.1, 5.1), 1.0, Eigen::Vector2d(0.0, 0.0)); // bufferSize(8, 5) SubmapIterator iterator(map, submapTopLeftIndex, submapBufferSize); @@ -94,7 +92,7 @@ TEST(SubmapIterator, CircularBuffer) { Eigen::Array2i submapIndex; vector types; - types.push_back("type"); + types.emplace_back("type"); GridMap map(types); map.setGeometry(Length(8.1, 5.1), 1.0, Position(0.0, 0.0)); // bufferSize(8, 5) map.move(Position(-3.0, -2.0)); // bufferStartIndex(3, 2) @@ -203,8 +201,8 @@ TEST(SubmapIterator, InterleavedExecutionWithMove) { auto& layer = map.get("layer"); // Initialize the layer as sketched. - for (size_t colIndex = 0; colIndex < layer.cols(); colIndex++) { - layer.col(colIndex).setConstant(colIndex); + for (long colIndex = 0; colIndex < layer.cols(); colIndex++) { + layer.col(colIndex).setConstant(static_cast(colIndex)); } std::cout << "(4,7) contains " << map.at("layer", {4, 7}) << std::endl; @@ -213,7 +211,8 @@ TEST(SubmapIterator, InterleavedExecutionWithMove) { // check that the submap iterator returns {1,1,2,2} auto checkCorrectValues = [](std::array given) { - int countOnes = 0, countTwos = 0; + int countOnes = 0; + int countTwos = 0; for (auto& value : given) { if (std::abs(value - 1.0) < 1e-6) { countOnes++; @@ -227,7 +226,7 @@ TEST(SubmapIterator, InterleavedExecutionWithMove) { EXPECT_EQ(countTwos, 2); }; - std::array returnedSequence; + std::array returnedSequence{}; returnedSequence.fill(0); for (size_t submapIndex = 0; submapIndex < 4; submapIndex++) { @@ -263,4 +262,6 @@ TEST(SubmapIterator, InterleavedExecutionWithMove) { // returnedSequence.at(submapIndex) = map.at("layer", *iterator); // ++iterator; // }); -} \ No newline at end of file +} + +} // namespace grid_map diff --git a/grid_map_core/test/test_grid_map_core.cpp b/grid_map_core/test/test_grid_map_core.cpp index 862017d3b..78b7680c9 100644 --- a/grid_map_core/test/test_grid_map_core.cpp +++ b/grid_map_core/test/test_grid_map_core.cpp @@ -13,6 +13,6 @@ int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); - srand((int)time(0)); + srand((int)time(nullptr)); return RUN_ALL_TESTS(); } diff --git a/grid_map_core/test/test_helpers.cpp b/grid_map_core/test/test_helpers.cpp index d00dcb49c..96d78faef 100644 --- a/grid_map_core/test/test_helpers.cpp +++ b/grid_map_core/test/test_helpers.cpp @@ -23,7 +23,7 @@ AnalyticalFunctions createFlatWorld(grid_map::GridMap *map) AnalyticalFunctions func; - func.f_ = [](double x, double y) { + func.f_ = [](double /*x*/, double /*y*/) { return 0.0; }; @@ -111,7 +111,7 @@ AnalyticalFunctions createTanhWorld(grid_map::GridMap *map) std::uniform_real_distribution scaling(0.1, 2.0); const double s = scaling(rndGenerator); - func.f_ = [s](double x,double y) { + func.f_ = [s](double x,double /*y*/) { const double expZ = std::exp(2 *s* x); return (expZ - 1) / (expZ + 1); }; @@ -149,12 +149,12 @@ AnalyticalFunctions createGaussianWorld(grid_map::GridMap *map) func.f_ = [g](double x,double y) { double value = 0.0; - for (int i = 0; i < g.size(); ++i) { - const double x0 = g.at(i).x0; - const double y0 = g.at(i).y0; - const double varX = g.at(i).varX; - const double varY = g.at(i).varY; - const double s = g.at(i).s; + for (const auto & i : g) { + const double x0 = i.x0; + const double y0 = i.y0; + const double varX = i.varX; + const double varY = i.varY; + const double s = i.s; value += s * std::exp(-(x-x0)*(x-x0) / (2.0*varX) - (y-y0)*(y-y0) / (2.0 * varY)); } @@ -168,12 +168,18 @@ AnalyticalFunctions createGaussianWorld(grid_map::GridMap *map) void fillGridMap(grid_map::GridMap *map, const AnalyticalFunctions &functions) { - grid_map::Matrix& data = (*map)[testLayer]; - for (grid_map::GridMapIterator iterator(*map); !iterator.isPastEnd(); ++iterator) { - const grid_map::Index index(*iterator); - grid_map::Position pos; + using grid_map::DataType; + using grid_map::GridMapIterator; + using grid_map::Index; + using grid_map::Matrix; + using grid_map::Position; + + Matrix& data = (*map)[testLayer]; + for (GridMapIterator iterator(*map); !iterator.isPastEnd(); ++iterator) { + const Index index(*iterator); + Position pos; map->getPosition(index, pos); - data(index(0), index(1)) = functions.f_(pos.x(), pos.y()); + data(index(0), index(1)) = static_cast(functions.f_(pos.x(), pos.y())); } } diff --git a/grid_map_core/test/test_helpers.hpp b/grid_map_core/test/test_helpers.hpp index d6cf51ad6..075963c7b 100644 --- a/grid_map_core/test/test_helpers.hpp +++ b/grid_map_core/test/test_helpers.hpp @@ -93,4 +93,4 @@ void verifyValuesAtQueryPointsAreClose(const grid_map::GridMap &map, const Analy const std::vector &queryPoints, grid_map::InterpolationMethods interpolationMethod); -} /*grid_map_test*/ +} // namespace grid_map_test diff --git a/grid_map_costmap_2d/CHANGELOG.rst b/grid_map_costmap_2d/CHANGELOG.rst index e6192cc91..474859e03 100644 --- a/grid_map_costmap_2d/CHANGELOG.rst +++ b/grid_map_costmap_2d/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_costmap_2d ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_costmap_2d/CMakeLists.txt b/grid_map_costmap_2d/CMakeLists.txt index d59d4121f..33737e8f5 100644 --- a/grid_map_costmap_2d/CMakeLists.txt +++ b/grid_map_costmap_2d/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_costmap_2d) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries @@ -71,7 +71,8 @@ if(CATKIN_ENABLE_TESTING) test/test_grid_map_costmap_2d.cpp test/Costmap2DConverterTest.cpp ) - add_subdirectory(rostest) + # TODO(mw): Disabled rostest because pipeline was failing. Locally not reproducible. + # add_subdirectory(rostest) target_include_directories(${PROJECT_NAME}-test PRIVATE include ) @@ -103,4 +104,4 @@ if(cmake_clang_tools_FOUND) add_default_clang_tooling( DISABLE_CLANG_FORMAT ) -endif(cmake_clang_tools_FOUND) \ No newline at end of file +endif(cmake_clang_tools_FOUND) diff --git a/grid_map_costmap_2d/package.xml b/grid_map_costmap_2d/package.xml index 634db3281..18b4d1c6a 100644 --- a/grid_map_costmap_2d/package.xml +++ b/grid_map_costmap_2d/package.xml @@ -1,10 +1,10 @@ grid_map_costmap_2d - 1.6.2 + 1.7.18 Interface for grid maps to the costmap_2d format. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Francisco Eli Viña Barrientos BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_costmap_2d/rostest/README.md b/grid_map_costmap_2d/rostest/README.md index 9a1f1c28e..4ca99ae30 100644 --- a/grid_map_costmap_2d/rostest/README.md +++ b/grid_map_costmap_2d/rostest/README.md @@ -7,4 +7,3 @@ I can never remember how to run them... rostest --text mytest.test ``` - diff --git a/grid_map_cv/CHANGELOG.rst b/grid_map_cv/CHANGELOG.rst index 4ac85c6c8..b770b0af0 100644 --- a/grid_map_cv/CHANGELOG.rst +++ b/grid_map_cv/CHANGELOG.rst @@ -1,6 +1,13 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Changelog for package grid_map_cv ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.3 (2020-09-30) ------------------ * Added getTransformedMap. diff --git a/grid_map_cv/CMakeLists.txt b/grid_map_cv/CMakeLists.txt index d23798f4c..d67dbc209 100644 --- a/grid_map_cv/CMakeLists.txt +++ b/grid_map_cv/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_cv) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries diff --git a/grid_map_cv/include/grid_map_cv/GridMapCvConverter.hpp b/grid_map_cv/include/grid_map_cv/GridMapCvConverter.hpp index 540f77eca..bc1d412ce 100644 --- a/grid_map_cv/include/grid_map_cv/GridMapCvConverter.hpp +++ b/grid_map_cv/include/grid_map_cv/GridMapCvConverter.hpp @@ -116,7 +116,7 @@ class GridMapCvConverter } return true; - }; + } /*! * Adds a color layer with data from an image. @@ -177,7 +177,7 @@ class GridMapCvConverter const float minValue = gridMap.get(layer).minCoeffOfFinites(); const float maxValue = gridMap.get(layer).maxCoeffOfFinites(); return toImage(gridMap, layer, encoding, minValue, maxValue, image); - }; + } /*! * Creates a cv mat from a grid map layer. @@ -244,7 +244,7 @@ class GridMapCvConverter } return true; - }; + } }; diff --git a/grid_map_cv/include/grid_map_cv/InpaintFilter.hpp b/grid_map_cv/include/grid_map_cv/InpaintFilter.hpp index dcf023c11..f57c21ed4 100644 --- a/grid_map_cv/include/grid_map_cv/InpaintFilter.hpp +++ b/grid_map_cv/include/grid_map_cv/InpaintFilter.hpp @@ -8,7 +8,7 @@ #pragma once -#include +#include //OpenCV #include "grid_map_cv/grid_map_cv.hpp" diff --git a/grid_map_cv/package.xml b/grid_map_cv/package.xml index 2904c4e66..377ceaa27 100644 --- a/grid_map_cv/package.xml +++ b/grid_map_cv/package.xml @@ -1,10 +1,10 @@ grid_map_cv - 1.6.3 + 1.7.18 Conversions between grid maps and OpenCV images. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues @@ -15,6 +15,7 @@ grid_map_core cv_bridge filters + libopencv-dev gtest diff --git a/grid_map_demos/CHANGELOG.rst b/grid_map_demos/CHANGELOG.rst index 0c0156b03..500764b84 100644 --- a/grid_map_demos/CHANGELOG.rst +++ b/grid_map_demos/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_demos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.3 (2020-10-19) ------------------ * Added GridMapToImageDemo. Saves the elevation layer of a grid_map to a file. diff --git a/grid_map_demos/CMakeLists.txt b/grid_map_demos/CMakeLists.txt index 2ac7f1335..375fea9f0 100644 --- a/grid_map_demos/CMakeLists.txt +++ b/grid_map_demos/CMakeLists.txt @@ -1,21 +1,22 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_demos) -set(CMAKE_CXX_STANDARD 11) - +set(CMAKE_CXX_STANDARD 17) +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries find_package(catkin REQUIRED COMPONENTS roscpp grid_map_core - grid_map_ros grid_map_cv grid_map_filters grid_map_loader grid_map_msgs grid_map_octomap + grid_map_ros grid_map_rviz_plugin + grid_map_sdf grid_map_visualization geometry_msgs sensor_msgs @@ -120,6 +121,11 @@ add_executable(interpolation_demo src/InterpolationDemo.cpp ) +add_executable(sdf_demo + src/sdf_demo_node.cpp + src/SdfDemo.cpp +) + ## Specify libraries to link a library or executable target against target_link_libraries( simple_demo @@ -188,6 +194,11 @@ target_link_libraries( ${catkin_LIBRARIES} ) +target_link_libraries( + sdf_demo + ${catkin_LIBRARIES} +) + ############# ## Install ## ############# @@ -213,6 +224,7 @@ install( resolution_change_demo simple_demo tutorial_demo + sdf_demo ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} diff --git a/grid_map_demos/config/filters_demo_filter_chain.yaml b/grid_map_demos/config/filters_demo_filter_chain.yaml index 7c3214602..5de01d98a 100644 --- a/grid_map_demos/config/filters_demo_filter_chain.yaml +++ b/grid_map_demos/config/filters_demo_filter_chain.yaml @@ -3,6 +3,7 @@ grid_map_filters: type: gridMapFilters/MockFilter params: processing_time: 100 + print_name: true - name: buffer_normalizer type: gridMapFilters/BufferNormalizerFilter @@ -110,7 +111,8 @@ grid_map_filters: - name: traversability_lower_threshold type: gridMapFilters/ThresholdFilter params: - layer: traversability + condition_layer: traversability + output_layer: traversability lower_threshold: 0.0 set_to: 0.0 @@ -118,6 +120,7 @@ grid_map_filters: - name: traversability_upper_threshold type: gridMapFilters/ThresholdFilter params: - layer: traversability + condition_layer: traversability + output_layer: traversability upper_threshold: 1.0 set_to: 1.0 # Other uses: .nan, .inf diff --git a/grid_map_demos/config/normal_filter_comparison.yaml b/grid_map_demos/config/normal_filter_comparison.yaml index c25b7aa86..f669149e4 100644 --- a/grid_map_demos/config/normal_filter_comparison.yaml +++ b/grid_map_demos/config/normal_filter_comparison.yaml @@ -1,23 +1,26 @@ grid_map_filters: + filter_chain: # Compute surface normals. - - name: surface_normals_area - type: gridMapFilters/NormalVectorsFilter - params: - input_layer: elevation_filtered - algorithm: area - output_layers_prefix: normal_area_ - radius: 0.05 - normal_vector_positive_axis: z - parallelization_enabled: true - thread_number: 4 - # thread_number: -1 doesn't limit the number of threads to be used + - + name: surface_normals_area + type: gridMapFilters/NormalVectorsFilter + params: + input_layer: elevation_filtered + algorithm: area + output_layers_prefix: normal_area_ + radius: 0.05 + normal_vector_positive_axis: z + parallelization_enabled: true + thread_number: 4 + # thread_number: -1 doesn't limit the number of threads to be used - - name: surface_normals_raster - type: gridMapFilters/NormalVectorsFilter - params: - input_layer: elevation_filtered - algorithm: raster - output_layers_prefix: normal_raster_ - normal_vector_positive_axis: z - parallelization_enabled: true - thread_number: 4 \ No newline at end of file + - + name: surface_normals_raster + type: gridMapFilters/NormalVectorsFilter + params: + input_layer: elevation_filtered + algorithm: raster + output_layers_prefix: normal_raster_ + normal_vector_positive_axis: z + parallelization_enabled: true + thread_number: 4 \ No newline at end of file diff --git a/grid_map_demos/config/sdf_demo.yaml b/grid_map_demos/config/sdf_demo.yaml new file mode 100644 index 000000000..1b3299b0b --- /dev/null +++ b/grid_map_demos/config/sdf_demo.yaml @@ -0,0 +1,16 @@ +grid_map_topic: /image_to_gridmap_demo/grid_map +pointcloud_topic: /grid_map_sdf_demo/signed_distance_field +elevation_layer: "elevation" +image_to_gridmap_demo: + image_topic: "/image_publisher/image" + resolution: 0.03 + map_frame_id: "map" + min_height: -0.3 + max_height: 0.4 +grid_map_visualization: + grid_map_topic: /image_to_gridmap_demo/grid_map + grid_map_visualizations: + - name: elevation_points + type: point_cloud + params: + layer: elevation \ No newline at end of file diff --git a/grid_map_demos/include/grid_map_demos/FiltersDemo.hpp b/grid_map_demos/include/grid_map_demos/FiltersDemo.hpp index 2a81c25a9..55bef936a 100644 --- a/grid_map_demos/include/grid_map_demos/FiltersDemo.hpp +++ b/grid_map_demos/include/grid_map_demos/FiltersDemo.hpp @@ -11,7 +11,11 @@ #include -#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wformat" +#include +#pragma GCC diagnostic pop #include #include diff --git a/grid_map_demos/include/grid_map_demos/SdfDemo.hpp b/grid_map_demos/include/grid_map_demos/SdfDemo.hpp new file mode 100644 index 000000000..a6f367573 --- /dev/null +++ b/grid_map_demos/include/grid_map_demos/SdfDemo.hpp @@ -0,0 +1,56 @@ +/* + * SdfDemo.hpp + * + * Created on: May 3, 2022 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +#include + +// ROS +#include + +// gridmap +#include +#include + +namespace grid_map_demos { + +/** + * Subscribes to a gridmap, converts it to a signed distance field, and publishes the signed distance field as a pointcloud. + */ +class SdfDemo { + public: + + /** + * Constructor. + * @param nodeHandle : the ROS node handle. + * @param mapTopic : grid map topic to subscribe to + * @param elevationLayer : name of the elevation layer + * @param pointcloudTopic : point cloud topic to publish the result to + */ + SdfDemo(ros::NodeHandle& nodeHandle, const std::string& mapTopic, std::string elevationLayer, const std::string& pointcloudTopic); + + private: + void callback(const grid_map_msgs::GridMap& message); + + //! Grid map subscriber. + ros::Subscriber gridmapSubscriber_; + + //! Pointcloud publisher. + ros::Publisher pointcloudPublisher_; + + //! Free space publisher. + ros::Publisher freespacePublisher_; + + //! Occupied space publisher. + ros::Publisher occupiedPublisher_; + + //! Elevation layer in the received grid map + std::string elevationLayer_; +}; + +} // namespace grid_map_demos \ No newline at end of file diff --git a/grid_map_demos/launch/sdf_demo.launch b/grid_map_demos/launch/sdf_demo.launch new file mode 100644 index 000000000..3bb155db6 --- /dev/null +++ b/grid_map_demos/launch/sdf_demo.launch @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grid_map_demos/package.xml b/grid_map_demos/package.xml index 9b31a038a..98b4650f9 100644 --- a/grid_map_demos/package.xml +++ b/grid_map_demos/package.xml @@ -1,10 +1,10 @@ grid_map_demos - 1.6.3 + 1.7.18 Demo nodes to demonstrate the usage of the grid map library. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues @@ -13,13 +13,14 @@ roscpp grid_map_core - grid_map_ros grid_map_cv grid_map_filters grid_map_loader grid_map_msgs grid_map_octomap + grid_map_ros grid_map_rviz_plugin + grid_map_sdf grid_map_visualization geometry_msgs sensor_msgs diff --git a/grid_map_demos/rviz/sdf_demo.rviz b/grid_map_demos/rviz/sdf_demo.rviz new file mode 100644 index 000000000..aeae58829 --- /dev/null +++ b/grid_map_demos/rviz/sdf_demo.rviz @@ -0,0 +1,236 @@ +Panels: + - Class: rviz/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: ~ + Splitter Ratio: 0.6114649772644043 + Tree Height: 375 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: OccupiedSpace +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 1 + Autocompute Intensity Bounds: true + Class: grid_map_rviz_plugin/GridMap + Color: 200; 200; 200 + Color Layer: elevation + Color Transformer: GridMapLayer + ColorMap: default + Enabled: false + Grid Cell Decimation: 1 + Grid Line Thickness: 0.10000000149011612 + Height Layer: elevation + Height Transformer: GridMapLayer + History Length: 1 + Invert ColorMap: false + Max Color: 255; 255; 255 + Max Intensity: 10 + Min Color: 0; 0; 0 + Min Intensity: 0 + Name: GridMap + Show Grid Lines: true + Topic: /image_to_gridmap_demo/grid_map + Unreliable: false + Use ColorMap: true + Value: false + - Alpha: 1 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 0.08491750061511993 + Min Value: -0.16515299677848816 + Value: false + Axis: Z + Channel Name: z + Class: rviz/PointCloud2 + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 85; 127 + Name: Elevation + Position Transformer: XYZ + Queue Size: 10 + Selectable: false + Size (Pixels): 3 + Size (m): 0.029999999329447746 + Style: Boxes + Topic: /grid_map_visualization/elevation_points + Unreliable: false + Use Fixed Frame: true + Use rainbow: false + Value: true + - Alpha: 0.30000001192092896 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud2 + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: SignedDistanceField + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /grid_map_sdf_demo/signed_distance_field/full_sdf + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 0.30000001192092896 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud2 + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: FreeSpace + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /grid_map_sdf_demo/signed_distance_field/free_space + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: false + - Alpha: 0.30000001192092896 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 10 + Min Value: -10 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud2 + Color: 255; 255; 255 + Color Transformer: Intensity + Decay Time: 0 + Enabled: true + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: OccupiedSpace + Position Transformer: XYZ + Queue Size: 10 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /grid_map_sdf_demo/signed_distance_field/occupied_space + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: true + Enabled: true + Global Options: + Background Color: 255; 255; 255 + Default Light: true + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 8.372665405273438 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Field of View: 0.7853981852531433 + Focal Point: + X: 0.5306656956672668 + Y: -0.5836256742477417 + Z: -1.7025400400161743 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.4247995615005493 + Target Frame: + Yaw: 2.58538818359375 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 594 + Hide Left Dock: false + Hide Right Dock: true + QMainWindow State: 000000ff00000000fd00000004000000000000016a000001b4fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000001b4000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000040efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007300000000280000040e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000005000000003efc0100000002fb0000000800540069006d0065010000000000000500000002eb00fffffffb0000000800540069006d0065010000000000000450000000000000000000000390000001b400000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: true + Width: 1280 + X: 2386 + Y: 252 diff --git a/grid_map_demos/src/InterpolationDemo.cpp b/grid_map_demos/src/InterpolationDemo.cpp index 5f4d3678b..35f5ad392 100644 --- a/grid_map_demos/src/InterpolationDemo.cpp +++ b/grid_map_demos/src/InterpolationDemo.cpp @@ -138,7 +138,7 @@ AnalyticalFunctions createGaussianWorld(grid_map::GridMap *map) func.f_ = [g](double x,double y) { double value = 0.0; - for (int i = 0; i < g.size(); ++i) { + for (size_t i{0}; i < g.size(); ++i) { const double x0 = g.at(i).x0; const double y0 = g.at(i).y0; const double varX = g.at(i).varX; @@ -206,10 +206,6 @@ void interpolateInputMap(const grid_map::GridMap &dataMap, Error computeInterpolationError(const AnalyticalFunctions &groundTruth, const grid_map::GridMap &map) { - const double r = map.getResolution(); - const double dimX = map.getLength().x() / 2.0 - 3.0 * r; - const double dimY = map.getLength().y() / 2.0 - 3.0 * r; - unsigned int count = 0; Error error; const int nRow = map.getSize().x(); @@ -295,7 +291,7 @@ InterpolationDemo::Statistics InterpolationDemo::computeStatistics() const stats.insert( { world->first, methodsStats }); } - return std::move(stats); + return stats; } InterpolationDemo::ErrorAndDuration InterpolationDemo::interpolateAndComputeError( diff --git a/grid_map_demos/src/SdfDemo.cpp b/grid_map_demos/src/SdfDemo.cpp new file mode 100644 index 000000000..0764ed6b8 --- /dev/null +++ b/grid_map_demos/src/SdfDemo.cpp @@ -0,0 +1,58 @@ +/* +* SdfDemo.cpp +* +* Created on: May 3, 2022 +* Author: Ruben Grandia +* Institute: ETH Zurich + */ + +#include "grid_map_demos/SdfDemo.hpp" + +#include + +#include +#include + +namespace grid_map_demos { + +SdfDemo::SdfDemo(ros::NodeHandle& nodeHandle, const std::string& mapTopic, std::string elevationLayer, const std::string& pointcloudTopic) + : elevationLayer_(std::move(elevationLayer)) { + pointcloudPublisher_ = nodeHandle.advertise(pointcloudTopic + "/full_sdf", 1); + freespacePublisher_ = nodeHandle.advertise(pointcloudTopic + "/free_space", 1); + occupiedPublisher_ = nodeHandle.advertise(pointcloudTopic + "/occupied_space", 1); + gridmapSubscriber_ = nodeHandle.subscribe(mapTopic, 1, &SdfDemo::callback, this); +} + +void SdfDemo::callback(const grid_map_msgs::GridMap& message) { + // Convert message to map. + grid_map::GridMap map; + std::vector layers{elevationLayer_}; + grid_map::GridMapRosConverter::fromMessage(message, map, layers, false, false); + auto& elevationData = map.get(elevationLayer_); + + // Inpaint if needed. + if (elevationData.hasNaN()) { + const float inpaint{elevationData.minCoeffOfFinites()}; + ROS_WARN("[SdfDemo] Map contains NaN values. Will apply inpainting with min value."); + elevationData = elevationData.unaryExpr([=](float v) { return std::isfinite(v)? v : inpaint; }); + } + + // Generate SDF. + const float heightMargin{0.1}; + const float minValue{elevationData.minCoeffOfFinites() - heightMargin}; + const float maxValue{elevationData.maxCoeffOfFinites() + heightMargin}; + grid_map::SignedDistanceField sdf(map, elevationLayer_, minValue, maxValue); + + // Extract as point clouds. + sensor_msgs::PointCloud2 pointCloud2Msg; + grid_map::GridMapRosConverter::toPointCloud(sdf, pointCloud2Msg); + pointcloudPublisher_.publish(pointCloud2Msg); + + grid_map::GridMapRosConverter::toPointCloud(sdf, pointCloud2Msg, 1, [](float sdfValue) { return sdfValue > 0.0; }); + freespacePublisher_.publish(pointCloud2Msg); + + grid_map::GridMapRosConverter::toPointCloud(sdf, pointCloud2Msg, 1, [](float sdfValue) { return sdfValue <= 0.0; }); + occupiedPublisher_.publish(pointCloud2Msg); +} + +} // namespace grid_map_demos \ No newline at end of file diff --git a/grid_map_demos/src/iterator_benchmark.cpp b/grid_map_demos/src/iterator_benchmark.cpp index ec09714e4..7ead4fb69 100644 --- a/grid_map_demos/src/iterator_benchmark.cpp +++ b/grid_map_demos/src/iterator_benchmark.cpp @@ -74,8 +74,8 @@ void runCustomIndexIteration(GridMap& map, const string& layer_from, const strin { const auto& data_from = map[layer_from]; auto& data_to = map[layer_to]; - for (size_t j = 0; j < data_to.cols(); ++j) { - for (size_t i = 0; i < data_to.rows(); ++i) { + for (Eigen::Index j{0}; j < data_to.cols(); ++j) { + for (Eigen::Index i{0}; i < data_to.rows(); ++i) { const float value_from = data_from(i, j); float& value_to = data_to(i, j); value_to = value_to > value_from ? value_to : value_from; @@ -90,12 +90,12 @@ void runCustomLinearIndexIteration(GridMap& map, const string& layer_from, const { const auto& data_from = map[layer_from]; auto& data_to = map[layer_to]; - for (size_t i = 0; i < data_to.size(); ++i) { + for (Eigen::Index i = 0; i < data_to.size(); ++i) { data_to(i) = data_to(i) > data_from(i) ? data_to(i) : data_from(i); } } -int main(int argc, char* argv[]) +int main() { GridMap map; map.setGeometry(Length(20.0, 20.0), 0.004, Position(0.0, 0.0)); diff --git a/grid_map_demos/src/normal_filter_comparison_node.cpp b/grid_map_demos/src/normal_filter_comparison_node.cpp index bb78e1ca8..01a314382 100644 --- a/grid_map_demos/src/normal_filter_comparison_node.cpp +++ b/grid_map_demos/src/normal_filter_comparison_node.cpp @@ -4,7 +4,11 @@ * @brief Node for comparing different normal filters performances. */ -#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wformat" +#include +#pragma GCC diagnostic pop #include #include #include diff --git a/grid_map_demos/src/sdf_demo_node.cpp b/grid_map_demos/src/sdf_demo_node.cpp new file mode 100644 index 000000000..3c0c4f1d1 --- /dev/null +++ b/grid_map_demos/src/sdf_demo_node.cpp @@ -0,0 +1,33 @@ +/* +* sdf_demo_node.cpp +* +* Created on: May 3, 2022 +* Author: Ruben Grandia +* Institute: ETH Zurich +*/ + +#include + +#include + +#include "grid_map_demos/SdfDemo.hpp" + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "grid_map_sdf_demo"); + ros::NodeHandle nodeHandle(""); + + std::string elevationLayer; + nodeHandle.getParam("elevation_layer", elevationLayer); + + std::string mapTopic; + nodeHandle.getParam("grid_map_topic", mapTopic); + + std::string pointcloudTopic; + nodeHandle.getParam("pointcloud_topic", pointcloudTopic); + + grid_map_demos::SdfDemo sdfDemo(nodeHandle, mapTopic, elevationLayer, pointcloudTopic); + + ros::spin(); + return 0; +} \ No newline at end of file diff --git a/grid_map_demos/src/tutorial_demo_node.cpp b/grid_map_demos/src/tutorial_demo_node.cpp index 343dea3d5..76e6341cd 100644 --- a/grid_map_demos/src/tutorial_demo_node.cpp +++ b/grid_map_demos/src/tutorial_demo_node.cpp @@ -88,9 +88,6 @@ int main(int argc, char** argv) // Show absolute difference and compute mean squared error. map.add("error", (map.get("elevation_filtered") - map.get("elevation")).cwiseAbs()); - unsigned int nCells = map.getSize().prod(); - // cppcheck-suppress unreadVariable - double rootMeanSquaredError = sqrt((map["error"].array().pow(2).sum()) / nCells); // Publish grid map. map.setTimestamp(time.toNSec()); diff --git a/grid_map_filters/CHANGELOG.rst b/grid_map_filters/CHANGELOG.rst index 32e70199e..e80862cf6 100644 --- a/grid_map_filters/CHANGELOG.rst +++ b/grid_map_filters/CHANGELOG.rst @@ -2,6 +2,22 @@ Changelog for package grid_map_filters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.8 (2021-12-15) +------------------ +* Improved the template definition structure: +* - grid_map_filters do not need to be template classes anymore. +* Contributors: Magnus Gärtner + +1.6.4 (2020-12-04) +------------------ +* Extended the ThresholdFilter: +* - The layer parameter was replaced by condition_layer and output_layer. +* - The previous behavior can be restored by setting both new parameters to the same value. +* Contributors: Magnus Gärtner + 1.6.3 (2020-09-08) ------------------ * Implemented a new filter: diff --git a/grid_map_filters/CMakeLists.txt b/grid_map_filters/CMakeLists.txt index b18882ec3..37194096b 100644 --- a/grid_map_filters/CMakeLists.txt +++ b/grid_map_filters/CMakeLists.txt @@ -1,41 +1,40 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_filters) -# Better with parallelized algorithms. -#set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS} -ffast-math") - # Better with serial algorithms. -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) # We want performance (fast-math) but also need a representation for NaN values to represent missing values. # Therefore, we disable the finite-math-only flag that was set by fast-math. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -fno-finite-math-only") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Other possible options. #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native -ffast-math") #set(TARGET_ARCHITECTURE "kaby-lake") -## Find catkin macros and libraries -find_package(catkin REQUIRED COMPONENTS +## Create a list of catkin package dependencies used in header files of this package. +set(CATKIN_PACKAGE_HEADER_DEPENDENCIES grid_map_core grid_map_ros grid_map_msgs filters ) -## System dependencies are found with CMake's conventions -find_package(PkgConfig REQUIRED) +## Create a list of catkin package dependencies, now for both header and source files. +set(CATKIN_PACKAGE_DEPENDENCIES + ${CATKIN_PACKAGE_HEADER_DEPENDENCIES} +) -pkg_check_modules(TBB "tbb") -if (TBB_FOUND) - add_definitions( - ${TBB_CFLAGS} - ${TBB_CFLAGS_OTHER} - ) -else() - message([FATAL_ERROR] "tbb module not found") -endif () +## Find catkin dependencies for building this package. +find_package(catkin REQUIRED + COMPONENTS + ${CATKIN_PACKAGE_DEPENDENCIES} +) + +find_package(TBB 2020.1 EXACT REQUIRED) + +find_package(OpenCV REQUIRED) ################################### ## catkin specific configuration ## @@ -49,14 +48,14 @@ endif () catkin_package( INCLUDE_DIRS include - ${TBB_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} # TODO Remove this include directory, currently it exported by the medianFillFilter header. LIBRARIES ${PROJECT_NAME} + ${PROJECT_NAME}_plugins CATKIN_DEPENDS - grid_map_ros - grid_map_core - grid_map_msgs - filters + ${CATKIN_PACKAGE_HEADER_DEPENDENCIES} + DEPENDS + OpenCV ) ########### @@ -68,7 +67,7 @@ include_directories( include SYSTEM ${catkin_INCLUDE_DIRS} - ${TBB_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} ) ## Declare a cpp library @@ -93,9 +92,45 @@ add_library(${PROJECT_NAME} src/BufferNormalizerFilter.cpp ) +target_include_directories(${PROJECT_NAME} + PRIVATE + ${TBB_INCLUDE_DIRS} +) + +target_include_directories(${PROJECT_NAME} + SYSTEM PUBLIC + ${catkin_INCLUDE_DIRS} +) + target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} ${TBB_LIBRARIES} + ${OpenCV_LIBRARIES} +) + +add_dependencies(${PROJECT_NAME} + ${catkin_EXPORTED_TARGETS} +) + +# Instantiate plugin exports for every filter in this package. +add_library(${PROJECT_NAME}_plugins + src/plugins.cpp +) + +target_include_directories(${PROJECT_NAME}_plugins + SYSTEM PUBLIC + ${catkin_INCLUDE_DIRS} +) + +target_link_libraries(${PROJECT_NAME}_plugins + ${PROJECT_NAME} + ${TBB_LIBRARIES} # is this necessary? + ${OpenCV_LIBRARIES} # is this necessary? +) + +add_dependencies(${PROJECT_NAME}_plugins + ${catkin_EXPORTED_TARGETS} + ${PROJECT_NAME} ) ############# @@ -104,23 +139,32 @@ target_link_libraries(${PROJECT_NAME} # Mark executables and/or libraries for installation install( - TARGETS ${PROJECT_NAME} - ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} - RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} + TARGETS + ${PROJECT_NAME} + ${PROJECT_NAME}_plugins + ARCHIVE DESTINATION + ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION + ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION + ${CATKIN_PACKAGE_BIN_DESTINATION} ) # Mark cpp header files for installation install( - DIRECTORY include/${PROJECT_NAME}/ - DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} + DIRECTORY + include/${PROJECT_NAME}/ + DESTINATION + ${CATKIN_PACKAGE_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.hpp" ) # Mark other files for installation install( - FILES filter_plugins.xml - DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} + FILES + filter_plugins.xml + DESTINATION + ${CATKIN_PACKAGE_SHARE_DESTINATION} ) ############# @@ -131,6 +175,8 @@ if (CATKIN_ENABLE_TESTING) catkin_add_gtest(${PROJECT_NAME}-test test/test_grid_map_filters.cpp test/median_fill_filter_test.cpp + test/mock_filter_test.cpp + test/threshold_filter_test.cpp ) target_include_directories(${PROJECT_NAME}-test PRIVATE include @@ -164,4 +210,4 @@ if(cmake_clang_tools_FOUND) add_default_clang_tooling( DISABLE_CLANG_FORMAT ) -endif(cmake_clang_tools_FOUND) \ No newline at end of file +endif(cmake_clang_tools_FOUND) diff --git a/grid_map_filters/filter_plugins.xml b/grid_map_filters/filter_plugins.xml index 669d80a1a..d8e96ac60 100644 --- a/grid_map_filters/filter_plugins.xml +++ b/grid_map_filters/filter_plugins.xml @@ -1,91 +1,91 @@ - - + + Set values below/above a threshold to a specified value. - + Compute for each cell of a layer the minimal value inside a radius. - + Compute for each cell of a layer the mean value inside a radius. - + Fill holes (NaNs) in a layer using the median value inside a radius. Optionally, apply median calculations for values that are already finite. - + Does Identity, but allows to set a delay and debug prints. Used for testing purposes. - + Compute the normal vectors of a layer in a map. - + Compute the curvature (second derivative) of a layer in the map. - + Compute a new color layer based on normal vectors layers. - + Compute the diffuse lighting of a surface as new black and white color layer. - + Parse and evaluate a mathematical matrix expression with layers of a grid map. - + Parse and evaluate a mathematical matrix expression within a sliding window on a layer of a grid map. - + Duplicate a layer of a grid map. - + Delete layers from a grid map. - + Creates a new color layer. - + Creates a new color layer with the color mapped between min. and max. value. - + Blend two color layers. - + Set specified layers of a grid map as basic layers. - + Normalizes the buffer of a map such that it has default (zero) start index. diff --git a/grid_map_filters/include/grid_map_filters/BufferNormalizerFilter.hpp b/grid_map_filters/include/grid_map_filters/BufferNormalizerFilter.hpp index 999191ad6..a22a8f3dd 100644 --- a/grid_map_filters/include/grid_map_filters/BufferNormalizerFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/BufferNormalizerFilter.hpp @@ -8,19 +8,17 @@ #pragma once -#include - #include +#include +#include + namespace grid_map { /*! * Normalizes the buffer of a map such that it has default (zero) start index. */ -template -class BufferNormalizerFilter : public filters::FilterBase -{ - +class BufferNormalizerFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,19 +28,19 @@ class BufferNormalizerFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~BufferNormalizerFilter(); + ~BufferNormalizerFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Normalizes the buffer of a map. * @param mapIn the input map before normalization. * @param mapOut the normalized map. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/ColorBlendingFilter.hpp b/grid_map_filters/include/grid_map_filters/ColorBlendingFilter.hpp index c9b51d50c..3f4c56645 100644 --- a/grid_map_filters/include/grid_map_filters/ColorBlendingFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/ColorBlendingFilter.hpp @@ -8,19 +8,18 @@ #pragma once -#include - #include #include +#include +#include + namespace grid_map { /*! * Blend two color layers. */ -template -class ColorBlendingFilter : public filters::FilterBase -{ +class ColorBlendingFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,39 +29,34 @@ class ColorBlendingFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~ColorBlendingFilter(); + ~ColorBlendingFilter() override; /*! * Configures the filter. */ - virtual bool configure(); + bool configure() override; /*! * Compute a new color layer based on blending two color layers. * @param mapIn grid map containing the two color layers. * @param mapOut grid map containing mapIn and the blended color layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: - - enum class BlendModes { - Normal, - HardLight, - SoftLight - }; + enum class BlendModes { Normal, HardLight, SoftLight }; //! Input layers. std::string backgroundLayer_, foregroundLayer_; - //! Blend mode. - BlendModes blendMode_; - //! Opacity of foreground layer. double opacity_; + //! Blend mode. + BlendModes blendMode_; + //! Output layer name. std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/ColorFillFilter.hpp b/grid_map_filters/include/grid_map_filters/ColorFillFilter.hpp index 21b6bcb15..a6ccfac72 100644 --- a/grid_map_filters/include/grid_map_filters/ColorFillFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/ColorFillFilter.hpp @@ -8,20 +8,17 @@ #pragma once -#include - #include -#include + +#include +#include namespace grid_map { /*! * Creates a new color layer. */ -template -class ColorFillFilter : public filters::FilterBase -{ - +class ColorFillFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,19 +28,19 @@ class ColorFillFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~ColorFillFilter(); + ~ColorFillFilter() override; /*! * Configures the filter. */ - virtual bool configure(); + bool configure() override; /*! * Adds a new color layer. * @param mapIn grid map to add the new layer. * @param mapOut grid map the grid map with the new color layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Color. @@ -56,4 +53,4 @@ class ColorFillFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/ColorMapFilter.hpp b/grid_map_filters/include/grid_map_filters/ColorMapFilter.hpp index 87b1dda38..d19e2d1ce 100644 --- a/grid_map_filters/include/grid_map_filters/ColorMapFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/ColorMapFilter.hpp @@ -8,21 +8,19 @@ #pragma once -#include -#include - #include #include +#include +#include +#include + namespace grid_map { /*! * Creates a new color layer with the color mapped between min. and max. value. */ -template -class ColorMapFilter : public filters::FilterBase -{ - +class ColorMapFilter : public filters::FilterBase { public: /*! * Constructor @@ -32,19 +30,19 @@ class ColorMapFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~ColorMapFilter(); + ~ColorMapFilter() override; /*! * Configures the filter. */ - virtual bool configure(); + bool configure() override; /*! * Adds a new color layer. * @param mapIn grid map to add the new layer. * @param mapOut grid map the grid map with the new color layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Min./max. colors. @@ -60,4 +58,4 @@ class ColorMapFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/CurvatureFilter.hpp b/grid_map_filters/include/grid_map_filters/CurvatureFilter.hpp index 18858bd43..0f3762a32 100644 --- a/grid_map_filters/include/grid_map_filters/CurvatureFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/CurvatureFilter.hpp @@ -8,20 +8,18 @@ #pragma once -#include - #include #include +#include +#include + namespace grid_map { /*! * Compute the curvature (second derivative) of a layer in the map. */ -template -class CurvatureFilter : public filters::FilterBase -{ - +class CurvatureFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,12 +29,12 @@ class CurvatureFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~CurvatureFilter(); + ~CurvatureFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Compute the curvature of a layer in a map and @@ -44,7 +42,7 @@ class CurvatureFilter : public filters::FilterBase * @param mapIn grid map containing the layer for which the curvature is computed for. * @param mapOut grid map containing mapIn and the new layer for the curvature. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Input layer name. @@ -54,4 +52,4 @@ class CurvatureFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/DeletionFilter.hpp b/grid_map_filters/include/grid_map_filters/DeletionFilter.hpp index 048da3219..658b02720 100644 --- a/grid_map_filters/include/grid_map_filters/DeletionFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/DeletionFilter.hpp @@ -8,20 +8,18 @@ #pragma once -#include - -#include #include +#include + +#include +#include namespace grid_map { /*! * Deletion filter class deletes layers of a grid map. */ -template -class DeletionFilter : public filters::FilterBase -{ - +class DeletionFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,25 +29,23 @@ class DeletionFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~DeletionFilter(); + ~DeletionFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Deletes the specified layers of a grid map. * @param mapIn gridMap with the different layers. * @param mapOut gridMap without the deleted layers. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: - //! List of layers that should be deleted. std::vector layers_; - }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/DuplicationFilter.hpp b/grid_map_filters/include/grid_map_filters/DuplicationFilter.hpp index f93bdac79..825518b6e 100644 --- a/grid_map_filters/include/grid_map_filters/DuplicationFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/DuplicationFilter.hpp @@ -8,19 +8,17 @@ #pragma once -#include - #include +#include +#include + namespace grid_map { /*! * Duplication filter class duplicates a layer of a grid map. */ -template -class DuplicationFilter : public filters::FilterBase -{ - +class DuplicationFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,19 +28,19 @@ class DuplicationFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~DuplicationFilter(); + ~DuplicationFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Duplicates the specified layers of a grid map. * @param mapIn with the layer to duplicate. * @param mapOut with the layer duplicated. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Name of the layer that is duplicated. @@ -52,4 +50,4 @@ class DuplicationFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/LightIntensityFilter.hpp b/grid_map_filters/include/grid_map_filters/LightIntensityFilter.hpp index 581a5dfb6..0572c84ec 100644 --- a/grid_map_filters/include/grid_map_filters/LightIntensityFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/LightIntensityFilter.hpp @@ -8,20 +8,18 @@ #pragma once -#include - #include #include +#include +#include + namespace grid_map { /*! * Compute the diffuse lighting of a surface as new black and white color layer. */ -template -class LightIntensityFilter : public filters::FilterBase -{ - +class LightIntensityFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,19 +29,19 @@ class LightIntensityFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~LightIntensityFilter(); + ~LightIntensityFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Compute the diffuse lighting layer. * @param mapIn grid map containing the layers of the normal vectors. * @param mapOut grid map containing mapIn and the black and white lighting color layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Input layers prefix. @@ -56,4 +54,4 @@ class LightIntensityFilter : public filters::FilterBase Eigen::Vector3f lightDirection_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/MathExpressionFilter.hpp b/grid_map_filters/include/grid_map_filters/MathExpressionFilter.hpp index c921f40a1..c909b8ae8 100644 --- a/grid_map_filters/include/grid_map_filters/MathExpressionFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/MathExpressionFilter.hpp @@ -8,21 +8,18 @@ #pragma once +#include #include "EigenLab/EigenLab.h" -#include - -#include +#include +#include namespace grid_map { /*! * Parses and evaluates a mathematical matrix expression with layers of a grid map. */ -template -class MathExpressionFilter : public filters::FilterBase -{ - +class MathExpressionFilter : public filters::FilterBase { public: /*! * Constructor @@ -32,19 +29,19 @@ class MathExpressionFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~MathExpressionFilter(); + ~MathExpressionFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Takes the minimum out of different layers of a grid map. * @param mapIn gridMap with the different layers to take the min. * @param mapOut gridMap with an additional layer containing the sum. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! EigenLab parser. @@ -57,4 +54,4 @@ class MathExpressionFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/MeanInRadiusFilter.hpp b/grid_map_filters/include/grid_map_filters/MeanInRadiusFilter.hpp index 3a652705a..78de1151b 100644 --- a/grid_map_filters/include/grid_map_filters/MeanInRadiusFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/MeanInRadiusFilter.hpp @@ -8,19 +8,17 @@ #pragma once -#include - -#include #include +#include +#include + namespace grid_map { /*! * Filter class to find the mean of the values inside a radius. */ -template -class MeanInRadiusFilter : public filters::FilterBase { - +class MeanInRadiusFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,12 +28,12 @@ class MeanInRadiusFilter : public filters::FilterBase { /*! * Destructor. */ - virtual ~MeanInRadiusFilter(); + ~MeanInRadiusFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Computes for each value in the input layer the mean of all values in a radius around it @@ -43,7 +41,7 @@ class MeanInRadiusFilter : public filters::FilterBase { * @param mapIn grid map containing the input layer. * @param mapOut grid map containing the layers of the input map and the new layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Radius to take the mean from. @@ -56,4 +54,4 @@ class MeanInRadiusFilter : public filters::FilterBase { std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/MedianFillFilter.hpp b/grid_map_filters/include/grid_map_filters/MedianFillFilter.hpp index 7c3061514..be05a8551 100644 --- a/grid_map_filters/include/grid_map_filters/MedianFillFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/MedianFillFilter.hpp @@ -8,20 +8,21 @@ #pragma once -#include - #include #include +#include +#include #include +#include namespace grid_map { /*! - * Uses Boost accumulators to fill holes in the input layer by the median of the surrounding values. + * Uses std::nth_element to fill holes in the input layer by the median of the surrounding values. The result is put into the output_layer. + * Note: Only values for which the fill_layer is true will be filled. The fill_layer is auto computed if not present in the input. */ -template -class MedianFillFilter : public filters::FilterBase { +class MedianFillFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,12 +32,12 @@ class MedianFillFilter : public filters::FilterBase { /*! * Destructor. */ - virtual ~MedianFillFilter(); + ~MedianFillFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Adds a new output layer to the map. @@ -45,7 +46,7 @@ class MedianFillFilter : public filters::FilterBase { * @param mapIn grid map containing input layer * @param mapOut grid map containing mapIn and median filtered input layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; protected: /*! @@ -58,8 +59,44 @@ class MedianFillFilter : public filters::FilterBase { * @param bufferSize The buffer size of the input * @return The median of finites in the specified neighbourhood. */ - float getMedian(Eigen::Ref inputMap, const grid_map::Index& centerIndex, const size_t radiusInPixels, - const grid_map::Size bufferSize); + static float getMedian(Eigen::Ref inputMap, const Index& centerIndex, size_t radiusInPixels, Size bufferSize); + + /** + * Computes a mask of which cells to fill-in based on the validity of input cells. I.e small holes between (and including) valid cells are + * marked to be filled. + * + * @remark The returned fill_mask is also added as layer to the output to be reused in following iterations of this filter. + * If debug is enabled, an intermediate mask is added as layer to the output grid map. + * @param inputMap The input layer, used to check which cells contain valid values. + * @param mapOut The output GridMap will contain the additional fill_mask layer afterwards. + * @return An eigen mask indicating which cells should be filled by the median filter. + */ + Eigen::MatrixXf computeAndAddFillMask(const Eigen::MatrixXf& inputMap, GridMap& mapOut); + + /** + * Remove sparse valid regions by morphological opening. + * @remark Check https://docs.opencv.org/master/d9/d61/tutorial_py_morphological_ops.html + * for more information about the opening operation. + * @param inputMask Initial mask possibly containing also sparse valid regions that will be removed. + * @return An opencv mask of the same size as input mask with small sparse valid regions removed. + */ + static cv::Mat_ cleanedMask(const cv::Mat_& inputMask); + + /** + * Performs morphological closing on a boolean cv matrix mask. + * @param [in] isValidMask A 2d mask where holes up to a certain size will be filled. + * @param [in] numDilationClosingIterations Algorithm specific parameter. Higher means that bigger holes will still be filled. + * @return A mask of the same size as isValidMask but with small holes filled. + */ + static cv::Mat_ fillHoles(const cv::Mat_& isValidMask, size_t numDilationClosingIterations); + + /** + * Adds a float cv matrix as layer to a given map. + * @param [in, out] gridMap The map to add the layer. + * @param [in] cvLayer The cv matrix to add. + * @param [in] layerName The layer name + */ + static void addCvMatAsLayer(GridMap& gridMap, const cv::Mat& cvLayer, const std::string& layerName); private: //! Median filtering radius of NaN values in the input. @@ -71,11 +108,23 @@ class MedianFillFilter : public filters::FilterBase { //! Flag indicating whether to also filter finite values. bool filterExistingValues_; + //! Number of erode-dilate iterations to calculate mask. Higher means that bigger holes will still be filled. + int numErodeDilationIterations_; + //! Input layer name. std::string inputLayer_; //! Output layer name. std::string outputLayer_; + + //! Layer containing indicating which areas to fill, will be computed if not present. + std::string fillMaskLayer_ = "should_fill"; + + //! Layer used to visualize the intermediate, sparse outlier removed fill mask. + std::string debugInfillMaskLayer_ = "debug_infill_mask"; + + //! If set, the filtered grid_map is augmented with additional debug layers. + bool debug_; }; } // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/MinInRadiusFilter.hpp b/grid_map_filters/include/grid_map_filters/MinInRadiusFilter.hpp index 6e4be578c..521d244eb 100644 --- a/grid_map_filters/include/grid_map_filters/MinInRadiusFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/MinInRadiusFilter.hpp @@ -8,18 +8,17 @@ #pragma once -#include - #include +#include +#include + namespace grid_map { /*! * Filter class to compute the minimal value inside a radius. */ -template -class MinInRadiusFilter : public filters::FilterBase { - +class MinInRadiusFilter : public filters::FilterBase { public: /*! * Constructor. @@ -29,12 +28,12 @@ class MinInRadiusFilter : public filters::FilterBase { /*! * Destructor. */ - virtual ~MinInRadiusFilter(); + ~MinInRadiusFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Computes for each value in the input layer the minimum of all values in a radius around it. @@ -42,7 +41,7 @@ class MinInRadiusFilter : public filters::FilterBase { * @param mapIn grid map containing the input layer. * @param mapOut grid map containing the original layers and the new layer with the minimal values. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Radius to take the minimum in. @@ -55,4 +54,4 @@ class MinInRadiusFilter : public filters::FilterBase { std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/MockFilter.hpp b/grid_map_filters/include/grid_map_filters/MockFilter.hpp index d3c36b906..f7d174f8b 100644 --- a/grid_map_filters/include/grid_map_filters/MockFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/MockFilter.hpp @@ -8,19 +8,17 @@ #pragma once -#include - #include +#include +#include + namespace grid_map { /*! * Duplication filter class duplicates a layer of a grid map. */ -template -class MockFilter : public filters::FilterBase -{ - +class MockFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,27 +28,26 @@ class MockFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~MockFilter(); + ~MockFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Copies the input to the output. The time for the update is specified by processingTime_. Optionally the update is logged. * @param mapIn Input. * @param mapOut Output. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: - //! Flag indicating wheter to also log on update. - bool printName_; + bool printName_{false}; //! The time [ms] that the update function takes. - uint processingTime_; + uint processingTime_{0}; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/NormalColorMapFilter.hpp b/grid_map_filters/include/grid_map_filters/NormalColorMapFilter.hpp index 0a00d02e8..26093bf5f 100644 --- a/grid_map_filters/include/grid_map_filters/NormalColorMapFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/NormalColorMapFilter.hpp @@ -8,20 +8,18 @@ #pragma once -#include - #include #include +#include +#include + namespace grid_map { /*! * Compute a new color layer based on normal vectors layers. */ -template -class NormalColorMapFilter : public filters::FilterBase -{ - +class NormalColorMapFilter : public filters::FilterBase { public: /*! * Constructor @@ -31,19 +29,19 @@ class NormalColorMapFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~NormalColorMapFilter(); + ~NormalColorMapFilter() override; /*! * Configures the filter from parameters on the Parameter Server */ - virtual bool configure(); + bool configure() override; /*! * Compute a new color layer based on normal vectors layers. * @param mapIn grid map containing the layers of the normal vectors. * @param mapOut grid map containing mapIn and the new color layer. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Input layers prefix. @@ -53,4 +51,4 @@ class NormalColorMapFilter : public filters::FilterBase std::string outputLayer_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/NormalVectorsFilter.hpp b/grid_map_filters/include/grid_map_filters/NormalVectorsFilter.hpp index fc792a79c..0d01850ae 100644 --- a/grid_map_filters/include/grid_map_filters/NormalVectorsFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/NormalVectorsFilter.hpp @@ -8,19 +8,18 @@ #pragma once -#include -#include - #include #include +#include +#include + namespace grid_map { /*! * Compute the normal vectors of a layer in a map. */ -template -class NormalVectorsFilter : public filters::FilterBase { +class NormalVectorsFilter : public filters::FilterBase { public: /*! * Constructor @@ -56,7 +55,7 @@ class NormalVectorsFilter : public filters::FilterBase { * @param mapIn grid map containing the layer for which the normal vectors are computed for. * @param mapOut grid map containing mapIn and the new layers for the normal vectors. */ - bool update(const T& mapIn, T& mapOut) override; + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: /*! @@ -106,7 +105,7 @@ class NormalVectorsFilter : public filters::FilterBase { * @param index: Index of point in the grid map for which this function calculates the normal vector. */ void areaSingleNormalComputation(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix, - const grid_map::Index& index); + const grid_map::Index& index); /*! * Estimate the normal vector at each point of the input layer by using the rasterSingleNormalComputation function. * This function makes use of the raster method and is the serial version of such normal vector computation using a @@ -160,7 +159,7 @@ class NormalVectorsFilter : public filters::FilterBase { * @param index: Index of point in the grid map for which this function calculates the normal vector. */ void rasterSingleNormalComputation(GridMap& map, const std::string& outputLayersPrefix, const grid_map::Matrix& dataMap, - const grid_map::Index& index); + const grid_map::Index& index); enum class Method { AreaSerial, AreaParallel, RasterSerial, RasterParallel }; diff --git a/grid_map_filters/include/grid_map_filters/SetBasicLayersFilter.hpp b/grid_map_filters/include/grid_map_filters/SetBasicLayersFilter.hpp index 8cf66298f..e8d598118 100644 --- a/grid_map_filters/include/grid_map_filters/SetBasicLayersFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/SetBasicLayersFilter.hpp @@ -8,19 +8,18 @@ #pragma once -#include - -#include #include +#include + +#include +#include namespace grid_map { /*! * Set specified layers of a grid map as basic layers. */ -template -class SetBasicLayersFilter : public filters::FilterBase -{ +class SetBasicLayersFilter : public filters::FilterBase { public: /*! * Constructor @@ -30,24 +29,23 @@ class SetBasicLayersFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~SetBasicLayersFilter(); + ~SetBasicLayersFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Set the specified layers as basic layers. * @param mapIn input grid map. * @param mapOut output grid map with basic layers set. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! List of layers that should be set as basic layers. std::vector layers_; - }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/SlidingWindowMathExpressionFilter.hpp b/grid_map_filters/include/grid_map_filters/SlidingWindowMathExpressionFilter.hpp index ee65afb1c..f759bf2d2 100644 --- a/grid_map_filters/include/grid_map_filters/SlidingWindowMathExpressionFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/SlidingWindowMathExpressionFilter.hpp @@ -8,24 +8,20 @@ #pragma once -#include "EigenLab/EigenLab.h" +#include +#include +#include #include -#include - -#include -#include +#include "EigenLab/EigenLab.h" namespace grid_map { /*! * Parse and evaluate a mathematical matrix expression within a sliding window on a layer of a grid map. */ -template -class SlidingWindowMathExpressionFilter : public filters::FilterBase -{ - +class SlidingWindowMathExpressionFilter : public filters::FilterBase { public: /*! * Constructor @@ -35,19 +31,19 @@ class SlidingWindowMathExpressionFilter : public filters::FilterBase /*! * Destructor. */ - virtual ~SlidingWindowMathExpressionFilter(); + ~SlidingWindowMathExpressionFilter() override; /*! * Configures the filter from parameters on the parameter server. */ - virtual bool configure(); + bool configure() override; /*! * Takes the minimum out of different layers of a grid map. * @param mapIn gridMap with the different layers to take the min. * @param mapOut gridMap with an additional layer containing the sum. */ - virtual bool update(const T& mapIn, T& mapOut); + bool update(const GridMap& mapIn, GridMap& mapOut) override; private: //! Input layer name. @@ -78,4 +74,4 @@ class SlidingWindowMathExpressionFilter : public filters::FilterBase SlidingWindowIterator::EdgeHandling edgeHandling_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/include/grid_map_filters/ThresholdFilter.hpp b/grid_map_filters/include/grid_map_filters/ThresholdFilter.hpp index 85861f08c..7073b50dc 100644 --- a/grid_map_filters/include/grid_map_filters/ThresholdFilter.hpp +++ b/grid_map_filters/include/grid_map_filters/ThresholdFilter.hpp @@ -8,21 +8,19 @@ #pragma once -#include - #include #include +#include +#include + namespace grid_map { /*! * Threshold filter class to set values below/above a threshold to a * specified value. */ -template -class ThresholdFilter : public filters::FilterBase -{ - +class ThresholdFilter : public filters::FilterBase { public: /*! * Constructor @@ -45,12 +43,14 @@ class ThresholdFilter : public filters::FilterBase * @param mapIn GridMap with the different layers to apply a threshold. * @param mapOut GridMap with the threshold applied to the layers. */ - virtual bool update(const T& mapIn, T& mapOut); + virtual bool update(const GridMap& mapIn, GridMap& mapOut); private: + //! Layer the threshold will be evaluated. + std::string conditionLayer_; //! Layer the threshold should be applied to. - std::string layer_; + std::string outputLayer_; //! Lower Threshold double lowerThreshold_; @@ -58,11 +58,11 @@ class ThresholdFilter : public filters::FilterBase //! Upper Threshold double upperThreshold_; - //! If threshold triggered set to this value - double setTo_; - //! Booleans to decide which threshold should be used. bool useLowerThreshold_, useUpperThreshold_; + + //! If threshold triggered set to this value + double setTo_; }; -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_filters/package.xml b/grid_map_filters/package.xml index 82941cf68..1358882b5 100644 --- a/grid_map_filters/package.xml +++ b/grid_map_filters/package.xml @@ -1,10 +1,10 @@ grid_map_filters - 1.6.3 + 1.7.18 Processing grid maps as a sequence of ROS filters. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues @@ -17,6 +17,7 @@ grid_map_ros grid_map_msgs tbb + libopencv-dev gtest google-mock diff --git a/grid_map_filters/src/BufferNormalizerFilter.cpp b/grid_map_filters/src/BufferNormalizerFilter.cpp index 40fed7272..aac60d577 100644 --- a/grid_map_filters/src/BufferNormalizerFilter.cpp +++ b/grid_map_filters/src/BufferNormalizerFilter.cpp @@ -9,36 +9,24 @@ #include "grid_map_filters/BufferNormalizerFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -BufferNormalizerFilter::BufferNormalizerFilter() -{ -} +BufferNormalizerFilter::BufferNormalizerFilter() = default; -template -BufferNormalizerFilter::~BufferNormalizerFilter() -{ -} +BufferNormalizerFilter::~BufferNormalizerFilter() = default; -template -bool BufferNormalizerFilter::configure() -{ +bool BufferNormalizerFilter::configure() { return true; } -template -bool BufferNormalizerFilter::update(const T& mapIn, T& mapOut) -{ +bool BufferNormalizerFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; mapOut.convertToDefaultStartIndex(); + return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::BufferNormalizerFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/ColorBlendingFilter.cpp b/grid_map_filters/src/ColorBlendingFilter.cpp index 480d03f3a..82b0eb42f 100644 --- a/grid_map_filters/src/ColorBlendingFilter.cpp +++ b/grid_map_filters/src/ColorBlendingFilter.cpp @@ -6,65 +6,58 @@ * Institute: ETH Zurich, ANYbotics */ -#include +#include "grid_map_filters/ColorBlendingFilter.hpp" -#include -#include +#include #include -#include + +#include using namespace filters; namespace grid_map { -template -ColorBlendingFilter::ColorBlendingFilter() - : opacity_(1.0), - blendMode_(BlendModes::Normal) -{ -} +ColorBlendingFilter::ColorBlendingFilter() : opacity_(1.0), blendMode_(BlendModes::Normal) {} -template -ColorBlendingFilter::~ColorBlendingFilter() -{ -} +ColorBlendingFilter::~ColorBlendingFilter() = default; -template -bool ColorBlendingFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("background_layer"), backgroundLayer_)) { +bool ColorBlendingFilter::configure() { + if (!FilterBase::getParam(std::string("background_layer"), backgroundLayer_)) { ROS_ERROR("Color blending filter did not find parameter `background_layer`."); return false; } ROS_DEBUG("Color blending filter background layer is = %s.", backgroundLayer_.c_str()); - if (!FilterBase < T > ::getParam(std::string("foreground_layer"), foregroundLayer_)) { + if (!FilterBase::getParam(std::string("foreground_layer"), foregroundLayer_)) { ROS_ERROR("Color blending filter did not find parameter `foreground_layer`."); return false; } ROS_DEBUG("Color blending filter foreground layer is = %s.", foregroundLayer_.c_str()); std::string blendMode; - if (!FilterBase < T > ::getParam(std::string("blend_mode"), blendMode)) { + if (!FilterBase::getParam(std::string("blend_mode"), blendMode)) { blendMode = "normal"; } ROS_DEBUG("Color blending filter blend mode is = %s.", blendMode.c_str()); - if (blendMode == "normal") blendMode_ = BlendModes::Normal; - else if (blendMode == "hard_light") blendMode_ = BlendModes::HardLight; - else if (blendMode == "soft_light") blendMode_ = BlendModes::SoftLight; - else { + if (blendMode == "normal") { + blendMode_ = BlendModes::Normal; + } else if (blendMode == "hard_light") { + blendMode_ = BlendModes::HardLight; + } else if (blendMode == "soft_light") { + blendMode_ = BlendModes::SoftLight; + } else { ROS_ERROR("Color blending filter blend mode `%s` does not exist.", blendMode.c_str()); return false; } - if (!FilterBase < T > ::getParam(std::string("opacity"), opacity_)) { + if (!FilterBase::getParam(std::string("opacity"), opacity_)) { ROS_ERROR("Color blending filter did not find parameter `opacity`."); return false; } ROS_DEBUG("Color blending filter opacity is = %f.", opacity_); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Color blending filter did not find parameter `output_layer`."); return false; } @@ -72,9 +65,7 @@ bool ColorBlendingFilter::configure() return true; } -template -bool ColorBlendingFilter::update(const T& mapIn, T& mapOut) -{ +bool ColorBlendingFilter::update(const GridMap& mapIn, GridMap& mapOut) { const auto& background = mapIn[backgroundLayer_]; const auto& foreground = mapIn[foregroundLayer_]; @@ -83,13 +74,15 @@ bool ColorBlendingFilter::update(const T& mapIn, T& mapOut) auto& output = mapOut[outputLayer_]; // For each cell in map. - for (size_t i = 0; i < output.size(); ++i) { + for (Eigen::Index i = 0; i < output.size(); ++i) { if (std::isnan(background(i))) { output(i) = foreground(i); } else if (std::isnan(foreground(i))) { output(i) = background(i); } else { - Eigen::Array3f backgroundColor, foregroundColor, outputColor; + Eigen::Array3f backgroundColor; + Eigen::Array3f foregroundColor; + Eigen::Array3f outputColor; Eigen::Vector3f color; colorValueToVector(background(i), color); backgroundColor = color.array(); @@ -100,8 +93,7 @@ bool ColorBlendingFilter::update(const T& mapIn, T& mapOut) case BlendModes::Normal: outputColor = (1.0 - opacity_) * backgroundColor + opacity_ * foregroundColor; break; - case BlendModes::HardLight: - { + case BlendModes::HardLight: { Eigen::Array3f blendedColor; if (foregroundColor.mean() < 0.5) { blendedColor = 2.0 * backgroundColor * foregroundColor; @@ -116,15 +108,15 @@ bool ColorBlendingFilter::update(const T& mapIn, T& mapOut) break; } - case BlendModes::SoftLight: - { + case BlendModes::SoftLight: { Eigen::Array3f blendedColor; // Photoshop. -// if (foregroundColor.mean() < 0.5) { -// blendedColor = 2.0 * backgroundColor * foregroundColor + backgroundColor.square() * (1.0 - 2.0 * foregroundColor); -// } else { -// blendedColor = 2.0 * backgroundColor * (1.0 - foregroundColor) + backgroundColor.sqrt() * (2.0 * foregroundColor - 1.0); -// } + // if (foregroundColor.mean() < 0.5) { + // blendedColor = 2.0 * backgroundColor * foregroundColor + backgroundColor.square() * (1.0 - 2.0 * foregroundColor); + // } else { + // blendedColor = 2.0 * backgroundColor * (1.0 - foregroundColor) + backgroundColor.sqrt() * (2.0 * foregroundColor + // - 1.0); + // } // Pegtop. blendedColor = ((1.0 - 2.0 * foregroundColor) * backgroundColor.square() + 2.0 * backgroundColor * foregroundColor); if (opacity_ == 1.0) { @@ -144,6 +136,4 @@ bool ColorBlendingFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::ColorBlendingFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/ColorFillFilter.cpp b/grid_map_filters/src/ColorFillFilter.cpp index bc32fb760..0076449bc 100644 --- a/grid_map_filters/src/ColorFillFilter.cpp +++ b/grid_map_filters/src/ColorFillFilter.cpp @@ -6,55 +6,47 @@ * Institute: ETH Zurich, ANYbotics */ -#include - -#include -#include +#include "grid_map_filters/ColorFillFilter.hpp" +#include #include +#include + using namespace filters; namespace grid_map { -template -ColorFillFilter::ColorFillFilter() - : r_(0.0), - g_(0.0), - b_(0.0) -{ -} +ColorFillFilter::ColorFillFilter() : r_(0.0), g_(0.0), b_(0.0) {} -template -ColorFillFilter::~ColorFillFilter() -{ -} +ColorFillFilter::~ColorFillFilter() = default; -template -bool ColorFillFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("red"), r_)) { +bool ColorFillFilter::configure() { + if (!FilterBase::getParam(std::string("red"), r_)) { ROS_ERROR("Color fill filter did not find parameter `red`."); return false; } ROS_DEBUG("Color fill filter red is = %f.", r_); - if (!FilterBase < T > ::getParam(std::string("green"), g_)) { + if (!FilterBase::getParam(std::string("green"), g_)) { ROS_ERROR("Color fill filter did not find parameter `green`."); return false; } ROS_DEBUG("Color fill filter green is = %f.", g_); - if (!FilterBase < T > ::getParam(std::string("blue"), b_)) { + if (!FilterBase::getParam(std::string("blue"), b_)) { ROS_ERROR("Color fill filter did not find parameter `blue`."); return false; } ROS_DEBUG("Color fill filter blue is = %f.", b_); - if (!FilterBase < T > ::getParam(std::string("mask_layer"), maskLayer_)); - ROS_DEBUG("Color fill filter mask_layer = %s.", maskLayer_.c_str()); + if (!FilterBase::getParam(std::string("mask_layer"), maskLayer_)) { + ROS_WARN("Color fill filter did not find parameter `mask_layer`."); + } else { + ROS_DEBUG("Color fill filter mask_layer = %s.", maskLayer_.c_str()); + } - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Color fill filter did not find parameter `output_layer`."); return false; } @@ -62,12 +54,10 @@ bool ColorFillFilter::configure() return true; } -template -bool ColorFillFilter::update(const T& mapIn, T& mapOut) -{ +bool ColorFillFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; const Eigen::Vector3f colorVector(r_, g_, b_); - float colorValue; + float colorValue{NAN}; colorVectorToValue(colorVector, colorValue); if (maskLayer_.empty()) { @@ -79,13 +69,11 @@ bool ColorFillFilter::update(const T& mapIn, T& mapOut) auto& mask = mapOut[maskLayer_]; // For each cell in map. - for (size_t i = 0; i < output.size(); ++i) { + for (Eigen::Index i = 0; i < output.size(); ++i) { output(i) = std::isfinite(mask(i)) ? colorValue : NAN; } } return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::ColorFillFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/ColorMapFilter.cpp b/grid_map_filters/src/ColorMapFilter.cpp index 25b7fca4a..4ad6301a7 100644 --- a/grid_map_filters/src/ColorMapFilter.cpp +++ b/grid_map_filters/src/ColorMapFilter.cpp @@ -6,55 +6,44 @@ * Institute: ETH Zurich, ANYbotics */ -#include - -#include -#include +#include "grid_map_filters/ColorMapFilter.hpp" #include +#include + using namespace filters; namespace grid_map { -template -ColorMapFilter::ColorMapFilter() - : min_(0.0), - max_(1.0) -{ -} +ColorMapFilter::ColorMapFilter() : min_(0.0), max_(1.0) {} -template -ColorMapFilter::~ColorMapFilter() -{ -} +ColorMapFilter::~ColorMapFilter() = default; -template -bool ColorMapFilter::configure() -{ - if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { +bool ColorMapFilter::configure() { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("Color map filter did not find parameter `input_layer`."); return false; } ROS_DEBUG("Color map filter input_layer = %s.", inputLayer_.c_str()); - if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Color map filter did not find parameter `output_layer`."); return false; } ROS_DEBUG("Color map filter output_layer = %s.", outputLayer_.c_str()); - if (!FilterBase::getParam(std::string("min/value"), min_)) { + if (!FilterBase::getParam(std::string("min/value"), min_)) { ROS_ERROR("Color map filter did not find parameter `min/value`."); return false; } - if (!FilterBase::getParam(std::string("max/value"), max_)) { + if (!FilterBase::getParam(std::string("max/value"), max_)) { ROS_ERROR("Color map filter did not find parameter `max/value`."); return false; } std::vector minColor; - if (!FilterBase::getParam(std::string("min/color"), minColor)) { + if (!FilterBase::getParam(std::string("min/color"), minColor)) { ROS_ERROR("Color map filter did not find parameter `min/color`."); return false; } @@ -65,7 +54,7 @@ bool ColorMapFilter::configure() minColor_ << minColor[0], minColor[1], minColor[2]; std::vector maxColor; - if (!FilterBase::getParam(std::string("max/color"), maxColor)) { + if (!FilterBase::getParam(std::string("max/color"), maxColor)) { ROS_ERROR("Color map filter did not find parameter `max/color`."); return false; } @@ -78,11 +67,9 @@ bool ColorMapFilter::configure() return true; } -template -bool ColorMapFilter::update(const T& mapIn, T& mapOut) -{ +bool ColorMapFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; - auto& input = mapIn[inputLayer_]; + const auto& input = mapIn[inputLayer_]; mapOut.add(outputLayer_); auto& output = mapOut[outputLayer_]; @@ -90,8 +77,10 @@ bool ColorMapFilter::update(const T& mapIn, T& mapOut) const Eigen::Vector3f colorRange = maxColor_ - minColor_; // For each cell in map. - for (size_t i = 0; i < output.size(); ++i) { - if (!std::isfinite(input(i))) continue; + for (Eigen::Index i = 0; i < output.size(); ++i) { + if (!std::isfinite(input(i))) { + continue; + } const double value = std::min(std::max(input(i), min_), max_); const double factor = (value - min_) / range; const Eigen::Vector3f color = minColor_ + factor * colorRange; @@ -101,6 +90,4 @@ bool ColorMapFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::ColorMapFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/CurvatureFilter.cpp b/grid_map_filters/src/CurvatureFilter.cpp index ccb9a3d1e..7f5b2c21d 100644 --- a/grid_map_filters/src/CurvatureFilter.cpp +++ b/grid_map_filters/src/CurvatureFilter.cpp @@ -6,37 +6,28 @@ * Institute: ETH Zurich, ANYbotics */ -#include - -#include -#include +#include "grid_map_filters/CurvatureFilter.hpp" #include +#include + using namespace filters; namespace grid_map { -template -CurvatureFilter::CurvatureFilter() -{ -} +CurvatureFilter::CurvatureFilter() = default; -template -CurvatureFilter::~CurvatureFilter() -{ -} +CurvatureFilter::~CurvatureFilter() = default; -template -bool CurvatureFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("input_layer"), inputLayer_)) { +bool CurvatureFilter::configure() { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("Curvature filter did not find parameter `input_layer`."); return false; } ROS_DEBUG("Curvature filter input layer is = %s.", inputLayer_.c_str()); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Curvature filter did not find parameter `output_layer`."); return false; } @@ -45,11 +36,10 @@ bool CurvatureFilter::configure() return true; } -template -bool CurvatureFilter::update(const T& mapIn, T& mapOut) -{ - if (!mapIn.isDefaultStartIndex()) throw std::runtime_error( - "CurvatureFilter cannot be used with grid maps that don't have a default buffer start index."); +bool CurvatureFilter::update(const GridMap& mapIn, GridMap& mapOut) { + if (!mapIn.isDefaultStartIndex()) { + throw std::runtime_error("CurvatureFilter cannot be used with grid maps that don't have a default buffer start index."); + } mapOut = mapIn; mapOut.add(outputLayer_); @@ -57,14 +47,20 @@ bool CurvatureFilter::update(const T& mapIn, T& mapOut) auto& curvature = mapOut[outputLayer_]; const float L2 = mapOut.getResolution() * mapOut.getResolution(); - for (size_t j = 0; j < input.cols(); ++j) { - for (size_t i = 0; i < input.rows(); ++i) { + for (Eigen::Index j{0}; j < input.cols(); ++j) { + for (Eigen::Index i{0}; i < input.rows(); ++i) { // http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/How_Curvature_works/00q90000000t000000/ - if (!std::isfinite(input(i, j))) continue; - float D = ((input(i, j==0 ? j : j-1) + input(i, j==input.cols()-1 ? j : j + 1)) / 2.0 - input(i, j)) / L2; - float E = ((input(i==0 ? i : i-1, j) + input(i==input.rows()-1 ? i : i + 1, j)) / 2.0 - input(i, j)) / L2; - if (!std::isfinite(D)) D = 0.0; - if (!std::isfinite(E)) E = 0.0; + if (!std::isfinite(input(i, j))) { + continue; + } + float D = ((input(i, j == 0 ? j : j - 1) + input(i, j == input.cols() - 1 ? j : j + 1)) / 2.0 - input(i, j)) / L2; + float E = ((input(i == 0 ? i : i - 1, j) + input(i == input.rows() - 1 ? i : i + 1, j)) / 2.0 - input(i, j)) / L2; + if (!std::isfinite(D)) { + D = 0.0; + } + if (!std::isfinite(E)) { + E = 0.0; + } curvature(i, j) = -2.0 * (D + E); } } @@ -72,6 +68,4 @@ bool CurvatureFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::CurvatureFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/DeletionFilter.cpp b/grid_map_filters/src/DeletionFilter.cpp index d916a9f12..4d9e42b1b 100644 --- a/grid_map_filters/src/DeletionFilter.cpp +++ b/grid_map_filters/src/DeletionFilter.cpp @@ -9,27 +9,18 @@ #include "grid_map_filters/DeletionFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -DeletionFilter::DeletionFilter() -{ -} +DeletionFilter::DeletionFilter() = default; -template -DeletionFilter::~DeletionFilter() -{ -} +DeletionFilter::~DeletionFilter() = default; -template -bool DeletionFilter::configure() -{ +bool DeletionFilter::configure() { // Load Parameters - if (!FilterBase::getParam(std::string("layers"), layers_)) { + if (!FilterBase::getParam(std::string("layers"), layers_)) { ROS_ERROR("DeletionFilter did not find parameter 'layers'."); return false; } @@ -37,16 +28,13 @@ bool DeletionFilter::configure() return true; } -template -bool DeletionFilter::update(const T& mapIn, T& mapOut) -{ +bool DeletionFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; for (const auto& layer : layers_) { // Check if layer exists. if (!mapOut.exists(layer)) { - ROS_ERROR("Check your deletion layers! Type %s does not exist.", - layer.c_str()); + ROS_ERROR("Check your deletion layers! Type %s does not exist.", layer.c_str()); continue; } @@ -58,6 +46,4 @@ bool DeletionFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::DeletionFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/DuplicationFilter.cpp b/grid_map_filters/src/DuplicationFilter.cpp index 1244bca67..f5ef2fca5 100644 --- a/grid_map_filters/src/DuplicationFilter.cpp +++ b/grid_map_filters/src/DuplicationFilter.cpp @@ -9,31 +9,22 @@ #include "grid_map_filters/DuplicationFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -DuplicationFilter::DuplicationFilter() -{ -} +DuplicationFilter::DuplicationFilter() = default; -template -DuplicationFilter::~DuplicationFilter() -{ -} +DuplicationFilter::~DuplicationFilter() = default; -template -bool DuplicationFilter::configure() -{ - if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { +bool DuplicationFilter::configure() { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("DuplicationFilter did not find parameter 'input_layer'."); return false; } - if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("DuplicationFilter did not find parameter 'output_layer'."); return false; } @@ -41,14 +32,10 @@ bool DuplicationFilter::configure() return true; } -template -bool DuplicationFilter::update(const T& mapIn, T& mapOut) -{ +bool DuplicationFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; mapOut.add(outputLayer_, mapIn[inputLayer_]); return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::DuplicationFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/LightIntensityFilter.cpp b/grid_map_filters/src/LightIntensityFilter.cpp index f7afb6568..df676a61f 100644 --- a/grid_map_filters/src/LightIntensityFilter.cpp +++ b/grid_map_filters/src/LightIntensityFilter.cpp @@ -6,44 +6,35 @@ * Institute: ETH Zurich, ANYbotics */ -#include - -#include -#include +#include "grid_map_filters/LightIntensityFilter.hpp" #include +#include + using namespace filters; namespace grid_map { -template -LightIntensityFilter::LightIntensityFilter() -{ -} +LightIntensityFilter::LightIntensityFilter() = default; -template -LightIntensityFilter::~LightIntensityFilter() -{ -} +LightIntensityFilter::~LightIntensityFilter() = default; -template -bool LightIntensityFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("input_layers_prefix"), inputLayersPrefix_)) { +bool LightIntensityFilter::configure() { + if (!FilterBase::getParam(std::string("input_layers_prefix"), inputLayersPrefix_)) { ROS_ERROR("Light intensity filter did not find parameter `input_layers_prefix`."); return false; } ROS_DEBUG("Light intensity filter input layers prefix is = %s.", inputLayersPrefix_.c_str()); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Light intensity filter did not find parameter `output_layer`."); return false; } ROS_DEBUG("Light intensity filter output_layer = %s.", outputLayer_.c_str()); std::vector lightDirection; - if (!FilterBase < T > ::getParam(std::string("light_direction"), lightDirection)) { + if (!FilterBase::getParam(std::string("light_direction"), lightDirection)) { ROS_ERROR("Light intensity filter did not find parameter `light_direction`."); return false; } @@ -58,9 +49,7 @@ bool LightIntensityFilter::configure() return true; } -template -bool LightIntensityFilter::update(const T& mapIn, T& mapOut) -{ +bool LightIntensityFilter::update(const GridMap& mapIn, GridMap& mapOut) { const auto& normalX = mapIn[inputLayersPrefix_ + "x"]; const auto& normalY = mapIn[inputLayersPrefix_ + "y"]; const auto& normalZ = mapIn[inputLayersPrefix_ + "z"]; @@ -70,7 +59,7 @@ bool LightIntensityFilter::update(const T& mapIn, T& mapOut) auto& color = mapOut[outputLayer_]; // For each cell in map. - for (size_t i = 0; i < color.size(); ++i) { + for (Eigen::Index i = 0; i < color.size(); ++i) { if (!std::isfinite(normalZ(i))) { color(i) = NAN; continue; @@ -84,6 +73,4 @@ bool LightIntensityFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::LightIntensityFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/MathExpressionFilter.cpp b/grid_map_filters/src/MathExpressionFilter.cpp index 961af417a..faac76bad 100644 --- a/grid_map_filters/src/MathExpressionFilter.cpp +++ b/grid_map_filters/src/MathExpressionFilter.cpp @@ -9,43 +9,32 @@ #include "grid_map_filters/MathExpressionFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -MathExpressionFilter::MathExpressionFilter() -{ -} +MathExpressionFilter::MathExpressionFilter() = default; -template -MathExpressionFilter::~MathExpressionFilter() -{ -} +MathExpressionFilter::~MathExpressionFilter() = default; -template -bool MathExpressionFilter::configure() -{ - if (!FilterBase::getParam(std::string("expression"), expression_)) { +bool MathExpressionFilter::configure() { + if (!FilterBase::getParam(std::string("expression"), expression_)) { ROS_ERROR("MathExpressionFilter did not find parameter 'expression'."); return false; } - if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("MathExpressionFilter did not find parameter 'output_layer'."); return false; } // TODO Can we make caching work with changing shared variable? -// parser_.setCacheExpressions(true); + // parser_.setCacheExpressions(true); return true; } -template -bool MathExpressionFilter::update(const T& mapIn, T& mapOut) -{ +bool MathExpressionFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; for (const auto& layer : mapOut.getLayers()) { parser_.var(layer).setShared(mapOut[layer]); @@ -55,6 +44,4 @@ bool MathExpressionFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::MathExpressionFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/MeanInRadiusFilter.cpp b/grid_map_filters/src/MeanInRadiusFilter.cpp index 956f82cb0..b06257fcc 100644 --- a/grid_map_filters/src/MeanInRadiusFilter.cpp +++ b/grid_map_filters/src/MeanInRadiusFilter.cpp @@ -8,28 +8,19 @@ #include "grid_map_filters/MeanInRadiusFilter.hpp" +#include #include -#include using namespace filters; namespace grid_map { -template -MeanInRadiusFilter::MeanInRadiusFilter() - : radius_(0.0) -{ -} +MeanInRadiusFilter::MeanInRadiusFilter() : radius_(0.0) {} -template -MeanInRadiusFilter::~MeanInRadiusFilter() -{ -} +MeanInRadiusFilter::~MeanInRadiusFilter() = default; -template -bool MeanInRadiusFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("radius"), radius_)) { +bool MeanInRadiusFilter::configure() { + if (!FilterBase::getParam(std::string("radius"), radius_)) { ROS_ERROR("MeanInRadius filter did not find parameter `radius`."); return false; } @@ -41,14 +32,14 @@ bool MeanInRadiusFilter::configure() ROS_DEBUG("Radius = %f.", radius_); - if (!FilterBase < T > ::getParam(std::string("input_layer"), inputLayer_)) { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("MeanInRadius filter did not find parameter `input_layer`."); return false; } ROS_DEBUG("MeanInRadius input layer is = %s.", inputLayer_.c_str()); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("MeanInRadius filter did not find parameter `output_layer`."); return false; } @@ -57,18 +48,15 @@ bool MeanInRadiusFilter::configure() return true; } -template -bool MeanInRadiusFilter::update(const T& mapIn, T& mapOut) -{ +bool MeanInRadiusFilter::update(const GridMap& mapIn, GridMap& mapOut) { // Add new layers to the elevation map. mapOut = mapIn; mapOut.add(outputLayer_); - double value; + double value{NAN}; // First iteration through the elevation map. for (grid_map::GridMapIterator iterator(mapOut); !iterator.isPastEnd(); ++iterator) { - double valueSum = 0.0; int counter = 0; // Requested position (center) of circle in map. @@ -76,22 +64,21 @@ bool MeanInRadiusFilter::update(const T& mapIn, T& mapOut) mapOut.getPosition(*iterator, center); // Find the mean in a circle around the center - for (grid_map::CircleIterator submapIterator(mapOut, center, radius_); !submapIterator.isPastEnd(); - ++submapIterator) { - if (!mapOut.isValid(*submapIterator, inputLayer_)) + for (grid_map::CircleIterator submapIterator(mapOut, center, radius_); !submapIterator.isPastEnd(); ++submapIterator) { + if (!mapOut.isValid(*submapIterator, inputLayer_)) { continue; + } value = mapOut.at(inputLayer_, *submapIterator); valueSum += value; counter++; } - if (counter != 0) + if (counter != 0) { mapOut.at(outputLayer_, *iterator) = valueSum / counter; + } } return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::MeanInRadiusFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/MedianFillFilter.cpp b/grid_map_filters/src/MedianFillFilter.cpp index 3431789fe..6ee643477 100644 --- a/grid_map_filters/src/MedianFillFilter.cpp +++ b/grid_map_filters/src/MedianFillFilter.cpp @@ -8,25 +8,26 @@ #include "grid_map_filters/MedianFillFilter.hpp" -// Wrap as ROS Filter -#include +#include // Grid Map #include +#include +#include +#include + using namespace filters; namespace grid_map { -template -MedianFillFilter::MedianFillFilter() : fillHoleRadius_(0.05), filterExistingValues_(false) {} +MedianFillFilter::MedianFillFilter() + : fillHoleRadius_(0.05), existingValueRadius_(0.0), filterExistingValues_(false), numErodeDilationIterations_(4), debug_(false) {} -template -MedianFillFilter::~MedianFillFilter() = default; +MedianFillFilter::~MedianFillFilter() = default; -template -bool MedianFillFilter::configure() { - if (!FilterBase::getParam(std::string("fill_hole_radius"), fillHoleRadius_)) { +bool MedianFillFilter::configure() { + if (!FilterBase::getParam(std::string("fill_hole_radius"), fillHoleRadius_)) { ROS_ERROR("Median filter did not find parameter fill_hole_radius."); return false; } @@ -38,20 +39,25 @@ bool MedianFillFilter::configure() { ROS_DEBUG("fill_hole_radius = %f.", fillHoleRadius_); - if (!FilterBase::getParam(std::string("filter_existing_values"), filterExistingValues_)) { + if (!FilterBase::getParam(std::string("filter_existing_values"), filterExistingValues_)) { ROS_INFO("Median filter did not find parameter filter_existing_values. Not filtering existing values."); filterExistingValues_ = false; } ROS_DEBUG("Filter_existing_values = %s.", filterExistingValues_ ? "true" : "false"); - if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("Median filter did not find parameter `input_layer`."); return false; } + if (!FilterBase::getParam(std::string("num_erode_dilation_iterations"), numErodeDilationIterations_)) { + ROS_ERROR("Median filter did not find parameter `num_erode_dilation_iterations`."); + return false; + } + if (filterExistingValues_) { - if (!FilterBase::getParam(std::string("existing_value_radius"), existingValueRadius_)) { + if (!FilterBase::getParam(std::string("existing_value_radius"), existingValueRadius_)) { ROS_ERROR("Median filter did not find parameter existing_value_radius."); return false; } @@ -66,92 +72,176 @@ bool MedianFillFilter::configure() { ROS_DEBUG("Median input layer is = %s.", inputLayer_.c_str()); - if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Median filter did not find parameter `output_layer`."); return false; } ROS_DEBUG("Median output layer = %s.", outputLayer_.c_str()); - return true; -} - -template -float MedianFillFilter::getMedian(Eigen::Ref inputMap, const grid_map::Index& centerIndex, - const size_t radiusInPixels, const grid_map::Size bufferSize) { - // Bound the median window to the GridMap boundaries. Calculate the neighbour patch. - grid_map::Index topLeftIndex = centerIndex - grid_map::Index(radiusInPixels, radiusInPixels); - grid_map::Index bottomRightIndex = centerIndex + grid_map::Index(radiusInPixels, radiusInPixels); - grid_map::boundIndexToRange(topLeftIndex, bufferSize); - grid_map::boundIndexToRange(bottomRightIndex, bufferSize); - const grid_map::Index neighbourPatchSize = bottomRightIndex - topLeftIndex + grid_map::Index{1, 1}; + if (!FilterBase::getParam(std::string("fill_mask_layer"), fillMaskLayer_)) { + ROS_ERROR("Median filter did not find parameter `fill_mask_layer`."); + return false; + } - // Extract local neighbourhood. - const auto& neighbourhood = inputMap.block(topLeftIndex(0), topLeftIndex(1), neighbourPatchSize(0), neighbourPatchSize(1)); + ROS_DEBUG("Median fill mask layer = %s.", fillMaskLayer_.c_str()); - const size_t cols = neighbourhood.cols(); + if (!FilterBase::getParam(std::string("debug"), debug_)) { + ROS_INFO("Median filter did not find parameter debug. Disabling debug output."); + debug_ = false; + } - std::vector cellValues; - cellValues.reserve(neighbourhood.rows() * neighbourhood.cols()); + ROS_DEBUG("Debug mode= %s.", debug_ ? "true" : "false"); - for (size_t row = 0; row < neighbourhood.rows(); ++row) { - const auto& currentRow = neighbourhood.row(row); - for (size_t col = 0; col < cols; ++col) { - const float& cellValue = currentRow[col]; - if (std::isfinite(cellValue)) { // Calculate the median of the finite neighbours. - cellValues.emplace_back(cellValue); - } - } + if (debug_ && !FilterBase::getParam(std::string("debug_infill_mask_layer"), debugInfillMaskLayer_)) { + ROS_ERROR("Median filter did not find parameter `debug_infill_mask_layer`."); + return false; } - if (cellValues.empty()) { - return static_cast(NAN); - } else { // Compute the median of the finite values in the neighbourhood. - std::nth_element(cellValues.begin(), cellValues.begin() + cellValues.size() / 2, cellValues.end()); - return cellValues[cellValues.size() / 2]; - } + ROS_DEBUG("Median debug infill mask layer = %s.", debugInfillMaskLayer_.c_str()); + + return true; } -template -bool MedianFillFilter::update(const T& mapIn, T& mapOut) { +bool MedianFillFilter::update(const GridMap& mapIn, GridMap& mapOut) { // Copy input map and add new layer to it. mapOut = mapIn; if (!mapOut.exists(outputLayer_)) { mapOut.add(outputLayer_); } + mapOut.convertToDefaultStartIndex(); - const grid_map::Matrix inputMap = mapOut[inputLayer_]; - grid_map::Matrix& outputMap = mapOut[outputLayer_]; - grid_map::Matrix& varianceMap = mapOut["variance"]; - const auto radiusInPixels = static_cast(fillHoleRadius_ / mapIn.getResolution()); - const auto existingValueRadiusInPixels = static_cast(existingValueRadius_ / mapIn.getResolution()); - const grid_map::Index& bufferSize = mapOut.getSize(); + // Avoid hash map lookups afterwards. I.e, get data matrices as references. + Matrix inputMap{mapOut[inputLayer_]}; // copy by value to do filtering first. + Matrix& outputMap{mapOut[outputLayer_]}; + + // Check if mask is already computed from a previous iteration. + Eigen::MatrixXf shouldFill; + if (std::find(mapOut.getLayers().begin(), mapOut.getLayers().end(), fillMaskLayer_) == mapOut.getLayers().end()) { + shouldFill = computeAndAddFillMask(inputMap, mapOut); + } else { // The mask already exists, retrieve it from a previous iteration. + shouldFill = mapOut[fillMaskLayer_]; + } - unsigned int numNans = 0; + const size_t radiusInPixels{static_cast(fillHoleRadius_ / mapIn.getResolution())}; + const size_t existingValueRadiusInPixels{static_cast(existingValueRadius_ / mapIn.getResolution())}; + const Index& bufferSize{mapOut.getSize()}; + unsigned int numNans{0u}; // Iterate through the entire GridMap and update NaN values with the median. for (GridMapIterator iterator(mapOut); !iterator.isPastEnd(); ++iterator) { const Index index(*iterator); - const auto& inputValue = inputMap(index(0), index(1)); - auto& outputValue = outputMap(index(0), index(1)); - auto& variance = varianceMap(index(0), index(1)); - if (not std::isfinite(inputValue)) { // Fill the NaN input value with the median. + const auto& inputValue{inputMap(index(0), index(1))}; + const float& shouldFillThisCell{shouldFill(index(0), index(1))}; + auto& outputValue{outputMap(index(0), index(1))}; + if (!std::isfinite(inputValue) && (shouldFillThisCell != 0.0f)) { // Fill the NaN input value with the median. outputValue = getMedian(inputMap, index, radiusInPixels, bufferSize); numNans++; - } else if (filterExistingValues_) { // Value is already finite. Optionally add some filtering. + } else if (filterExistingValues_ && (shouldFillThisCell != 0.0f)) { // Value is already finite. Optionally add some filtering. outputValue = getMedian(inputMap, index, existingValueRadiusInPixels, bufferSize); } else { // Dont do any filtering, just take the input value. outputValue = inputValue; } } ROS_DEBUG_STREAM("Median fill filter " << this->getName() << " observed " << numNans << " Nans in input layer!"); + // By removing all basic layers the selected layer will always be visualized, otherwise isValid will also check for the basic layers + // and hide infilled values where the corresponding basic layers are still NAN. mapOut.setBasicLayers({}); return true; } -} // namespace grid_map +float MedianFillFilter::getMedian(Eigen::Ref inputMap, const Index& centerIndex, const size_t radiusInPixels, + const Size bufferSize) { + // Bound the median window to the GridMap boundaries. Calculate the neighbour patch. + Index topLeftIndex{centerIndex - Index(radiusInPixels, radiusInPixels)}; + Index bottomRightIndex{centerIndex + Index(radiusInPixels, radiusInPixels)}; + boundIndexToRange(topLeftIndex, bufferSize); + boundIndexToRange(bottomRightIndex, bufferSize); + const Index neighbourPatchSize{bottomRightIndex - topLeftIndex + Index{1, 1}}; + + // Extract local neighbourhood. + const auto& neighbourhood{inputMap.block(topLeftIndex(0), topLeftIndex(1), neighbourPatchSize(0), neighbourPatchSize(1))}; + + const size_t cols{static_cast(neighbourhood.cols())}; -// Explicitly define the specialization for GridMap to have the filter implementation available for testing. -template class grid_map::MedianFillFilter; -// Export the filter. -PLUGINLIB_EXPORT_CLASS(grid_map::MedianFillFilter, filters::FilterBase) \ No newline at end of file + std::vector cellValues; + cellValues.reserve(neighbourhood.rows() * neighbourhood.cols()); + + for (Eigen::Index row = 0; row < neighbourhood.rows(); ++row) { + const auto& currentRow{neighbourhood.row(row)}; + for (size_t col = 0; col < cols; ++col) { + const float& cellValue{currentRow[col]}; + if (std::isfinite(cellValue)) { // Calculate the median of the finite neighbours. + cellValues.emplace_back(cellValue); + } + } + } + + if (cellValues.empty()) { + return std::numeric_limits::quiet_NaN(); + } else { // Compute the median of the finite values in the neighbourhood. + std::nth_element(cellValues.begin(), cellValues.begin() + cellValues.size() / 2, cellValues.end()); + return cellValues[cellValues.size() / 2]; + } +} + +Eigen::MatrixXf MedianFillFilter::computeAndAddFillMask(const Eigen::MatrixXf& inputMap, GridMap& mapOut) { + Eigen::MatrixXf shouldFill; + // Precompute mask of valid height values + using MaskMatrix = Eigen::Matrix; + const MaskMatrix isValid{inputMap.array().unaryExpr([&](float v) { return std::isfinite(v); })}; + + // Remove sparse valid values and fill holes. + cv::Mat isValidCV; + cv::eigen2cv(isValid, isValidCV); + cv::Mat_ isValidOutlierRemoved{cleanedMask(isValidCV)}; + cv::Mat shouldFillCV{fillHoles(isValidOutlierRemoved, numErodeDilationIterations_)}; + + // Outlier removed mask to eigen. + if (debug_) { + addCvMatAsLayer(mapOut, isValidOutlierRemoved, debugInfillMaskLayer_); + } + + // Convert to eigen and add to the map. + cv::cv2eigen(shouldFillCV, shouldFill); + mapOut.add(fillMaskLayer_, shouldFill); + + return shouldFill; +} + +cv::Mat_ MedianFillFilter::cleanedMask(const cv::Mat_& inputMask) { + auto element{cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1, 1))}; + + cv::Mat_ cleanedInputMask(inputMask.size(), false); + + // Erode then dilate to remove sparse points + cv::dilate(inputMask, cleanedInputMask, element); + cv::erode(cleanedInputMask, cleanedInputMask, element); + cv::erode(inputMask, cleanedInputMask, element); + cv::dilate(cleanedInputMask, cleanedInputMask, element); + + return cleanedInputMask; +} + +cv::Mat_ MedianFillFilter::fillHoles(const cv::Mat_& isValidMask, const size_t numDilationClosingIterations) { + auto element{cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1, 1))}; + cv::Mat_ holesFilledMask(isValidMask.size(), false); + // Remove holes in the mask by morphological closing. + cv::dilate(isValidMask, holesFilledMask, element); + for (size_t iteration = 1; iteration < numDilationClosingIterations; iteration++) { + cv::dilate(holesFilledMask, holesFilledMask, element); + } + for (size_t iteration = 0; iteration < numDilationClosingIterations; iteration++) { + cv::erode(holesFilledMask, holesFilledMask, element); + } + + return holesFilledMask; +} + +void MedianFillFilter::addCvMatAsLayer(GridMap& gridMap, const cv::Mat& cvLayer, const std::string& layerName) { + Eigen::MatrixXf tmpEigenMatrix; + cv::cv2eigen(cvLayer, tmpEigenMatrix); + gridMap.add(layerName, tmpEigenMatrix); +} + +} // namespace grid_map diff --git a/grid_map_filters/src/MinInRadiusFilter.cpp b/grid_map_filters/src/MinInRadiusFilter.cpp index 5934c1460..0967217a9 100644 --- a/grid_map_filters/src/MinInRadiusFilter.cpp +++ b/grid_map_filters/src/MinInRadiusFilter.cpp @@ -8,28 +8,19 @@ #include "grid_map_filters/MinInRadiusFilter.hpp" +#include #include -#include using namespace filters; namespace grid_map { -template -MinInRadiusFilter::MinInRadiusFilter() - : radius_(0.0) -{ -} +MinInRadiusFilter::MinInRadiusFilter() : radius_(0.0) {} -template -MinInRadiusFilter::~MinInRadiusFilter() -{ -} +MinInRadiusFilter::~MinInRadiusFilter() = default; -template -bool MinInRadiusFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("radius"), radius_)) { +bool MinInRadiusFilter::configure() { + if (!FilterBase::getParam(std::string("radius"), radius_)) { ROS_ERROR("MinInRadius filter did not find parameter `radius`."); return false; } @@ -40,14 +31,14 @@ bool MinInRadiusFilter::configure() } ROS_DEBUG("Radius = %f.", radius_); - if (!FilterBase < T > ::getParam(std::string("input_layer"), inputLayer_)) { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("MinInRadius filter did not find parameter `input_layer`."); return false; } ROS_DEBUG("MinInRadius input layer is = %s.", inputLayer_.c_str()); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Step filter did not find parameter `output_layer`."); return false; } @@ -56,19 +47,18 @@ bool MinInRadiusFilter::configure() return true; } -template -bool MinInRadiusFilter::update(const T& mapIn, T& mapOut) -{ +bool MinInRadiusFilter::update(const GridMap& mapIn, GridMap& mapOut) { // Add new layer to the elevation map. mapOut = mapIn; mapOut.add(outputLayer_); - double value; + double value{NAN}; // First iteration through the elevation map. for (grid_map::GridMapIterator iterator(mapOut); !iterator.isPastEnd(); ++iterator) { - if (!mapOut.isValid(*iterator, inputLayer_)) + if (!mapOut.isValid(*iterator, inputLayer_)) { continue; + } value = mapOut.at(inputLayer_, *iterator); double valueMin = 0.0; @@ -78,10 +68,10 @@ bool MinInRadiusFilter::update(const T& mapIn, T& mapOut) // Get minimal value in the circular window. bool init = false; - for (grid_map::CircleIterator submapIterator(mapOut, center, radius_); !submapIterator.isPastEnd(); - ++submapIterator) { - if (!mapOut.isValid(*submapIterator, inputLayer_)) + for (grid_map::CircleIterator submapIterator(mapOut, center, radius_); !submapIterator.isPastEnd(); ++submapIterator) { + if (!mapOut.isValid(*submapIterator, inputLayer_)) { continue; + } value = mapOut.at(inputLayer_, *submapIterator); if (!init) { @@ -89,17 +79,17 @@ bool MinInRadiusFilter::update(const T& mapIn, T& mapOut) init = true; continue; } - if (value < valueMin) + if (value < valueMin) { valueMin = value; + } } - if (init) + if (init) { mapOut.at(outputLayer_, *iterator) = valueMin; + } } return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::MinInRadiusFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/MockFilter.cpp b/grid_map_filters/src/MockFilter.cpp index 539fdc152..7643e6f62 100644 --- a/grid_map_filters/src/MockFilter.cpp +++ b/grid_map_filters/src/MockFilter.cpp @@ -6,54 +6,42 @@ * Institute: ETH Zurich, ANYbotics */ -#include -#include -#include +#include "grid_map_filters/MockFilter.hpp" + #include +#include -#include "grid_map_filters/MockFilter.hpp" +#include using namespace filters; namespace grid_map { -template -MockFilter::MockFilter() -{ -} +MockFilter::MockFilter() = default; -template -MockFilter::~MockFilter() -{ -} +MockFilter::~MockFilter() = default; -template -bool MockFilter::configure() -{ - if (!FilterBase::getParam(std::string("processing_time"), processingTime_)) { +bool MockFilter::configure() { + if (!FilterBase::getParam(std::string("processing_time"), processingTime_)) { ROS_ERROR("MockFilter did not find parameter 'processing_time'."); return false; } - if (!FilterBase::getParam(std::string("print_name"), printName_)) { - ROS_ERROR("MockFilter did not find parameter 'print_name'."); - return false; + if (!FilterBase::getParam(std::string("print_name"), printName_)) { + ROS_INFO("MockFilter did not find parameter 'print_name'. Not printing the name. "); + printName_ = false; } return true; } -template -bool MockFilter::update(const T& mapIn, T& mapOut) -{ +bool MockFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; - if(printName_){ - ROS_INFO_STREAM(this->getName()<<": update()"); + if (printName_) { + ROS_INFO_STREAM(this->getName() << ": update()"); } std::this_thread::sleep_for(std::chrono::milliseconds(processingTime_)); return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::MockFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/NormalColorMapFilter.cpp b/grid_map_filters/src/NormalColorMapFilter.cpp index 3a729735e..9a8e92b16 100644 --- a/grid_map_filters/src/NormalColorMapFilter.cpp +++ b/grid_map_filters/src/NormalColorMapFilter.cpp @@ -6,37 +6,28 @@ * Institute: ETH Zurich, ANYbotics */ -#include - -#include -#include +#include "grid_map_filters/NormalColorMapFilter.hpp" #include +#include + using namespace filters; namespace grid_map { -template -NormalColorMapFilter::NormalColorMapFilter() -{ -} +NormalColorMapFilter::NormalColorMapFilter() = default; -template -NormalColorMapFilter::~NormalColorMapFilter() -{ -} +NormalColorMapFilter::~NormalColorMapFilter() = default; -template -bool NormalColorMapFilter::configure() -{ - if (!FilterBase < T > ::getParam(std::string("input_layers_prefix"), inputLayersPrefix_)) { +bool NormalColorMapFilter::configure() { + if (!FilterBase::getParam(std::string("input_layers_prefix"), inputLayersPrefix_)) { ROS_ERROR("Normal color map filter did not find parameter `input_layers_prefix`."); return false; } ROS_DEBUG("Normal color map filter input layers prefix is = %s.", inputLayersPrefix_.c_str()); - if (!FilterBase < T > ::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("Normal color map filter did not find parameter `output_layer`."); return false; } @@ -44,9 +35,7 @@ bool NormalColorMapFilter::configure() return true; } -template -bool NormalColorMapFilter::update(const T& mapIn, T& mapOut) -{ +bool NormalColorMapFilter::update(const GridMap& mapIn, GridMap& mapOut) { const auto& normalX = mapIn[inputLayersPrefix_ + "x"]; const auto& normalY = mapIn[inputLayersPrefix_ + "y"]; const auto& normalZ = mapIn[inputLayersPrefix_ + "z"]; @@ -60,16 +49,12 @@ bool NormalColorMapFilter::update(const T& mapIn, T& mapOut) // Z: 0 to 1 : Blue: 128 to 255 // For each cell in map. - for (size_t i = 0; i < color.size(); ++i) { - const Eigen::Vector3f colorVector((normalX(i) + 1.0) / 2.0, - (normalY(i) + 1.0) / 2.0, - (normalZ(i) / 2.0) + 0.5); + for (Eigen::Index i = 0; i < color.size(); ++i) { + const Eigen::Vector3f colorVector((normalX(i) + 1.0) / 2.0, (normalY(i) + 1.0) / 2.0, (normalZ(i) / 2.0) + 0.5); colorVectorToValue(colorVector, color(i)); } return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::NormalColorMapFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/NormalVectorsFilter.cpp b/grid_map_filters/src/NormalVectorsFilter.cpp index f43b19a89..f6cdce240 100644 --- a/grid_map_filters/src/NormalVectorsFilter.cpp +++ b/grid_map_filters/src/NormalVectorsFilter.cpp @@ -6,32 +6,29 @@ * Institute: ETH Zurich, ANYbotics */ -#include -#include +#include "grid_map_filters/NormalVectorsFilter.hpp" + +#include #include #include #include #include +#include #include -#include - namespace grid_map { -template -NormalVectorsFilter::NormalVectorsFilter() +NormalVectorsFilter::NormalVectorsFilter() : method_(Method::RasterSerial), estimationRadius_(0.0), parallelizationEnabled_(false), threadCount_(1), gridMapResolution_(0.02) {} -template -NormalVectorsFilter::~NormalVectorsFilter() = default; +NormalVectorsFilter::~NormalVectorsFilter() = default; -template -bool NormalVectorsFilter::configure() { +bool NormalVectorsFilter::configure() { // Read which algorithm is chosen: area or raster. std::string algorithm; - if (!filters::FilterBase::getParam(std::string("algorithm"), algorithm)) { + if (!FilterBase::getParam(std::string("algorithm"), algorithm)) { ROS_WARN("Could not find the parameter: `algorithm`. Setting to default value: 'area'."); // Default value. algorithm = "area"; @@ -41,7 +38,7 @@ bool NormalVectorsFilter::configure() { // on purpose it throws unwanted errors. if (algorithm != "raster") { // Read radius, if found, its value will be used for area method. If radius parameter is not found, raster method will be used. - if (!filters::FilterBase::getParam(std::string("radius"), estimationRadius_)) { + if (!FilterBase::getParam(std::string("radius"), estimationRadius_)) { ROS_WARN("Could not find the parameter: `radius`. Switching to raster method."); algorithm = "raster"; } @@ -55,7 +52,7 @@ bool NormalVectorsFilter::configure() { // Read parallelization_enabled to decide whether parallelization has to be used, if parameter is not found an error is thrown and // the false default value will be used. - if (!filters::FilterBase::getParam(std::string("parallelization_enabled"), parallelizationEnabled_)) { + if (!FilterBase::getParam(std::string("parallelization_enabled"), parallelizationEnabled_)) { ROS_WARN("Could not find the parameter: `parallelization_enabled`. Setting to default value: 'false'."); parallelizationEnabled_ = false; } @@ -63,7 +60,7 @@ bool NormalVectorsFilter::configure() { // Read thread_number to set the number of threads to be used if parallelization is enebled, // if parameter is not found an error is thrown and the default is to set it to automatic. - if (!filters::FilterBase::getParam(std::string("thread_number"), threadCount_)) { + if (!FilterBase::getParam(std::string("thread_number"), threadCount_)) { ROS_WARN("Could not find the parameter: `thread_number`. Setting to default value: 'automatic'."); threadCount_ = tbb::task_scheduler_init::automatic; } @@ -94,7 +91,7 @@ bool NormalVectorsFilter::configure() { // Read normal_vector_positive_axis, to define normal vector positive direction. std::string normalVectorPositiveAxis; - if (!filters::FilterBase::getParam(std::string("normal_vector_positive_axis"), normalVectorPositiveAxis)) { + if (!FilterBase::getParam(std::string("normal_vector_positive_axis"), normalVectorPositiveAxis)) { ROS_ERROR("Normal vectors filter did not find parameter `normal_vector_positive_axis`."); return false; } @@ -110,14 +107,14 @@ bool NormalVectorsFilter::configure() { } // Read input_layer, to define input grid map layer. - if (!filters::FilterBase::getParam(std::string("input_layer"), inputLayer_)) { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("Normal vectors filter did not find parameter `input_layer`."); return false; } ROS_DEBUG("Normal vectors filter input layer is = %s.", inputLayer_.c_str()); // Read output_layers_prefix, to define output grid map layers prefix. - if (!filters::FilterBase::getParam(std::string("output_layers_prefix"), outputLayersPrefix_)) { + if (!FilterBase::getParam(std::string("output_layers_prefix"), outputLayersPrefix_)) { ROS_ERROR("Normal vectors filter did not find parameter `output_layers_prefix`."); return false; } @@ -127,8 +124,7 @@ bool NormalVectorsFilter::configure() { return true; } -template -bool NormalVectorsFilter::update(const T& mapIn, T& mapOut) { +bool NormalVectorsFilter::update(const GridMap& mapIn, GridMap& mapOut) { std::vector normalVectorsLayers; normalVectorsLayers.push_back(outputLayersPrefix_ + "x"); normalVectorsLayers.push_back(outputLayersPrefix_ + "y"); @@ -157,8 +153,7 @@ bool NormalVectorsFilter::update(const T& mapIn, T& mapOut) { } // SVD Area based methods. -template -void NormalVectorsFilter::computeWithAreaSerial(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { +void NormalVectorsFilter::computeWithAreaSerial(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { const double start = ros::Time::now().toSec(); // For each cell in submap. @@ -174,8 +169,7 @@ void NormalVectorsFilter::computeWithAreaSerial(GridMap& map, const std::stri ROS_DEBUG_THROTTLE(2.0, "NORMAL COMPUTATION TIME = %f", (end - start)); } -template -void NormalVectorsFilter::computeWithAreaParallel(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { +void NormalVectorsFilter::computeWithAreaParallel(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { const double start = ros::Time::now().toSec(); grid_map::Size gridMapSize = map.getSize(); @@ -198,9 +192,8 @@ void NormalVectorsFilter::computeWithAreaParallel(GridMap& map, const std::st ROS_DEBUG_THROTTLE(2.0, "NORMAL COMPUTATION TIME = %f", (end - start)); } -template -void NormalVectorsFilter::areaSingleNormalComputation(GridMap& map, const std::string& inputLayer, - const std::string& outputLayersPrefix, const grid_map::Index& index) { +void NormalVectorsFilter::areaSingleNormalComputation(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix, + const grid_map::Index& index) { // Requested position (center) of circle in map. Position center; map.getPosition(index, center); @@ -257,8 +250,7 @@ void NormalVectorsFilter::areaSingleNormalComputation(GridMap& map, const std map.at(outputLayersPrefix + "z", index) = unitaryNormalVector.z(); } // Raster based methods. -template -void NormalVectorsFilter::computeWithRasterSerial(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { +void NormalVectorsFilter::computeWithRasterSerial(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { // Inspiration for algorithm: http://www.flipcode.com/archives/Calculating_Vertex_Normals_for_Height_Maps.shtml const double start = ros::Time::now().toSec(); @@ -280,8 +272,7 @@ void NormalVectorsFilter::computeWithRasterSerial(GridMap& map, const std::st ROS_DEBUG_THROTTLE(2.0, "NORMAL COMPUTATION TIME = %f", (end - start)); } -template -void NormalVectorsFilter::computeWithRasterParallel(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { +void NormalVectorsFilter::computeWithRasterParallel(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix) { const double start = ros::Time::now().toSec(); const grid_map::Size gridMapSize = map.getSize(); @@ -291,7 +282,7 @@ void NormalVectorsFilter::computeWithRasterParallel(GridMap& map, const std:: // Height and width of submap. Submap is Map without the outermost line of cells, no need to check if index is inside. const Index submapStartIndex(1, 1); const Index submapBufferSize(gridMapSize(0) - 2, gridMapSize(1) - 2); - if (submapBufferSize(1)!=0) { + if (submapBufferSize(1) != 0) { // Set number of thread to use for parallel programming std::unique_ptr TBBInitPtr; if (threadCount_ != -1) { @@ -310,9 +301,8 @@ void NormalVectorsFilter::computeWithRasterParallel(GridMap& map, const std:: ROS_DEBUG_THROTTLE(2.0, "NORMAL COMPUTATION TIME = %f", (end - start)); } -template -void NormalVectorsFilter::rasterSingleNormalComputation(GridMap& map, const std::string& outputLayersPrefix, - const grid_map::Matrix& dataMap, const grid_map::Index& index) { +void NormalVectorsFilter::rasterSingleNormalComputation(GridMap& map, const std::string& outputLayersPrefix, + const grid_map::Matrix& dataMap, const grid_map::Index& index) { // Inspiration for algorithm: // http://www.flipcode.com/archives/Calculating_Vertex_Normals_for_Height_Maps.shtml const double centralCell = dataMap(index(0), index(1)); @@ -331,14 +321,14 @@ void NormalVectorsFilter::rasterSingleNormalComputation(GridMap& map, const s // Each configuration will have a different number associated with it and then use a switch. const int configurationDirX = 1 * static_cast(std::isfinite(topCell)) + 2 * static_cast(std::isfinite(centralCell)) + - 4 * static_cast(std::isfinite(bottomCell)); + 4 * static_cast(std::isfinite(bottomCell)); const int configurationDirY = 1 * static_cast(std::isfinite(leftCell)) + 2 * static_cast(std::isfinite(centralCell)) + - 4 * static_cast(std::isfinite(rightCell)); + 4 * static_cast(std::isfinite(rightCell)); // If outer cell height value is missing use the central value, however the formula for the normal calculation // has to take into account that the distance of the cells used for normal calculation is different. bool validConfiguration = true; - double distanceX; + double distanceX{NAN}; switch (configurationDirX) { case 7: // All 3 cell height values are valid. distanceX = 2 * gridMapResolution_; // Top and bottom cell centers are 2 cell resolution distant. @@ -359,7 +349,7 @@ void NormalVectorsFilter::rasterSingleNormalComputation(GridMap& map, const s validConfiguration = false; } - double distanceY; + double distanceY{NAN}; switch (configurationDirY) { case 7: // All 3 cell height values are valid. distanceY = 2 * gridMapResolution_; // Left and right cell centers are 2 call resolution distant. @@ -404,5 +394,3 @@ void NormalVectorsFilter::rasterSingleNormalComputation(GridMap& map, const s } } // namespace grid_map - -PLUGINLIB_EXPORT_CLASS(grid_map::NormalVectorsFilter, filters::FilterBase) diff --git a/grid_map_filters/src/SetBasicLayersFilter.cpp b/grid_map_filters/src/SetBasicLayersFilter.cpp index 458bc64f2..967399b49 100644 --- a/grid_map_filters/src/SetBasicLayersFilter.cpp +++ b/grid_map_filters/src/SetBasicLayersFilter.cpp @@ -6,29 +6,20 @@ * Institute: ETH Zurich, ANYbotics */ -#include "../include/grid_map_filters/SetBasicLayersFilter.hpp" +#include "grid_map_filters/SetBasicLayersFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -SetBasicLayersFilter::SetBasicLayersFilter() -{ -} +SetBasicLayersFilter::SetBasicLayersFilter() = default; -template -SetBasicLayersFilter::~SetBasicLayersFilter() -{ -} +SetBasicLayersFilter::~SetBasicLayersFilter() = default; -template -bool SetBasicLayersFilter::configure() -{ - if (!FilterBase::getParam(std::string("layers"), layers_)) { +bool SetBasicLayersFilter::configure() { + if (!FilterBase::getParam(std::string("layers"), layers_)) { ROS_ERROR("SetBasicLayersFilters did not find parameter 'layers'."); return false; } @@ -36,9 +27,7 @@ bool SetBasicLayersFilter::configure() return true; } -template -bool SetBasicLayersFilter::update(const T& mapIn, T& mapOut) -{ +bool SetBasicLayersFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; std::vector layersChecked; @@ -54,6 +43,4 @@ bool SetBasicLayersFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::SetBasicLayersFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/SlidingWindowMathExpressionFilter.cpp b/grid_map_filters/src/SlidingWindowMathExpressionFilter.cpp index 5f7e4b096..7c983727f 100644 --- a/grid_map_filters/src/SlidingWindowMathExpressionFilter.cpp +++ b/grid_map_filters/src/SlidingWindowMathExpressionFilter.cpp @@ -8,83 +8,77 @@ #include "grid_map_filters/SlidingWindowMathExpressionFilter.hpp" -#include - using namespace filters; namespace grid_map { -template -SlidingWindowMathExpressionFilter::SlidingWindowMathExpressionFilter() -: windowSize_(3), - useWindowLength_(false), - windowLength_(0.0), - isComputeEmptyCells_(true), - edgeHandling_(SlidingWindowIterator::EdgeHandling::INSIDE) -{ -} +SlidingWindowMathExpressionFilter::SlidingWindowMathExpressionFilter() + : windowSize_(3), + useWindowLength_(false), + windowLength_(0.0), + isComputeEmptyCells_(true), + edgeHandling_(SlidingWindowIterator::EdgeHandling::INSIDE) {} -template -SlidingWindowMathExpressionFilter::~SlidingWindowMathExpressionFilter() -{ -} +SlidingWindowMathExpressionFilter::~SlidingWindowMathExpressionFilter() = default; -template -bool SlidingWindowMathExpressionFilter::configure() -{ - if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { +bool SlidingWindowMathExpressionFilter::configure() { + if (!FilterBase::getParam(std::string("input_layer"), inputLayer_)) { ROS_ERROR("SlidingWindowMathExpressionFilter did not find parameter 'input_layer'."); return false; } - if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { ROS_ERROR("SlidingWindowMathExpressionFilter did not find parameter 'output_layer'."); return false; } - if (!FilterBase::getParam(std::string("expression"), expression_)) { + if (!FilterBase::getParam(std::string("expression"), expression_)) { ROS_ERROR("SlidingWindowMathExpressionFilter did not find parameter 'expression'."); return false; } - if (!FilterBase::getParam(std::string("window_size"), windowSize_)) { - if (FilterBase::getParam(std::string("window_length"), windowLength_)) { + if (!FilterBase::getParam(std::string("window_size"), windowSize_)) { + if (FilterBase::getParam(std::string("window_length"), windowLength_)) { useWindowLength_ = true; } } - if (!FilterBase::getParam(std::string("compute_empty_cells"), isComputeEmptyCells_)) { + if (!FilterBase::getParam(std::string("compute_empty_cells"), isComputeEmptyCells_)) { ROS_ERROR("SlidingWindowMathExpressionFilter did not find parameter 'compute_empty_cells'."); return false; } std::string edgeHandlingMethod; - if (!FilterBase::getParam(std::string("edge_handling"), edgeHandlingMethod)) { + if (!FilterBase::getParam(std::string("edge_handling"), edgeHandlingMethod)) { ROS_ERROR("SlidingWindowMathExpressionFilter did not find parameter 'edge_handling'."); return false; } - if (edgeHandlingMethod == "inside") edgeHandling_ = SlidingWindowIterator::EdgeHandling::INSIDE; - else if (edgeHandlingMethod == "crop") edgeHandling_ = SlidingWindowIterator::EdgeHandling::CROP; - else if (edgeHandlingMethod == "empty") edgeHandling_ = SlidingWindowIterator::EdgeHandling::EMPTY; - else if (edgeHandlingMethod == "mean") edgeHandling_ = SlidingWindowIterator::EdgeHandling::MEAN; - else { + if (edgeHandlingMethod == "inside") { + edgeHandling_ = SlidingWindowIterator::EdgeHandling::INSIDE; + } else if (edgeHandlingMethod == "crop") { + edgeHandling_ = SlidingWindowIterator::EdgeHandling::CROP; + } else if (edgeHandlingMethod == "empty") { + edgeHandling_ = SlidingWindowIterator::EdgeHandling::EMPTY; + } else if (edgeHandlingMethod == "mean") { + edgeHandling_ = SlidingWindowIterator::EdgeHandling::MEAN; + } else { ROS_ERROR("SlidingWindowMathExpressionFilter did not find method '%s' for edge handling.", edgeHandlingMethod.c_str()); return false; } - // TODO Can we make caching work with changing shared variable? -// parser_.setCacheExpressions(true); + // TODO(magnus): Can we make caching work with changing shared variable? + // parser_.setCacheExpressions(true); return true; } -template -bool SlidingWindowMathExpressionFilter::update(const T& mapIn, T& mapOut) -{ +bool SlidingWindowMathExpressionFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; mapOut.add(outputLayer_); Matrix& outputData = mapOut[outputLayer_]; grid_map::SlidingWindowIterator iterator(mapIn, inputLayer_, edgeHandling_, windowSize_); - if (useWindowLength_) iterator.setWindowLength(mapIn, windowLength_); + if (useWindowLength_) { + iterator.setWindowLength(mapIn, windowLength_); + } for (; !iterator.isPastEnd(); ++iterator) { parser_.var(inputLayer_).setLocal(iterator.getData()); EigenLab::Value result(parser_.eval(expression_)); @@ -97,6 +91,4 @@ bool SlidingWindowMathExpressionFilter::update(const T& mapIn, T& mapOut) return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::SlidingWindowMathExpressionFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/ThresholdFilter.cpp b/grid_map_filters/src/ThresholdFilter.cpp index 91f01a844..ff0471719 100644 --- a/grid_map_filters/src/ThresholdFilter.cpp +++ b/grid_map_filters/src/ThresholdFilter.cpp @@ -9,92 +9,87 @@ #include "grid_map_filters/ThresholdFilter.hpp" #include -#include using namespace filters; namespace grid_map { -template -ThresholdFilter::ThresholdFilter() - : useLowerThreshold_(false), - useUpperThreshold_(false), - lowerThreshold_(0.0), - upperThreshold_(1.0), - setTo_(0.5) -{ -} +ThresholdFilter::ThresholdFilter() + : lowerThreshold_(0.0), upperThreshold_(1.0), useLowerThreshold_(false), useUpperThreshold_(false), setTo_(0.5) {} -template -ThresholdFilter::~ThresholdFilter() -{ -} +ThresholdFilter::~ThresholdFilter() = default; -template -bool ThresholdFilter::configure() -{ +bool ThresholdFilter::configure() { // Load Parameters - if (FilterBase::getParam(std::string("lower_threshold"), - lowerThreshold_)) { + if (FilterBase::getParam(std::string("lower_threshold"), lowerThreshold_)) { useLowerThreshold_ = true; ROS_DEBUG("lower threshold = %f", lowerThreshold_); } - if (FilterBase::getParam(std::string("upper_threshold"), - upperThreshold_)) { + if (FilterBase::getParam(std::string("upper_threshold"), upperThreshold_)) { useUpperThreshold_ = true; ROS_DEBUG("upper threshold = %f", upperThreshold_); } if (!useLowerThreshold_ && !useUpperThreshold_) { - ROS_ERROR( - "ThresholdFilter did not find parameter 'lower_threshold' or 'upper_threshold',"); + ROS_ERROR("ThresholdFilter did not find parameter 'lower_threshold' or 'upper_threshold',"); return false; } if (useLowerThreshold_ && useUpperThreshold_) { - ROS_ERROR( - "Set either 'lower_threshold' or 'upper_threshold'! Only one threshold can be used!"); + ROS_ERROR("Set either 'lower_threshold' or 'upper_threshold'! Only one threshold can be used!"); return false; } - if (!FilterBase::getParam(std::string("set_to"), setTo_)) { + if (!FilterBase::getParam(std::string("set_to"), setTo_)) { ROS_ERROR("ThresholdFilter did not find parameter 'set_to'."); return false; } - if (!FilterBase::getParam(std::string("layer"), layer_)) { - ROS_ERROR("ThresholdFilter did not find parameter 'layer'."); + if (!FilterBase::getParam(std::string("condition_layer"), conditionLayer_)) { + ROS_ERROR("ThresholdFilter did not find parameter 'condition_layer'."); + return false; + } + + if (!FilterBase::getParam(std::string("output_layer"), outputLayer_)) { + ROS_ERROR("ThresholdFilter did not find parameter 'ouput_layer'."); return false; } return true; } -template -bool ThresholdFilter::update(const T& mapIn, T& mapOut) -{ +bool ThresholdFilter::update(const GridMap& mapIn, GridMap& mapOut) { mapOut = mapIn; // Check if layer exists. - if (!mapOut.exists(layer_)) { - ROS_ERROR("Check your threshold types! Type %s does not exist", layer_.c_str()); + if (!mapOut.exists(conditionLayer_)) { + ROS_ERROR("Check your condition_layer! Layer %s does not exist", conditionLayer_.c_str()); + return false; + } + + if (!mapOut.exists(outputLayer_)) { + ROS_ERROR("Check your output_layer! Layer %s does not exist", outputLayer_.c_str()); return false; } // For each cell in map. - auto& data = mapOut[layer_]; + const auto& condition = mapOut[conditionLayer_]; + auto& data = mapOut[outputLayer_]; for (grid_map::GridMapIterator iterator(mapOut); !iterator.isPastEnd(); ++iterator) { - if (!mapOut.isValid(*iterator, layer_)) continue; const size_t i = iterator.getLinearIndex(); - float& value = data(i); - if (useLowerThreshold_) if (value < lowerThreshold_) value = setTo_; - if (useUpperThreshold_) if (value > upperThreshold_) value = setTo_; + const float& conditionValue = condition(i); + float& outputValue = data(i); + // If the condition_value is nan, the output will also be set to the setTo value (NaN comparisons evaluate to false). + if (useLowerThreshold_ && !(conditionValue >= lowerThreshold_)) { + outputValue = setTo_; + } + if (useUpperThreshold_ && !(conditionValue <= upperThreshold_)) { + outputValue = setTo_; + } } return true; } -} /* namespace */ - -PLUGINLIB_EXPORT_CLASS(grid_map::ThresholdFilter, filters::FilterBase) +} // namespace grid_map diff --git a/grid_map_filters/src/plugins.cpp b/grid_map_filters/src/plugins.cpp new file mode 100644 index 000000000..b8aa5a209 --- /dev/null +++ b/grid_map_filters/src/plugins.cpp @@ -0,0 +1,39 @@ +#include + +#include "grid_map_filters/BufferNormalizerFilter.hpp" +#include "grid_map_filters/ColorBlendingFilter.hpp" +#include "grid_map_filters/ColorFillFilter.hpp" +#include "grid_map_filters/ColorMapFilter.hpp" +#include "grid_map_filters/CurvatureFilter.hpp" +#include "grid_map_filters/DeletionFilter.hpp" +#include "grid_map_filters/DuplicationFilter.hpp" +#include "grid_map_filters/LightIntensityFilter.hpp" +#include "grid_map_filters/MathExpressionFilter.hpp" +#include "grid_map_filters/MeanInRadiusFilter.hpp" +#include "grid_map_filters/MedianFillFilter.hpp" +#include "grid_map_filters/MinInRadiusFilter.hpp" +#include "grid_map_filters/MockFilter.hpp" +#include "grid_map_filters/NormalColorMapFilter.hpp" +#include "grid_map_filters/NormalVectorsFilter.hpp" +#include "grid_map_filters/SetBasicLayersFilter.hpp" +#include "grid_map_filters/SlidingWindowMathExpressionFilter.hpp" +#include "grid_map_filters/ThresholdFilter.hpp" + +PLUGINLIB_EXPORT_CLASS(grid_map::BufferNormalizerFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::ColorBlendingFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::ColorFillFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::ColorMapFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::CurvatureFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::DeletionFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::DuplicationFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::LightIntensityFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::MathExpressionFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::MeanInRadiusFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::MedianFillFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::MinInRadiusFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::MockFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::NormalColorMapFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::NormalVectorsFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::SetBasicLayersFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::SlidingWindowMathExpressionFilter, filters::FilterBase) +PLUGINLIB_EXPORT_CLASS(grid_map::ThresholdFilter, filters::FilterBase) diff --git a/grid_map_filters/test/median_fill_filter_test.cpp b/grid_map_filters/test/median_fill_filter_test.cpp index 1ecf6f7c1..28012f26c 100644 --- a/grid_map_filters/test/median_fill_filter_test.cpp +++ b/grid_map_filters/test/median_fill_filter_test.cpp @@ -1,8 +1,9 @@ #include #include +#include #include -#include +#include #include "grid_map_filters/MedianFillFilter.hpp" @@ -10,15 +11,14 @@ using namespace grid_map; using namespace ::testing; using BASE = filters::FilterBase; -using MedianFillFilterT = MedianFillFilter; TEST(MedianFillFilter, ConstructFilterTest) { // NOLINT - MedianFillFilterT medianFillFilter{}; + MedianFillFilter medianFillFilter{}; SUCCEED(); } TEST(MedianFillFilter, LoadParametersAndUpdateTest) { // NOLINT - MedianFillFilterT medianFillFilter{}; + MedianFillFilter medianFillFilter{}; // Set up the parameters XmlRpc::XmlRpcValue config; @@ -28,9 +28,12 @@ TEST(MedianFillFilter, LoadParametersAndUpdateTest) { // NOLINT XmlRpc::XmlRpcValue params; params["input_layer"] = "elevation"; params["output_layer"] = "elevation_filtered"; - params["fill_hole_radius"] = 0.11; + params["fill_hole_radius"] = 0.02; params["filter_existing_values"] = true; params["existing_value_radius"] = 0.02; + params["fill_mask_layer"] = "fill_mask"; + params["debug"] = false; + params["num_erode_dilation_iterations"] = 4; config["params"] = params; @@ -51,16 +54,25 @@ TEST(MedianFillFilter, LoadParametersAndUpdateTest) { // NOLINT GridMap filterOutput; medianFillFilter.update(filterInput, filterOutput); - ASSERT_THAT(filterOutput.getLayers(), ElementsAre(StrEq("elevation"), StrEq("variance"), StrEq("elevation_filtered"))); + ASSERT_THAT(filterOutput.getLayers(), + ElementsAre(StrEq("elevation"), StrEq("variance"), StrEq("elevation_filtered"), StrEq("fill_mask"))); - ASSERT_TRUE(filterInput["elevation"].isApprox(filterOutput["elevation"])); - ASSERT_TRUE(filterInput["variance"].isApprox(filterOutput["variance"])); + ASSERT_TRUE(filterInput["elevation"].isApprox(filterOutput["elevation"])) + << "Expected output:\n " << filterInput["elevation"] << "\nReceived:\n " << filterOutput["elevation"]; + ASSERT_TRUE(filterInput["variance"].isApprox(filterOutput["variance"])) + << "Expected output:\n " << filterInput["variance"] << "\nReceived:\n " << filterOutput["variance"]; + ASSERT_TRUE(filterOutput["fill_mask"].isApprox(Matrix::Ones(filterOutput.getSize().x(), filterOutput.getSize().y()))) + << "Expected output:\n " << Matrix::Ones(filterOutput.getSize().x(), filterOutput.getSize().y()) << "\nReceived:\n " + << filterOutput["fill_mask"]; // Now we add some NaNs and see if they are filtered out. + // add a large unobserved area that should not be filled arbitrarily + filterInput["elevation"].bottomRightCorner<5, 5>().setConstant(NAN); + GridMap noisyFilterInput{filterInput}; - // Add some holes + // Add some holes that should be filled. noisyFilterInput["elevation"](0, 0) = std::numeric_limits::quiet_NaN(); noisyFilterInput["elevation"](10, 5) = std::numeric_limits::quiet_NaN(); noisyFilterInput["elevation"](2, 4) = std::numeric_limits::quiet_NaN(); @@ -69,9 +81,8 @@ TEST(MedianFillFilter, LoadParametersAndUpdateTest) { // NOLINT noisyFilterInput["elevation"](3, 15) = std::numeric_limits::quiet_NaN(); noisyFilterInput["elevation"](10, 10) = std::numeric_limits::quiet_NaN(); - // Check if the filter has removed the nans by comparing it with the original input. + // Check if the filter has removed the sparse nans by comparing it with the original input. GridMap noisyFilterOutput; medianFillFilter.update(noisyFilterInput, noisyFilterOutput); - ASSERT_TRUE(filterInput["elevation"].isApprox(noisyFilterOutput["elevation_filtered"])) - << "Expected output: " << filterInput["elevation"] << "\nReceived: " << noisyFilterOutput["elevation"]; + ASSERT_MATRICES_EQ_WITH_NAN(filterInput["elevation"], noisyFilterOutput["elevation_filtered"]); } diff --git a/grid_map_filters/test/mock_filter_test.cpp b/grid_map_filters/test/mock_filter_test.cpp new file mode 100644 index 000000000..37078f26a --- /dev/null +++ b/grid_map_filters/test/mock_filter_test.cpp @@ -0,0 +1,11 @@ +#include + +#include "grid_map_filters/MockFilter.hpp" + +using namespace grid_map; +using namespace ::testing; + +TEST(MockFilter, ConstructFilterTest) { // NOLINT + MockFilter mockFilter{}; + SUCCEED(); +} \ No newline at end of file diff --git a/grid_map_filters/test/threshold_filter_test.cpp b/grid_map_filters/test/threshold_filter_test.cpp new file mode 100644 index 000000000..cd0197ea4 --- /dev/null +++ b/grid_map_filters/test/threshold_filter_test.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include +#include +#include + +#include "grid_map_filters/ThresholdFilter.hpp" + +#include + +using namespace grid_map; +using namespace ::testing; + +using BASE = filters::FilterBase; + +TEST(ThresholdFilter, LoadParametersAndUpdateTest) { // NOLINT + ThresholdFilter thresholdFilter{}; + + // Set up the parameters + XmlRpc::XmlRpcValue config; + config["name"] = "threshold_filter"; + config["type"] = "gridMapFilters/ThresholdFilter"; + + XmlRpc::XmlRpcValue params; + params["condition_layer"] = "standard_deviation"; + params["output_layer"] = "standard_deviation_filtered"; + params["upper_threshold"] = 0.05; + params["set_to"] = NAN; + + config["params"] = params; + + thresholdFilter.BASE::configure(config); + thresholdFilter.configure(); + + // Set up some test data + + // General grid map geometry. + GridMap filterInput = GridMap({"standard_deviation", "standard_deviation_filtered"}); + filterInput.setGeometry(Length(0.4, 0.4), 0.02, Position(1.0, 5.0)); + filterInput.setFrameId("map"); + + // Set the condition layer to be generally within the threshold except two corners. + Matrix& conditionLayer = filterInput["standard_deviation"]; + conditionLayer.setConstant(0.03); + conditionLayer.bottomRightCorner<5, 5>().setConstant(0.06); + conditionLayer.topLeftCorner<3, 3>().setConstant(NAN); + + // Initialize the output layer with ones. + Matrix& outputLayer = filterInput["standard_deviation_filtered"]; + outputLayer.setConstant(1.0); + + // Setup the expected output, eg all values unchanged except the two corners set to nan. + Matrix outputLayerExpected{outputLayer.rows(), outputLayer.cols()}; + outputLayerExpected.setConstant(1.0f); + outputLayerExpected.topLeftCorner<3, 3>().setConstant(NAN); + outputLayerExpected.bottomRightCorner<5, 5>().setConstant(NAN); + + // Run the filter. + GridMap filterOutput; + thresholdFilter.update(filterInput, filterOutput); + + // Check the layers existent in the output and the expected outputs of them. + ASSERT_THAT(filterOutput.getLayers(), ElementsAre(StrEq("standard_deviation"), StrEq("standard_deviation_filtered"))); + ASSERT_MATRICES_EQ_WITH_NAN(conditionLayer, filterOutput["standard_deviation"]); + ASSERT_MATRICES_EQ_WITH_NAN(filterOutput["standard_deviation_filtered"], outputLayerExpected); +} diff --git a/grid_map_loader/CHANGELOG.rst b/grid_map_loader/CHANGELOG.rst index 04b5d8a3e..d3a32c10a 100644 --- a/grid_map_loader/CHANGELOG.rst +++ b/grid_map_loader/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_loader ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_loader/CMakeLists.txt b/grid_map_loader/CMakeLists.txt index d7d437e09..40a8a53bb 100644 --- a/grid_map_loader/CMakeLists.txt +++ b/grid_map_loader/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_loader) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries diff --git a/grid_map_loader/package.xml b/grid_map_loader/package.xml index e52e762f3..210e331c2 100644 --- a/grid_map_loader/package.xml +++ b/grid_map_loader/package.xml @@ -1,10 +1,10 @@ grid_map_loader - 1.6.2 + 1.7.18 Loading and publishing grid maps from bag files. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Daniel López Madrid BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_msgs/CHANGELOG.rst b/grid_map_msgs/CHANGELOG.rst index e517e8795..db488ef2a 100644 --- a/grid_map_msgs/CHANGELOG.rst +++ b/grid_map_msgs/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_msgs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_msgs/msg/GridMapInfo.msg b/grid_map_msgs/msg/GridMapInfo.msg index 74511f5c1..44e644519 100644 --- a/grid_map_msgs/msg/GridMapInfo.msg +++ b/grid_map_msgs/msg/GridMapInfo.msg @@ -1,5 +1,5 @@ # Header (time and frame) -Header header +std_msgs/Header header # Resolution of the grid [m/cell]. float64 resolution diff --git a/grid_map_msgs/package.xml b/grid_map_msgs/package.xml index 7081a3118..643ecbf65 100644 --- a/grid_map_msgs/package.xml +++ b/grid_map_msgs/package.xml @@ -1,10 +1,11 @@ grid_map_msgs - 1.6.2 + 1.7.18 Definition of the multi-layered grid map message type. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Daniel López Madrid + Zichong Li BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_octomap/CHANGELOG.rst b/grid_map_octomap/CHANGELOG.rst index e49c1fde3..2a5d652dc 100644 --- a/grid_map_octomap/CHANGELOG.rst +++ b/grid_map_octomap/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_octomap ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_octomap/CMakeLists.txt b/grid_map_octomap/CMakeLists.txt index 7f6134c87..a89b8db68 100644 --- a/grid_map_octomap/CMakeLists.txt +++ b/grid_map_octomap/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_octomap) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries diff --git a/grid_map_octomap/package.xml b/grid_map_octomap/package.xml index 1e7854a29..04bbb69f4 100644 --- a/grid_map_octomap/package.xml +++ b/grid_map_octomap/package.xml @@ -1,10 +1,10 @@ grid_map_octomap - 1.6.2 + 1.7.18 Conversions between grid maps and OctoMap types. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_pcl/CHANGELOG.rst b/grid_map_pcl/CHANGELOG.rst index 44656beed..ab8d2914b 100644 --- a/grid_map_pcl/CHANGELOG.rst +++ b/grid_map_pcl/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_pcl ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_pcl/CMakeLists.txt b/grid_map_pcl/CMakeLists.txt index a8dc5573d..91ddea729 100644 --- a/grid_map_pcl/CMakeLists.txt +++ b/grid_map_pcl/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_pcl) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -99,7 +99,7 @@ target_link_libraries(${PROJECT_NAME} ) -# Node. +# Nodes. add_executable(grid_map_pcl_loader_node src/grid_map_pcl_loader_node.cpp ) @@ -118,6 +118,24 @@ target_link_libraries(grid_map_pcl_loader_node ${catkin_LIBRARIES} ) +add_executable(pointcloud_publisher_node + src/pointcloud_publisher_node.cpp +) +add_dependencies(pointcloud_publisher_node + ${PROJECT_NAME} +) +target_include_directories(pointcloud_publisher_node PRIVATE + include +) +target_include_directories(pointcloud_publisher_node SYSTEM PUBLIC + ${catkin_INCLUDE_DIRS} + ${EIGEN3_INCLUDE_DIR} +) +target_link_libraries(pointcloud_publisher_node + ${PROJECT_NAME} + ${catkin_LIBRARIES} +) + ############# ## Install ## ############# @@ -125,6 +143,7 @@ install( TARGETS ${PROJECT_NAME} grid_map_pcl_loader_node + pointcloud_publisher_node ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} @@ -162,6 +181,14 @@ install( if(CATKIN_ENABLE_TESTING) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + find_package(catkin REQUIRED + COMPONENTS + ${CATKIN_PACKAGE_DEPENDENCIES} + roslaunch + ) + + roslaunch_add_file_check(launch) + ## Add gtest based cpp test target and link libraries catkin_add_gtest(${PROJECT_NAME}-test test/test_grid_map_pcl.cpp diff --git a/grid_map_pcl/README.md b/grid_map_pcl/README.md index 51c30ed55..8947b5a56 100644 --- a/grid_map_pcl/README.md +++ b/grid_map_pcl/README.md @@ -14,6 +14,19 @@ The elevation is computed by slicing the point cloud in the x-y plane into colum **Authors: Edo Jelavic, Dominic Jud
Affiliation: [ETH Zurich, Robotics Systems Lab](https://rsl.ethz.ch/)
** +## Publications +The code for point cloud to grid map conversion has been developed as a part of research on autonomous precision harvesting. If you are using the point cloud to grid map conversion, please add the following citation to your publication: + +Jelavic, E., Jud, D., Egli, P. and Hutter, M., 2021. Towards Autonomous Robotic Precision Harvesting: Mapping, Localization, Planning and Control for a Legged Tree Harvester. + + @article{jelavic2021towards, + title={Towards Autonomous Robotic Precision Harvesting: Mapping, Localization, Planning and Control for a Legged Tree Harvester}, + author={Jelavic, Edo and Jud, Dominic and Egli, Pascal and Hutter, Marco}, + journal={Field Robotics}, + year={2021}, + publisher={Wiley} + } + ## Examples Examples of elevation maps computed from point clouds using this package: @@ -30,15 +43,15 @@ Examples of elevation maps computed from point clouds using this package: ## Usage -The algorithm will open the .pcd file, convert the point cloud to a grid map and save the grid map as a rosbag into the folder specified by the user. +The algorithm will open the `.pcd` file, convert the point cloud to a grid map and save the grid map as a rosbag into the folder specified by the user. -1. Place .pcd files in the package folder or anywhere on the system (e.g. grid_map_pcl/data/example.pcd). -2. Modify the *folder_path* inside the launch file such that the folder file points to the folder containing .pcd files (e.g. /*"path to the grid_map_pcl folder"*/data). -3. Change the *pcd_filename* to the point cloud file that you want to process -4. You can run the algorithm with: *roslaunch grid_map_pcl grid_map_pcl_loader_node.launch* -5. Once the algorithm is done you will see the output in the console, then you can run rviz in a separate terminal (make sure that you have sourced your workspace, **DO NOT CLOSE** the terminal where *grid_map_pcl_loader_node* is running ) and visualize the resulting grid map. Instructions on how to visualize a grid map are in the grid map [README](../README.md). +1. Place your `.pcd` file in the package folder or anywhere on the system (e.g. `/.../grid_map_pcl/data/example.pcd`). +2. Modify the `pcd_filename` inside the launch file such that it points to the `.pcd` file you would like to process (e.g. `/.../grid_map_pcl/data/example.pcd`). Set the `output_grid_map` variable to point to the location where you wish to save the resulting grid map (e.g. `/.../grid_map_pcl/data/example_grid_map.bag`) +3. Change the `configFilePath_` variable in the launch file to point to the `.yaml` file with configuration parameters (e.g. `/.../grid_map_pcl/config/parameters.yaml`) +4. You can run the algorithm with: `roslaunch grid_map_pcl grid_map_pcl_loader_node.launch` +5. Once the algorithm is done you will see the output in the console, and it should visualize the point cloud and the grid map in rviz. If that does not happen, then you can run rviz in a separate terminal (make sure that you have sourced your workspace, **DO NOT CLOSE** the terminal where `grid_map_pcl_loader_node` is running ) and visualize the resulting grid map. Instructions on how to visualize a grid map are in the grid map [README](../README.md). -The resulting grid map will be saved in the folder given by *folder_path* variable in the launch file and will be named with a string contained inside the *output_grid_map* variable. For large point clouds (100M-140M points) the algorithm takes about 30-60 min to finish (with 6 threads). For sizes that are in the range of 40M to 60M points, the runtime varies between 5 and 15 min, depending on the number of points. Point cloud with around 10M points or less can be processed in a minute or two. +The resulting grid map will be saved in to the location pointed by `output_grid_map` variable in the launch file. For large point clouds (100M-140M points) the algorithm takes about 30-60 min to finish (with 6 threads). For sizes that are in the range of 40M to 60M points, the runtime varies between 5 and 15 min, depending on the number of points. Point cloud with around 10M points or less can be processed in a minute or two. ## Parameters @@ -50,6 +63,7 @@ The resulting grid map will be saved in the folder given by *folder_path* variab ##### Grid map parameters Resulting grid map parameters. * **pcl_grid_map_extraction/grid_map/min_num_points_per_cell** Minimum number of points in the point cloud that have to fall within any of the grid map cells. Otherwise the cell elevation will be set to NaN. +* **pcl_grid_map_extraction/grid_map/max_num_points_per_cell** Maximum number of points in the point cloud that are allowed to fall within any of the grid map cells. If there is more points, the value will be set to NaN. This number can be used to ignore cells with a lot of points which speeds up the processing (but results with some holes in the map). * **pcl_grid_map_extraction/grid_map/resolution** Resolution of the grid map. Width and lengths are computed automatically. ### Point Cloud Pre-processing Parameters diff --git a/grid_map_pcl/config/parameters.yaml b/grid_map_pcl/config/parameters.yaml index ea4854ab9..4db5d418a 100644 --- a/grid_map_pcl/config/parameters.yaml +++ b/grid_map_pcl/config/parameters.yaml @@ -15,17 +15,18 @@ pcl_grid_map_extraction: max_num_points: 1000000 use_max_height_as_cell_elevation: false outlier_removal: - is_remove_outliers: true + is_remove_outliers: false mean_K: 10 stddev_threshold: 1.0 downsampling: - is_downsample_cloud: true + is_downsample_cloud: false voxel_size: x: 0.02 y: 0.02 z: 0.02 grid_map: min_num_points_per_cell: 4 + max_num_points_per_cell: 100000 resolution: 0.1 diff --git a/grid_map_pcl/data/input_cloud.pcd b/grid_map_pcl/data/input_cloud.pcd new file mode 100644 index 000000000..fe7feee5d Binary files /dev/null and b/grid_map_pcl/data/input_cloud.pcd differ diff --git a/grid_map_pcl/include/grid_map_pcl/GridMapPclLoader.hpp b/grid_map_pcl/include/grid_map_pcl/GridMapPclLoader.hpp index 920be8e98..e7ba410ab 100644 --- a/grid_map_pcl/include/grid_map_pcl/GridMapPclLoader.hpp +++ b/grid_map_pcl/include/grid_map_pcl/GridMapPclLoader.hpp @@ -92,6 +92,12 @@ class GridMapPclLoader { */ void loadParameters(const std::string& filename); + /*! + * Set algorithm's parameters. + * @param[in] parameters of the algorithm + */ + void setParameters(const grid_map_pcl::PclLoaderParameters::Parameters parameters); + //! @return the parameters. const grid_map_pcl::PclLoaderParameters::Parameters& getParameters() const { return params_.get(); } diff --git a/grid_map_pcl/include/grid_map_pcl/PclLoaderParameters.hpp b/grid_map_pcl/include/grid_map_pcl/PclLoaderParameters.hpp index 18b2b2e7f..81f2172eb 100644 --- a/grid_map_pcl/include/grid_map_pcl/PclLoaderParameters.hpp +++ b/grid_map_pcl/include/grid_map_pcl/PclLoaderParameters.hpp @@ -46,6 +46,7 @@ class PclLoaderParameters { struct GridMapParameters { double resolution_ = 0.1; unsigned int minCloudPointsPerCell_ = 2; + unsigned int maxCloudPointsPerCell_ = 100000; }; struct Parameters { @@ -64,7 +65,6 @@ class PclLoaderParameters { PclLoaderParameters() = default; ~PclLoaderParameters() = default; - private: /*! * Load parameters for the GridMapPclLoader class. * @param[in] full path to the config file with parameters @@ -76,7 +76,7 @@ class PclLoaderParameters { * Invoke operator[] on the yaml node. Finds * the parameters in the yaml tree. */ - void handleYamlNode(const YAML::Node& yamlNode); + void loadParameters(const YAML::Node& yamlNode); /*! * Saves typing parameters_ in the owner class. The owner of this @@ -85,6 +85,7 @@ class PclLoaderParameters { */ const Parameters& get() const; + private: // Parameters for the GridMapPclLoader class. Parameters parameters_; }; diff --git a/grid_map_pcl/include/grid_map_pcl/helpers.hpp b/grid_map_pcl/include/grid_map_pcl/helpers.hpp index 824787076..d818e28bf 100644 --- a/grid_map_pcl/include/grid_map_pcl/helpers.hpp +++ b/grid_map_pcl/include/grid_map_pcl/helpers.hpp @@ -22,7 +22,7 @@ namespace grid_map_pcl { void setVerbosityLevelToDebugIfFlagSet(const ros::NodeHandle& nh); -std::string getParameterPath(); +std::string getParameterPath(const ros::NodeHandle& nh); std::string getOutputBagPath(const ros::NodeHandle& nh); diff --git a/grid_map_pcl/launch/grid_map_pcl_loader_node.launch b/grid_map_pcl/launch/grid_map_pcl_loader_node.launch index fde5fd36a..06fd69ed5 100644 --- a/grid_map_pcl/launch/grid_map_pcl_loader_node.launch +++ b/grid_map_pcl/launch/grid_map_pcl_loader_node.launch @@ -1,24 +1,44 @@ - - + + + - + + - - + + + - + - \ No newline at end of file + + + + + + + + + + diff --git a/grid_map_pcl/package.xml b/grid_map_pcl/package.xml index 0c371c520..15a9c91bd 100644 --- a/grid_map_pcl/package.xml +++ b/grid_map_pcl/package.xml @@ -1,18 +1,20 @@ grid_map_pcl - 1.6.2 + 1.7.18 Conversions between grid maps and Point Cloud Library (PCL) types. - Maximilian Wulf - Yoshua Nava + Guoxiang Zhou + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues Dominic Jud Edo Jelavic - + catkin + + grid_map_core grid_map_msgs grid_map_ros @@ -20,6 +22,9 @@ pcl_ros roscpp yaml-cpp + + rviz + gtest diff --git a/grid_map_pcl/rviz/grid_map_vis.rviz b/grid_map_pcl/rviz/grid_map_vis.rviz new file mode 100644 index 000000000..4e753d636 --- /dev/null +++ b/grid_map_pcl/rviz/grid_map_vis.rviz @@ -0,0 +1,162 @@ +Panels: + - Class: rviz/Displays + Help Height: 0 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /PointCloud21/Autocompute Value Bounds1 + Splitter Ratio: 0.6114649772644043 + Tree Height: 797 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Experimental: false + Name: Time + SyncMode: 0 + SyncSource: PointCloud2 +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 1 + Class: rviz/Axes + Enabled: true + Length: 2 + Name: Axes + Radius: 0.20000000298023224 + Reference Frame: + Value: true + - Alpha: 1 + Autocompute Intensity Bounds: true + Class: grid_map_rviz_plugin/GridMap + Color: 200; 200; 200 + Color Layer: elevation + Color Transformer: GridMapLayer + ColorMap: default + Enabled: false + Grid Cell Decimation: 1 + Grid Line Thickness: 0.10000000149011612 + Height Layer: elevation + Height Transformer: GridMapLayer + History Length: 1 + Invert ColorMap: false + Max Color: 255; 255; 255 + Max Intensity: 10 + Min Color: 85; 87; 83 + Min Intensity: 0 + Name: GridMap + Show Grid Lines: true + Topic: /grid_map_pcl_loader_node/grid_map_from_raw_pointcloud + Unreliable: false + Use ColorMap: true + Value: false + - Alpha: 0.9900000095367432 + Autocompute Intensity Bounds: true + Autocompute Value Bounds: + Max Value: 4.018562316894531 + Min Value: -2.210437059402466 + Value: true + Axis: Z + Channel Name: intensity + Class: rviz/PointCloud2 + Color: 164; 0; 0 + Color Transformer: AxisColor + Decay Time: 0 + Enabled: false + Invert Rainbow: false + Max Color: 255; 255; 255 + Min Color: 0; 0; 0 + Name: PointCloud2 + Position Transformer: XYZ + Queue Size: 1 + Selectable: true + Size (Pixels): 3 + Size (m): 0.009999999776482582 + Style: Flat Squares + Topic: /point_cloud_publisher_node/raw_pointcloud + Unreliable: false + Use Fixed Frame: true + Use rainbow: true + Value: false + Enabled: true + Global Options: + Background Color: 46; 52; 54 + Default Light: true + Fixed Frame: map + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 26.305757522583008 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Field of View: 0.7853981852531433 + Focal Point: + X: -3.3914899826049805 + Y: -4.434649467468262 + Z: -1.8720630407333374 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.5947977900505066 + Target Frame: + Yaw: 1.7004344463348389 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1016 + Hide Left Dock: false + Hide Right Dock: true + QMainWindow State: 000000ff00000000fd00000004000000000000016a0000035afc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000035a000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000040efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007300000000280000040e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000003efc0100000002fb0000000800540069006d0065010000000000000738000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000005c80000035a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: true + Width: 1848 + X: 72 + Y: 27 diff --git a/grid_map_pcl/src/GridMapPclLoader.cpp b/grid_map_pcl/src/GridMapPclLoader.cpp index 50ef8a8d4..281d39dd7 100644 --- a/grid_map_pcl/src/GridMapPclLoader.cpp +++ b/grid_map_pcl/src/GridMapPclLoader.cpp @@ -100,17 +100,18 @@ void GridMapPclLoader::addLayerFromInputCloud(const std::string& layer) { ROS_INFO_STREAM("Started adding layer: " << layer); // Preprocess: allocate memory in the internal data structure preprocessGridMapCells(); + ROS_INFO("Finished preprocessing"); workingGridMap_.add(layer); grid_map::Matrix& gridMapData = workingGridMap_.get(layer); unsigned int linearGridMapSize = workingGridMap_.getSize().prod(); + // Iterate through grid map and calculate the corresponding height based on the point cloud #ifndef GRID_MAP_PCL_OPENMP_FOUND ROS_WARN_STREAM("OpemMP not found, defaulting to single threaded implementation"); #else omp_set_num_threads(params_.get().numThreads_); #pragma omp parallel for schedule(dynamic, 10) #endif - // Iterate through grid map and calculate the corresponding height based on the point cloud for (unsigned int linearIndex = 0; linearIndex < linearGridMapSize; ++linearIndex) { processGridMapCell(linearIndex, &gridMapData); } @@ -125,7 +126,11 @@ void GridMapPclLoader::processGridMapCell(const unsigned int linearGridMapIndex, pointsInsideCellBorder = getPointcloudInsideGridMapCellBorder(index); const bool isTooFewPointsInCell = pointsInsideCellBorder->size() < params_.get().gridMap_.minCloudPointsPerCell_; if (isTooFewPointsInCell) { - ROS_WARN_STREAM_THROTTLE(10.0, "Less than " << params_.get().gridMap_.minCloudPointsPerCell_ << " points in a cell"); + ROS_WARN_STREAM_THROTTLE(10.0, "Less than " << params_.get().gridMap_.minCloudPointsPerCell_ << " points in a cell. Skipping."); + return; + } + if (pointsInsideCellBorder->size() > params_.get().gridMap_.maxCloudPointsPerCell_) { + ROS_WARN_STREAM_THROTTLE(10.0, "More than " << params_.get().gridMap_.maxCloudPointsPerCell_ << " points in a cell. Skipping."); return; } auto& clusterHeights = clusterHeightsWithingGridMapCell_[index(0)][index(1)]; @@ -165,6 +170,10 @@ void GridMapPclLoader::loadParameters(const std::string& filename) { pointcloudProcessor_.loadParameters(filename); } +void GridMapPclLoader::setParameters(grid_map_pcl::PclLoaderParameters::Parameters parameters) { + params_.parameters_ = std::move(parameters); +} + void GridMapPclLoader::savePointCloudAsPcdFile(const std::string& filename) const { pointcloudProcessor_.savePointCloudAsPcdFile(filename, *workingCloud_); } diff --git a/grid_map_pcl/src/PclLoaderParameters.cpp b/grid_map_pcl/src/PclLoaderParameters.cpp index 35c0fd817..29fc80380 100644 --- a/grid_map_pcl/src/PclLoaderParameters.cpp +++ b/grid_map_pcl/src/PclLoaderParameters.cpp @@ -14,36 +14,35 @@ namespace grid_map { namespace grid_map_pcl { -void PclLoaderParameters::handleYamlNode(const YAML::Node& yamlNode) { - const std::string prefix = "pcl_grid_map_extraction"; +void PclLoaderParameters::loadParameters(const YAML::Node& yamlNode) { + parameters_.numThreads_ = yamlNode["num_processing_threads"].as(); - parameters_.numThreads_ = yamlNode[prefix]["num_processing_threads"].as(); + parameters_.cloudTransformation_.translation_.x() = yamlNode["cloud_transform"]["translation"]["x"].as(); + parameters_.cloudTransformation_.translation_.y() = yamlNode["cloud_transform"]["translation"]["y"].as(); + parameters_.cloudTransformation_.translation_.z() = yamlNode["cloud_transform"]["translation"]["z"].as(); - parameters_.cloudTransformation_.translation_.x() = yamlNode[prefix]["cloud_transform"]["translation"]["x"].as(); - parameters_.cloudTransformation_.translation_.y() = yamlNode[prefix]["cloud_transform"]["translation"]["y"].as(); - parameters_.cloudTransformation_.translation_.z() = yamlNode[prefix]["cloud_transform"]["translation"]["z"].as(); - - parameters_.cloudTransformation_.rpyIntrinsic_.x() = yamlNode[prefix]["cloud_transform"]["rotation"]["r"].as(); - parameters_.cloudTransformation_.rpyIntrinsic_.y() = yamlNode[prefix]["cloud_transform"]["rotation"]["p"].as(); - parameters_.cloudTransformation_.rpyIntrinsic_.z() = yamlNode[prefix]["cloud_transform"]["rotation"]["y"].as(); + parameters_.cloudTransformation_.rpyIntrinsic_.x() = yamlNode["cloud_transform"]["rotation"]["r"].as(); + parameters_.cloudTransformation_.rpyIntrinsic_.y() = yamlNode["cloud_transform"]["rotation"]["p"].as(); + parameters_.cloudTransformation_.rpyIntrinsic_.z() = yamlNode["cloud_transform"]["rotation"]["y"].as(); parameters_.clusterExtraction_.useMaxHeightAsCellElevation_ = - yamlNode[prefix]["cluster_extraction"]["use_max_height_as_cell_elevation"].as(); - parameters_.clusterExtraction_.clusterTolerance_ = yamlNode[prefix]["cluster_extraction"]["cluster_tolerance"].as(); - parameters_.clusterExtraction_.minNumPoints_ = yamlNode[prefix]["cluster_extraction"]["min_num_points"].as(); - parameters_.clusterExtraction_.maxNumPoints_ = yamlNode[prefix]["cluster_extraction"]["max_num_points"].as(); - - parameters_.outlierRemoval_.isRemoveOutliers_ = yamlNode[prefix]["outlier_removal"]["is_remove_outliers"].as(); - parameters_.outlierRemoval_.meanK_ = yamlNode[prefix]["outlier_removal"]["mean_K"].as(); - parameters_.outlierRemoval_.stddevThreshold_ = yamlNode[prefix]["outlier_removal"]["stddev_threshold"].as(); - - parameters_.gridMap_.resolution_ = yamlNode[prefix]["grid_map"]["resolution"].as(); - parameters_.gridMap_.minCloudPointsPerCell_ = yamlNode[prefix]["grid_map"]["min_num_points_per_cell"].as(); - - parameters_.downsampling_.isDownsampleCloud_ = yamlNode[prefix]["downsampling"]["is_downsample_cloud"].as(); - parameters_.downsampling_.voxelSize_.x() = yamlNode[prefix]["downsampling"]["voxel_size"]["x"].as(); - parameters_.downsampling_.voxelSize_.y() = yamlNode[prefix]["downsampling"]["voxel_size"]["y"].as(); - parameters_.downsampling_.voxelSize_.z() = yamlNode[prefix]["downsampling"]["voxel_size"]["z"].as(); + yamlNode["cluster_extraction"]["use_max_height_as_cell_elevation"].as(); + parameters_.clusterExtraction_.clusterTolerance_ = yamlNode["cluster_extraction"]["cluster_tolerance"].as(); + parameters_.clusterExtraction_.minNumPoints_ = yamlNode["cluster_extraction"]["min_num_points"].as(); + parameters_.clusterExtraction_.maxNumPoints_ = yamlNode["cluster_extraction"]["max_num_points"].as(); + + parameters_.outlierRemoval_.isRemoveOutliers_ = yamlNode["outlier_removal"]["is_remove_outliers"].as(); + parameters_.outlierRemoval_.meanK_ = yamlNode["outlier_removal"]["mean_K"].as(); + parameters_.outlierRemoval_.stddevThreshold_ = yamlNode["outlier_removal"]["stddev_threshold"].as(); + + parameters_.gridMap_.resolution_ = yamlNode["grid_map"]["resolution"].as(); + parameters_.gridMap_.minCloudPointsPerCell_ = yamlNode["grid_map"]["min_num_points_per_cell"].as(); + parameters_.gridMap_.maxCloudPointsPerCell_ = yamlNode["grid_map"]["max_num_points_per_cell"].as(); + + parameters_.downsampling_.isDownsampleCloud_ = yamlNode["downsampling"]["is_downsample_cloud"].as(); + parameters_.downsampling_.voxelSize_.x() = yamlNode["downsampling"]["voxel_size"]["x"].as(); + parameters_.downsampling_.voxelSize_.y() = yamlNode["downsampling"]["voxel_size"]["y"].as(); + parameters_.downsampling_.voxelSize_.z() = yamlNode["downsampling"]["voxel_size"]["z"].as(); } bool PclLoaderParameters::loadParameters(const std::string& filename) { @@ -56,7 +55,8 @@ bool PclLoaderParameters::loadParameters(const std::string& filename) { } try { - handleYamlNode(yamlNode); + const std::string prefix{"pcl_grid_map_extraction"}; + loadParameters(yamlNode[prefix]); } catch (const std::runtime_error& exception) { ROS_ERROR_STREAM("PclLoaderParameters: Loading parameters failed: " << exception.what()); return false; diff --git a/grid_map_pcl/src/grid_map_pcl_loader_node.cpp b/grid_map_pcl/src/grid_map_pcl_loader_node.cpp index 7371a6e75..16531050c 100644 --- a/grid_map_pcl/src/grid_map_pcl_loader_node.cpp +++ b/grid_map_pcl/src/grid_map_pcl_loader_node.cpp @@ -26,7 +26,7 @@ int main(int argc, char** argv) { grid_map::GridMapPclLoader gridMapPclLoader; const std::string pathToCloud = gm::getPcdFilePath(nh); - gridMapPclLoader.loadParameters(gm::getParameterPath()); + gridMapPclLoader.loadParameters(gm::getParameterPath(nh)); gridMapPclLoader.loadCloudFromPcdFile(pathToCloud); gm::processPointcloud(&gridMapPclLoader, nh); @@ -37,7 +37,6 @@ int main(int argc, char** argv) { gm::saveGridMap(gridMap, nh, gm::getMapRosbagTopic(nh)); // publish grid map - grid_map_msgs::GridMap msg; grid_map::GridMapRosConverter::toMessage(gridMap, msg); gridMapPub.publish(msg); diff --git a/grid_map_pcl/src/helpers.cpp b/grid_map_pcl/src/helpers.cpp index bdcad43c2..4c016c26f 100644 --- a/grid_map_pcl/src/helpers.cpp +++ b/grid_map_pcl/src/helpers.cpp @@ -41,25 +41,27 @@ void setVerbosityLevelToDebugIfFlagSet(const ros::NodeHandle& nh) { } } -std::string getParameterPath() { - std::string filePath = ros::package::getPath("grid_map_pcl") + "/config/parameters.yaml"; - return filePath; +std::string getParameterPath(const ros::NodeHandle& nh) { + std::string defaultPath = ros::package::getPath("grid_map_pcl") + "/config/parameters.yaml"; + std::string pathToConfig; + nh.param("config_file_path", pathToConfig, defaultPath); + return pathToConfig; } std::string getOutputBagPath(const ros::NodeHandle& nh) { - std::string outputRosbagName, folderPath; - nh.param("folder_path", folderPath, ""); - nh.param("output_grid_map", outputRosbagName, "output_grid_map.bag"); - std::string pathToOutputBag = folderPath + "/" + outputRosbagName; + std::string pathToOutputBag; + const std::string defaultPath = ros::package::getPath("grid_map_pcl") + "/data/output_grid_map.bag"; + nh.param("output_grid_map", pathToOutputBag, defaultPath); return pathToOutputBag; } std::string getPcdFilePath(const ros::NodeHandle& nh) { - std::string inputCloudName, folderPath; - nh.param("folder_path", folderPath, ""); - nh.param("pcd_filename", inputCloudName, "input_cloud"); - std::string pathToCloud = folderPath + "/" + inputCloudName; - return pathToCloud; + std::string pathToCloud, folderPath; + const std::string defaultPathToCloud = "/data/input_cloud.pcd"; + const std::string defaultFolderPath = ros::package::getPath("grid_map_pcl"); + nh.param("pcd_filename", pathToCloud, defaultPathToCloud); + nh.param("folder_path", folderPath, defaultFolderPath); + return folderPath + "/" + pathToCloud; } std::string getMapFrame(const ros::NodeHandle& nh) { diff --git a/grid_map_pcl/src/pointcloud_publisher_node.cpp b/grid_map_pcl/src/pointcloud_publisher_node.cpp new file mode 100644 index 000000000..82e2b8e0f --- /dev/null +++ b/grid_map_pcl/src/pointcloud_publisher_node.cpp @@ -0,0 +1,42 @@ +/* + * pointcloud_publisher_node.cpp + * + * Created on: Aug 19, 2021 + * Author: Edo Jelavic + * Institute: ETH Zurich, Robotic Systems Lab + */ + +#include +#include +#include +#include "grid_map_pcl/helpers.hpp" + +namespace gm = ::grid_map::grid_map_pcl; +using Point = ::pcl::PointXYZ; +using PointCloud = ::pcl::PointCloud; + +void publishCloud(const std::string& filename, const ros::Publisher& pub, const std::string& frame) { + PointCloud::Ptr cloud(new pcl::PointCloud); + cloud = gm::loadPointcloudFromPcd(filename); + cloud->header.frame_id = frame; + sensor_msgs::PointCloud2 msg; + pcl::toROSMsg(*cloud, msg); + ROS_INFO_STREAM("Publishing loaded cloud, number of points: " << cloud->points.size()); + msg.header.stamp = ros::Time::now(); + pub.publish(msg); +} + +int main(int argc, char** argv) { + ros::init(argc, argv, "point_cloud_pub_node"); + ros::NodeHandle nh("~"); + + const std::string pathToCloud = gm::getPcdFilePath(nh); + const std::string cloudFrame = nh.param("cloud_frame", ""); + // publish cloud + ros::Publisher cloudPub = nh.advertise("raw_pointcloud", 1, true); + publishCloud(pathToCloud, cloudPub, cloudFrame); + + // run + ros::spin(); + return EXIT_SUCCESS; +} diff --git a/grid_map_pcl/test/GridMapPclLoaderTest.cpp b/grid_map_pcl/test/GridMapPclLoaderTest.cpp index b9e52e009..5b0f10913 100644 --- a/grid_map_pcl/test/GridMapPclLoaderTest.cpp +++ b/grid_map_pcl/test/GridMapPclLoaderTest.cpp @@ -37,7 +37,7 @@ TEST(GridMapPclLoaderTest, FlatGroundRealDataset) // NOLINT // allow for some difference (2cm) since the input cloud is noisy (real dataset) double referenceElevation = elevationValues.front(); for (const auto& elevation : elevationValues) { - EXPECT_NEAR(elevation, referenceElevation, 2e-2); + EXPECT_NEAR(elevation, referenceElevation, 3e-2); } } diff --git a/grid_map_pcl/test/test_data/parameters.yaml b/grid_map_pcl/test/test_data/parameters.yaml index ea4854ab9..772a8dadd 100644 --- a/grid_map_pcl/test/test_data/parameters.yaml +++ b/grid_map_pcl/test/test_data/parameters.yaml @@ -26,6 +26,7 @@ pcl_grid_map_extraction: z: 0.02 grid_map: min_num_points_per_cell: 4 + max_num_points_per_cell: 100000 resolution: 0.1 diff --git a/grid_map_pcl/test/test_helpers.cpp b/grid_map_pcl/test/test_helpers.cpp index 037fce436..44926b205 100644 --- a/grid_map_pcl/test/test_helpers.cpp +++ b/grid_map_pcl/test/test_helpers.cpp @@ -115,13 +115,9 @@ Pointcloud::Ptr concatenate(Pointcloud::Ptr cloud1, Pointcloud::Ptr cloud2) { Pointcloud::Ptr concatenatedCloud(new grid_map_pcl_test::Pointcloud()); concatenatedCloud->points.reserve(cloud1->points.size() + cloud2->points.size()); - for (const auto& point : cloud2->points) { - concatenatedCloud->push_back(point); - } + std::copy(cloud2->points.begin(), cloud2->points.end(), std::back_inserter(concatenatedCloud->points)); - for (const auto& point : cloud1->points) { - concatenatedCloud->push_back(point); - } + std::copy(cloud1->points.begin(), cloud1->points.end(), std::back_inserter(concatenatedCloud->points)); return concatenatedCloud; } diff --git a/grid_map_ros/CHANGELOG.rst b/grid_map_ros/CHANGELOG.rst index 7d51ea2c3..bd2babaff 100644 --- a/grid_map_ros/CHANGELOG.rst +++ b/grid_map_ros/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_ros ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_ros/CMakeLists.txt b/grid_map_ros/CMakeLists.txt index c20199fe7..48e443180 100644 --- a/grid_map_ros/CMakeLists.txt +++ b/grid_map_ros/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_ros) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries @@ -11,6 +11,7 @@ find_package(catkin REQUIRED COMPONENTS grid_map_core grid_map_msgs grid_map_cv + grid_map_sdf sensor_msgs nav_msgs std_msgs @@ -43,6 +44,7 @@ catkin_package( grid_map_core grid_map_msgs grid_map_cv + grid_map_sdf sensor_msgs nav_msgs std_msgs diff --git a/grid_map_ros/include/grid_map_ros/GridMapRosConverter.hpp b/grid_map_ros/include/grid_map_ros/GridMapRosConverter.hpp index d5ddad1ff..05915bd96 100644 --- a/grid_map_ros/include/grid_map_ros/GridMapRosConverter.hpp +++ b/grid_map_ros/include/grid_map_ros/GridMapRosConverter.hpp @@ -9,6 +9,7 @@ #pragma once #include +#include #include // STL @@ -106,6 +107,17 @@ class GridMapRosConverter const std::string& pointLayer, sensor_msgs::PointCloud2& pointCloud); + /*! + * Converts a grid map signed distance field object to a ROS PointCloud2 message. + * @param[in] gridMap the grid map object. + * @param[out] pointCloud the message to be populated. + * @param[in] decimation : specifies how many points are returned. 1: all points, 2: every second point, etc. + * @param[in] condition : specifies the condition on the distance value to add it to the pointcloud (default = any distance is added) + */ + static void toPointCloud( + const grid_map::SignedDistanceField& signedDistanceField, sensor_msgs::PointCloud2& pointCloud, size_t decimation = 1, + const std::function& condition = [](float) { return true; }); + /*! * Converts an occupancy grid message to a layer of a grid map. * @param[in] occupancyGrid the occupancy grid to be converted. diff --git a/grid_map_ros/package.xml b/grid_map_ros/package.xml index ea98da401..7ab54b6f5 100644 --- a/grid_map_ros/package.xml +++ b/grid_map_ros/package.xml @@ -1,10 +1,10 @@ grid_map_ros - 1.6.2 + 1.7.18 ROS interface for the grid map library to manage two-dimensional grid maps with multiple data layers. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Guoxiang Zhou BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues @@ -15,6 +15,7 @@ grid_map_core grid_map_msgs grid_map_cv + grid_map_sdf sensor_msgs nav_msgs std_msgs diff --git a/grid_map_ros/src/GridMapRosConverter.cpp b/grid_map_ros/src/GridMapRosConverter.cpp index addbf1d9d..9db026b21 100644 --- a/grid_map_ros/src/GridMapRosConverter.cpp +++ b/grid_map_ros/src/GridMapRosConverter.cpp @@ -222,6 +222,64 @@ void GridMapRosConverter::toPointCloud(const grid_map::GridMap& gridMap, } } +void GridMapRosConverter::toPointCloud(const grid_map::SignedDistanceField& signedDistanceField, sensor_msgs::PointCloud2& pointCloud, + size_t decimation, const std::function& condition) { + pointCloud.header.stamp.fromNSec(signedDistanceField.getTime()); + pointCloud.header.frame_id = signedDistanceField.getFrameId(); + + // Fields: Store each point analogous to pcl::PointXYZI + const std::vector fieldNames{"x", "y", "z", "intensity"}; + pointCloud.fields.clear(); + pointCloud.fields.reserve(fieldNames.size()); + size_t offset = 0; + for (const auto& name : fieldNames) { + sensor_msgs::PointField pointField; + pointField.name = name; + pointField.count = 1; + pointField.datatype = sensor_msgs::PointField::FLOAT32; + pointField.offset = offset; + pointCloud.fields.push_back(pointField); + offset += pointField.count * sizeof(float); + } + + // Resize + const size_t pointCloudMaxSize{signedDistanceField.size()}; + pointCloud.height = 1; + pointCloud.width = pointCloudMaxSize; + pointCloud.point_step = offset; + pointCloud.row_step = pointCloud.width * pointCloud.point_step; + pointCloud.data.resize(pointCloud.height * pointCloud.row_step); + + // Fill data + sensor_msgs::PointCloud2Iterator iter_x(pointCloud, fieldNames[0]); + sensor_msgs::PointCloud2Iterator iter_y(pointCloud, fieldNames[1]); + sensor_msgs::PointCloud2Iterator iter_z(pointCloud, fieldNames[2]); + sensor_msgs::PointCloud2Iterator iter_i(pointCloud, fieldNames[3]); + + size_t addedPoints = 0; + signedDistanceField.filterPoints( + [&](const Position3& p, float sdfValue, const SignedDistanceField::Derivative3&) { + if (condition(sdfValue)) { + *iter_x = static_cast(p.x()); + *iter_y = static_cast(p.y()); + *iter_z = static_cast(p.z()); + *iter_i = sdfValue; + ++iter_x; + ++iter_y; + ++iter_z; + ++iter_i; + ++addedPoints; + } + }, + decimation); + + if (addedPoints != pointCloudMaxSize) { + pointCloud.width = addedPoints; + pointCloud.row_step = pointCloud.width * pointCloud.point_step; + pointCloud.data.resize(pointCloud.height * pointCloud.row_step); + } +} + bool GridMapRosConverter::fromOccupancyGrid(const nav_msgs::OccupancyGrid& occupancyGrid, const std::string& layer, grid_map::GridMap& gridMap) { @@ -241,7 +299,7 @@ bool GridMapRosConverter::fromOccupancyGrid(const nav_msgs::OccupancyGrid& occup return false; } - if (size.prod() != occupancyGrid.data.size()) { + if (static_cast(size.prod()) != occupancyGrid.data.size()) { ROS_WARN_STREAM("Conversion of occupancy grid: Size of data does not correspond to width * height."); return false; } diff --git a/grid_map_ros/test/GridMapRosTest.cpp b/grid_map_ros/test/GridMapRosTest.cpp index 8727610a1..c9da85940 100644 --- a/grid_map_ros/test/GridMapRosTest.cpp +++ b/grid_map_ros/test/GridMapRosTest.cpp @@ -236,3 +236,22 @@ TEST(ImageConversion, roundTripMONO16) EXPECT_TRUE((mapIn.getLength() == mapOut.getLength()).all()); EXPECT_TRUE((mapIn.getSize() == mapOut.getSize()).all()); } + +TEST(SdfConversion, toPointcloud) { + grid_map::GridMap map; + const std::string elevationLayer{"layer"}; + map.setGeometry(grid_map::Length(8.0, 5.0), 0.03, grid_map::Position(0.0, 0.0)); + map.add(elevationLayer, 1.0); + auto& elevationData = map.get(elevationLayer); + elevationData.setRandom(); + + // Create SDF + const float heightMargin{0.1}; + const float minValue{elevationData.minCoeffOfFinites() - heightMargin}; + const float maxValue{elevationData.maxCoeffOfFinites() + heightMargin}; + grid_map::SignedDistanceField sdf(map, elevationLayer, minValue, maxValue); + + // Extract as point clouds. + sensor_msgs::PointCloud2 pointCloud2Msg; + ASSERT_NO_THROW(grid_map::GridMapRosConverter::toPointCloud(sdf, pointCloud2Msg)); +} diff --git a/grid_map_rviz_plugin/CHANGELOG.rst b/grid_map_rviz_plugin/CHANGELOG.rst index 1c69b3af8..dd3c87de9 100644 --- a/grid_map_rviz_plugin/CHANGELOG.rst +++ b/grid_map_rviz_plugin/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_rviz_plugin ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_rviz_plugin/CMakeLists.txt b/grid_map_rviz_plugin/CMakeLists.txt index b42d21ba5..dc4f74f4f 100644 --- a/grid_map_rviz_plugin/CMakeLists.txt +++ b/grid_map_rviz_plugin/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_rviz_plugin) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) find_package(catkin REQUIRED @@ -36,9 +36,8 @@ set(CMAKE_AUTOMOC ON) set(INCLUDE_FILES_QT include/grid_map_rviz_plugin/GridMapDisplay.hpp -) -set(INCLUDE_FILES - include/grid_map_rviz_plugin/GridMapVisual.hpp + include/grid_map_rviz_plugin/modified/message_filter_display.h + include/grid_map_rviz_plugin/modified/frame_manager.h ) ## This plugin includes Qt widgets, so we must include Qt. @@ -69,6 +68,7 @@ add_definitions(-DQT_NO_KEYWORDS) set(SOURCE_FILES src/GridMapDisplay.cpp src/GridMapVisual.cpp + src/GridMapColorMaps.cpp ) ## An rviz plugin is just a shared library, so here we declare the @@ -90,7 +90,7 @@ target_link_libraries(${PROJECT_NAME} ## Install rules install( - DIRECTORY include/${PROJECT_NAME} + DIRECTORY include/${PROJECT_NAME}/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} ) @@ -157,4 +157,4 @@ if(cmake_clang_tools_FOUND) add_default_clang_tooling( DISABLE_CLANG_FORMAT ) -endif(cmake_clang_tools_FOUND) \ No newline at end of file +endif(cmake_clang_tools_FOUND) diff --git a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapColorMaps.hpp b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapColorMaps.hpp new file mode 100644 index 000000000..13a377cf8 --- /dev/null +++ b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapColorMaps.hpp @@ -0,0 +1,51 @@ +/* + * GridMapColorMaps.hpp + * + * Created on: Apr 27, 2021 + * Author: Matias Mattamala + * Institute: University of Oxford + */ + +#pragma once + +#include +#include +#include + +#include + +namespace grid_map_rviz_plugin { + +extern std::map>> const colorMap; + +inline Ogre::ColourValue getColorMap(float intensity, const std::vector>& ctable) { + intensity = std::min(intensity, 1.0f); + intensity = std::max(intensity, 0.0f); + + int idx = int(floor(intensity*255)); + idx = std::min(idx, 255); + idx = std::max(idx, 0); + + // Get color from table + std::vector const& rgb = ctable.at(idx); + + Ogre::ColourValue color; + color.r = rgb[0]; + color.g = rgb[1]; + color.b = rgb[2]; + + return color; +} + +inline std::vector getColorMapNames() { + std::vector types; + types.reserve(colorMap.size()); + + for(auto const& pair : colorMap) { + types.push_back(pair.first); + } + + return types; +} + +} // end namespace grid_map_rviz_plugin \ No newline at end of file diff --git a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapDisplay.hpp b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapDisplay.hpp index 4375cd4af..a2e0de9a1 100644 --- a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapDisplay.hpp +++ b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapDisplay.hpp @@ -32,7 +32,7 @@ class EditableEnumProperty; namespace grid_map_rviz_plugin { class GridMapVisual; -class GridMapDisplay : public rviz::MessageFilterDisplay +class GridMapDisplay : public MessageFilterDisplay { Q_OBJECT public: @@ -42,20 +42,35 @@ Q_OBJECT protected: virtual void onInitialize(); + virtual void onEnable(); + + virtual void onDisable(); + virtual void reset(); + Q_SIGNALS: + // Signal to ensure that the rendering happens in the ui thread. + void process(const grid_map_msgs::GridMap::ConstPtr& msg); + private Q_SLOTS: void updateHistoryLength(); void updateHeightMode(); void updateColorMode(); - void updateUseRainbow(); + void updateUseColorMap(); void updateAutocomputeIntensityBounds(); void updateVisualization(); + void updateColorMapList(); + void updateGridLines(); + // Slot to ensure that the rendering happens in the ui thread. + void onProcessMessage(const grid_map_msgs::GridMap::ConstPtr& msg); private: // Callback for incoming ROS messages void processMessage(const grid_map_msgs::GridMap::ConstPtr& msg); + // Flag to ensure that after the reset the scene is not updated again. + std::atomic isEnabled_{true}; + // Circular buffer for visuals boost::circular_buffer > visuals_; @@ -67,14 +82,17 @@ Q_OBJECT rviz::EditableEnumProperty* heightTransformerProperty_; rviz::EnumProperty* colorModeProperty_; rviz::EditableEnumProperty* colorTransformerProperty_; + rviz::EditableEnumProperty* colorMapProperty_; rviz::ColorProperty* colorProperty_; - rviz::BoolProperty* useRainbowProperty_; - rviz::BoolProperty* invertRainbowProperty_; + rviz::BoolProperty* useColorMapProperty_; + rviz::BoolProperty* invertColorMapProperty_; rviz::ColorProperty* minColorProperty_; rviz::ColorProperty* maxColorProperty_; rviz::BoolProperty* autocomputeIntensityBoundsProperty_; rviz::FloatProperty* minIntensityProperty_; rviz::FloatProperty* maxIntensityProperty_; + rviz::FloatProperty* gridLinesThicknessProperty_; + rviz::IntProperty* gridCellDecimationProperty_; }; } // end namespace grid_map_rviz_plugin diff --git a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapVisual.hpp b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapVisual.hpp index 74094178c..650535766 100644 --- a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapVisual.hpp +++ b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/GridMapVisual.hpp @@ -32,6 +32,7 @@ class GridMapVisual { public: using MaskArray = Eigen::Array; using ColorArray = Eigen::Array; + using MatrixConstRef = Eigen::Ref; GridMapVisual(Ogre::SceneManager* sceneManager, Ogre::SceneNode* parentNode); virtual ~GridMapVisual(); @@ -41,9 +42,9 @@ class GridMapVisual { // Compute the visualization of map_. void computeVisualization(float alpha, bool showGridLines, bool flatTerrain, std::string heightLayer, bool flatColor, bool noColor, - Ogre::ColourValue meshColor, bool mapLayerColor, std::string colorLayer, bool useRainbow, bool invertRainbow, - Ogre::ColourValue minColor, Ogre::ColourValue maxColor, bool autocomputeIntensity, float minIntensity, - float maxIntensity); + Ogre::ColourValue meshColor, bool mapLayerColor, std::string colorLayer, std::string colorMap, bool useColorMap, + bool invertColorMap, Ogre::ColourValue minColor, Ogre::ColourValue maxColor, bool autocomputeIntensity, + float minIntensity, float maxIntensity, float gridLineThickness, int gridCellDecimation); // Set the coordinate frame pose. void setFramePosition(const Ogre::Vector3& position); @@ -53,7 +54,7 @@ class GridMapVisual { std::vector getLayerNames(); private: - enum class ColoringMethod { FLAT, COLOR_LAYER, INTENSITY_LAYER_MANUAL, INTENSITY_LAYER_RAINBOW, INTENSITY_LAYER_INVERTED_RAINBOW }; + enum class ColoringMethod { FLAT, COLOR_LAYER, INTENSITY_LAYER_MANUAL, INTENSITY_LAYER_COLORMAP, INTENSITY_LAYER_INVERTED_COLORMAP }; Ogre::SceneNode* frameNode_; Ogre::SceneManager* sceneManager_; @@ -71,6 +72,10 @@ class GridMapVisual { // Helper methods. bool haveMap_; + + // Warning logs + static constexpr double warningMessageThrottlePeriod_{10.0}; + /** * Initialized the manualObject if not already initialized and clears all previously added data. * @param nVertices An estimate on the number of vertices to be added. @@ -82,6 +87,7 @@ class GridMapVisual { * @param heightData Height values of the cells. * @param colorData Values of the layer specified for coloring the mesh. * @param coloringMethod The strategy to color, see ColoringMethod. + * @param colorMap colorMap selected (string). See GridMapColorMaps.hpp * @param flatColor Used only if coloringMethod is FLAT * @param minIntensity Used for the intensity based coloring methods only. * @param maxIntensity Used for the intensity based coloring methods only. @@ -91,9 +97,10 @@ class GridMapVisual { * @param maxColor Used only if coloringMethod is COLOR_LAYER. * @return The color for each cell. */ - ColorArray computeColorValues(Eigen::Ref heightData, Eigen::Ref colorData, - GridMapVisual::ColoringMethod coloringMethod, Ogre::ColourValue flatColor, double minIntensity, - double maxIntensity, bool autocomputeIntensity, Ogre::ColourValue minColor, Ogre::ColourValue maxColor); + ColorArray computeColorValues(MatrixConstRef heightData, MatrixConstRef colorData, + ColoringMethod coloringMethod, std::string colorMap, Ogre::ColourValue flatColor, + double minIntensity, double maxIntensity, bool autocomputeIntensity, Ogre::ColourValue minColor, + Ogre::ColourValue maxColor); /** * Initialized the meshLines_ object. Should be called before adding lines. Sets the drawing style and allocates the buffer. @@ -101,8 +108,9 @@ class GridMapVisual { * @param rows Number of rows that will be drawn. * @param resolution Resolution of the map. Used to compute the line thickness. * @param alpha Line opacity. + * @param lineWidth line thickness for the mesh lines */ - void initializeMeshLines(size_t cols, size_t rows, double resolution, double alpha); + void initializeMeshLines(size_t cols, size_t rows, double resolution, double alpha, double lineWidth); /** * Computes a mask where all the provided basicLayers are finite. Used to do fast lockups during mesh creation. @@ -111,6 +119,13 @@ class GridMapVisual { */ MaskArray computeIsValidMask(std::vector basicLayers); + /** + * @brief Logs a warning message which lists the basic layers that are missing from the grid map. + * + * @param basicLayers Basic layers. + */ + void printMissingBasicLayers(const std::vector& basicLayers) const; + /** * Transforms the intensity into [0,1] range where 0 corresponds to the minIntensity and 1 to maxIntensity. The given value is clipped to * that range. @@ -136,6 +151,26 @@ class GridMapVisual { * @return */ Ogre::ColourValue getInterpolatedColor(float intensity, Ogre::ColourValue minColor, Ogre::ColourValue maxColor); + + /** + * Returns a vector of ogre coordinates. Each coordinate is a vertex for a mesh line. + * @param i Index of the current point in x. + * @param j Index of the current point in y. + * @param gridCellDecimation Integer that defines how many cells to skip between mesh lines. E.g. if n=3, + * every third mesh line will be displayed. + * @param isNthRow Flag to indicate the n-th row, where n = gridCellDecimation. + * @param isNthCol Flag to indicate the n-th column, where n = gridCellDecimation. + * @param isLastRow Flag to indicate the last row. + * @param isLastCol Flag to indicate the last column. + * @param resolution The resolution. + * @param topLeft The (x,y) position of the top left corner in the map. + * @param heightOrFlatData The height data for the elevation (z coordinates). + * @param isValid Mask of indices with valid data (i.e. not nan or inf) + * @return + */ + std::vector computeMeshLineVertices(int i, int j, int gridCellDecimation, bool isNthRow, bool isNthCol, bool isLastRow, + bool isLastCol, double resolution, const grid_map::Position& topLeft, + const Eigen::ArrayXXf& heightOrFlatData, const MaskArray& isValid) const; }; } // namespace grid_map_rviz_plugin diff --git a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/modified/message_filter_display.h b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/modified/message_filter_display.h index f3bc1cfa4..8959b1089 100644 --- a/grid_map_rviz_plugin/include/grid_map_rviz_plugin/modified/message_filter_display.h +++ b/grid_map_rviz_plugin/include/grid_map_rviz_plugin/modified/message_filter_display.h @@ -26,8 +26,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MESSAGE_FILTER_DISPLAY_H -#define MESSAGE_FILTER_DISPLAY_H +#pragma once #ifndef Q_MOC_RUN #include @@ -37,43 +36,28 @@ // The following replaces #include "grid_map_rviz_plugin/modified/frame_manager.h" #include -#include #include #include -#include -namespace rviz -{ +namespace grid_map_rviz_plugin { /** @brief Helper superclass for MessageFilterDisplay, needed because * Qt's moc and c++ templates don't work nicely together. Not * intended to be used directly. */ -class RVIZ_EXPORT _RosTopicDisplay : public Display -{ - Q_OBJECT -public: - _RosTopicDisplay() - { - topic_property_ = new RosTopicProperty("Topic", "", "", "", this, SLOT(updateTopic())); - unreliable_property_ = - new BoolProperty("Unreliable", false, "Prefer UDP topic transport", this, SLOT(updateTopic())); - queue_size_property_ = - new IntProperty("Queue Size", 1, - "Size of TF message filter queue.\n" - "Increasing this is useful if your TF data is delayed significantly " - "w.r.t. your data, but it can greatly increase memory usage as well.", - this, SLOT(updateQueueSize())); - queue_size_property_->setMin(0); +class _RosTopicDisplay : public rviz::Display { + Q_OBJECT + public: + _RosTopicDisplay() { + topic_property_ = new rviz::RosTopicProperty("Topic", "", "", "", this, SLOT(updateTopic())); + unreliable_property_ = new rviz::BoolProperty("Unreliable", false, "Prefer UDP topic transport", this, SLOT(updateTopic())); } -protected Q_SLOTS: + protected Q_SLOTS: virtual void updateTopic() = 0; - virtual void updateQueueSize() = 0; -protected: - RosTopicProperty* topic_property_; - BoolProperty* unreliable_property_; - IntProperty* queue_size_property_; + protected: + rviz::RosTopicProperty* topic_property_; + rviz::BoolProperty* unreliable_property_; }; /** @brief Display subclass using a tf2_ros::MessageFilter, templated on the ROS message type. @@ -83,114 +67,86 @@ protected Q_SLOTS: * it handles subscribing and unsubscribing when the display is * enabled or disabled. It also has an Ogre::SceneNode which */ template -class MessageFilterDisplay : public _RosTopicDisplay -{ +class MessageFilterDisplay : public _RosTopicDisplay { // No Q_OBJECT macro here, moc does not support Q_OBJECT in a templated class. -public: + public: /** @brief Convenience typedef so subclasses don't have to use * the long templated class name to refer to their super class. */ typedef MessageFilterDisplay MFDClass; - MessageFilterDisplay() : tf_filter_(nullptr), messages_received_(0) - { + MessageFilterDisplay() : tf_filter_(nullptr), messages_received_(0) { QString message_type = QString::fromStdString(ros::message_traits::datatype()); topic_property_->setMessageType(message_type); topic_property_->setDescription(message_type + " topic to subscribe to."); } - void onInitialize() override - { - tf_filter_ = - new tf2_ros::MessageFilter(*context_->getTF2BufferPtr(), fixed_frame_.toStdString(), - static_cast(queue_size_property_->getInt()), - update_nh_); + void onInitialize() override { + tf_filter_ = new tf2_ros::MessageFilter(*context_->getTF2BufferPtr(), fixed_frame_.toStdString(), 1u, update_nh_); tf_filter_->connectInput(sub_); - tf_filter_->registerCallback( - boost::bind(&MessageFilterDisplay::incomingMessage, this, _1)); + tf_filter_->registerCallback(boost::bind(&MessageFilterDisplay::incomingMessage, this, _1)); context_->getFrameManager()->registerFilterForTransformStatusCheck(tf_filter_, this); } - ~MessageFilterDisplay() override - { + ~MessageFilterDisplay() override { MessageFilterDisplay::unsubscribe(); MessageFilterDisplay::reset(); delete tf_filter_; } - void reset() override - { + void reset() override { Display::reset(); tf_filter_->clear(); // Quick fix for #1372. Can be removed if https://github.com/ros/geometry2/pull/402 is released - if (tf_filter_) - update_nh_.getCallbackQueue()->removeByID((uint64_t)tf_filter_); + if (tf_filter_) update_nh_.getCallbackQueue()->removeByID((uint64_t)tf_filter_); messages_received_ = 0; } - void setTopic(const QString& topic, const QString& /*datatype*/) override - { - topic_property_->setString(topic); - } + void setTopic(const QString& topic, const QString& /*datatype*/) override { topic_property_->setString(topic); } -protected: - void updateTopic() override - { + protected: + void updateTopic() override { unsubscribe(); + + // reset and enable again. + bool wasEnabled = isEnabled(); reset(); - subscribe(); - context_->queueRender(); - } + if(wasEnabled){ + onEnable(); + } - void updateQueueSize() override - { - tf_filter_->setQueueSize(static_cast(queue_size_property_->getInt())); subscribe(); + context_->queueRender(); } - virtual void subscribe() - { - if (!isEnabled()) - { + virtual void subscribe() { + if (!isEnabled()) { return; } - try - { + try { ros::TransportHints transport_hint = ros::TransportHints().reliable(); // Determine UDP vs TCP transport for user selection. - if (unreliable_property_->getBool()) - { + if (unreliable_property_->getBool()) { transport_hint = ros::TransportHints().unreliable(); } - sub_.subscribe(update_nh_, topic_property_->getTopicStd(), - static_cast(queue_size_property_->getInt()), transport_hint); - setStatus(StatusProperty::Ok, "Topic", "OK"); - } - catch (ros::Exception& e) - { - setStatus(StatusProperty::Error, "Topic", QString("Error subscribing: ") + e.what()); + sub_.subscribe(update_nh_, topic_property_->getTopicStd(), 1u, transport_hint); + setStatus(rviz::StatusProperty::Ok, "Topic", "OK"); + } catch (ros::Exception& e) { + setStatus(rviz::StatusProperty::Error, "Topic", QString("Error subscribing: ") + e.what()); } } - virtual void unsubscribe() - { - sub_.unsubscribe(); - } + virtual void unsubscribe() { sub_.unsubscribe(); } - void onEnable() override - { - subscribe(); - } + void onEnable() override { subscribe(); } - void onDisable() override - { + void onDisable() override { unsubscribe(); reset(); } - void fixedFrameChanged() override - { + void fixedFrameChanged() override { tf_filter_->setTargetFrame(fixed_frame_.toStdString()); reset(); } @@ -198,15 +154,13 @@ class MessageFilterDisplay : public _RosTopicDisplay /** @brief Incoming message callback. Checks if the message pointer * is valid, increments messages_received_, then calls * processMessage(). */ - void incomingMessage(const typename MessageType::ConstPtr& msg) - { - if (!msg) - { + void incomingMessage(const typename MessageType::ConstPtr& msg) { + if (!msg) { return; } ++messages_received_; - setStatus(StatusProperty::Ok, "Topic", QString::number(messages_received_) + " messages received"); + setStatus(rviz::StatusProperty::Ok, "Topic", QString::number(messages_received_) + " messages received"); processMessage(msg); } @@ -221,6 +175,5 @@ class MessageFilterDisplay : public _RosTopicDisplay uint32_t messages_received_; }; -} // end namespace rviz +} // end namespace grid_map_rviz_plugin -#endif // MESSAGE_FILTER_DISPLAY_H diff --git a/grid_map_rviz_plugin/package.xml b/grid_map_rviz_plugin/package.xml index 0edfec04f..8b48a4691 100644 --- a/grid_map_rviz_plugin/package.xml +++ b/grid_map_rviz_plugin/package.xml @@ -1,10 +1,10 @@ grid_map_rviz_plugin - 1.6.2 + 1.7.18 RViz plugin for displaying grid map messages. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Guoxiang Zhou BSD Philipp Krüsi Péter Fankhauser diff --git a/grid_map_rviz_plugin/src/GridMapColorMaps.cpp b/grid_map_rviz_plugin/src/GridMapColorMaps.cpp new file mode 100644 index 000000000..3f70c2725 --- /dev/null +++ b/grid_map_rviz_plugin/src/GridMapColorMaps.cpp @@ -0,0 +1,38 @@ + +#include "grid_map_rviz_plugin/GridMapColorMaps.hpp" + +namespace grid_map_rviz_plugin { + +std::map>> const colorMap +{ + {"viridis", {{0.267004, 0.004874, 0.329415},{0.268510, 0.009605, 0.335427},{0.269944, 0.014625, 0.341379},{0.271305, 0.019942, 0.347269},{0.272594, 0.025563, 0.353093},{0.273809, 0.031497, 0.358853},{0.274952, 0.037752, 0.364543},{0.276022, 0.044167, 0.370164},{0.277018, 0.050344, 0.375715},{0.277941, 0.056324, 0.381191},{0.278791, 0.062145, 0.386592},{0.279566, 0.067836, 0.391917},{0.280267, 0.073417, 0.397163},{0.280894, 0.078907, 0.402329},{0.281446, 0.084320, 0.407414},{0.281924, 0.089666, 0.412415},{0.282327, 0.094955, 0.417331},{0.282656, 0.100196, 0.422160},{0.282910, 0.105393, 0.426902},{0.283091, 0.110553, 0.431554},{0.283197, 0.115680, 0.436115},{0.283229, 0.120777, 0.440584},{0.283187, 0.125848, 0.444960},{0.283072, 0.130895, 0.449241},{0.282884, 0.135920, 0.453427},{0.282623, 0.140926, 0.457517},{0.282290, 0.145912, 0.461510},{0.281887, 0.150881, 0.465405},{0.281412, 0.155834, 0.469201},{0.280868, 0.160771, 0.472899},{0.280255, 0.165693, 0.476498},{0.279574, 0.170599, 0.479997},{0.278826, 0.175490, 0.483397},{0.278012, 0.180367, 0.486697},{0.277134, 0.185228, 0.489898},{0.276194, 0.190074, 0.493001},{0.275191, 0.194905, 0.496005},{0.274128, 0.199721, 0.498911},{0.273006, 0.204520, 0.501721},{0.271828, 0.209303, 0.504434},{0.270595, 0.214069, 0.507052},{0.269308, 0.218818, 0.509577},{0.267968, 0.223549, 0.512008},{0.266580, 0.228262, 0.514349},{0.265145, 0.232956, 0.516599},{0.263663, 0.237631, 0.518762},{0.262138, 0.242286, 0.520837},{0.260571, 0.246922, 0.522828},{0.258965, 0.251537, 0.524736},{0.257322, 0.256130, 0.526563},{0.255645, 0.260703, 0.528312},{0.253935, 0.265254, 0.529983},{0.252194, 0.269783, 0.531579},{0.250425, 0.274290, 0.533103},{0.248629, 0.278775, 0.534556},{0.246811, 0.283237, 0.535941},{0.244972, 0.287675, 0.537260},{0.243113, 0.292092, 0.538516},{0.241237, 0.296485, 0.539709},{0.239346, 0.300855, 0.540844},{0.237441, 0.305202, 0.541921},{0.235526, 0.309527, 0.542944},{0.233603, 0.313828, 0.543914},{0.231674, 0.318106, 0.544834},{0.229739, 0.322361, 0.545706},{0.227802, 0.326594, 0.546532},{0.225863, 0.330805, 0.547314},{0.223925, 0.334994, 0.548053},{0.221989, 0.339161, 0.548752},{0.220057, 0.343307, 0.549413},{0.218130, 0.347432, 0.550038},{0.216210, 0.351535, 0.550627},{0.214298, 0.355619, 0.551184},{0.212395, 0.359683, 0.551710},{0.210503, 0.363727, 0.552206},{0.208623, 0.367752, 0.552675},{0.206756, 0.371758, 0.553117},{0.204903, 0.375746, 0.553533},{0.203063, 0.379716, 0.553925},{0.201239, 0.383670, 0.554294},{0.199430, 0.387607, 0.554642},{0.197636, 0.391528, 0.554969},{0.195860, 0.395433, 0.555276},{0.194100, 0.399323, 0.555565},{0.192357, 0.403199, 0.555836},{0.190631, 0.407061, 0.556089},{0.188923, 0.410910, 0.556326},{0.187231, 0.414746, 0.556547},{0.185556, 0.418570, 0.556753},{0.183898, 0.422383, 0.556944},{0.182256, 0.426184, 0.557120},{0.180629, 0.429975, 0.557282},{0.179019, 0.433756, 0.557430},{0.177423, 0.437527, 0.557565},{0.175841, 0.441290, 0.557685},{0.174274, 0.445044, 0.557792},{0.172719, 0.448791, 0.557885},{0.171176, 0.452530, 0.557965},{0.169646, 0.456262, 0.558030},{0.168126, 0.459988, 0.558082},{0.166617, 0.463708, 0.558119},{0.165117, 0.467423, 0.558141},{0.163625, 0.471133, 0.558148},{0.162142, 0.474838, 0.558140},{0.160665, 0.478540, 0.558115},{0.159194, 0.482237, 0.558073},{0.157729, 0.485932, 0.558013},{0.156270, 0.489624, 0.557936},{0.154815, 0.493313, 0.557840},{0.153364, 0.497000, 0.557724},{0.151918, 0.500685, 0.557587},{0.150476, 0.504369, 0.557430},{0.149039, 0.508051, 0.557250},{0.147607, 0.511733, 0.557049},{0.146180, 0.515413, 0.556823},{0.144759, 0.519093, 0.556572},{0.143343, 0.522773, 0.556295},{0.141935, 0.526453, 0.555991},{0.140536, 0.530132, 0.555659},{0.139147, 0.533812, 0.555298},{0.137770, 0.537492, 0.554906},{0.136408, 0.541173, 0.554483},{0.135066, 0.544853, 0.554029},{0.133743, 0.548535, 0.553541},{0.132444, 0.552216, 0.553018},{0.131172, 0.555899, 0.552459},{0.129933, 0.559582, 0.551864},{0.128729, 0.563265, 0.551229},{0.127568, 0.566949, 0.550556},{0.126453, 0.570633, 0.549841},{0.125394, 0.574318, 0.549086},{0.124395, 0.578002, 0.548287},{0.123463, 0.581687, 0.547445},{0.122606, 0.585371, 0.546557},{0.121831, 0.589055, 0.545623},{0.121148, 0.592739, 0.544641},{0.120565, 0.596422, 0.543611},{0.120092, 0.600104, 0.542530},{0.119738, 0.603785, 0.541400},{0.119512, 0.607464, 0.540218},{0.119423, 0.611141, 0.538982},{0.119483, 0.614817, 0.537692},{0.119699, 0.618490, 0.536347},{0.120081, 0.622161, 0.534946},{0.120638, 0.625828, 0.533488},{0.121380, 0.629492, 0.531973},{0.122312, 0.633153, 0.530398},{0.123444, 0.636809, 0.528763},{0.124780, 0.640461, 0.527068},{0.126326, 0.644107, 0.525311},{0.128087, 0.647749, 0.523491},{0.130067, 0.651384, 0.521608},{0.132268, 0.655014, 0.519661},{0.134692, 0.658636, 0.517649},{0.137339, 0.662252, 0.515571},{0.140210, 0.665859, 0.513427},{0.143303, 0.669459, 0.511215},{0.146616, 0.673050, 0.508936},{0.150148, 0.676631, 0.506589},{0.153894, 0.680203, 0.504172},{0.157851, 0.683765, 0.501686},{0.162016, 0.687316, 0.499129},{0.166383, 0.690856, 0.496502},{0.170948, 0.694384, 0.493803},{0.175707, 0.697900, 0.491033},{0.180653, 0.701402, 0.488189},{0.185783, 0.704891, 0.485273},{0.191090, 0.708366, 0.482284},{0.196571, 0.711827, 0.479221},{0.202219, 0.715272, 0.476084},{0.208030, 0.718701, 0.472873},{0.214000, 0.722114, 0.469588},{0.220124, 0.725509, 0.466226},{0.226397, 0.728888, 0.462789},{0.232815, 0.732247, 0.459277},{0.239374, 0.735588, 0.455688},{0.246070, 0.738910, 0.452024},{0.252899, 0.742211, 0.448284},{0.259857, 0.745492, 0.444467},{0.266941, 0.748751, 0.440573},{0.274149, 0.751988, 0.436601},{0.281477, 0.755203, 0.432552},{0.288921, 0.758394, 0.428426},{0.296479, 0.761561, 0.424223},{0.304148, 0.764704, 0.419943},{0.311925, 0.767822, 0.415586},{0.319809, 0.770914, 0.411152},{0.327796, 0.773980, 0.406640},{0.335885, 0.777018, 0.402049},{0.344074, 0.780029, 0.397381},{0.352360, 0.783011, 0.392636},{0.360741, 0.785964, 0.387814},{0.369214, 0.788888, 0.382914},{0.377779, 0.791781, 0.377939},{0.386433, 0.794644, 0.372886},{0.395174, 0.797475, 0.367757},{0.404001, 0.800275, 0.362552},{0.412913, 0.803041, 0.357269},{0.421908, 0.805774, 0.351910},{0.430983, 0.808473, 0.346476},{0.440137, 0.811138, 0.340967},{0.449368, 0.813768, 0.335384},{0.458674, 0.816363, 0.329727},{0.468053, 0.818921, 0.323998},{0.477504, 0.821444, 0.318195},{0.487026, 0.823929, 0.312321},{0.496615, 0.826376, 0.306377},{0.506271, 0.828786, 0.300362},{0.515992, 0.831158, 0.294279},{0.525776, 0.833491, 0.288127},{0.535621, 0.835785, 0.281908},{0.545524, 0.838039, 0.275626},{0.555484, 0.840254, 0.269281},{0.565498, 0.842430, 0.262877},{0.575563, 0.844566, 0.256415},{0.585678, 0.846661, 0.249897},{0.595839, 0.848717, 0.243329},{0.606045, 0.850733, 0.236712},{0.616293, 0.852709, 0.230052},{0.626579, 0.854645, 0.223353},{0.636902, 0.856542, 0.216620},{0.647257, 0.858400, 0.209861},{0.657642, 0.860219, 0.203082},{0.668054, 0.861999, 0.196293},{0.678489, 0.863742, 0.189503},{0.688944, 0.865448, 0.182725},{0.699415, 0.867117, 0.175971},{0.709898, 0.868751, 0.169257},{0.720391, 0.870350, 0.162603},{0.730889, 0.871916, 0.156029},{0.741388, 0.873449, 0.149561},{0.751884, 0.874951, 0.143228},{0.762373, 0.876424, 0.137064},{0.772852, 0.877868, 0.131109},{0.783315, 0.879285, 0.125405},{0.793760, 0.880678, 0.120005},{0.804182, 0.882046, 0.114965},{0.814576, 0.883393, 0.110347},{0.824940, 0.884720, 0.106217},{0.835270, 0.886029, 0.102646},{0.845561, 0.887322, 0.099702},{0.855810, 0.888601, 0.097452},{0.866013, 0.889868, 0.095953},{0.876168, 0.891125, 0.095250},{0.886271, 0.892374, 0.095374},{0.896320, 0.893616, 0.096335},{0.906311, 0.894855, 0.098125},{0.916242, 0.896091, 0.100717},{0.926106, 0.897330, 0.104071},{0.935904, 0.898570, 0.108131},{0.945636, 0.899815, 0.112838},{0.955300, 0.901065, 0.118128},{0.964894, 0.902323, 0.123941},{0.974417, 0.903590, 0.130215},{0.983868, 0.904867, 0.136897},{0.993248, 0.906157, 0.143936}}}, + {"plasma", {{0.050383, 0.029803, 0.527975},{0.063536, 0.028426, 0.533124},{0.075353, 0.027206, 0.538007},{0.086222, 0.026125, 0.542658},{0.096379, 0.025165, 0.547103},{0.105980, 0.024309, 0.551368},{0.115124, 0.023556, 0.555468},{0.123903, 0.022878, 0.559423},{0.132381, 0.022258, 0.563250},{0.140603, 0.021687, 0.566959},{0.148607, 0.021154, 0.570562},{0.156421, 0.020651, 0.574065},{0.164070, 0.020171, 0.577478},{0.171574, 0.019706, 0.580806},{0.178950, 0.019252, 0.584054},{0.186213, 0.018803, 0.587228},{0.193374, 0.018354, 0.590330},{0.200445, 0.017902, 0.593364},{0.207435, 0.017442, 0.596333},{0.214350, 0.016973, 0.599239},{0.221197, 0.016497, 0.602083},{0.227983, 0.016007, 0.604867},{0.234715, 0.015502, 0.607592},{0.241396, 0.014979, 0.610259},{0.248032, 0.014439, 0.612868},{0.254627, 0.013882, 0.615419},{0.261183, 0.013308, 0.617911},{0.267703, 0.012716, 0.620346},{0.274191, 0.012109, 0.622722},{0.280648, 0.011488, 0.625038},{0.287076, 0.010855, 0.627295},{0.293478, 0.010213, 0.629490},{0.299855, 0.009561, 0.631624},{0.306210, 0.008902, 0.633694},{0.312543, 0.008239, 0.635700},{0.318856, 0.007576, 0.637640},{0.325150, 0.006915, 0.639512},{0.331426, 0.006261, 0.641316},{0.337683, 0.005618, 0.643049},{0.343925, 0.004991, 0.644710},{0.350150, 0.004382, 0.646298},{0.356359, 0.003798, 0.647810},{0.362553, 0.003243, 0.649245},{0.368733, 0.002724, 0.650601},{0.374897, 0.002245, 0.651876},{0.381047, 0.001814, 0.653068},{0.387183, 0.001434, 0.654177},{0.393304, 0.001114, 0.655199},{0.399411, 0.000859, 0.656133},{0.405503, 0.000678, 0.656977},{0.411580, 0.000577, 0.657730},{0.417642, 0.000564, 0.658390},{0.423689, 0.000646, 0.658956},{0.429719, 0.000831, 0.659425},{0.435734, 0.001127, 0.659797},{0.441732, 0.001540, 0.660069},{0.447714, 0.002080, 0.660240},{0.453677, 0.002755, 0.660310},{0.459623, 0.003574, 0.660277},{0.465550, 0.004545, 0.660139},{0.471457, 0.005678, 0.659897},{0.477344, 0.006980, 0.659549},{0.483210, 0.008460, 0.659095},{0.489055, 0.010127, 0.658534},{0.494877, 0.011990, 0.657865},{0.500678, 0.014055, 0.657088},{0.506454, 0.016333, 0.656202},{0.512206, 0.018833, 0.655209},{0.517933, 0.021563, 0.654109},{0.523633, 0.024532, 0.652901},{0.529306, 0.027747, 0.651586},{0.534952, 0.031217, 0.650165},{0.540570, 0.034950, 0.648640},{0.546157, 0.038954, 0.647010},{0.551715, 0.043136, 0.645277},{0.557243, 0.047331, 0.643443},{0.562738, 0.051545, 0.641509},{0.568201, 0.055778, 0.639477},{0.573632, 0.060028, 0.637349},{0.579029, 0.064296, 0.635126},{0.584391, 0.068579, 0.632812},{0.589719, 0.072878, 0.630408},{0.595011, 0.077190, 0.627917},{0.600266, 0.081516, 0.625342},{0.605485, 0.085854, 0.622686},{0.610667, 0.090204, 0.619951},{0.615812, 0.094564, 0.617140},{0.620919, 0.098934, 0.614257},{0.625987, 0.103312, 0.611305},{0.631017, 0.107699, 0.608287},{0.636008, 0.112092, 0.605205},{0.640959, 0.116492, 0.602065},{0.645872, 0.120898, 0.598867},{0.650746, 0.125309, 0.595617},{0.655580, 0.129725, 0.592317},{0.660374, 0.134144, 0.588971},{0.665129, 0.138566, 0.585582},{0.669845, 0.142992, 0.582154},{0.674522, 0.147419, 0.578688},{0.679160, 0.151848, 0.575189},{0.683758, 0.156278, 0.571660},{0.688318, 0.160709, 0.568103},{0.692840, 0.165141, 0.564522},{0.697324, 0.169573, 0.560919},{0.701769, 0.174005, 0.557296},{0.706178, 0.178437, 0.553657},{0.710549, 0.182868, 0.550004},{0.714883, 0.187299, 0.546338},{0.719181, 0.191729, 0.542663},{0.723444, 0.196158, 0.538981},{0.727670, 0.200586, 0.535293},{0.731862, 0.205013, 0.531601},{0.736019, 0.209439, 0.527908},{0.740143, 0.213864, 0.524216},{0.744232, 0.218288, 0.520524},{0.748289, 0.222711, 0.516834},{0.752312, 0.227133, 0.513149},{0.756304, 0.231555, 0.509468},{0.760264, 0.235976, 0.505794},{0.764193, 0.240396, 0.502126},{0.768090, 0.244817, 0.498465},{0.771958, 0.249237, 0.494813},{0.775796, 0.253658, 0.491171},{0.779604, 0.258078, 0.487539},{0.783383, 0.262500, 0.483918},{0.787133, 0.266922, 0.480307},{0.790855, 0.271345, 0.476706},{0.794549, 0.275770, 0.473117},{0.798216, 0.280197, 0.469538},{0.801855, 0.284626, 0.465971},{0.805467, 0.289057, 0.462415},{0.809052, 0.293491, 0.458870},{0.812612, 0.297928, 0.455338},{0.816144, 0.302368, 0.451816},{0.819651, 0.306812, 0.448306},{0.823132, 0.311261, 0.444806},{0.826588, 0.315714, 0.441316},{0.830018, 0.320172, 0.437836},{0.833422, 0.324635, 0.434366},{0.836801, 0.329105, 0.430905},{0.840155, 0.333580, 0.427455},{0.843484, 0.338062, 0.424013},{0.846788, 0.342551, 0.420579},{0.850066, 0.347048, 0.417153},{0.853319, 0.351553, 0.413734},{0.856547, 0.356066, 0.410322},{0.859750, 0.360588, 0.406917},{0.862927, 0.365119, 0.403519},{0.866078, 0.369660, 0.400126},{0.869203, 0.374212, 0.396738},{0.872303, 0.378774, 0.393355},{0.875376, 0.383347, 0.389976},{0.878423, 0.387932, 0.386600},{0.881443, 0.392529, 0.383229},{0.884436, 0.397139, 0.379860},{0.887402, 0.401762, 0.376494},{0.890340, 0.406398, 0.373130},{0.893250, 0.411048, 0.369768},{0.896131, 0.415712, 0.366407},{0.898984, 0.420392, 0.363047},{0.901807, 0.425087, 0.359688},{0.904601, 0.429797, 0.356329},{0.907365, 0.434524, 0.352970},{0.910098, 0.439268, 0.349610},{0.912800, 0.444029, 0.346251},{0.915471, 0.448807, 0.342890},{0.918109, 0.453603, 0.339529},{0.920714, 0.458417, 0.336166},{0.923287, 0.463251, 0.332801},{0.925825, 0.468103, 0.329435},{0.928329, 0.472975, 0.326067},{0.930798, 0.477867, 0.322697},{0.933232, 0.482780, 0.319325},{0.935630, 0.487712, 0.315952},{0.937990, 0.492667, 0.312575},{0.940313, 0.497642, 0.309197},{0.942598, 0.502639, 0.305816},{0.944844, 0.507658, 0.302433},{0.947051, 0.512699, 0.299049},{0.949217, 0.517763, 0.295662},{0.951344, 0.522850, 0.292275},{0.953428, 0.527960, 0.288883},{0.955470, 0.533093, 0.285490},{0.957469, 0.538250, 0.282096},{0.959424, 0.543431, 0.278701},{0.961336, 0.548636, 0.275305},{0.963203, 0.553865, 0.271909},{0.965024, 0.559118, 0.268513},{0.966798, 0.564396, 0.265118},{0.968526, 0.569700, 0.261721},{0.970205, 0.575028, 0.258325},{0.971835, 0.580382, 0.254931},{0.973416, 0.585761, 0.251540},{0.974947, 0.591165, 0.248151},{0.976428, 0.596595, 0.244767},{0.977856, 0.602051, 0.241387},{0.979233, 0.607532, 0.238013},{0.980556, 0.613039, 0.234646},{0.981826, 0.618572, 0.231287},{0.983041, 0.624131, 0.227937},{0.984199, 0.629718, 0.224595},{0.985301, 0.635330, 0.221265},{0.986345, 0.640969, 0.217948},{0.987332, 0.646633, 0.214648},{0.988260, 0.652325, 0.211364},{0.989128, 0.658043, 0.208100},{0.989935, 0.663787, 0.204859},{0.990681, 0.669558, 0.201642},{0.991365, 0.675355, 0.198453},{0.991985, 0.681179, 0.195295},{0.992541, 0.687030, 0.192170},{0.993032, 0.692907, 0.189084},{0.993456, 0.698810, 0.186041},{0.993814, 0.704741, 0.183043},{0.994103, 0.710698, 0.180097},{0.994324, 0.716681, 0.177208},{0.994474, 0.722691, 0.174381},{0.994553, 0.728728, 0.171622},{0.994561, 0.734791, 0.168938},{0.994495, 0.740880, 0.166335},{0.994355, 0.746995, 0.163821},{0.994141, 0.753137, 0.161404},{0.993851, 0.759304, 0.159092},{0.993482, 0.765499, 0.156891},{0.993033, 0.771720, 0.154808},{0.992505, 0.777967, 0.152855},{0.991897, 0.784239, 0.151042},{0.991209, 0.790537, 0.149377},{0.990439, 0.796859, 0.147870},{0.989587, 0.803205, 0.146529},{0.988648, 0.809579, 0.145357},{0.987621, 0.815978, 0.144363},{0.986509, 0.822401, 0.143557},{0.985314, 0.828846, 0.142945},{0.984031, 0.835315, 0.142528},{0.982653, 0.841812, 0.142303},{0.981190, 0.848329, 0.142279},{0.979644, 0.854866, 0.142453},{0.977995, 0.861432, 0.142808},{0.976265, 0.868016, 0.143351},{0.974443, 0.874622, 0.144061},{0.972530, 0.881250, 0.144923},{0.970533, 0.887896, 0.145919},{0.968443, 0.894564, 0.147014},{0.966271, 0.901249, 0.148180},{0.964021, 0.907950, 0.149370},{0.961681, 0.914672, 0.150520},{0.959276, 0.921407, 0.151566},{0.956808, 0.928152, 0.152409},{0.954287, 0.934908, 0.152921},{0.951726, 0.941671, 0.152925},{0.949151, 0.948435, 0.152178},{0.946602, 0.955190, 0.150328},{0.944152, 0.961916, 0.146861},{0.941896, 0.968590, 0.140956},{0.940015, 0.975158, 0.131326}}}, + {"inferno", {{0.001462, 0.000466, 0.013866},{0.002267, 0.001270, 0.018570},{0.003299, 0.002249, 0.024239},{0.004547, 0.003392, 0.030909},{0.006006, 0.004692, 0.038558},{0.007676, 0.006136, 0.046836},{0.009561, 0.007713, 0.055143},{0.011663, 0.009417, 0.063460},{0.013995, 0.011225, 0.071862},{0.016561, 0.013136, 0.080282},{0.019373, 0.015133, 0.088767},{0.022447, 0.017199, 0.097327},{0.025793, 0.019331, 0.105930},{0.029432, 0.021503, 0.114621},{0.033385, 0.023702, 0.123397},{0.037668, 0.025921, 0.132232},{0.042253, 0.028139, 0.141141},{0.046915, 0.030324, 0.150164},{0.051644, 0.032474, 0.159254},{0.056449, 0.034569, 0.168414},{0.061340, 0.036590, 0.177642},{0.066331, 0.038504, 0.186962},{0.071429, 0.040294, 0.196354},{0.076637, 0.041905, 0.205799},{0.081962, 0.043328, 0.215289},{0.087411, 0.044556, 0.224813},{0.092990, 0.045583, 0.234358},{0.098702, 0.046402, 0.243904},{0.104551, 0.047008, 0.253430},{0.110536, 0.047399, 0.262912},{0.116656, 0.047574, 0.272321},{0.122908, 0.047536, 0.281624},{0.129285, 0.047293, 0.290788},{0.135778, 0.046856, 0.299776},{0.142378, 0.046242, 0.308553},{0.149073, 0.045468, 0.317085},{0.155850, 0.044559, 0.325338},{0.162689, 0.043554, 0.333277},{0.169575, 0.042489, 0.340874},{0.176493, 0.041402, 0.348111},{0.183429, 0.040329, 0.354971},{0.190367, 0.039309, 0.361447},{0.197297, 0.038400, 0.367535},{0.204209, 0.037632, 0.373238},{0.211095, 0.037030, 0.378563},{0.217949, 0.036615, 0.383522},{0.224763, 0.036405, 0.388129},{0.231538, 0.036405, 0.392400},{0.238273, 0.036621, 0.396353},{0.244967, 0.037055, 0.400007},{0.251620, 0.037705, 0.403378},{0.258234, 0.038571, 0.406485},{0.264810, 0.039647, 0.409345},{0.271347, 0.040922, 0.411976},{0.277850, 0.042353, 0.414392},{0.284321, 0.043933, 0.416608},{0.290763, 0.045644, 0.418637},{0.297178, 0.047470, 0.420491},{0.303568, 0.049396, 0.422182},{0.309935, 0.051407, 0.423721},{0.316282, 0.053490, 0.425116},{0.322610, 0.055634, 0.426377},{0.328921, 0.057827, 0.427511},{0.335217, 0.060060, 0.428524},{0.341500, 0.062325, 0.429425},{0.347771, 0.064616, 0.430217},{0.354032, 0.066925, 0.430906},{0.360284, 0.069247, 0.431497},{0.366529, 0.071579, 0.431994},{0.372768, 0.073915, 0.432400},{0.379001, 0.076253, 0.432719},{0.385228, 0.078591, 0.432955},{0.391453, 0.080927, 0.433109},{0.397674, 0.083257, 0.433183},{0.403894, 0.085580, 0.433179},{0.410113, 0.087896, 0.433098},{0.416331, 0.090203, 0.432943},{0.422549, 0.092501, 0.432714},{0.428768, 0.094790, 0.432412},{0.434987, 0.097069, 0.432039},{0.441207, 0.099338, 0.431594},{0.447428, 0.101597, 0.431080},{0.453651, 0.103848, 0.430498},{0.459875, 0.106089, 0.429846},{0.466100, 0.108322, 0.429125},{0.472328, 0.110547, 0.428334},{0.478558, 0.112764, 0.427475},{0.484789, 0.114974, 0.426548},{0.491022, 0.117179, 0.425552},{0.497257, 0.119379, 0.424488},{0.503493, 0.121575, 0.423356},{0.509730, 0.123769, 0.422156},{0.515967, 0.125960, 0.420887},{0.522206, 0.128150, 0.419549},{0.528444, 0.130341, 0.418142},{0.534683, 0.132534, 0.416667},{0.540920, 0.134729, 0.415123},{0.547157, 0.136929, 0.413511},{0.553392, 0.139134, 0.411829},{0.559624, 0.141346, 0.410078},{0.565854, 0.143567, 0.408258},{0.572081, 0.145797, 0.406369},{0.578304, 0.148039, 0.404411},{0.584521, 0.150294, 0.402385},{0.590734, 0.152563, 0.400290},{0.596940, 0.154848, 0.398125},{0.603139, 0.157151, 0.395891},{0.609330, 0.159474, 0.393589},{0.615513, 0.161817, 0.391219},{0.621685, 0.164184, 0.388781},{0.627847, 0.166575, 0.386276},{0.633998, 0.168992, 0.383704},{0.640135, 0.171438, 0.381065},{0.646260, 0.173914, 0.378359},{0.652369, 0.176421, 0.375586},{0.658463, 0.178962, 0.372748},{0.664540, 0.181539, 0.369846},{0.670599, 0.184153, 0.366879},{0.676638, 0.186807, 0.363849},{0.682656, 0.189501, 0.360757},{0.688653, 0.192239, 0.357603},{0.694627, 0.195021, 0.354388},{0.700576, 0.197851, 0.351113},{0.706500, 0.200728, 0.347777},{0.712396, 0.203656, 0.344383},{0.718264, 0.206636, 0.340931},{0.724103, 0.209670, 0.337424},{0.729909, 0.212759, 0.333861},{0.735683, 0.215906, 0.330245},{0.741423, 0.219112, 0.326576},{0.747127, 0.222378, 0.322856},{0.752794, 0.225706, 0.319085},{0.758422, 0.229097, 0.315266},{0.764010, 0.232554, 0.311399},{0.769556, 0.236077, 0.307485},{0.775059, 0.239667, 0.303526},{0.780517, 0.243327, 0.299523},{0.785929, 0.247056, 0.295477},{0.791293, 0.250856, 0.291390},{0.796607, 0.254728, 0.287264},{0.801871, 0.258674, 0.283099},{0.807082, 0.262692, 0.278898},{0.812239, 0.266786, 0.274661},{0.817341, 0.270954, 0.270390},{0.822386, 0.275197, 0.266085},{0.827372, 0.279517, 0.261750},{0.832299, 0.283913, 0.257383},{0.837165, 0.288385, 0.252988},{0.841969, 0.292933, 0.248564},{0.846709, 0.297559, 0.244113},{0.851384, 0.302260, 0.239636},{0.855992, 0.307038, 0.235133},{0.860533, 0.311892, 0.230606},{0.865006, 0.316822, 0.226055},{0.869409, 0.321827, 0.221482},{0.873741, 0.326906, 0.216886},{0.878001, 0.332060, 0.212268},{0.882188, 0.337287, 0.207628},{0.886302, 0.342586, 0.202968},{0.890341, 0.347957, 0.198286},{0.894305, 0.353399, 0.193584},{0.898192, 0.358911, 0.188860},{0.902003, 0.364492, 0.184116},{0.905735, 0.370140, 0.179350},{0.909390, 0.375856, 0.174563},{0.912966, 0.381636, 0.169755},{0.916462, 0.387481, 0.164924},{0.919879, 0.393389, 0.160070},{0.923215, 0.399359, 0.155193},{0.926470, 0.405389, 0.150292},{0.929644, 0.411479, 0.145367},{0.932737, 0.417627, 0.140417},{0.935747, 0.423831, 0.135440},{0.938675, 0.430091, 0.130438},{0.941521, 0.436405, 0.125409},{0.944285, 0.442772, 0.120354},{0.946965, 0.449191, 0.115272},{0.949562, 0.455660, 0.110164},{0.952075, 0.462178, 0.105031},{0.954506, 0.468744, 0.099874},{0.956852, 0.475356, 0.094695},{0.959114, 0.482014, 0.089499},{0.961293, 0.488716, 0.084289},{0.963387, 0.495462, 0.079073},{0.965397, 0.502249, 0.073859},{0.967322, 0.509078, 0.068659},{0.969163, 0.515946, 0.063488},{0.970919, 0.522853, 0.058367},{0.972590, 0.529798, 0.053324},{0.974176, 0.536780, 0.048392},{0.975677, 0.543798, 0.043618},{0.977092, 0.550850, 0.039050},{0.978422, 0.557937, 0.034931},{0.979666, 0.565057, 0.031409},{0.980824, 0.572209, 0.028508},{0.981895, 0.579392, 0.026250},{0.982881, 0.586606, 0.024661},{0.983779, 0.593849, 0.023770},{0.984591, 0.601122, 0.023606},{0.985315, 0.608422, 0.024202},{0.985952, 0.615750, 0.025592},{0.986502, 0.623105, 0.027814},{0.986964, 0.630485, 0.030908},{0.987337, 0.637890, 0.034916},{0.987622, 0.645320, 0.039886},{0.987819, 0.652773, 0.045581},{0.987926, 0.660250, 0.051750},{0.987945, 0.667748, 0.058329},{0.987874, 0.675267, 0.065257},{0.987714, 0.682807, 0.072489},{0.987464, 0.690366, 0.079990},{0.987124, 0.697944, 0.087731},{0.986694, 0.705540, 0.095694},{0.986175, 0.713153, 0.103863},{0.985566, 0.720782, 0.112229},{0.984865, 0.728427, 0.120785},{0.984075, 0.736087, 0.129527},{0.983196, 0.743758, 0.138453},{0.982228, 0.751442, 0.147565},{0.981173, 0.759135, 0.156863},{0.980032, 0.766837, 0.166353},{0.978806, 0.774545, 0.176037},{0.977497, 0.782258, 0.185923},{0.976108, 0.789974, 0.196018},{0.974638, 0.797692, 0.206332},{0.973088, 0.805409, 0.216877},{0.971468, 0.813122, 0.227658},{0.969783, 0.820825, 0.238686},{0.968041, 0.828515, 0.249972},{0.966243, 0.836191, 0.261534},{0.964394, 0.843848, 0.273391},{0.962517, 0.851476, 0.285546},{0.960626, 0.859069, 0.298010},{0.958720, 0.866624, 0.310820},{0.956834, 0.874129, 0.323974},{0.954997, 0.881569, 0.337475},{0.953215, 0.888942, 0.351369},{0.951546, 0.896226, 0.365627},{0.950018, 0.903409, 0.380271},{0.948683, 0.910473, 0.395289},{0.947594, 0.917399, 0.410665},{0.946809, 0.924168, 0.426373},{0.946392, 0.930761, 0.442367},{0.946403, 0.937159, 0.458592},{0.946903, 0.943348, 0.474970},{0.947937, 0.949318, 0.491426},{0.949545, 0.955063, 0.507860},{0.951740, 0.960587, 0.524203},{0.954529, 0.965896, 0.540361},{0.957896, 0.971003, 0.556275},{0.961812, 0.975924, 0.571925},{0.966249, 0.980678, 0.587206},{0.971162, 0.985282, 0.602154},{0.976511, 0.989753, 0.616760},{0.982257, 0.994109, 0.631017},{0.988362, 0.998364, 0.644924}}}, + {"magma", {{0.001462, 0.000466, 0.013866},{0.002258, 0.001295, 0.018331},{0.003279, 0.002305, 0.023708},{0.004512, 0.003490, 0.029965},{0.005950, 0.004843, 0.037130},{0.007588, 0.006356, 0.044973},{0.009426, 0.008022, 0.052844},{0.011465, 0.009828, 0.060750},{0.013708, 0.011771, 0.068667},{0.016156, 0.013840, 0.076603},{0.018815, 0.016026, 0.084584},{0.021692, 0.018320, 0.092610},{0.024792, 0.020715, 0.100676},{0.028123, 0.023201, 0.108787},{0.031696, 0.025765, 0.116965},{0.035520, 0.028397, 0.125209},{0.039608, 0.031090, 0.133515},{0.043830, 0.033830, 0.141886},{0.048062, 0.036607, 0.150327},{0.052320, 0.039407, 0.158841},{0.056615, 0.042160, 0.167446},{0.060949, 0.044794, 0.176129},{0.065330, 0.047318, 0.184892},{0.069764, 0.049726, 0.193735},{0.074257, 0.052017, 0.202660},{0.078815, 0.054184, 0.211667},{0.083446, 0.056225, 0.220755},{0.088155, 0.058133, 0.229922},{0.092949, 0.059904, 0.239164},{0.097833, 0.061531, 0.248477},{0.102815, 0.063010, 0.257854},{0.107899, 0.064335, 0.267289},{0.113094, 0.065492, 0.276784},{0.118405, 0.066479, 0.286321},{0.123833, 0.067295, 0.295879},{0.129380, 0.067935, 0.305443},{0.135053, 0.068391, 0.315000},{0.140858, 0.068654, 0.324538},{0.146785, 0.068738, 0.334011},{0.152839, 0.068637, 0.343404},{0.159018, 0.068354, 0.352688},{0.165308, 0.067911, 0.361816},{0.171713, 0.067305, 0.370771},{0.178212, 0.066576, 0.379497},{0.184801, 0.065732, 0.387973},{0.191460, 0.064818, 0.396152},{0.198177, 0.063862, 0.404009},{0.204935, 0.062907, 0.411514},{0.211718, 0.061992, 0.418647},{0.218512, 0.061158, 0.425392},{0.225302, 0.060445, 0.431742},{0.232077, 0.059889, 0.437695},{0.238826, 0.059517, 0.443256},{0.245543, 0.059352, 0.448436},{0.252220, 0.059415, 0.453248},{0.258857, 0.059706, 0.457710},{0.265447, 0.060237, 0.461840},{0.271994, 0.060994, 0.465660},{0.278493, 0.061978, 0.469190},{0.284951, 0.063168, 0.472451},{0.291366, 0.064553, 0.475462},{0.297740, 0.066117, 0.478243},{0.304081, 0.067835, 0.480812},{0.310382, 0.069702, 0.483186},{0.316654, 0.071690, 0.485380},{0.322899, 0.073782, 0.487408},{0.329114, 0.075972, 0.489287},{0.335308, 0.078236, 0.491024},{0.341482, 0.080564, 0.492631},{0.347636, 0.082946, 0.494121},{0.353773, 0.085373, 0.495501},{0.359898, 0.087831, 0.496778},{0.366012, 0.090314, 0.497960},{0.372116, 0.092816, 0.499053},{0.378211, 0.095332, 0.500067},{0.384299, 0.097855, 0.501002},{0.390384, 0.100379, 0.501864},{0.396467, 0.102902, 0.502658},{0.402548, 0.105420, 0.503386},{0.408629, 0.107930, 0.504052},{0.414709, 0.110431, 0.504662},{0.420791, 0.112920, 0.505215},{0.426877, 0.115395, 0.505714},{0.432967, 0.117855, 0.506160},{0.439062, 0.120298, 0.506555},{0.445163, 0.122724, 0.506901},{0.451271, 0.125132, 0.507198},{0.457386, 0.127522, 0.507448},{0.463508, 0.129893, 0.507652},{0.469640, 0.132245, 0.507809},{0.475780, 0.134577, 0.507921},{0.481929, 0.136891, 0.507989},{0.488088, 0.139186, 0.508011},{0.494258, 0.141462, 0.507988},{0.500438, 0.143719, 0.507920},{0.506629, 0.145958, 0.507806},{0.512831, 0.148179, 0.507648},{0.519045, 0.150383, 0.507443},{0.525270, 0.152569, 0.507192},{0.531507, 0.154739, 0.506895},{0.537755, 0.156894, 0.506551},{0.544015, 0.159033, 0.506159},{0.550287, 0.161158, 0.505719},{0.556571, 0.163269, 0.505230},{0.562866, 0.165368, 0.504692},{0.569172, 0.167454, 0.504105},{0.575490, 0.169530, 0.503466},{0.581819, 0.171596, 0.502777},{0.588158, 0.173652, 0.502035},{0.594508, 0.175701, 0.501241},{0.600868, 0.177743, 0.500394},{0.607238, 0.179779, 0.499492},{0.613617, 0.181811, 0.498536},{0.620005, 0.183840, 0.497524},{0.626401, 0.185867, 0.496456},{0.632805, 0.187893, 0.495332},{0.639216, 0.189921, 0.494150},{0.645633, 0.191952, 0.492910},{0.652056, 0.193986, 0.491611},{0.658483, 0.196027, 0.490253},{0.664915, 0.198075, 0.488836},{0.671349, 0.200133, 0.487358},{0.677786, 0.202203, 0.485819},{0.684224, 0.204286, 0.484219},{0.690661, 0.206384, 0.482558},{0.697098, 0.208501, 0.480835},{0.703532, 0.210638, 0.479049},{0.709962, 0.212797, 0.477201},{0.716387, 0.214982, 0.475290},{0.722805, 0.217194, 0.473316},{0.729216, 0.219437, 0.471279},{0.735616, 0.221713, 0.469180},{0.742004, 0.224025, 0.467018},{0.748378, 0.226377, 0.464794},{0.754737, 0.228772, 0.462509},{0.761077, 0.231214, 0.460162},{0.767398, 0.233705, 0.457755},{0.773695, 0.236249, 0.455289},{0.779968, 0.238851, 0.452765},{0.786212, 0.241514, 0.450184},{0.792427, 0.244242, 0.447543},{0.798608, 0.247040, 0.444848},{0.804752, 0.249911, 0.442102},{0.810855, 0.252861, 0.439305},{0.816914, 0.255895, 0.436461},{0.822926, 0.259016, 0.433573},{0.828886, 0.262229, 0.430644},{0.834791, 0.265540, 0.427671},{0.840636, 0.268953, 0.424666},{0.846416, 0.272473, 0.421631},{0.852126, 0.276106, 0.418573},{0.857763, 0.279857, 0.415496},{0.863320, 0.283729, 0.412403},{0.868793, 0.287728, 0.409303},{0.874176, 0.291859, 0.406205},{0.879464, 0.296125, 0.403118},{0.884651, 0.300530, 0.400047},{0.889731, 0.305079, 0.397002},{0.894700, 0.309773, 0.393995},{0.899552, 0.314616, 0.391037},{0.904281, 0.319610, 0.388137},{0.908884, 0.324755, 0.385308},{0.913354, 0.330052, 0.382563},{0.917689, 0.335500, 0.379915},{0.921884, 0.341098, 0.377376},{0.925937, 0.346844, 0.374959},{0.929845, 0.352734, 0.372677},{0.933606, 0.358764, 0.370541},{0.937221, 0.364929, 0.368567},{0.940687, 0.371224, 0.366762},{0.944006, 0.377643, 0.365136},{0.947180, 0.384178, 0.363701},{0.950210, 0.390820, 0.362468},{0.953099, 0.397563, 0.361438},{0.955849, 0.404400, 0.360619},{0.958464, 0.411324, 0.360014},{0.960949, 0.418323, 0.359630},{0.963310, 0.425390, 0.359469},{0.965549, 0.432519, 0.359529},{0.967671, 0.439703, 0.359810},{0.969680, 0.446936, 0.360311},{0.971582, 0.454210, 0.361030},{0.973381, 0.461520, 0.361965},{0.975082, 0.468861, 0.363111},{0.976690, 0.476226, 0.364466},{0.978210, 0.483612, 0.366025},{0.979645, 0.491014, 0.367783},{0.981000, 0.498428, 0.369734},{0.982279, 0.505851, 0.371874},{0.983485, 0.513280, 0.374198},{0.984622, 0.520713, 0.376698},{0.985693, 0.528148, 0.379371},{0.986700, 0.535582, 0.382210},{0.987646, 0.543015, 0.385210},{0.988533, 0.550446, 0.388365},{0.989363, 0.557873, 0.391671},{0.990138, 0.565296, 0.395122},{0.990871, 0.572706, 0.398714},{0.991558, 0.580107, 0.402441},{0.992196, 0.587502, 0.406299},{0.992785, 0.594891, 0.410283},{0.993326, 0.602275, 0.414390},{0.993834, 0.609644, 0.418613},{0.994309, 0.616999, 0.422950},{0.994738, 0.624350, 0.427397},{0.995122, 0.631696, 0.431951},{0.995480, 0.639027, 0.436607},{0.995810, 0.646344, 0.441361},{0.996096, 0.653659, 0.446213},{0.996341, 0.660969, 0.451160},{0.996580, 0.668256, 0.456192},{0.996775, 0.675541, 0.461314},{0.996925, 0.682828, 0.466526},{0.997077, 0.690088, 0.471811},{0.997186, 0.697349, 0.477182},{0.997254, 0.704611, 0.482635},{0.997325, 0.711848, 0.488154},{0.997351, 0.719089, 0.493755},{0.997351, 0.726324, 0.499428},{0.997341, 0.733545, 0.505167},{0.997285, 0.740772, 0.510983},{0.997228, 0.747981, 0.516859},{0.997138, 0.755190, 0.522806},{0.997019, 0.762398, 0.528821},{0.996898, 0.769591, 0.534892},{0.996727, 0.776795, 0.541039},{0.996571, 0.783977, 0.547233},{0.996369, 0.791167, 0.553499},{0.996162, 0.798348, 0.559820},{0.995932, 0.805527, 0.566202},{0.995680, 0.812706, 0.572645},{0.995424, 0.819875, 0.579140},{0.995131, 0.827052, 0.585701},{0.994851, 0.834213, 0.592307},{0.994524, 0.841387, 0.598983},{0.994222, 0.848540, 0.605696},{0.993866, 0.855711, 0.612482},{0.993545, 0.862859, 0.619299},{0.993170, 0.870024, 0.626189},{0.992831, 0.877168, 0.633109},{0.992440, 0.884330, 0.640099},{0.992089, 0.891470, 0.647116},{0.991688, 0.898627, 0.654202},{0.991332, 0.905763, 0.661309},{0.990930, 0.912915, 0.668481},{0.990570, 0.920049, 0.675675},{0.990175, 0.927196, 0.682926},{0.989815, 0.934329, 0.690198},{0.989434, 0.941470, 0.697519},{0.989077, 0.948604, 0.704863},{0.988717, 0.955742, 0.712242},{0.988367, 0.962878, 0.719649},{0.988033, 0.970012, 0.727077},{0.987691, 0.977154, 0.734536},{0.987387, 0.984288, 0.742002},{0.987053, 0.991438, 0.749504}}}, + {"cividis", {{0.000000, 0.135112, 0.304751},{0.000000, 0.138068, 0.311105},{0.000000, 0.141013, 0.317579},{0.000000, 0.143951, 0.323982},{0.000000, 0.146877, 0.330479},{0.000000, 0.149791, 0.337065},{0.000000, 0.152673, 0.343704},{0.000000, 0.155377, 0.350500},{0.000000, 0.157932, 0.357521},{0.000000, 0.160495, 0.364534},{0.000000, 0.163058, 0.371608},{0.000000, 0.165621, 0.378769},{0.000000, 0.168204, 0.385902},{0.000000, 0.170800, 0.393100},{0.000000, 0.173420, 0.400353},{0.000000, 0.176082, 0.407577},{0.000000, 0.178802, 0.414764},{0.000000, 0.181610, 0.421859},{0.000000, 0.184550, 0.428802},{0.000000, 0.186915, 0.435532},{0.000000, 0.188769, 0.439563},{0.000000, 0.190950, 0.441085},{0.000000, 0.193366, 0.441561},{0.003602, 0.195911, 0.441564},{0.017852, 0.198528, 0.441248},{0.032110, 0.201199, 0.440785},{0.046205, 0.203903, 0.440196},{0.058378, 0.206629, 0.439531},{0.068968, 0.209372, 0.438863},{0.078624, 0.212122, 0.438105},{0.087465, 0.214879, 0.437342},{0.095645, 0.217643, 0.436593},{0.103401, 0.220406, 0.435790},{0.110658, 0.223170, 0.435067},{0.117612, 0.225935, 0.434308},{0.124291, 0.228697, 0.433547},{0.130669, 0.231458, 0.432840},{0.136830, 0.234216, 0.432148},{0.142852, 0.236972, 0.431404},{0.148638, 0.239724, 0.430752},{0.154261, 0.242475, 0.430120},{0.159733, 0.245221, 0.429528},{0.165113, 0.247965, 0.428908},{0.170362, 0.250707, 0.428325},{0.175490, 0.253444, 0.427790},{0.180503, 0.256180, 0.427299},{0.185453, 0.258914, 0.426788},{0.190303, 0.261644, 0.426329},{0.195057, 0.264372, 0.425924},{0.199764, 0.267099, 0.425497},{0.204385, 0.269823, 0.425126},{0.208926, 0.272546, 0.424809},{0.213431, 0.275266, 0.424480},{0.217863, 0.277985, 0.424206},{0.222264, 0.280702, 0.423914},{0.226598, 0.283419, 0.423678},{0.230871, 0.286134, 0.423498},{0.235120, 0.288848, 0.423304},{0.239312, 0.291562, 0.423167},{0.243485, 0.294274, 0.423014},{0.247605, 0.296986, 0.422917},{0.251675, 0.299698, 0.422873},{0.255731, 0.302409, 0.422814},{0.259740, 0.305120, 0.422810},{0.263738, 0.307831, 0.422789},{0.267693, 0.310542, 0.422821},{0.271639, 0.313253, 0.422837},{0.275513, 0.315965, 0.422979},{0.279411, 0.318677, 0.423031},{0.283240, 0.321390, 0.423211},{0.287065, 0.324103, 0.423373},{0.290884, 0.326816, 0.423517},{0.294669, 0.329531, 0.423716},{0.298421, 0.332247, 0.423973},{0.302169, 0.334963, 0.424213},{0.305886, 0.337681, 0.424512},{0.309601, 0.340399, 0.424790},{0.313287, 0.343120, 0.425120},{0.316941, 0.345842, 0.425512},{0.320595, 0.348565, 0.425889},{0.324250, 0.351289, 0.426250},{0.327875, 0.354016, 0.426670},{0.331474, 0.356744, 0.427144},{0.335073, 0.359474, 0.427605},{0.338673, 0.362206, 0.428053},{0.342246, 0.364939, 0.428559},{0.345793, 0.367676, 0.429127},{0.349341, 0.370414, 0.429685},{0.352892, 0.373153, 0.430226},{0.356418, 0.375896, 0.430823},{0.359916, 0.378641, 0.431501},{0.363446, 0.381388, 0.432075},{0.366923, 0.384139, 0.432796},{0.370430, 0.386890, 0.433428},{0.373884, 0.389646, 0.434209},{0.377371, 0.392404, 0.434890},{0.380830, 0.395164, 0.435653},{0.384268, 0.397928, 0.436475},{0.387705, 0.400694, 0.437305},{0.391151, 0.403464, 0.438096},{0.394568, 0.406236, 0.438986},{0.397991, 0.409011, 0.439848},{0.401418, 0.411790, 0.440708},{0.404820, 0.414572, 0.441642},{0.408226, 0.417357, 0.442570},{0.411607, 0.420145, 0.443577},{0.414992, 0.422937, 0.444578},{0.418383, 0.425733, 0.445560},{0.421748, 0.428531, 0.446640},{0.425120, 0.431334, 0.447692},{0.428462, 0.434140, 0.448864},{0.431817, 0.436950, 0.449982},{0.435168, 0.439763, 0.451134},{0.438504, 0.442580, 0.452341},{0.441810, 0.445402, 0.453659},{0.445148, 0.448226, 0.454885},{0.448447, 0.451053, 0.456264},{0.451759, 0.453887, 0.457582},{0.455072, 0.456718, 0.458976},{0.458366, 0.459552, 0.460457},{0.461616, 0.462405, 0.461969},{0.464947, 0.465241, 0.463395},{0.468254, 0.468083, 0.464908},{0.471501, 0.470960, 0.466357},{0.474812, 0.473832, 0.467681},{0.478186, 0.476699, 0.468845},{0.481622, 0.479573, 0.469767},{0.485141, 0.482451, 0.470384},{0.488697, 0.485318, 0.471008},{0.492278, 0.488198, 0.471453},{0.495913, 0.491076, 0.471751},{0.499552, 0.493960, 0.472032},{0.503185, 0.496851, 0.472305},{0.506866, 0.499743, 0.472432},{0.510540, 0.502643, 0.472550},{0.514226, 0.505546, 0.472640},{0.517920, 0.508454, 0.472707},{0.521643, 0.511367, 0.472639},{0.525348, 0.514285, 0.472660},{0.529086, 0.517207, 0.472543},{0.532829, 0.520135, 0.472401},{0.536553, 0.523067, 0.472352},{0.540307, 0.526005, 0.472163},{0.544069, 0.528948, 0.471947},{0.547840, 0.531895, 0.471704},{0.551612, 0.534849, 0.471439},{0.555393, 0.537807, 0.471147},{0.559181, 0.540771, 0.470829},{0.562972, 0.543741, 0.470488},{0.566802, 0.546715, 0.469988},{0.570607, 0.549695, 0.469593},{0.574417, 0.552682, 0.469172},{0.578236, 0.555673, 0.468724},{0.582087, 0.558670, 0.468118},{0.585916, 0.561674, 0.467618},{0.589753, 0.564682, 0.467090},{0.593622, 0.567697, 0.466401},{0.597469, 0.570718, 0.465821},{0.601354, 0.573743, 0.465074},{0.605211, 0.576777, 0.464441},{0.609105, 0.579816, 0.463638},{0.612977, 0.582861, 0.462950},{0.616852, 0.585913, 0.462237},{0.620765, 0.588970, 0.461351},{0.624654, 0.592034, 0.460583},{0.628576, 0.595104, 0.459641},{0.632506, 0.598180, 0.458668},{0.636412, 0.601264, 0.457818},{0.640352, 0.604354, 0.456791},{0.644270, 0.607450, 0.455886},{0.648222, 0.610553, 0.454801},{0.652178, 0.613664, 0.453689},{0.656114, 0.616780, 0.452702},{0.660082, 0.619904, 0.451534},{0.664055, 0.623034, 0.450338},{0.668008, 0.626171, 0.449270},{0.671991, 0.629316, 0.448018},{0.675981, 0.632468, 0.446736},{0.679979, 0.635626, 0.445424},{0.683950, 0.638793, 0.444251},{0.687957, 0.641966, 0.442886},{0.691971, 0.645145, 0.441491},{0.695985, 0.648334, 0.440072},{0.700008, 0.651529, 0.438624},{0.704037, 0.654731, 0.437147},{0.708067, 0.657942, 0.435647},{0.712105, 0.661160, 0.434117},{0.716177, 0.664384, 0.432386},{0.720222, 0.667618, 0.430805},{0.724274, 0.670859, 0.429194},{0.728334, 0.674107, 0.427554},{0.732422, 0.677364, 0.425717},{0.736488, 0.680629, 0.424028},{0.740589, 0.683900, 0.422131},{0.744664, 0.687181, 0.420393},{0.748772, 0.690470, 0.418448},{0.752886, 0.693766, 0.416472},{0.756975, 0.697071, 0.414659},{0.761096, 0.700384, 0.412638},{0.765223, 0.703705, 0.410587},{0.769353, 0.707035, 0.408516},{0.773486, 0.710373, 0.406422},{0.777651, 0.713719, 0.404112},{0.781795, 0.717074, 0.401966},{0.785965, 0.720438, 0.399613},{0.790116, 0.723810, 0.397423},{0.794298, 0.727190, 0.395016},{0.798480, 0.730580, 0.392597},{0.802667, 0.733978, 0.390153},{0.806859, 0.737385, 0.387684},{0.811054, 0.740801, 0.385198},{0.815274, 0.744226, 0.382504},{0.819499, 0.747659, 0.379785},{0.823729, 0.751101, 0.377043},{0.827959, 0.754553, 0.374292},{0.832192, 0.758014, 0.371529},{0.836429, 0.761483, 0.368747},{0.840693, 0.764962, 0.365746},{0.844957, 0.768450, 0.362741},{0.849223, 0.771947, 0.359729},{0.853515, 0.775454, 0.356500},{0.857809, 0.778969, 0.353259},{0.862105, 0.782494, 0.350011},{0.866421, 0.786028, 0.346571},{0.870717, 0.789572, 0.343333},{0.875057, 0.793125, 0.339685},{0.879378, 0.796687, 0.336241},{0.883720, 0.800258, 0.332599},{0.888081, 0.803839, 0.328770},{0.892440, 0.807430, 0.324968},{0.896818, 0.811030, 0.320982},{0.901195, 0.814639, 0.317021},{0.905589, 0.818257, 0.312889},{0.910000, 0.821885, 0.308594},{0.914407, 0.825522, 0.304348},{0.918828, 0.829168, 0.299960},{0.923279, 0.832822, 0.295244},{0.927724, 0.836486, 0.290611},{0.932180, 0.840159, 0.285880},{0.936660, 0.843841, 0.280876},{0.941147, 0.847530, 0.275815},{0.945654, 0.851228, 0.270532},{0.950178, 0.854933, 0.265085},{0.954725, 0.858646, 0.259365},{0.959284, 0.862365, 0.253563},{0.963872, 0.866089, 0.247445},{0.968469, 0.869819, 0.241310},{0.973114, 0.873550, 0.234677},{0.977780, 0.877281, 0.227954},{0.982497, 0.881008, 0.220878},{0.987293, 0.884718, 0.213336},{0.992218, 0.888385, 0.205468},{0.994847, 0.892954, 0.203445},{0.995249, 0.898384, 0.207561},{0.995503, 0.903866, 0.212370},{0.995737, 0.909344, 0.217772}}}, + {"gnuplot", {{0.000000, 0.000000, 0.000000},{0.062622, 0.000000, 0.024637},{0.088561, 0.000000, 0.049260},{0.108465, 0.000002, 0.073853},{0.125245, 0.000004, 0.098400},{0.140028, 0.000008, 0.122888},{0.153393, 0.000013, 0.147302},{0.165683, 0.000021, 0.171626},{0.177123, 0.000031, 0.195845},{0.187867, 0.000044, 0.219946},{0.198030, 0.000060, 0.243914},{0.207695, 0.000080, 0.267733},{0.216930, 0.000104, 0.291390},{0.225788, 0.000132, 0.314870},{0.234312, 0.000165, 0.338158},{0.242536, 0.000204, 0.361242},{0.250490, 0.000247, 0.384106},{0.258199, 0.000296, 0.406737},{0.265684, 0.000352, 0.429121},{0.272965, 0.000414, 0.451244},{0.280056, 0.000482, 0.473094},{0.286972, 0.000559, 0.494656},{0.293725, 0.000642, 0.515918},{0.300327, 0.000734, 0.536867},{0.306786, 0.000834, 0.557489},{0.313112, 0.000942, 0.577774},{0.319313, 0.001060, 0.597707},{0.325396, 0.001187, 0.617278},{0.331367, 0.001324, 0.636474},{0.337232, 0.001471, 0.655284},{0.342997, 0.001628, 0.673696},{0.348667, 0.001797, 0.691698},{0.354246, 0.001976, 0.709281},{0.359738, 0.002167, 0.726434},{0.365148, 0.002370, 0.743145},{0.370479, 0.002586, 0.759405},{0.375735, 0.002814, 0.775204},{0.380917, 0.003055, 0.790532},{0.386031, 0.003309, 0.805381},{0.391077, 0.003577, 0.819740},{0.396059, 0.003860, 0.833602},{0.400979, 0.004157, 0.846958},{0.405840, 0.004468, 0.859800},{0.410643, 0.004795, 0.872120},{0.415390, 0.005137, 0.883910},{0.420084, 0.005496, 0.895163},{0.424726, 0.005870, 0.905873},{0.429318, 0.006261, 0.916034},{0.433861, 0.006670, 0.925638},{0.438357, 0.007095, 0.934680},{0.442807, 0.007539, 0.943154},{0.447214, 0.008000, 0.951057},{0.451577, 0.008480, 0.958381},{0.455898, 0.008979, 0.965124},{0.460179, 0.009496, 0.971281},{0.464420, 0.010034, 0.976848},{0.468623, 0.010591, 0.981823},{0.472789, 0.011169, 0.986201},{0.476918, 0.011767, 0.989980},{0.481012, 0.012386, 0.993159},{0.485071, 0.013027, 0.995734},{0.489097, 0.013689, 0.997705},{0.493089, 0.014373, 0.999070},{0.497050, 0.015080, 0.999829},{0.500979, 0.015810, 0.999981},{0.504878, 0.016562, 0.999526},{0.508747, 0.017338, 0.998464},{0.512587, 0.018139, 0.996795},{0.516398, 0.018963, 0.994522},{0.520181, 0.019812, 0.991645},{0.523937, 0.020686, 0.988165},{0.527666, 0.021585, 0.984086},{0.531369, 0.022510, 0.979410},{0.535046, 0.023461, 0.974139},{0.538699, 0.024439, 0.968276},{0.542326, 0.025443, 0.961826},{0.545930, 0.026474, 0.954791},{0.549510, 0.027533, 0.947177},{0.553066, 0.028620, 0.938988},{0.556600, 0.029735, 0.930229},{0.560112, 0.030878, 0.920906},{0.563602, 0.032050, 0.911023},{0.567070, 0.033252, 0.900587},{0.570517, 0.034484, 0.889604},{0.573944, 0.035745, 0.878081},{0.577350, 0.037037, 0.866025},{0.580737, 0.038360, 0.853444},{0.584103, 0.039713, 0.840344},{0.587450, 0.041099, 0.826734},{0.590779, 0.042516, 0.812622},{0.594089, 0.043965, 0.798017},{0.597380, 0.045447, 0.782928},{0.600653, 0.046962, 0.767363},{0.603909, 0.048510, 0.751332},{0.607147, 0.050091, 0.734845},{0.610368, 0.051707, 0.717912},{0.613572, 0.053357, 0.700543},{0.616759, 0.055042, 0.682749},{0.619930, 0.056762, 0.664540},{0.623085, 0.058517, 0.645928},{0.626224, 0.060309, 0.626924},{0.629348, 0.062136, 0.607539},{0.632456, 0.064000, 0.587785},{0.635548, 0.065901, 0.567675},{0.638626, 0.067839, 0.547220},{0.641689, 0.069815, 0.526432},{0.644737, 0.071829, 0.505325},{0.647771, 0.073881, 0.483911},{0.650791, 0.075972, 0.462204},{0.653797, 0.078101, 0.440216},{0.656790, 0.080271, 0.417960},{0.659768, 0.082480, 0.395451},{0.662733, 0.084729, 0.372702},{0.665686, 0.087019, 0.349727},{0.668625, 0.089350, 0.326539},{0.671551, 0.091722, 0.303153},{0.674464, 0.094135, 0.279583},{0.677365, 0.096591, 0.255843},{0.680254, 0.099089, 0.231948},{0.683130, 0.101630, 0.207912},{0.685994, 0.104213, 0.183750},{0.688847, 0.106840, 0.159476},{0.691687, 0.109511, 0.135105},{0.694516, 0.112226, 0.110653},{0.697334, 0.114986, 0.086133},{0.700140, 0.117790, 0.061561},{0.702935, 0.120640, 0.036951},{0.705719, 0.123535, 0.012320},{0.708492, 0.126476, 0.000000},{0.711254, 0.129464, 0.000000},{0.714006, 0.132498, 0.000000},{0.716746, 0.135579, 0.000000},{0.719477, 0.138708, 0.000000},{0.722197, 0.141884, 0.000000},{0.724907, 0.145109, 0.000000},{0.727607, 0.148382, 0.000000},{0.730297, 0.151704, 0.000000},{0.732977, 0.155075, 0.000000},{0.735647, 0.158495, 0.000000},{0.738308, 0.161966, 0.000000},{0.740959, 0.165487, 0.000000},{0.743600, 0.169058, 0.000000},{0.746232, 0.172681, 0.000000},{0.748855, 0.176355, 0.000000},{0.751469, 0.180081, 0.000000},{0.754074, 0.183858, 0.000000},{0.756670, 0.187689, 0.000000},{0.759257, 0.191572, 0.000000},{0.761835, 0.195508, 0.000000},{0.764404, 0.199498, 0.000000},{0.766965, 0.203542, 0.000000},{0.769517, 0.207640, 0.000000},{0.772061, 0.211792, 0.000000},{0.774597, 0.216000, 0.000000},{0.777124, 0.220263, 0.000000},{0.779643, 0.224582, 0.000000},{0.782154, 0.228957, 0.000000},{0.784657, 0.233388, 0.000000},{0.787152, 0.237876, 0.000000},{0.789639, 0.242421, 0.000000},{0.792118, 0.247024, 0.000000},{0.794590, 0.251685, 0.000000},{0.797053, 0.256404, 0.000000},{0.799510, 0.261181, 0.000000},{0.801958, 0.266018, 0.000000},{0.804400, 0.270914, 0.000000},{0.806834, 0.275870, 0.000000},{0.809260, 0.280885, 0.000000},{0.811679, 0.285961, 0.000000},{0.814092, 0.291098, 0.000000},{0.816497, 0.296296, 0.000000},{0.818895, 0.301556, 0.000000},{0.821285, 0.306877, 0.000000},{0.823669, 0.312261, 0.000000},{0.826047, 0.317707, 0.000000},{0.828417, 0.323217, 0.000000},{0.830780, 0.328789, 0.000000},{0.833137, 0.334425, 0.000000},{0.835487, 0.340126, 0.000000},{0.837831, 0.345890, 0.000000},{0.840168, 0.351720, 0.000000},{0.842499, 0.357615, 0.000000},{0.844823, 0.363575, 0.000000},{0.847141, 0.369601, 0.000000},{0.849452, 0.375693, 0.000000},{0.851757, 0.381852, 0.000000},{0.854056, 0.388077, 0.000000},{0.856349, 0.394370, 0.000000},{0.858635, 0.400731, 0.000000},{0.860916, 0.407160, 0.000000},{0.863191, 0.413657, 0.000000},{0.865459, 0.420223, 0.000000},{0.867722, 0.426858, 0.000000},{0.869979, 0.433562, 0.000000},{0.872230, 0.440336, 0.000000},{0.874475, 0.447181, 0.000000},{0.876714, 0.454096, 0.000000},{0.878948, 0.461082, 0.000000},{0.881176, 0.468139, 0.000000},{0.883398, 0.475268, 0.000000},{0.885615, 0.482469, 0.000000},{0.887826, 0.489742, 0.000000},{0.890032, 0.497088, 0.000000},{0.892232, 0.504507, 0.000000},{0.894427, 0.512000, 0.000000},{0.896617, 0.519566, 0.000000},{0.898801, 0.527207, 0.000000},{0.900980, 0.534922, 0.000000},{0.903154, 0.542712, 0.000000},{0.905322, 0.550577, 0.000000},{0.907485, 0.558518, 0.000000},{0.909643, 0.566535, 0.000000},{0.911796, 0.574628, 0.000000},{0.913944, 0.582798, 0.000000},{0.916087, 0.591045, 0.000000},{0.918225, 0.599370, 0.000000},{0.920358, 0.607772, 0.000000},{0.922486, 0.616252, 0.000000},{0.924609, 0.624811, 0.000000},{0.926727, 0.633449, 0.000000},{0.928841, 0.642166, 0.000000},{0.930949, 0.650963, 0.000000},{0.933053, 0.659840, 0.000000},{0.935152, 0.668797, 0.000000},{0.937247, 0.677834, 0.000000},{0.939336, 0.686953, 0.000000},{0.941422, 0.696153, 0.000000},{0.943502, 0.705435, 0.000000},{0.945578, 0.714799, 0.000000},{0.947649, 0.724246, 0.000000},{0.949716, 0.733775, 0.000000},{0.951779, 0.743388, 0.000000},{0.953836, 0.753084, 0.000000},{0.955890, 0.762864, 0.000000},{0.957939, 0.772729, 0.000000},{0.959984, 0.782678, 0.000000},{0.962024, 0.792712, 0.000000},{0.964060, 0.802832, 0.000000},{0.966092, 0.813037, 0.000000},{0.968119, 0.823329, 0.000000},{0.970143, 0.833706, 0.000000},{0.972162, 0.844171, 0.000000},{0.974176, 0.854723, 0.000000},{0.976187, 0.865363, 0.000000},{0.978194, 0.876090, 0.000000},{0.980196, 0.886906, 0.000000},{0.982194, 0.897811, 0.000000},{0.984189, 0.908804, 0.000000},{0.986179, 0.919887, 0.000000},{0.988165, 0.931060, 0.000000},{0.990148, 0.942322, 0.000000},{0.992126, 0.953675, 0.000000},{0.994100, 0.965119, 0.000000},{0.996071, 0.976655, 0.000000},{0.998037, 0.988281, 0.000000},{1.000000, 1.000000, 0.000000}}}, + {"gnuplot2", {{0.000000, 0.000000, 0.000000},{0.000000, 0.000000, 0.015686},{0.000000, 0.000000, 0.031373},{0.000000, 0.000000, 0.047059},{0.000000, 0.000000, 0.062745},{0.000000, 0.000000, 0.078431},{0.000000, 0.000000, 0.094118},{0.000000, 0.000000, 0.109804},{0.000000, 0.000000, 0.125490},{0.000000, 0.000000, 0.141176},{0.000000, 0.000000, 0.156863},{0.000000, 0.000000, 0.172549},{0.000000, 0.000000, 0.188235},{0.000000, 0.000000, 0.203922},{0.000000, 0.000000, 0.219608},{0.000000, 0.000000, 0.235294},{0.000000, 0.000000, 0.250980},{0.000000, 0.000000, 0.266667},{0.000000, 0.000000, 0.282353},{0.000000, 0.000000, 0.298039},{0.000000, 0.000000, 0.313725},{0.000000, 0.000000, 0.329412},{0.000000, 0.000000, 0.345098},{0.000000, 0.000000, 0.360784},{0.000000, 0.000000, 0.376471},{0.000000, 0.000000, 0.392157},{0.000000, 0.000000, 0.407843},{0.000000, 0.000000, 0.423529},{0.000000, 0.000000, 0.439216},{0.000000, 0.000000, 0.454902},{0.000000, 0.000000, 0.470588},{0.000000, 0.000000, 0.486275},{0.000000, 0.000000, 0.501961},{0.000000, 0.000000, 0.517647},{0.000000, 0.000000, 0.533333},{0.000000, 0.000000, 0.549020},{0.000000, 0.000000, 0.564706},{0.000000, 0.000000, 0.580392},{0.000000, 0.000000, 0.596078},{0.000000, 0.000000, 0.611765},{0.000000, 0.000000, 0.627451},{0.000000, 0.000000, 0.643137},{0.000000, 0.000000, 0.658824},{0.000000, 0.000000, 0.674510},{0.000000, 0.000000, 0.690196},{0.000000, 0.000000, 0.705882},{0.000000, 0.000000, 0.721569},{0.000000, 0.000000, 0.737255},{0.000000, 0.000000, 0.752941},{0.000000, 0.000000, 0.768627},{0.000000, 0.000000, 0.784314},{0.000000, 0.000000, 0.800000},{0.000000, 0.000000, 0.815686},{0.000000, 0.000000, 0.831373},{0.000000, 0.000000, 0.847059},{0.000000, 0.000000, 0.862745},{0.000000, 0.000000, 0.878431},{0.000000, 0.000000, 0.894118},{0.000000, 0.000000, 0.909804},{0.000000, 0.000000, 0.925490},{0.000000, 0.000000, 0.941176},{0.000000, 0.000000, 0.956863},{0.000000, 0.000000, 0.972549},{0.000000, 0.000000, 0.988235},{0.003064, 0.000000, 1.000000},{0.015319, 0.000000, 1.000000},{0.027574, 0.000000, 1.000000},{0.039828, 0.000000, 1.000000},{0.052083, 0.000000, 1.000000},{0.064338, 0.000000, 1.000000},{0.076593, 0.000000, 1.000000},{0.088848, 0.000000, 1.000000},{0.101103, 0.000000, 1.000000},{0.113358, 0.000000, 1.000000},{0.125613, 0.000000, 1.000000},{0.137868, 0.000000, 1.000000},{0.150123, 0.000000, 1.000000},{0.162377, 0.000000, 1.000000},{0.174632, 0.000000, 1.000000},{0.186887, 0.000000, 1.000000},{0.199142, 0.000000, 1.000000},{0.211397, 0.000000, 1.000000},{0.223652, 0.000000, 1.000000},{0.235907, 0.000000, 1.000000},{0.248162, 0.000000, 1.000000},{0.260417, 0.000000, 1.000000},{0.272672, 0.000000, 1.000000},{0.284926, 0.000000, 1.000000},{0.297181, 0.000000, 1.000000},{0.309436, 0.000000, 1.000000},{0.321691, 0.000000, 1.000000},{0.333946, 0.000000, 1.000000},{0.346201, 0.000000, 1.000000},{0.358456, 0.000000, 1.000000},{0.370711, 0.000000, 1.000000},{0.382966, 0.000000, 1.000000},{0.395221, 0.000000, 1.000000},{0.407475, 0.000000, 1.000000},{0.419730, 0.000000, 1.000000},{0.431985, 0.000000, 1.000000},{0.444240, 0.000000, 1.000000},{0.456495, 0.000000, 1.000000},{0.468750, 0.000000, 1.000000},{0.481005, 0.000000, 1.000000},{0.493260, 0.000000, 1.000000},{0.505515, 0.000000, 1.000000},{0.517770, 0.000000, 1.000000},{0.530025, 0.000000, 1.000000},{0.542279, 0.007059, 0.992941},{0.554534, 0.014902, 0.985098},{0.566789, 0.022745, 0.977255},{0.579044, 0.030588, 0.969412},{0.591299, 0.038431, 0.961569},{0.603554, 0.046275, 0.953725},{0.615809, 0.054118, 0.945882},{0.628064, 0.061961, 0.938039},{0.640319, 0.069804, 0.930196},{0.652574, 0.077647, 0.922353},{0.664828, 0.085490, 0.914510},{0.677083, 0.093333, 0.906667},{0.689338, 0.101176, 0.898824},{0.701593, 0.109020, 0.890980},{0.713848, 0.116863, 0.883137},{0.726103, 0.124706, 0.875294},{0.738358, 0.132549, 0.867451},{0.750613, 0.140392, 0.859608},{0.762868, 0.148235, 0.851765},{0.775123, 0.156078, 0.843922},{0.787377, 0.163922, 0.836078},{0.799632, 0.171765, 0.828235},{0.811887, 0.179608, 0.820392},{0.824142, 0.187451, 0.812549},{0.836397, 0.195294, 0.804706},{0.848652, 0.203137, 0.796863},{0.860907, 0.210980, 0.789020},{0.873162, 0.218824, 0.781176},{0.885417, 0.226667, 0.773333},{0.897672, 0.234510, 0.765490},{0.909926, 0.242353, 0.757647},{0.922181, 0.250196, 0.749804},{0.934436, 0.258039, 0.741961},{0.946691, 0.265882, 0.734118},{0.958946, 0.273725, 0.726275},{0.971201, 0.281569, 0.718431},{0.983456, 0.289412, 0.710588},{0.995711, 0.297255, 0.702745},{1.000000, 0.305098, 0.694902},{1.000000, 0.312941, 0.687059},{1.000000, 0.320784, 0.679216},{1.000000, 0.328627, 0.671373},{1.000000, 0.336471, 0.663529},{1.000000, 0.344314, 0.655686},{1.000000, 0.352157, 0.647843},{1.000000, 0.360000, 0.640000},{1.000000, 0.367843, 0.632157},{1.000000, 0.375686, 0.624314},{1.000000, 0.383529, 0.616471},{1.000000, 0.391373, 0.608627},{1.000000, 0.399216, 0.600784},{1.000000, 0.407059, 0.592941},{1.000000, 0.414902, 0.585098},{1.000000, 0.422745, 0.577255},{1.000000, 0.430588, 0.569412},{1.000000, 0.438431, 0.561569},{1.000000, 0.446275, 0.553725},{1.000000, 0.454118, 0.545882},{1.000000, 0.461961, 0.538039},{1.000000, 0.469804, 0.530196},{1.000000, 0.477647, 0.522353},{1.000000, 0.485490, 0.514510},{1.000000, 0.493333, 0.506667},{1.000000, 0.501176, 0.498824},{1.000000, 0.509020, 0.490980},{1.000000, 0.516863, 0.483137},{1.000000, 0.524706, 0.475294},{1.000000, 0.532549, 0.467451},{1.000000, 0.540392, 0.459608},{1.000000, 0.548235, 0.451765},{1.000000, 0.556078, 0.443922},{1.000000, 0.563922, 0.436078},{1.000000, 0.571765, 0.428235},{1.000000, 0.579608, 0.420392},{1.000000, 0.587451, 0.412549},{1.000000, 0.595294, 0.404706},{1.000000, 0.603137, 0.396863},{1.000000, 0.610980, 0.389020},{1.000000, 0.618824, 0.381176},{1.000000, 0.626667, 0.373333},{1.000000, 0.634510, 0.365490},{1.000000, 0.642353, 0.357647},{1.000000, 0.650196, 0.349804},{1.000000, 0.658039, 0.341961},{1.000000, 0.665882, 0.334118},{1.000000, 0.673725, 0.326275},{1.000000, 0.681569, 0.318431},{1.000000, 0.689412, 0.310588},{1.000000, 0.697255, 0.302745},{1.000000, 0.705098, 0.294902},{1.000000, 0.712941, 0.287059},{1.000000, 0.720784, 0.279216},{1.000000, 0.728627, 0.271373},{1.000000, 0.736471, 0.263529},{1.000000, 0.744314, 0.255686},{1.000000, 0.752157, 0.247843},{1.000000, 0.760000, 0.240000},{1.000000, 0.767843, 0.232157},{1.000000, 0.775686, 0.224314},{1.000000, 0.783529, 0.216471},{1.000000, 0.791373, 0.208627},{1.000000, 0.799216, 0.200784},{1.000000, 0.807059, 0.192941},{1.000000, 0.814902, 0.185098},{1.000000, 0.822745, 0.177255},{1.000000, 0.830588, 0.169412},{1.000000, 0.838431, 0.161569},{1.000000, 0.846275, 0.153725},{1.000000, 0.854118, 0.145882},{1.000000, 0.861961, 0.138039},{1.000000, 0.869804, 0.130196},{1.000000, 0.877647, 0.122353},{1.000000, 0.885490, 0.114510},{1.000000, 0.893333, 0.106667},{1.000000, 0.901176, 0.098824},{1.000000, 0.909020, 0.090980},{1.000000, 0.916863, 0.083137},{1.000000, 0.924706, 0.075294},{1.000000, 0.932549, 0.067451},{1.000000, 0.940392, 0.059608},{1.000000, 0.948235, 0.051765},{1.000000, 0.956078, 0.043922},{1.000000, 0.963922, 0.036078},{1.000000, 0.971765, 0.028235},{1.000000, 0.979608, 0.020392},{1.000000, 0.987451, 0.012549},{1.000000, 0.995294, 0.004706},{1.000000, 1.000000, 0.019608},{1.000000, 1.000000, 0.068627},{1.000000, 1.000000, 0.117647},{1.000000, 1.000000, 0.166667},{1.000000, 1.000000, 0.215686},{1.000000, 1.000000, 0.264706},{1.000000, 1.000000, 0.313725},{1.000000, 1.000000, 0.362745},{1.000000, 1.000000, 0.411765},{1.000000, 1.000000, 0.460784},{1.000000, 1.000000, 0.509804},{1.000000, 1.000000, 0.558824},{1.000000, 1.000000, 0.607843},{1.000000, 1.000000, 0.656863},{1.000000, 1.000000, 0.705882},{1.000000, 1.000000, 0.754902},{1.000000, 1.000000, 0.803922},{1.000000, 1.000000, 0.852941},{1.000000, 1.000000, 0.901961},{1.000000, 1.000000, 0.950980},{1.000000, 1.000000, 1.000000}}}, + {"gist_rainbow", {{1.000000, 0.000000, 0.160000},{1.000000, 0.000000, 0.139085},{1.000000, 0.000000, 0.118170},{1.000000, 0.000000, 0.097255},{1.000000, 0.000000, 0.076340},{1.000000, 0.000000, 0.055425},{1.000000, 0.000000, 0.034510},{1.000000, 0.000000, 0.013595},{1.000000, 0.007419, 0.000000},{1.000000, 0.028617, 0.000000},{1.000000, 0.049815, 0.000000},{1.000000, 0.071012, 0.000000},{1.000000, 0.092210, 0.000000},{1.000000, 0.113408, 0.000000},{1.000000, 0.134605, 0.000000},{1.000000, 0.155803, 0.000000},{1.000000, 0.177001, 0.000000},{1.000000, 0.198198, 0.000000},{1.000000, 0.219396, 0.000000},{1.000000, 0.240594, 0.000000},{1.000000, 0.261791, 0.000000},{1.000000, 0.282989, 0.000000},{1.000000, 0.304187, 0.000000},{1.000000, 0.325384, 0.000000},{1.000000, 0.346582, 0.000000},{1.000000, 0.367780, 0.000000},{1.000000, 0.388977, 0.000000},{1.000000, 0.410175, 0.000000},{1.000000, 0.431373, 0.000000},{1.000000, 0.452570, 0.000000},{1.000000, 0.473768, 0.000000},{1.000000, 0.494966, 0.000000},{1.000000, 0.516163, 0.000000},{1.000000, 0.537361, 0.000000},{1.000000, 0.558559, 0.000000},{1.000000, 0.579756, 0.000000},{1.000000, 0.600954, 0.000000},{1.000000, 0.622152, 0.000000},{1.000000, 0.643349, 0.000000},{1.000000, 0.664547, 0.000000},{1.000000, 0.685745, 0.000000},{1.000000, 0.706942, 0.000000},{1.000000, 0.728140, 0.000000},{1.000000, 0.749338, 0.000000},{1.000000, 0.770535, 0.000000},{1.000000, 0.791733, 0.000000},{1.000000, 0.812931, 0.000000},{1.000000, 0.834128, 0.000000},{1.000000, 0.855326, 0.000000},{1.000000, 0.876524, 0.000000},{1.000000, 0.897721, 0.000000},{1.000000, 0.918919, 0.000000},{1.000000, 0.940117, 0.000000},{1.000000, 0.961314, 0.000000},{1.000000, 0.982512, 0.000000},{0.996290, 1.000000, 0.000000},{0.975093, 1.000000, 0.000000},{0.953895, 1.000000, 0.000000},{0.932697, 1.000000, 0.000000},{0.911500, 1.000000, 0.000000},{0.890302, 1.000000, 0.000000},{0.869104, 1.000000, 0.000000},{0.847907, 1.000000, 0.000000},{0.826709, 1.000000, 0.000000},{0.805511, 1.000000, 0.000000},{0.784314, 1.000000, 0.000000},{0.763116, 1.000000, 0.000000},{0.741918, 1.000000, 0.000000},{0.720721, 1.000000, 0.000000},{0.699523, 1.000000, 0.000000},{0.678325, 1.000000, 0.000000},{0.657128, 1.000000, 0.000000},{0.635930, 1.000000, 0.000000},{0.614732, 1.000000, 0.000000},{0.593535, 1.000000, 0.000000},{0.572337, 1.000000, 0.000000},{0.551139, 1.000000, 0.000000},{0.529942, 1.000000, 0.000000},{0.508744, 1.000000, 0.000000},{0.487546, 1.000000, 0.000000},{0.466349, 1.000000, 0.000000},{0.445151, 1.000000, 0.000000},{0.423953, 1.000000, 0.000000},{0.402756, 1.000000, 0.000000},{0.381558, 1.000000, 0.000000},{0.360360, 1.000000, 0.000000},{0.339163, 1.000000, 0.000000},{0.317965, 1.000000, 0.000000},{0.296767, 1.000000, 0.000000},{0.275570, 1.000000, 0.000000},{0.254372, 1.000000, 0.000000},{0.233174, 1.000000, 0.000000},{0.211977, 1.000000, 0.000000},{0.190779, 1.000000, 0.000000},{0.169581, 1.000000, 0.000000},{0.148384, 1.000000, 0.000000},{0.127186, 1.000000, 0.000000},{0.105988, 1.000000, 0.000000},{0.084791, 1.000000, 0.000000},{0.063593, 1.000000, 0.000000},{0.042395, 1.000000, 0.000000},{0.021198, 1.000000, 0.000000},{0.000000, 1.000000, 0.000000},{0.000000, 1.000000, 0.021084},{0.000000, 1.000000, 0.042167},{0.000000, 1.000000, 0.063251},{0.000000, 1.000000, 0.084335},{0.000000, 1.000000, 0.105419},{0.000000, 1.000000, 0.126502},{0.000000, 1.000000, 0.147586},{0.000000, 1.000000, 0.168670},{0.000000, 1.000000, 0.189753},{0.000000, 1.000000, 0.210837},{0.000000, 1.000000, 0.231921},{0.000000, 1.000000, 0.253004},{0.000000, 1.000000, 0.274088},{0.000000, 1.000000, 0.295172},{0.000000, 1.000000, 0.316256},{0.000000, 1.000000, 0.337339},{0.000000, 1.000000, 0.358423},{0.000000, 1.000000, 0.379507},{0.000000, 1.000000, 0.400590},{0.000000, 1.000000, 0.421674},{0.000000, 1.000000, 0.442758},{0.000000, 1.000000, 0.463841},{0.000000, 1.000000, 0.484925},{0.000000, 1.000000, 0.506009},{0.000000, 1.000000, 0.527093},{0.000000, 1.000000, 0.548176},{0.000000, 1.000000, 0.569260},{0.000000, 1.000000, 0.590344},{0.000000, 1.000000, 0.611427},{0.000000, 1.000000, 0.632511},{0.000000, 1.000000, 0.653595},{0.000000, 1.000000, 0.674678},{0.000000, 1.000000, 0.695762},{0.000000, 1.000000, 0.716846},{0.000000, 1.000000, 0.737930},{0.000000, 1.000000, 0.759013},{0.000000, 1.000000, 0.780097},{0.000000, 1.000000, 0.801181},{0.000000, 1.000000, 0.822264},{0.000000, 1.000000, 0.843348},{0.000000, 1.000000, 0.864432},{0.000000, 1.000000, 0.885515},{0.000000, 1.000000, 0.906599},{0.000000, 1.000000, 0.927683},{0.000000, 1.000000, 0.948767},{0.000000, 1.000000, 0.969850},{0.000000, 1.000000, 0.990934},{0.000000, 0.987852, 1.000000},{0.000000, 0.966539, 1.000000},{0.000000, 0.945226, 1.000000},{0.000000, 0.923913, 1.000000},{0.000000, 0.902600, 1.000000},{0.000000, 0.881287, 1.000000},{0.000000, 0.859974, 1.000000},{0.000000, 0.838662, 1.000000},{0.000000, 0.817349, 1.000000},{0.000000, 0.796036, 1.000000},{0.000000, 0.774723, 1.000000},{0.000000, 0.753410, 1.000000},{0.000000, 0.732097, 1.000000},{0.000000, 0.710784, 1.000000},{0.000000, 0.689471, 1.000000},{0.000000, 0.668159, 1.000000},{0.000000, 0.646846, 1.000000},{0.000000, 0.625533, 1.000000},{0.000000, 0.604220, 1.000000},{0.000000, 0.582907, 1.000000},{0.000000, 0.561594, 1.000000},{0.000000, 0.540281, 1.000000},{0.000000, 0.518968, 1.000000},{0.000000, 0.497656, 1.000000},{0.000000, 0.476343, 1.000000},{0.000000, 0.455030, 1.000000},{0.000000, 0.433717, 1.000000},{0.000000, 0.412404, 1.000000},{0.000000, 0.391091, 1.000000},{0.000000, 0.369778, 1.000000},{0.000000, 0.348465, 1.000000},{0.000000, 0.327153, 1.000000},{0.000000, 0.305840, 1.000000},{0.000000, 0.284527, 1.000000},{0.000000, 0.263214, 1.000000},{0.000000, 0.241901, 1.000000},{0.000000, 0.220588, 1.000000},{0.000000, 0.199275, 1.000000},{0.000000, 0.177962, 1.000000},{0.000000, 0.156650, 1.000000},{0.000000, 0.135337, 1.000000},{0.000000, 0.114024, 1.000000},{0.000000, 0.092711, 1.000000},{0.000000, 0.071398, 1.000000},{0.000000, 0.050085, 1.000000},{0.000000, 0.028772, 1.000000},{0.000000, 0.007460, 1.000000},{0.013853, 0.000000, 1.000000},{0.035166, 0.000000, 1.000000},{0.056479, 0.000000, 1.000000},{0.077792, 0.000000, 1.000000},{0.099105, 0.000000, 1.000000},{0.120418, 0.000000, 1.000000},{0.141731, 0.000000, 1.000000},{0.163043, 0.000000, 1.000000},{0.184356, 0.000000, 1.000000},{0.205669, 0.000000, 1.000000},{0.226982, 0.000000, 1.000000},{0.248295, 0.000000, 1.000000},{0.269608, 0.000000, 1.000000},{0.290921, 0.000000, 1.000000},{0.312234, 0.000000, 1.000000},{0.333546, 0.000000, 1.000000},{0.354859, 0.000000, 1.000000},{0.376172, 0.000000, 1.000000},{0.397485, 0.000000, 1.000000},{0.418798, 0.000000, 1.000000},{0.440111, 0.000000, 1.000000},{0.461424, 0.000000, 1.000000},{0.482737, 0.000000, 1.000000},{0.504049, 0.000000, 1.000000},{0.525362, 0.000000, 1.000000},{0.546675, 0.000000, 1.000000},{0.567988, 0.000000, 1.000000},{0.589301, 0.000000, 1.000000},{0.610614, 0.000000, 1.000000},{0.631927, 0.000000, 1.000000},{0.653240, 0.000000, 1.000000},{0.674552, 0.000000, 1.000000},{0.695865, 0.000000, 1.000000},{0.717178, 0.000000, 1.000000},{0.738491, 0.000000, 1.000000},{0.759804, 0.000000, 1.000000},{0.781117, 0.000000, 1.000000},{0.802430, 0.000000, 1.000000},{0.823743, 0.000000, 1.000000},{0.845055, 0.000000, 1.000000},{0.866368, 0.000000, 1.000000},{0.887681, 0.000000, 1.000000},{0.908994, 0.000000, 1.000000},{0.930307, 0.000000, 1.000000},{0.951620, 0.000000, 1.000000},{0.972933, 0.000000, 1.000000},{0.994246, 0.000000, 1.000000},{1.000000, 0.000000, 0.984442},{1.000000, 0.000000, 0.963129},{1.000000, 0.000000, 0.941816},{1.000000, 0.000000, 0.920503},{1.000000, 0.000000, 0.899190},{1.000000, 0.000000, 0.877877},{1.000000, 0.000000, 0.856564},{1.000000, 0.000000, 0.835251},{1.000000, 0.000000, 0.813939},{1.000000, 0.000000, 0.792626},{1.000000, 0.000000, 0.771313},{1.000000, 0.000000, 0.750000}}}, + {"rainbow", {{0.500000, 0.000000, 1.000000},{0.492157, 0.012320, 0.999981},{0.484314, 0.024637, 0.999924},{0.476471, 0.036951, 0.999829},{0.468627, 0.049260, 0.999696},{0.460784, 0.061561, 0.999526},{0.452941, 0.073853, 0.999317},{0.445098, 0.086133, 0.999070},{0.437255, 0.098400, 0.998786},{0.429412, 0.110653, 0.998464},{0.421569, 0.122888, 0.998103},{0.413725, 0.135105, 0.997705},{0.405882, 0.147302, 0.997269},{0.398039, 0.159476, 0.996795},{0.390196, 0.171626, 0.996284},{0.382353, 0.183750, 0.995734},{0.374510, 0.195845, 0.995147},{0.366667, 0.207912, 0.994522},{0.358824, 0.219946, 0.993859},{0.350980, 0.231948, 0.993159},{0.343137, 0.243914, 0.992421},{0.335294, 0.255843, 0.991645},{0.327451, 0.267733, 0.990831},{0.319608, 0.279583, 0.989980},{0.311765, 0.291390, 0.989092},{0.303922, 0.303153, 0.988165},{0.296078, 0.314870, 0.987202},{0.288235, 0.326539, 0.986201},{0.280392, 0.338158, 0.985162},{0.272549, 0.349727, 0.984086},{0.264706, 0.361242, 0.982973},{0.256863, 0.372702, 0.981823},{0.249020, 0.384106, 0.980635},{0.241176, 0.395451, 0.979410},{0.233333, 0.406737, 0.978148},{0.225490, 0.417960, 0.976848},{0.217647, 0.429121, 0.975512},{0.209804, 0.440216, 0.974139},{0.201961, 0.451244, 0.972728},{0.194118, 0.462204, 0.971281},{0.186275, 0.473094, 0.969797},{0.178431, 0.483911, 0.968276},{0.170588, 0.494656, 0.966718},{0.162745, 0.505325, 0.965124},{0.154902, 0.515918, 0.963493},{0.147059, 0.526432, 0.961826},{0.139216, 0.536867, 0.960122},{0.131373, 0.547220, 0.958381},{0.123529, 0.557489, 0.956604},{0.115686, 0.567675, 0.954791},{0.107843, 0.577774, 0.952942},{0.100000, 0.587785, 0.951057},{0.092157, 0.597707, 0.949135},{0.084314, 0.607539, 0.947177},{0.076471, 0.617278, 0.945184},{0.068627, 0.626924, 0.943154},{0.060784, 0.636474, 0.941089},{0.052941, 0.645928, 0.938988},{0.045098, 0.655284, 0.936852},{0.037255, 0.664540, 0.934680},{0.029412, 0.673696, 0.932472},{0.021569, 0.682749, 0.930229},{0.013725, 0.691698, 0.927951},{0.005882, 0.700543, 0.925638},{0.001961, 0.709281, 0.923289},{0.009804, 0.717912, 0.920906},{0.017647, 0.726434, 0.918487},{0.025490, 0.734845, 0.916034},{0.033333, 0.743145, 0.913545},{0.041176, 0.751332, 0.911023},{0.049020, 0.759405, 0.908465},{0.056863, 0.767363, 0.905873},{0.064706, 0.775204, 0.903247},{0.072549, 0.782928, 0.900587},{0.080392, 0.790532, 0.897892},{0.088235, 0.798017, 0.895163},{0.096078, 0.805381, 0.892401},{0.103922, 0.812622, 0.889604},{0.111765, 0.819740, 0.886774},{0.119608, 0.826734, 0.883910},{0.127451, 0.833602, 0.881012},{0.135294, 0.840344, 0.878081},{0.143137, 0.846958, 0.875117},{0.150980, 0.853444, 0.872120},{0.158824, 0.859800, 0.869089},{0.166667, 0.866025, 0.866025},{0.174510, 0.872120, 0.862929},{0.182353, 0.878081, 0.859800},{0.190196, 0.883910, 0.856638},{0.198039, 0.889604, 0.853444},{0.205882, 0.895163, 0.850217},{0.213725, 0.900587, 0.846958},{0.221569, 0.905873, 0.843667},{0.229412, 0.911023, 0.840344},{0.237255, 0.916034, 0.836989},{0.245098, 0.920906, 0.833602},{0.252941, 0.925638, 0.830184},{0.260784, 0.930229, 0.826734},{0.268627, 0.934680, 0.823253},{0.276471, 0.938988, 0.819740},{0.284314, 0.943154, 0.816197},{0.292157, 0.947177, 0.812622},{0.300000, 0.951057, 0.809017},{0.307843, 0.954791, 0.805381},{0.315686, 0.958381, 0.801714},{0.323529, 0.961826, 0.798017},{0.331373, 0.965124, 0.794290},{0.339216, 0.968276, 0.790532},{0.347059, 0.971281, 0.786745},{0.354902, 0.974139, 0.782928},{0.362745, 0.976848, 0.779081},{0.370588, 0.979410, 0.775204},{0.378431, 0.981823, 0.771298},{0.386275, 0.984086, 0.767363},{0.394118, 0.986201, 0.763398},{0.401961, 0.988165, 0.759405},{0.409804, 0.989980, 0.755383},{0.417647, 0.991645, 0.751332},{0.425490, 0.993159, 0.747253},{0.433333, 0.994522, 0.743145},{0.441176, 0.995734, 0.739009},{0.449020, 0.996795, 0.734845},{0.456863, 0.997705, 0.730653},{0.464706, 0.998464, 0.726434},{0.472549, 0.999070, 0.722186},{0.480392, 0.999526, 0.717912},{0.488235, 0.999829, 0.713610},{0.496078, 0.999981, 0.709281},{0.503922, 0.999981, 0.704926},{0.511765, 0.999829, 0.700543},{0.519608, 0.999526, 0.696134},{0.527451, 0.999070, 0.691698},{0.535294, 0.998464, 0.687237},{0.543137, 0.997705, 0.682749},{0.550980, 0.996795, 0.678235},{0.558824, 0.995734, 0.673696},{0.566667, 0.994522, 0.669131},{0.574510, 0.993159, 0.664540},{0.582353, 0.991645, 0.659925},{0.590196, 0.989980, 0.655284},{0.598039, 0.988165, 0.650618},{0.605882, 0.986201, 0.645928},{0.613725, 0.984086, 0.641213},{0.621569, 0.981823, 0.636474},{0.629412, 0.979410, 0.631711},{0.637255, 0.976848, 0.626924},{0.645098, 0.974139, 0.622113},{0.652941, 0.971281, 0.617278},{0.660784, 0.968276, 0.612420},{0.668627, 0.965124, 0.607539},{0.676471, 0.961826, 0.602635},{0.684314, 0.958381, 0.597707},{0.692157, 0.954791, 0.592758},{0.700000, 0.951057, 0.587785},{0.707843, 0.947177, 0.582791},{0.715686, 0.943154, 0.577774},{0.723529, 0.938988, 0.572735},{0.731373, 0.934680, 0.567675},{0.739216, 0.930229, 0.562593},{0.747059, 0.925638, 0.557489},{0.754902, 0.920906, 0.552365},{0.762745, 0.916034, 0.547220},{0.770588, 0.911023, 0.542053},{0.778431, 0.905873, 0.536867},{0.786275, 0.900587, 0.531659},{0.794118, 0.895163, 0.526432},{0.801961, 0.889604, 0.521185},{0.809804, 0.883910, 0.515918},{0.817647, 0.878081, 0.510631},{0.825490, 0.872120, 0.505325},{0.833333, 0.866025, 0.500000},{0.841176, 0.859800, 0.494656},{0.849020, 0.853444, 0.489293},{0.856863, 0.846958, 0.483911},{0.864706, 0.840344, 0.478512},{0.872549, 0.833602, 0.473094},{0.880392, 0.826734, 0.467658},{0.888235, 0.819740, 0.462204},{0.896078, 0.812622, 0.456733},{0.903922, 0.805381, 0.451244},{0.911765, 0.798017, 0.445738},{0.919608, 0.790532, 0.440216},{0.927451, 0.782928, 0.434676},{0.935294, 0.775204, 0.429121},{0.943137, 0.767363, 0.423549},{0.950980, 0.759405, 0.417960},{0.958824, 0.751332, 0.412356},{0.966667, 0.743145, 0.406737},{0.974510, 0.734845, 0.401102},{0.982353, 0.726434, 0.395451},{0.990196, 0.717912, 0.389786},{0.998039, 0.709281, 0.384106},{1.000000, 0.700543, 0.378411},{1.000000, 0.691698, 0.372702},{1.000000, 0.682749, 0.366979},{1.000000, 0.673696, 0.361242},{1.000000, 0.664540, 0.355491},{1.000000, 0.655284, 0.349727},{1.000000, 0.645928, 0.343949},{1.000000, 0.636474, 0.338158},{1.000000, 0.626924, 0.332355},{1.000000, 0.617278, 0.326539},{1.000000, 0.607539, 0.320710},{1.000000, 0.597707, 0.314870},{1.000000, 0.587785, 0.309017},{1.000000, 0.577774, 0.303153},{1.000000, 0.567675, 0.297277},{1.000000, 0.557489, 0.291390},{1.000000, 0.547220, 0.285492},{1.000000, 0.536867, 0.279583},{1.000000, 0.526432, 0.273663},{1.000000, 0.515918, 0.267733},{1.000000, 0.505325, 0.261793},{1.000000, 0.494656, 0.255843},{1.000000, 0.483911, 0.249883},{1.000000, 0.473094, 0.243914},{1.000000, 0.462204, 0.237935},{1.000000, 0.451244, 0.231948},{1.000000, 0.440216, 0.225951},{1.000000, 0.429121, 0.219946},{1.000000, 0.417960, 0.213933},{1.000000, 0.406737, 0.207912},{1.000000, 0.395451, 0.201882},{1.000000, 0.384106, 0.195845},{1.000000, 0.372702, 0.189801},{1.000000, 0.361242, 0.183750},{1.000000, 0.349727, 0.177691},{1.000000, 0.338158, 0.171626},{1.000000, 0.326539, 0.165554},{1.000000, 0.314870, 0.159476},{1.000000, 0.303153, 0.153392},{1.000000, 0.291390, 0.147302},{1.000000, 0.279583, 0.141206},{1.000000, 0.267733, 0.135105},{1.000000, 0.255843, 0.128999},{1.000000, 0.243914, 0.122888},{1.000000, 0.231948, 0.116773},{1.000000, 0.219946, 0.110653},{1.000000, 0.207912, 0.104528},{1.000000, 0.195845, 0.098400},{1.000000, 0.183750, 0.092268},{1.000000, 0.171626, 0.086133},{1.000000, 0.159476, 0.079994},{1.000000, 0.147302, 0.073853},{1.000000, 0.135105, 0.067708},{1.000000, 0.122888, 0.061561},{1.000000, 0.110653, 0.055411},{1.000000, 0.098400, 0.049260},{1.000000, 0.086133, 0.043107},{1.000000, 0.073853, 0.036951},{1.000000, 0.061561, 0.030795},{1.000000, 0.049260, 0.024637},{1.000000, 0.036951, 0.018479},{1.000000, 0.024637, 0.012320},{1.000000, 0.012320, 0.006160},{1.000000, 0.000000, 0.000000}}}, + {"spectral", {{0.619608, 0.003922, 0.258824},{0.628066, 0.013303, 0.260823},{0.636524, 0.022684, 0.262822},{0.644983, 0.032065, 0.264821},{0.653441, 0.041446, 0.266820},{0.661899, 0.050827, 0.268820},{0.670358, 0.060208, 0.270819},{0.678816, 0.069589, 0.272818},{0.687274, 0.078970, 0.274817},{0.695732, 0.088351, 0.276817},{0.704191, 0.097732, 0.278816},{0.712649, 0.107113, 0.280815},{0.721107, 0.116494, 0.282814},{0.729566, 0.125875, 0.284814},{0.738024, 0.135256, 0.286813},{0.746482, 0.144637, 0.288812},{0.754940, 0.154018, 0.290811},{0.763399, 0.163399, 0.292810},{0.771857, 0.172780, 0.294810},{0.780315, 0.182161, 0.296809},{0.788774, 0.191542, 0.298808},{0.797232, 0.200923, 0.300807},{0.805690, 0.210304, 0.302807},{0.814148, 0.219685, 0.304806},{0.822607, 0.229066, 0.306805},{0.831065, 0.238447, 0.308804},{0.837678, 0.246751, 0.308881},{0.842445, 0.253979, 0.307036},{0.847213, 0.261207, 0.305190},{0.851980, 0.268435, 0.303345},{0.856747, 0.275663, 0.301499},{0.861515, 0.282891, 0.299654},{0.866282, 0.290119, 0.297809},{0.871050, 0.297347, 0.295963},{0.875817, 0.304575, 0.294118},{0.880584, 0.311803, 0.292272},{0.885352, 0.319031, 0.290427},{0.890119, 0.326259, 0.288581},{0.894887, 0.333487, 0.286736},{0.899654, 0.340715, 0.284890},{0.904421, 0.347943, 0.283045},{0.909189, 0.355171, 0.281200},{0.913956, 0.362399, 0.279354},{0.918724, 0.369627, 0.277509},{0.923491, 0.376855, 0.275663},{0.928258, 0.384083, 0.273818},{0.933026, 0.391311, 0.271972},{0.937793, 0.398539, 0.270127},{0.942561, 0.405767, 0.268281},{0.947328, 0.412995, 0.266436},{0.952095, 0.420223, 0.264591},{0.956863, 0.427451, 0.262745},{0.958247, 0.437447, 0.267359},{0.959631, 0.447443, 0.271972},{0.961015, 0.457439, 0.276586},{0.962399, 0.467436, 0.281200},{0.963783, 0.477432, 0.285813},{0.965167, 0.487428, 0.290427},{0.966551, 0.497424, 0.295040},{0.967935, 0.507420, 0.299654},{0.969319, 0.517416, 0.304268},{0.970704, 0.527413, 0.308881},{0.972088, 0.537409, 0.313495},{0.973472, 0.547405, 0.318108},{0.974856, 0.557401, 0.322722},{0.976240, 0.567397, 0.327336},{0.977624, 0.577393, 0.331949},{0.979008, 0.587389, 0.336563},{0.980392, 0.597386, 0.341176},{0.981776, 0.607382, 0.345790},{0.983160, 0.617378, 0.350404},{0.984544, 0.627374, 0.355017},{0.985928, 0.637370, 0.359631},{0.987313, 0.647366, 0.364245},{0.988697, 0.657363, 0.368858},{0.990081, 0.667359, 0.373472},{0.991465, 0.677355, 0.378085},{0.992234, 0.686198, 0.383622},{0.992388, 0.693887, 0.390081},{0.992541, 0.701576, 0.396540},{0.992695, 0.709266, 0.402999},{0.992849, 0.716955, 0.409458},{0.993003, 0.724644, 0.415917},{0.993156, 0.732334, 0.422376},{0.993310, 0.740023, 0.428835},{0.993464, 0.747712, 0.435294},{0.993618, 0.755402, 0.441753},{0.993772, 0.763091, 0.448212},{0.993925, 0.770780, 0.454671},{0.994079, 0.778470, 0.461130},{0.994233, 0.786159, 0.467589},{0.994387, 0.793849, 0.474048},{0.994541, 0.801538, 0.480507},{0.994694, 0.809227, 0.486967},{0.994848, 0.816917, 0.493426},{0.995002, 0.824606, 0.499885},{0.995156, 0.832295, 0.506344},{0.995309, 0.839985, 0.512803},{0.995463, 0.847674, 0.519262},{0.995617, 0.855363, 0.525721},{0.995771, 0.863053, 0.532180},{0.995925, 0.870742, 0.538639},{0.996078, 0.878431, 0.545098},{0.996232, 0.883199, 0.553095},{0.996386, 0.887966, 0.561092},{0.996540, 0.892734, 0.569089},{0.996694, 0.897501, 0.577086},{0.996847, 0.902268, 0.585083},{0.997001, 0.907036, 0.593080},{0.997155, 0.911803, 0.601077},{0.997309, 0.916571, 0.609073},{0.997463, 0.921338, 0.617070},{0.997616, 0.926105, 0.625067},{0.997770, 0.930873, 0.633064},{0.997924, 0.935640, 0.641061},{0.998078, 0.940408, 0.649058},{0.998231, 0.945175, 0.657055},{0.998385, 0.949942, 0.665052},{0.998539, 0.954710, 0.673049},{0.998693, 0.959477, 0.681046},{0.998847, 0.964245, 0.689043},{0.999000, 0.969012, 0.697040},{0.999154, 0.973779, 0.705037},{0.999308, 0.978547, 0.713033},{0.999462, 0.983314, 0.721030},{0.999616, 0.988082, 0.729027},{0.999769, 0.992849, 0.737024},{0.999923, 0.997616, 0.745021},{0.998078, 0.999231, 0.746021},{0.994233, 0.997693, 0.740023},{0.990388, 0.996155, 0.734025},{0.986544, 0.994617, 0.728028},{0.982699, 0.993080, 0.722030},{0.978854, 0.991542, 0.716032},{0.975010, 0.990004, 0.710035},{0.971165, 0.988466, 0.704037},{0.967320, 0.986928, 0.698039},{0.963476, 0.985390, 0.692042},{0.959631, 0.983852, 0.686044},{0.955786, 0.982314, 0.680046},{0.951942, 0.980777, 0.674048},{0.948097, 0.979239, 0.668051},{0.944252, 0.977701, 0.662053},{0.940408, 0.976163, 0.656055},{0.936563, 0.974625, 0.650058},{0.932718, 0.973087, 0.644060},{0.928874, 0.971549, 0.638062},{0.925029, 0.970012, 0.632065},{0.921184, 0.968474, 0.626067},{0.917339, 0.966936, 0.620069},{0.913495, 0.965398, 0.614072},{0.909650, 0.963860, 0.608074},{0.905805, 0.962322, 0.602076},{0.901961, 0.960784, 0.596078},{0.892887, 0.957093, 0.597924},{0.883814, 0.953403, 0.599769},{0.874740, 0.949712, 0.601615},{0.865667, 0.946021, 0.603460},{0.856594, 0.942330, 0.605306},{0.847520, 0.938639, 0.607151},{0.838447, 0.934948, 0.608997},{0.829373, 0.931257, 0.610842},{0.820300, 0.927566, 0.612687},{0.811226, 0.923875, 0.614533},{0.802153, 0.920185, 0.616378},{0.793080, 0.916494, 0.618224},{0.784006, 0.912803, 0.620069},{0.774933, 0.909112, 0.621915},{0.765859, 0.905421, 0.623760},{0.756786, 0.901730, 0.625606},{0.747712, 0.898039, 0.627451},{0.738639, 0.894348, 0.629296},{0.729566, 0.890657, 0.631142},{0.720492, 0.886967, 0.632987},{0.711419, 0.883276, 0.634833},{0.702345, 0.879585, 0.636678},{0.693272, 0.875894, 0.638524},{0.684198, 0.872203, 0.640369},{0.675125, 0.868512, 0.642215},{0.665283, 0.864591, 0.643214},{0.654671, 0.860438, 0.643368},{0.644060, 0.856286, 0.643522},{0.633449, 0.852134, 0.643676},{0.622837, 0.847982, 0.643829},{0.612226, 0.843829, 0.643983},{0.601615, 0.839677, 0.644137},{0.591003, 0.835525, 0.644291},{0.580392, 0.831373, 0.644444},{0.569781, 0.827220, 0.644598},{0.559170, 0.823068, 0.644752},{0.548558, 0.818916, 0.644906},{0.537947, 0.814764, 0.645060},{0.527336, 0.810611, 0.645213},{0.516724, 0.806459, 0.645367},{0.506113, 0.802307, 0.645521},{0.495502, 0.798155, 0.645675},{0.484890, 0.794002, 0.645829},{0.474279, 0.789850, 0.645982},{0.463668, 0.785698, 0.646136},{0.453057, 0.781546, 0.646290},{0.442445, 0.777393, 0.646444},{0.431834, 0.773241, 0.646597},{0.421223, 0.769089, 0.646751},{0.410611, 0.764937, 0.646905},{0.400000, 0.760784, 0.647059},{0.392003, 0.751865, 0.650750},{0.384006, 0.742945, 0.654441},{0.376009, 0.734025, 0.658131},{0.368012, 0.725106, 0.661822},{0.360015, 0.716186, 0.665513},{0.352018, 0.707266, 0.669204},{0.344022, 0.698347, 0.672895},{0.336025, 0.689427, 0.676586},{0.328028, 0.680507, 0.680277},{0.320031, 0.671588, 0.683968},{0.312034, 0.662668, 0.687659},{0.304037, 0.653749, 0.691349},{0.296040, 0.644829, 0.695040},{0.288043, 0.635909, 0.698731},{0.280046, 0.626990, 0.702422},{0.272049, 0.618070, 0.706113},{0.264052, 0.609150, 0.709804},{0.256055, 0.600231, 0.713495},{0.248058, 0.591311, 0.717186},{0.240062, 0.582391, 0.720877},{0.232065, 0.573472, 0.724567},{0.224068, 0.564552, 0.728258},{0.216071, 0.555632, 0.731949},{0.208074, 0.546713, 0.735640},{0.200077, 0.537793, 0.739331},{0.199462, 0.528950, 0.739100},{0.206228, 0.520185, 0.734948},{0.212995, 0.511419, 0.730796},{0.219762, 0.502653, 0.726644},{0.226528, 0.493887, 0.722491},{0.233295, 0.485121, 0.718339},{0.240062, 0.476355, 0.714187},{0.246828, 0.467589, 0.710035},{0.253595, 0.458824, 0.705882},{0.260361, 0.450058, 0.701730},{0.267128, 0.441292, 0.697578},{0.273895, 0.432526, 0.693426},{0.280661, 0.423760, 0.689273},{0.287428, 0.414994, 0.685121},{0.294195, 0.406228, 0.680969},{0.300961, 0.397463, 0.676817},{0.307728, 0.388697, 0.672664},{0.314494, 0.379931, 0.668512},{0.321261, 0.371165, 0.664360},{0.328028, 0.362399, 0.660208},{0.334794, 0.353633, 0.656055},{0.341561, 0.344867, 0.651903},{0.348328, 0.336101, 0.647751},{0.355094, 0.327336, 0.643599},{0.361861, 0.318570, 0.639446},{0.368627, 0.309804, 0.635294}}}, + {"hot", {{0.041600, 0.000000, 0.000000},{0.051895, 0.000000, 0.000000},{0.062190, 0.000000, 0.000000},{0.072485, 0.000000, 0.000000},{0.082779, 0.000000, 0.000000},{0.093074, 0.000000, 0.000000},{0.103369, 0.000000, 0.000000},{0.113664, 0.000000, 0.000000},{0.123959, 0.000000, 0.000000},{0.134254, 0.000000, 0.000000},{0.144548, 0.000000, 0.000000},{0.154843, 0.000000, 0.000000},{0.165138, 0.000000, 0.000000},{0.175433, 0.000000, 0.000000},{0.185728, 0.000000, 0.000000},{0.196023, 0.000000, 0.000000},{0.206318, 0.000000, 0.000000},{0.216612, 0.000000, 0.000000},{0.226907, 0.000000, 0.000000},{0.237202, 0.000000, 0.000000},{0.247497, 0.000000, 0.000000},{0.257792, 0.000000, 0.000000},{0.268087, 0.000000, 0.000000},{0.278381, 0.000000, 0.000000},{0.288676, 0.000000, 0.000000},{0.298971, 0.000000, 0.000000},{0.309266, 0.000000, 0.000000},{0.319561, 0.000000, 0.000000},{0.329856, 0.000000, 0.000000},{0.340150, 0.000000, 0.000000},{0.350445, 0.000000, 0.000000},{0.360740, 0.000000, 0.000000},{0.371035, 0.000000, 0.000000},{0.381330, 0.000000, 0.000000},{0.391625, 0.000000, 0.000000},{0.401920, 0.000000, 0.000000},{0.412214, 0.000000, 0.000000},{0.422509, 0.000000, 0.000000},{0.432804, 0.000000, 0.000000},{0.443099, 0.000000, 0.000000},{0.453394, 0.000000, 0.000000},{0.463689, 0.000000, 0.000000},{0.473983, 0.000000, 0.000000},{0.484278, 0.000000, 0.000000},{0.494573, 0.000000, 0.000000},{0.504868, 0.000000, 0.000000},{0.515163, 0.000000, 0.000000},{0.525458, 0.000000, 0.000000},{0.535753, 0.000000, 0.000000},{0.546047, 0.000000, 0.000000},{0.556342, 0.000000, 0.000000},{0.566637, 0.000000, 0.000000},{0.576932, 0.000000, 0.000000},{0.587227, 0.000000, 0.000000},{0.597522, 0.000000, 0.000000},{0.607816, 0.000000, 0.000000},{0.618111, 0.000000, 0.000000},{0.628406, 0.000000, 0.000000},{0.638701, 0.000000, 0.000000},{0.648996, 0.000000, 0.000000},{0.659291, 0.000000, 0.000000},{0.669585, 0.000000, 0.000000},{0.679880, 0.000000, 0.000000},{0.690175, 0.000000, 0.000000},{0.700470, 0.000000, 0.000000},{0.710765, 0.000000, 0.000000},{0.721060, 0.000000, 0.000000},{0.731355, 0.000000, 0.000000},{0.741649, 0.000000, 0.000000},{0.751944, 0.000000, 0.000000},{0.762239, 0.000000, 0.000000},{0.772534, 0.000000, 0.000000},{0.782829, 0.000000, 0.000000},{0.793124, 0.000000, 0.000000},{0.803418, 0.000000, 0.000000},{0.813713, 0.000000, 0.000000},{0.824008, 0.000000, 0.000000},{0.834303, 0.000000, 0.000000},{0.844598, 0.000000, 0.000000},{0.854893, 0.000000, 0.000000},{0.865188, 0.000000, 0.000000},{0.875482, 0.000000, 0.000000},{0.885777, 0.000000, 0.000000},{0.896072, 0.000000, 0.000000},{0.906367, 0.000000, 0.000000},{0.916662, 0.000000, 0.000000},{0.926957, 0.000000, 0.000000},{0.937251, 0.000000, 0.000000},{0.947546, 0.000000, 0.000000},{0.957841, 0.000000, 0.000000},{0.968136, 0.000000, 0.000000},{0.978431, 0.000000, 0.000000},{0.988726, 0.000000, 0.000000},{0.999020, 0.000000, 0.000000},{1.000000, 0.009315, 0.000000},{1.000000, 0.019609, 0.000000},{1.000000, 0.029903, 0.000000},{1.000000, 0.040197, 0.000000},{1.000000, 0.050491, 0.000000},{1.000000, 0.060785, 0.000000},{1.000000, 0.071079, 0.000000},{1.000000, 0.081373, 0.000000},{1.000000, 0.091667, 0.000000},{1.000000, 0.101962, 0.000000},{1.000000, 0.112256, 0.000000},{1.000000, 0.122550, 0.000000},{1.000000, 0.132844, 0.000000},{1.000000, 0.143138, 0.000000},{1.000000, 0.153432, 0.000000},{1.000000, 0.163726, 0.000000},{1.000000, 0.174020, 0.000000},{1.000000, 0.184314, 0.000000},{1.000000, 0.194608, 0.000000},{1.000000, 0.204903, 0.000000},{1.000000, 0.215197, 0.000000},{1.000000, 0.225491, 0.000000},{1.000000, 0.235785, 0.000000},{1.000000, 0.246079, 0.000000},{1.000000, 0.256373, 0.000000},{1.000000, 0.266667, 0.000000},{1.000000, 0.276961, 0.000000},{1.000000, 0.287255, 0.000000},{1.000000, 0.297549, 0.000000},{1.000000, 0.307844, 0.000000},{1.000000, 0.318138, 0.000000},{1.000000, 0.328432, 0.000000},{1.000000, 0.338726, 0.000000},{1.000000, 0.349020, 0.000000},{1.000000, 0.359314, 0.000000},{1.000000, 0.369608, 0.000000},{1.000000, 0.379902, 0.000000},{1.000000, 0.390196, 0.000000},{1.000000, 0.400491, 0.000000},{1.000000, 0.410785, 0.000000},{1.000000, 0.421079, 0.000000},{1.000000, 0.431373, 0.000000},{1.000000, 0.441667, 0.000000},{1.000000, 0.451961, 0.000000},{1.000000, 0.462255, 0.000000},{1.000000, 0.472549, 0.000000},{1.000000, 0.482843, 0.000000},{1.000000, 0.493137, 0.000000},{1.000000, 0.503432, 0.000000},{1.000000, 0.513726, 0.000000},{1.000000, 0.524020, 0.000000},{1.000000, 0.534314, 0.000000},{1.000000, 0.544608, 0.000000},{1.000000, 0.554902, 0.000000},{1.000000, 0.565196, 0.000000},{1.000000, 0.575490, 0.000000},{1.000000, 0.585784, 0.000000},{1.000000, 0.596078, 0.000000},{1.000000, 0.606373, 0.000000},{1.000000, 0.616667, 0.000000},{1.000000, 0.626961, 0.000000},{1.000000, 0.637255, 0.000000},{1.000000, 0.647549, 0.000000},{1.000000, 0.657843, 0.000000},{1.000000, 0.668137, 0.000000},{1.000000, 0.678431, 0.000000},{1.000000, 0.688725, 0.000000},{1.000000, 0.699019, 0.000000},{1.000000, 0.709314, 0.000000},{1.000000, 0.719608, 0.000000},{1.000000, 0.729902, 0.000000},{1.000000, 0.740196, 0.000000},{1.000000, 0.750490, 0.000000},{1.000000, 0.760784, 0.000000},{1.000000, 0.771078, 0.000000},{1.000000, 0.781372, 0.000000},{1.000000, 0.791666, 0.000000},{1.000000, 0.801960, 0.000000},{1.000000, 0.812255, 0.000000},{1.000000, 0.822549, 0.000000},{1.000000, 0.832843, 0.000000},{1.000000, 0.843137, 0.000000},{1.000000, 0.853431, 0.000000},{1.000000, 0.863725, 0.000000},{1.000000, 0.874019, 0.000000},{1.000000, 0.884313, 0.000000},{1.000000, 0.894607, 0.000000},{1.000000, 0.904901, 0.000000},{1.000000, 0.915196, 0.000000},{1.000000, 0.925490, 0.000000},{1.000000, 0.935784, 0.000000},{1.000000, 0.946078, 0.000000},{1.000000, 0.956372, 0.000000},{1.000000, 0.966666, 0.000000},{1.000000, 0.976960, 0.000000},{1.000000, 0.987254, 0.000000},{1.000000, 0.997548, 0.000000},{1.000000, 1.000000, 0.011764},{1.000000, 1.000000, 0.027205},{1.000000, 1.000000, 0.042646},{1.000000, 1.000000, 0.058087},{1.000000, 1.000000, 0.073528},{1.000000, 1.000000, 0.088970},{1.000000, 1.000000, 0.104411},{1.000000, 1.000000, 0.119852},{1.000000, 1.000000, 0.135293},{1.000000, 1.000000, 0.150734},{1.000000, 1.000000, 0.166176},{1.000000, 1.000000, 0.181617},{1.000000, 1.000000, 0.197058},{1.000000, 1.000000, 0.212499},{1.000000, 1.000000, 0.227940},{1.000000, 1.000000, 0.243382},{1.000000, 1.000000, 0.258823},{1.000000, 1.000000, 0.274264},{1.000000, 1.000000, 0.289705},{1.000000, 1.000000, 0.305146},{1.000000, 1.000000, 0.320588},{1.000000, 1.000000, 0.336029},{1.000000, 1.000000, 0.351470},{1.000000, 1.000000, 0.366911},{1.000000, 1.000000, 0.382352},{1.000000, 1.000000, 0.397794},{1.000000, 1.000000, 0.413235},{1.000000, 1.000000, 0.428676},{1.000000, 1.000000, 0.444117},{1.000000, 1.000000, 0.459558},{1.000000, 1.000000, 0.474999},{1.000000, 1.000000, 0.490441},{1.000000, 1.000000, 0.505882},{1.000000, 1.000000, 0.521323},{1.000000, 1.000000, 0.536764},{1.000000, 1.000000, 0.552205},{1.000000, 1.000000, 0.567647},{1.000000, 1.000000, 0.583088},{1.000000, 1.000000, 0.598529},{1.000000, 1.000000, 0.613970},{1.000000, 1.000000, 0.629411},{1.000000, 1.000000, 0.644853},{1.000000, 1.000000, 0.660294},{1.000000, 1.000000, 0.675735},{1.000000, 1.000000, 0.691176},{1.000000, 1.000000, 0.706617},{1.000000, 1.000000, 0.722059},{1.000000, 1.000000, 0.737500},{1.000000, 1.000000, 0.752941},{1.000000, 1.000000, 0.768382},{1.000000, 1.000000, 0.783823},{1.000000, 1.000000, 0.799265},{1.000000, 1.000000, 0.814706},{1.000000, 1.000000, 0.830147},{1.000000, 1.000000, 0.845588},{1.000000, 1.000000, 0.861029},{1.000000, 1.000000, 0.876470},{1.000000, 1.000000, 0.891912},{1.000000, 1.000000, 0.907353},{1.000000, 1.000000, 0.922794},{1.000000, 1.000000, 0.938235},{1.000000, 1.000000, 0.953676},{1.000000, 1.000000, 0.969118},{1.000000, 1.000000, 0.984559},{1.000000, 1.000000, 1.000000}}}, + {"afmhot", {{0.000000, 0.000000, 0.000000},{0.007843, 0.000000, 0.000000},{0.015686, 0.000000, 0.000000},{0.023529, 0.000000, 0.000000},{0.031373, 0.000000, 0.000000},{0.039216, 0.000000, 0.000000},{0.047059, 0.000000, 0.000000},{0.054902, 0.000000, 0.000000},{0.062745, 0.000000, 0.000000},{0.070588, 0.000000, 0.000000},{0.078431, 0.000000, 0.000000},{0.086275, 0.000000, 0.000000},{0.094118, 0.000000, 0.000000},{0.101961, 0.000000, 0.000000},{0.109804, 0.000000, 0.000000},{0.117647, 0.000000, 0.000000},{0.125490, 0.000000, 0.000000},{0.133333, 0.000000, 0.000000},{0.141176, 0.000000, 0.000000},{0.149020, 0.000000, 0.000000},{0.156863, 0.000000, 0.000000},{0.164706, 0.000000, 0.000000},{0.172549, 0.000000, 0.000000},{0.180392, 0.000000, 0.000000},{0.188235, 0.000000, 0.000000},{0.196078, 0.000000, 0.000000},{0.203922, 0.000000, 0.000000},{0.211765, 0.000000, 0.000000},{0.219608, 0.000000, 0.000000},{0.227451, 0.000000, 0.000000},{0.235294, 0.000000, 0.000000},{0.243137, 0.000000, 0.000000},{0.250980, 0.000000, 0.000000},{0.258824, 0.000000, 0.000000},{0.266667, 0.000000, 0.000000},{0.274510, 0.000000, 0.000000},{0.282353, 0.000000, 0.000000},{0.290196, 0.000000, 0.000000},{0.298039, 0.000000, 0.000000},{0.305882, 0.000000, 0.000000},{0.313725, 0.000000, 0.000000},{0.321569, 0.000000, 0.000000},{0.329412, 0.000000, 0.000000},{0.337255, 0.000000, 0.000000},{0.345098, 0.000000, 0.000000},{0.352941, 0.000000, 0.000000},{0.360784, 0.000000, 0.000000},{0.368627, 0.000000, 0.000000},{0.376471, 0.000000, 0.000000},{0.384314, 0.000000, 0.000000},{0.392157, 0.000000, 0.000000},{0.400000, 0.000000, 0.000000},{0.407843, 0.000000, 0.000000},{0.415686, 0.000000, 0.000000},{0.423529, 0.000000, 0.000000},{0.431373, 0.000000, 0.000000},{0.439216, 0.000000, 0.000000},{0.447059, 0.000000, 0.000000},{0.454902, 0.000000, 0.000000},{0.462745, 0.000000, 0.000000},{0.470588, 0.000000, 0.000000},{0.478431, 0.000000, 0.000000},{0.486275, 0.000000, 0.000000},{0.494118, 0.000000, 0.000000},{0.501961, 0.001961, 0.000000},{0.509804, 0.009804, 0.000000},{0.517647, 0.017647, 0.000000},{0.525490, 0.025490, 0.000000},{0.533333, 0.033333, 0.000000},{0.541176, 0.041176, 0.000000},{0.549020, 0.049020, 0.000000},{0.556863, 0.056863, 0.000000},{0.564706, 0.064706, 0.000000},{0.572549, 0.072549, 0.000000},{0.580392, 0.080392, 0.000000},{0.588235, 0.088235, 0.000000},{0.596078, 0.096078, 0.000000},{0.603922, 0.103922, 0.000000},{0.611765, 0.111765, 0.000000},{0.619608, 0.119608, 0.000000},{0.627451, 0.127451, 0.000000},{0.635294, 0.135294, 0.000000},{0.643137, 0.143137, 0.000000},{0.650980, 0.150980, 0.000000},{0.658824, 0.158824, 0.000000},{0.666667, 0.166667, 0.000000},{0.674510, 0.174510, 0.000000},{0.682353, 0.182353, 0.000000},{0.690196, 0.190196, 0.000000},{0.698039, 0.198039, 0.000000},{0.705882, 0.205882, 0.000000},{0.713725, 0.213725, 0.000000},{0.721569, 0.221569, 0.000000},{0.729412, 0.229412, 0.000000},{0.737255, 0.237255, 0.000000},{0.745098, 0.245098, 0.000000},{0.752941, 0.252941, 0.000000},{0.760784, 0.260784, 0.000000},{0.768627, 0.268627, 0.000000},{0.776471, 0.276471, 0.000000},{0.784314, 0.284314, 0.000000},{0.792157, 0.292157, 0.000000},{0.800000, 0.300000, 0.000000},{0.807843, 0.307843, 0.000000},{0.815686, 0.315686, 0.000000},{0.823529, 0.323529, 0.000000},{0.831373, 0.331373, 0.000000},{0.839216, 0.339216, 0.000000},{0.847059, 0.347059, 0.000000},{0.854902, 0.354902, 0.000000},{0.862745, 0.362745, 0.000000},{0.870588, 0.370588, 0.000000},{0.878431, 0.378431, 0.000000},{0.886275, 0.386275, 0.000000},{0.894118, 0.394118, 0.000000},{0.901961, 0.401961, 0.000000},{0.909804, 0.409804, 0.000000},{0.917647, 0.417647, 0.000000},{0.925490, 0.425490, 0.000000},{0.933333, 0.433333, 0.000000},{0.941176, 0.441176, 0.000000},{0.949020, 0.449020, 0.000000},{0.956863, 0.456863, 0.000000},{0.964706, 0.464706, 0.000000},{0.972549, 0.472549, 0.000000},{0.980392, 0.480392, 0.000000},{0.988235, 0.488235, 0.000000},{0.996078, 0.496078, 0.000000},{1.000000, 0.503922, 0.003922},{1.000000, 0.511765, 0.011765},{1.000000, 0.519608, 0.019608},{1.000000, 0.527451, 0.027451},{1.000000, 0.535294, 0.035294},{1.000000, 0.543137, 0.043137},{1.000000, 0.550980, 0.050980},{1.000000, 0.558824, 0.058824},{1.000000, 0.566667, 0.066667},{1.000000, 0.574510, 0.074510},{1.000000, 0.582353, 0.082353},{1.000000, 0.590196, 0.090196},{1.000000, 0.598039, 0.098039},{1.000000, 0.605882, 0.105882},{1.000000, 0.613725, 0.113725},{1.000000, 0.621569, 0.121569},{1.000000, 0.629412, 0.129412},{1.000000, 0.637255, 0.137255},{1.000000, 0.645098, 0.145098},{1.000000, 0.652941, 0.152941},{1.000000, 0.660784, 0.160784},{1.000000, 0.668627, 0.168627},{1.000000, 0.676471, 0.176471},{1.000000, 0.684314, 0.184314},{1.000000, 0.692157, 0.192157},{1.000000, 0.700000, 0.200000},{1.000000, 0.707843, 0.207843},{1.000000, 0.715686, 0.215686},{1.000000, 0.723529, 0.223529},{1.000000, 0.731373, 0.231373},{1.000000, 0.739216, 0.239216},{1.000000, 0.747059, 0.247059},{1.000000, 0.754902, 0.254902},{1.000000, 0.762745, 0.262745},{1.000000, 0.770588, 0.270588},{1.000000, 0.778431, 0.278431},{1.000000, 0.786275, 0.286275},{1.000000, 0.794118, 0.294118},{1.000000, 0.801961, 0.301961},{1.000000, 0.809804, 0.309804},{1.000000, 0.817647, 0.317647},{1.000000, 0.825490, 0.325490},{1.000000, 0.833333, 0.333333},{1.000000, 0.841176, 0.341176},{1.000000, 0.849020, 0.349020},{1.000000, 0.856863, 0.356863},{1.000000, 0.864706, 0.364706},{1.000000, 0.872549, 0.372549},{1.000000, 0.880392, 0.380392},{1.000000, 0.888235, 0.388235},{1.000000, 0.896078, 0.396078},{1.000000, 0.903922, 0.403922},{1.000000, 0.911765, 0.411765},{1.000000, 0.919608, 0.419608},{1.000000, 0.927451, 0.427451},{1.000000, 0.935294, 0.435294},{1.000000, 0.943137, 0.443137},{1.000000, 0.950980, 0.450980},{1.000000, 0.958824, 0.458824},{1.000000, 0.966667, 0.466667},{1.000000, 0.974510, 0.474510},{1.000000, 0.982353, 0.482353},{1.000000, 0.990196, 0.490196},{1.000000, 0.998039, 0.498039},{1.000000, 1.000000, 0.505882},{1.000000, 1.000000, 0.513725},{1.000000, 1.000000, 0.521569},{1.000000, 1.000000, 0.529412},{1.000000, 1.000000, 0.537255},{1.000000, 1.000000, 0.545098},{1.000000, 1.000000, 0.552941},{1.000000, 1.000000, 0.560784},{1.000000, 1.000000, 0.568627},{1.000000, 1.000000, 0.576471},{1.000000, 1.000000, 0.584314},{1.000000, 1.000000, 0.592157},{1.000000, 1.000000, 0.600000},{1.000000, 1.000000, 0.607843},{1.000000, 1.000000, 0.615686},{1.000000, 1.000000, 0.623529},{1.000000, 1.000000, 0.631373},{1.000000, 1.000000, 0.639216},{1.000000, 1.000000, 0.647059},{1.000000, 1.000000, 0.654902},{1.000000, 1.000000, 0.662745},{1.000000, 1.000000, 0.670588},{1.000000, 1.000000, 0.678431},{1.000000, 1.000000, 0.686275},{1.000000, 1.000000, 0.694118},{1.000000, 1.000000, 0.701961},{1.000000, 1.000000, 0.709804},{1.000000, 1.000000, 0.717647},{1.000000, 1.000000, 0.725490},{1.000000, 1.000000, 0.733333},{1.000000, 1.000000, 0.741176},{1.000000, 1.000000, 0.749020},{1.000000, 1.000000, 0.756863},{1.000000, 1.000000, 0.764706},{1.000000, 1.000000, 0.772549},{1.000000, 1.000000, 0.780392},{1.000000, 1.000000, 0.788235},{1.000000, 1.000000, 0.796078},{1.000000, 1.000000, 0.803922},{1.000000, 1.000000, 0.811765},{1.000000, 1.000000, 0.819608},{1.000000, 1.000000, 0.827451},{1.000000, 1.000000, 0.835294},{1.000000, 1.000000, 0.843137},{1.000000, 1.000000, 0.850980},{1.000000, 1.000000, 0.858824},{1.000000, 1.000000, 0.866667},{1.000000, 1.000000, 0.874510},{1.000000, 1.000000, 0.882353},{1.000000, 1.000000, 0.890196},{1.000000, 1.000000, 0.898039},{1.000000, 1.000000, 0.905882},{1.000000, 1.000000, 0.913725},{1.000000, 1.000000, 0.921569},{1.000000, 1.000000, 0.929412},{1.000000, 1.000000, 0.937255},{1.000000, 1.000000, 0.945098},{1.000000, 1.000000, 0.952941},{1.000000, 1.000000, 0.960784},{1.000000, 1.000000, 0.968627},{1.000000, 1.000000, 0.976471},{1.000000, 1.000000, 0.984314},{1.000000, 1.000000, 0.992157},{1.000000, 1.000000, 1.000000}}}, + {"copper", {{0.000000, 0.000000, 0.000000},{0.004844, 0.003064, 0.001951},{0.009689, 0.006127, 0.003902},{0.014533, 0.009191, 0.005853},{0.019377, 0.012254, 0.007804},{0.024221, 0.015318, 0.009755},{0.029066, 0.018381, 0.011706},{0.033910, 0.021445, 0.013657},{0.038754, 0.024508, 0.015608},{0.043599, 0.027572, 0.017559},{0.048443, 0.030635, 0.019510},{0.053287, 0.033699, 0.021461},{0.058131, 0.036762, 0.023412},{0.062976, 0.039826, 0.025363},{0.067820, 0.042889, 0.027314},{0.072664, 0.045953, 0.029265},{0.077509, 0.049016, 0.031216},{0.082353, 0.052080, 0.033167},{0.087197, 0.055144, 0.035118},{0.092042, 0.058207, 0.037069},{0.096886, 0.061271, 0.039020},{0.101730, 0.064334, 0.040971},{0.106574, 0.067398, 0.042922},{0.111419, 0.070461, 0.044873},{0.116263, 0.073525, 0.046824},{0.121107, 0.076588, 0.048775},{0.125952, 0.079652, 0.050725},{0.130796, 0.082715, 0.052676},{0.135640, 0.085779, 0.054627},{0.140484, 0.088842, 0.056578},{0.145329, 0.091906, 0.058529},{0.150173, 0.094969, 0.060480},{0.155017, 0.098033, 0.062431},{0.159862, 0.101096, 0.064382},{0.164706, 0.104160, 0.066333},{0.169550, 0.107224, 0.068284},{0.174394, 0.110287, 0.070235},{0.179239, 0.113351, 0.072186},{0.184083, 0.116414, 0.074137},{0.188927, 0.119478, 0.076088},{0.193772, 0.122541, 0.078039},{0.198616, 0.125605, 0.079990},{0.203460, 0.128668, 0.081941},{0.208304, 0.131732, 0.083892},{0.213149, 0.134795, 0.085843},{0.217993, 0.137859, 0.087794},{0.222837, 0.140922, 0.089745},{0.227682, 0.143986, 0.091696},{0.232526, 0.147049, 0.093647},{0.237370, 0.150113, 0.095598},{0.242214, 0.153176, 0.097549},{0.247059, 0.156240, 0.099500},{0.251903, 0.159304, 0.101451},{0.256747, 0.162367, 0.103402},{0.261592, 0.165431, 0.105353},{0.266436, 0.168494, 0.107304},{0.271280, 0.171558, 0.109255},{0.276125, 0.174621, 0.111206},{0.280969, 0.177685, 0.113157},{0.285813, 0.180748, 0.115108},{0.290657, 0.183812, 0.117059},{0.295502, 0.186875, 0.119010},{0.300346, 0.189939, 0.120961},{0.305190, 0.193002, 0.122912},{0.310035, 0.196066, 0.124863},{0.314879, 0.199129, 0.126814},{0.319723, 0.202193, 0.128765},{0.324567, 0.205256, 0.130716},{0.329412, 0.208320, 0.132667},{0.334256, 0.211384, 0.134618},{0.339100, 0.214447, 0.136569},{0.343945, 0.217511, 0.138520},{0.348789, 0.220574, 0.140471},{0.353633, 0.223638, 0.142422},{0.358477, 0.226701, 0.144373},{0.363322, 0.229765, 0.146324},{0.368166, 0.232828, 0.148275},{0.373010, 0.235892, 0.150225},{0.377855, 0.238955, 0.152176},{0.382699, 0.242019, 0.154127},{0.387543, 0.245082, 0.156078},{0.392387, 0.248146, 0.158029},{0.397232, 0.251209, 0.159980},{0.402076, 0.254273, 0.161931},{0.406920, 0.257336, 0.163882},{0.411765, 0.260400, 0.165833},{0.416609, 0.263464, 0.167784},{0.421453, 0.266527, 0.169735},{0.426297, 0.269591, 0.171686},{0.431142, 0.272654, 0.173637},{0.435986, 0.275718, 0.175588},{0.440830, 0.278781, 0.177539},{0.445675, 0.281845, 0.179490},{0.450519, 0.284908, 0.181441},{0.455363, 0.287972, 0.183392},{0.460208, 0.291035, 0.185343},{0.465052, 0.294099, 0.187294},{0.469896, 0.297162, 0.189245},{0.474740, 0.300226, 0.191196},{0.479585, 0.303289, 0.193147},{0.484429, 0.306353, 0.195098},{0.489273, 0.309416, 0.197049},{0.494118, 0.312480, 0.199000},{0.498962, 0.315544, 0.200951},{0.503806, 0.318607, 0.202902},{0.508650, 0.321671, 0.204853},{0.513495, 0.324734, 0.206804},{0.518339, 0.327798, 0.208755},{0.523183, 0.330861, 0.210706},{0.528028, 0.333925, 0.212657},{0.532872, 0.336988, 0.214608},{0.537716, 0.340052, 0.216559},{0.542560, 0.343115, 0.218510},{0.547405, 0.346179, 0.220461},{0.552249, 0.349242, 0.222412},{0.557093, 0.352306, 0.224363},{0.561938, 0.355369, 0.226314},{0.566782, 0.358433, 0.228265},{0.571626, 0.361496, 0.230216},{0.576470, 0.364560, 0.232167},{0.581315, 0.367624, 0.234118},{0.586159, 0.370687, 0.236069},{0.591003, 0.373751, 0.238020},{0.595848, 0.376814, 0.239971},{0.600692, 0.379878, 0.241922},{0.605536, 0.382941, 0.243873},{0.610380, 0.386005, 0.245824},{0.615225, 0.389068, 0.247775},{0.620069, 0.392132, 0.249725},{0.624913, 0.395195, 0.251676},{0.629758, 0.398259, 0.253627},{0.634602, 0.401322, 0.255578},{0.639446, 0.404386, 0.257529},{0.644291, 0.407449, 0.259480},{0.649135, 0.410513, 0.261431},{0.653979, 0.413576, 0.263382},{0.658823, 0.416640, 0.265333},{0.663668, 0.419704, 0.267284},{0.668512, 0.422767, 0.269235},{0.673356, 0.425831, 0.271186},{0.678201, 0.428894, 0.273137},{0.683045, 0.431958, 0.275088},{0.687889, 0.435021, 0.277039},{0.692733, 0.438085, 0.278990},{0.697578, 0.441148, 0.280941},{0.702422, 0.444212, 0.282892},{0.707266, 0.447275, 0.284843},{0.712111, 0.450339, 0.286794},{0.716955, 0.453402, 0.288745},{0.721799, 0.456466, 0.290696},{0.726643, 0.459529, 0.292647},{0.731488, 0.462593, 0.294598},{0.736332, 0.465656, 0.296549},{0.741176, 0.468720, 0.298500},{0.746021, 0.471784, 0.300451},{0.750865, 0.474847, 0.302402},{0.755709, 0.477911, 0.304353},{0.760553, 0.480974, 0.306304},{0.765398, 0.484038, 0.308255},{0.770242, 0.487101, 0.310206},{0.775086, 0.490165, 0.312157},{0.779931, 0.493228, 0.314108},{0.784775, 0.496292, 0.316059},{0.789619, 0.499355, 0.318010},{0.794463, 0.502419, 0.319961},{0.799308, 0.505482, 0.321912},{0.804152, 0.508546, 0.323863},{0.808996, 0.511609, 0.325814},{0.813841, 0.514673, 0.327765},{0.818685, 0.517736, 0.329716},{0.823529, 0.520800, 0.331667},{0.828374, 0.523864, 0.333618},{0.833218, 0.526927, 0.335569},{0.838062, 0.529991, 0.337520},{0.842906, 0.533054, 0.339471},{0.847751, 0.536118, 0.341422},{0.852595, 0.539181, 0.343373},{0.857439, 0.542245, 0.345324},{0.862284, 0.545308, 0.347275},{0.867128, 0.548372, 0.349225},{0.871972, 0.551435, 0.351176},{0.876816, 0.554499, 0.353127},{0.881661, 0.557562, 0.355078},{0.886505, 0.560626, 0.357029},{0.891349, 0.563689, 0.358980},{0.896194, 0.566753, 0.360931},{0.901038, 0.569816, 0.362882},{0.905882, 0.572880, 0.364833},{0.910726, 0.575944, 0.366784},{0.915571, 0.579007, 0.368735},{0.920415, 0.582071, 0.370686},{0.925259, 0.585134, 0.372637},{0.930104, 0.588198, 0.374588},{0.934948, 0.591261, 0.376539},{0.939792, 0.594325, 0.378490},{0.944636, 0.597388, 0.380441},{0.949481, 0.600452, 0.382392},{0.954325, 0.603515, 0.384343},{0.959169, 0.606579, 0.386294},{0.964014, 0.609642, 0.388245},{0.968858, 0.612706, 0.390196},{0.973702, 0.615769, 0.392147},{0.978546, 0.618833, 0.394098},{0.983391, 0.621896, 0.396049},{0.988235, 0.624960, 0.398000},{0.993079, 0.628024, 0.399951},{0.997924, 0.631087, 0.401902},{1.000000, 0.634151, 0.403853},{1.000000, 0.637214, 0.405804},{1.000000, 0.640278, 0.407755},{1.000000, 0.643341, 0.409706},{1.000000, 0.646405, 0.411657},{1.000000, 0.649468, 0.413608},{1.000000, 0.652532, 0.415559},{1.000000, 0.655595, 0.417510},{1.000000, 0.658659, 0.419461},{1.000000, 0.661722, 0.421412},{1.000000, 0.664786, 0.423363},{1.000000, 0.667849, 0.425314},{1.000000, 0.670913, 0.427265},{1.000000, 0.673976, 0.429216},{1.000000, 0.677040, 0.431167},{1.000000, 0.680104, 0.433118},{1.000000, 0.683167, 0.435069},{1.000000, 0.686231, 0.437020},{1.000000, 0.689294, 0.438971},{1.000000, 0.692358, 0.440922},{1.000000, 0.695421, 0.442873},{1.000000, 0.698485, 0.444824},{1.000000, 0.701548, 0.446775},{1.000000, 0.704612, 0.448725},{1.000000, 0.707675, 0.450676},{1.000000, 0.710739, 0.452627},{1.000000, 0.713802, 0.454578},{1.000000, 0.716866, 0.456529},{1.000000, 0.719929, 0.458480},{1.000000, 0.722993, 0.460431},{1.000000, 0.726056, 0.462382},{1.000000, 0.729120, 0.464333},{1.000000, 0.732184, 0.466284},{1.000000, 0.735247, 0.468235},{1.000000, 0.738311, 0.470186},{1.000000, 0.741374, 0.472137},{1.000000, 0.744438, 0.474088},{1.000000, 0.747501, 0.476039},{1.000000, 0.750565, 0.477990},{1.000000, 0.753628, 0.479941},{1.000000, 0.756692, 0.481892},{1.000000, 0.759755, 0.483843},{1.000000, 0.762819, 0.485794},{1.000000, 0.765882, 0.487745},{1.000000, 0.768946, 0.489696},{1.000000, 0.772009, 0.491647},{1.000000, 0.775073, 0.493598},{1.000000, 0.778136, 0.495549},{1.000000, 0.781200, 0.497500}}}, + {"coolwarm", {{0.229806, 0.298718, 0.753683},{0.234377, 0.305542, 0.759680},{0.238948, 0.312365, 0.765676},{0.243520, 0.319189, 0.771672},{0.248091, 0.326013, 0.777669},{0.252663, 0.332837, 0.783665},{0.257234, 0.339661, 0.789661},{0.261805, 0.346484, 0.795658},{0.266381, 0.353304, 0.801637},{0.271104, 0.360011, 0.807095},{0.275827, 0.366717, 0.812553},{0.280550, 0.373423, 0.818011},{0.285273, 0.380129, 0.823469},{0.289996, 0.386836, 0.828926},{0.294718, 0.393542, 0.834384},{0.299441, 0.400248, 0.839842},{0.304174, 0.406945, 0.845263},{0.309060, 0.413498, 0.850128},{0.313946, 0.420052, 0.854993},{0.318832, 0.426605, 0.859857},{0.323718, 0.433158, 0.864722},{0.328604, 0.439712, 0.869587},{0.333490, 0.446265, 0.874452},{0.338377, 0.452819, 0.879317},{0.343278, 0.459354, 0.884122},{0.348323, 0.465711, 0.888346},{0.353369, 0.472069, 0.892570},{0.358415, 0.478426, 0.896795},{0.363461, 0.484784, 0.901019},{0.368507, 0.491141, 0.905243},{0.373552, 0.497499, 0.909467},{0.378598, 0.503856, 0.913692},{0.383662, 0.510183, 0.917831},{0.388852, 0.516298, 0.921373},{0.394042, 0.522413, 0.924916},{0.399231, 0.528528, 0.928459},{0.404421, 0.534643, 0.932002},{0.409611, 0.540759, 0.935545},{0.414801, 0.546874, 0.939088},{0.419991, 0.552989, 0.942630},{0.425199, 0.559058, 0.946061},{0.430507, 0.564883, 0.948889},{0.435815, 0.570707, 0.951717},{0.441123, 0.576532, 0.954545},{0.446431, 0.582356, 0.957373},{0.451739, 0.588181, 0.960201},{0.457046, 0.594006, 0.963029},{0.462354, 0.599830, 0.965857},{0.467678, 0.605591, 0.968546},{0.473070, 0.611077, 0.970634},{0.478462, 0.616564, 0.972721},{0.483854, 0.622050, 0.974808},{0.489246, 0.627536, 0.976896},{0.494638, 0.633022, 0.978983},{0.500031, 0.638508, 0.981070},{0.505423, 0.643995, 0.983157},{0.510824, 0.649397, 0.985079},{0.516260, 0.654498, 0.986407},{0.521696, 0.659599, 0.987736},{0.527132, 0.664700, 0.989065},{0.532568, 0.669801, 0.990393},{0.538004, 0.674902, 0.991722},{0.543440, 0.680003, 0.993051},{0.548876, 0.685104, 0.994379},{0.554312, 0.690097, 0.995516},{0.559747, 0.694768, 0.996075},{0.565182, 0.699438, 0.996635},{0.570616, 0.704109, 0.997195},{0.576051, 0.708780, 0.997755},{0.581486, 0.713451, 0.998314},{0.586921, 0.718121, 0.998874},{0.592356, 0.722792, 0.999434},{0.597777, 0.727330, 0.999777},{0.603162, 0.731527, 0.999565},{0.608547, 0.735725, 0.999354},{0.613933, 0.739923, 0.999142},{0.619318, 0.744121, 0.998931},{0.624703, 0.748318, 0.998719},{0.630089, 0.752516, 0.998508},{0.635474, 0.756714, 0.998297},{0.640828, 0.760752, 0.997846},{0.646113, 0.764436, 0.996868},{0.651398, 0.768121, 0.995891},{0.656683, 0.771806, 0.994914},{0.661968, 0.775491, 0.993937},{0.667253, 0.779176, 0.992959},{0.672538, 0.782861, 0.991982},{0.677823, 0.786546, 0.991005},{0.683056, 0.790043, 0.989768},{0.688188, 0.793178, 0.988038},{0.693321, 0.796314, 0.986308},{0.698454, 0.799450, 0.984577},{0.703587, 0.802586, 0.982847},{0.708720, 0.805721, 0.981117},{0.713852, 0.808857, 0.979386},{0.718985, 0.811993, 0.977656},{0.724041, 0.814910, 0.975651},{0.728970, 0.817464, 0.973188},{0.733898, 0.820018, 0.970724},{0.738826, 0.822572, 0.968261},{0.743754, 0.825125, 0.965798},{0.748682, 0.827679, 0.963334},{0.753611, 0.830233, 0.960871},{0.758539, 0.832787, 0.958408},{0.763363, 0.835092, 0.955658},{0.768034, 0.837035, 0.952488},{0.772706, 0.838978, 0.949319},{0.777378, 0.840921, 0.946149},{0.782049, 0.842864, 0.942980},{0.786721, 0.844807, 0.939810},{0.791392, 0.846750, 0.936641},{0.796064, 0.848693, 0.933471},{0.800601, 0.850358, 0.930008},{0.804965, 0.851666, 0.926165},{0.809329, 0.852974, 0.922323},{0.813693, 0.854282, 0.918480},{0.818056, 0.855590, 0.914638},{0.822420, 0.856898, 0.910795},{0.826784, 0.858205, 0.906953},{0.831148, 0.859513, 0.903110},{0.835345, 0.860514, 0.898970},{0.839351, 0.861167, 0.894494},{0.843358, 0.861820, 0.890017},{0.847365, 0.862472, 0.885540},{0.851372, 0.863125, 0.881064},{0.855378, 0.863778, 0.876587},{0.859385, 0.864431, 0.872111},{0.863392, 0.865084, 0.867634},{0.867428, 0.864377, 0.862602},{0.871493, 0.862309, 0.857016},{0.875557, 0.860242, 0.851430},{0.879622, 0.858175, 0.845844},{0.883687, 0.856108, 0.840258},{0.887752, 0.854040, 0.834671},{0.891817, 0.851973, 0.829085},{0.895882, 0.849906, 0.823499},{0.899543, 0.847500, 0.817789},{0.902849, 0.844796, 0.811970},{0.906154, 0.842091, 0.806151},{0.909460, 0.839386, 0.800331},{0.912765, 0.836682, 0.794512},{0.916071, 0.833977, 0.788693},{0.919376, 0.831273, 0.782874},{0.922681, 0.828568, 0.777054},{0.925563, 0.825517, 0.771136},{0.928116, 0.822197, 0.765141},{0.930669, 0.818877, 0.759146},{0.933221, 0.815557, 0.753151},{0.935774, 0.812237, 0.747156},{0.938326, 0.808917, 0.741162},{0.940879, 0.805596, 0.735167},{0.943432, 0.802276, 0.729172},{0.945540, 0.798606, 0.723105},{0.947345, 0.794696, 0.716991},{0.949151, 0.790785, 0.710876},{0.950956, 0.786875, 0.704761},{0.952761, 0.782965, 0.698646},{0.954566, 0.779055, 0.692531},{0.956371, 0.775144, 0.686416},{0.958176, 0.771234, 0.680301},{0.959518, 0.766973, 0.674145},{0.960581, 0.762501, 0.667964},{0.961645, 0.758029, 0.661782},{0.962708, 0.753557, 0.655601},{0.963772, 0.749086, 0.649420},{0.964835, 0.744614, 0.643239},{0.965899, 0.740142, 0.637058},{0.966962, 0.735670, 0.630877},{0.967544, 0.730850, 0.624685},{0.967874, 0.725847, 0.618489},{0.968203, 0.720844, 0.612293},{0.968533, 0.715841, 0.606097},{0.968863, 0.710838, 0.599901},{0.969192, 0.705836, 0.593704},{0.969522, 0.700833, 0.587508},{0.969851, 0.695830, 0.581312},{0.969683, 0.690484, 0.575138},{0.969289, 0.684982, 0.568975},{0.968894, 0.679480, 0.562812},{0.968500, 0.673977, 0.556649},{0.968105, 0.668475, 0.550486},{0.967711, 0.662973, 0.544323},{0.967317, 0.657471, 0.538160},{0.966922, 0.651969, 0.531997},{0.966017, 0.646130, 0.525890},{0.964911, 0.640159, 0.519806},{0.963806, 0.634188, 0.513721},{0.962701, 0.628218, 0.507636},{0.961595, 0.622247, 0.501551},{0.960490, 0.616276, 0.495467},{0.959385, 0.610306, 0.489382},{0.958279, 0.604335, 0.483297},{0.956653, 0.598034, 0.477302},{0.954853, 0.591622, 0.471337},{0.953054, 0.585211, 0.465373},{0.951254, 0.578799, 0.459408},{0.949454, 0.572388, 0.453443},{0.947654, 0.565976, 0.447478},{0.945854, 0.559565, 0.441513},{0.944055, 0.553153, 0.435548},{0.941728, 0.546413, 0.429707},{0.939254, 0.539581, 0.423900},{0.936780, 0.532750, 0.418093},{0.934305, 0.525918, 0.412286},{0.931831, 0.519086, 0.406480},{0.929357, 0.512254, 0.400673},{0.926883, 0.505422, 0.394866},{0.924409, 0.498590, 0.389059},{0.921406, 0.491420, 0.383408},{0.918282, 0.484173, 0.377794},{0.915157, 0.476927, 0.372179},{0.912033, 0.469680, 0.366565},{0.908908, 0.462433, 0.360950},{0.905783, 0.455186, 0.355336},{0.902659, 0.447939, 0.349721},{0.899534, 0.440692, 0.344107},{0.895885, 0.433075, 0.338681},{0.892138, 0.425389, 0.333289},{0.888390, 0.417703, 0.327898},{0.884643, 0.410017, 0.322507},{0.880896, 0.402331, 0.317115},{0.877149, 0.394645, 0.311724},{0.873402, 0.386960, 0.306332},{0.869655, 0.379274, 0.300941},{0.865391, 0.371128, 0.295769},{0.861054, 0.362916, 0.290628},{0.856716, 0.354704, 0.285487},{0.852378, 0.346492, 0.280346},{0.848040, 0.338280, 0.275206},{0.843703, 0.330068, 0.270065},{0.839365, 0.321856, 0.264924},{0.835027, 0.313644, 0.259783},{0.830187, 0.304733, 0.254891},{0.825294, 0.295749, 0.250025},{0.820401, 0.286765, 0.245160},{0.815508, 0.277781, 0.240294},{0.810616, 0.268797, 0.235428},{0.805723, 0.259813, 0.230562},{0.800830, 0.250829, 0.225696},{0.795938, 0.241845, 0.220830},{0.790562, 0.231397, 0.216242},{0.785153, 0.220851, 0.211673},{0.779745, 0.210305, 0.207104},{0.774337, 0.199759, 0.202535},{0.768929, 0.189213, 0.197965},{0.763520, 0.178667, 0.193396},{0.758112, 0.168122, 0.188827},{0.752704, 0.157576, 0.184258},{0.746838, 0.140021, 0.179996},{0.740957, 0.122240, 0.175744},{0.735077, 0.104460, 0.171492},{0.729196, 0.086679, 0.167240},{0.723315, 0.068898, 0.162989},{0.717435, 0.051118, 0.158737},{0.711554, 0.033337, 0.154485},{0.705673, 0.015556, 0.150233}}}, + {"jet", {{0.000000, 0.000000, 0.500000},{0.000000, 0.000000, 0.517825},{0.000000, 0.000000, 0.535651},{0.000000, 0.000000, 0.553476},{0.000000, 0.000000, 0.571301},{0.000000, 0.000000, 0.589127},{0.000000, 0.000000, 0.606952},{0.000000, 0.000000, 0.624777},{0.000000, 0.000000, 0.642602},{0.000000, 0.000000, 0.660428},{0.000000, 0.000000, 0.678253},{0.000000, 0.000000, 0.696078},{0.000000, 0.000000, 0.713904},{0.000000, 0.000000, 0.731729},{0.000000, 0.000000, 0.749554},{0.000000, 0.000000, 0.767380},{0.000000, 0.000000, 0.785205},{0.000000, 0.000000, 0.803030},{0.000000, 0.000000, 0.820856},{0.000000, 0.000000, 0.838681},{0.000000, 0.000000, 0.856506},{0.000000, 0.000000, 0.874332},{0.000000, 0.000000, 0.892157},{0.000000, 0.000000, 0.909982},{0.000000, 0.000000, 0.927807},{0.000000, 0.000000, 0.945633},{0.000000, 0.000000, 0.963458},{0.000000, 0.000000, 0.981283},{0.000000, 0.000000, 0.999109},{0.000000, 0.000000, 1.000000},{0.000000, 0.000000, 1.000000},{0.000000, 0.000000, 1.000000},{0.000000, 0.001961, 1.000000},{0.000000, 0.017647, 1.000000},{0.000000, 0.033333, 1.000000},{0.000000, 0.049020, 1.000000},{0.000000, 0.064706, 1.000000},{0.000000, 0.080392, 1.000000},{0.000000, 0.096078, 1.000000},{0.000000, 0.111765, 1.000000},{0.000000, 0.127451, 1.000000},{0.000000, 0.143137, 1.000000},{0.000000, 0.158824, 1.000000},{0.000000, 0.174510, 1.000000},{0.000000, 0.190196, 1.000000},{0.000000, 0.205882, 1.000000},{0.000000, 0.221569, 1.000000},{0.000000, 0.237255, 1.000000},{0.000000, 0.252941, 1.000000},{0.000000, 0.268627, 1.000000},{0.000000, 0.284314, 1.000000},{0.000000, 0.300000, 1.000000},{0.000000, 0.315686, 1.000000},{0.000000, 0.331373, 1.000000},{0.000000, 0.347059, 1.000000},{0.000000, 0.362745, 1.000000},{0.000000, 0.378431, 1.000000},{0.000000, 0.394118, 1.000000},{0.000000, 0.409804, 1.000000},{0.000000, 0.425490, 1.000000},{0.000000, 0.441176, 1.000000},{0.000000, 0.456863, 1.000000},{0.000000, 0.472549, 1.000000},{0.000000, 0.488235, 1.000000},{0.000000, 0.503922, 1.000000},{0.000000, 0.519608, 1.000000},{0.000000, 0.535294, 1.000000},{0.000000, 0.550980, 1.000000},{0.000000, 0.566667, 1.000000},{0.000000, 0.582353, 1.000000},{0.000000, 0.598039, 1.000000},{0.000000, 0.613725, 1.000000},{0.000000, 0.629412, 1.000000},{0.000000, 0.645098, 1.000000},{0.000000, 0.660784, 1.000000},{0.000000, 0.676471, 1.000000},{0.000000, 0.692157, 1.000000},{0.000000, 0.707843, 1.000000},{0.000000, 0.723529, 1.000000},{0.000000, 0.739216, 1.000000},{0.000000, 0.754902, 1.000000},{0.000000, 0.770588, 1.000000},{0.000000, 0.786275, 1.000000},{0.000000, 0.801961, 1.000000},{0.000000, 0.817647, 1.000000},{0.000000, 0.833333, 1.000000},{0.000000, 0.849020, 1.000000},{0.000000, 0.864706, 0.996205},{0.000000, 0.880392, 0.983555},{0.000000, 0.896078, 0.970904},{0.009488, 0.911765, 0.958254},{0.022138, 0.927451, 0.945604},{0.034788, 0.943137, 0.932954},{0.047438, 0.958824, 0.920304},{0.060089, 0.974510, 0.907653},{0.072739, 0.990196, 0.895003},{0.085389, 1.000000, 0.882353},{0.098039, 1.000000, 0.869703},{0.110689, 1.000000, 0.857052},{0.123340, 1.000000, 0.844402},{0.135990, 1.000000, 0.831752},{0.148640, 1.000000, 0.819102},{0.161290, 1.000000, 0.806452},{0.173941, 1.000000, 0.793801},{0.186591, 1.000000, 0.781151},{0.199241, 1.000000, 0.768501},{0.211891, 1.000000, 0.755851},{0.224541, 1.000000, 0.743201},{0.237192, 1.000000, 0.730550},{0.249842, 1.000000, 0.717900},{0.262492, 1.000000, 0.705250},{0.275142, 1.000000, 0.692600},{0.287793, 1.000000, 0.679949},{0.300443, 1.000000, 0.667299},{0.313093, 1.000000, 0.654649},{0.325743, 1.000000, 0.641999},{0.338393, 1.000000, 0.629349},{0.351044, 1.000000, 0.616698},{0.363694, 1.000000, 0.604048},{0.376344, 1.000000, 0.591398},{0.388994, 1.000000, 0.578748},{0.401645, 1.000000, 0.566097},{0.414295, 1.000000, 0.553447},{0.426945, 1.000000, 0.540797},{0.439595, 1.000000, 0.528147},{0.452245, 1.000000, 0.515497},{0.464896, 1.000000, 0.502846},{0.477546, 1.000000, 0.490196},{0.490196, 1.000000, 0.477546},{0.502846, 1.000000, 0.464896},{0.515497, 1.000000, 0.452245},{0.528147, 1.000000, 0.439595},{0.540797, 1.000000, 0.426945},{0.553447, 1.000000, 0.414295},{0.566097, 1.000000, 0.401645},{0.578748, 1.000000, 0.388994},{0.591398, 1.000000, 0.376344},{0.604048, 1.000000, 0.363694},{0.616698, 1.000000, 0.351044},{0.629349, 1.000000, 0.338393},{0.641999, 1.000000, 0.325743},{0.654649, 1.000000, 0.313093},{0.667299, 1.000000, 0.300443},{0.679949, 1.000000, 0.287793},{0.692600, 1.000000, 0.275142},{0.705250, 1.000000, 0.262492},{0.717900, 1.000000, 0.249842},{0.730550, 1.000000, 0.237192},{0.743201, 1.000000, 0.224541},{0.755851, 1.000000, 0.211891},{0.768501, 1.000000, 0.199241},{0.781151, 1.000000, 0.186591},{0.793801, 1.000000, 0.173941},{0.806452, 1.000000, 0.161290},{0.819102, 1.000000, 0.148640},{0.831752, 1.000000, 0.135990},{0.844402, 1.000000, 0.123340},{0.857052, 1.000000, 0.110689},{0.869703, 1.000000, 0.098039},{0.882353, 1.000000, 0.085389},{0.895003, 1.000000, 0.072739},{0.907653, 1.000000, 0.060089},{0.920304, 1.000000, 0.047438},{0.932954, 1.000000, 0.034788},{0.945604, 0.988381, 0.022138},{0.958254, 0.973856, 0.009488},{0.970904, 0.959332, 0.000000},{0.983555, 0.944808, 0.000000},{0.996205, 0.930283, 0.000000},{1.000000, 0.915759, 0.000000},{1.000000, 0.901235, 0.000000},{1.000000, 0.886710, 0.000000},{1.000000, 0.872186, 0.000000},{1.000000, 0.857662, 0.000000},{1.000000, 0.843137, 0.000000},{1.000000, 0.828613, 0.000000},{1.000000, 0.814089, 0.000000},{1.000000, 0.799564, 0.000000},{1.000000, 0.785040, 0.000000},{1.000000, 0.770516, 0.000000},{1.000000, 0.755991, 0.000000},{1.000000, 0.741467, 0.000000},{1.000000, 0.726943, 0.000000},{1.000000, 0.712418, 0.000000},{1.000000, 0.697894, 0.000000},{1.000000, 0.683370, 0.000000},{1.000000, 0.668845, 0.000000},{1.000000, 0.654321, 0.000000},{1.000000, 0.639797, 0.000000},{1.000000, 0.625272, 0.000000},{1.000000, 0.610748, 0.000000},{1.000000, 0.596224, 0.000000},{1.000000, 0.581699, 0.000000},{1.000000, 0.567175, 0.000000},{1.000000, 0.552651, 0.000000},{1.000000, 0.538126, 0.000000},{1.000000, 0.523602, 0.000000},{1.000000, 0.509078, 0.000000},{1.000000, 0.494553, 0.000000},{1.000000, 0.480029, 0.000000},{1.000000, 0.465505, 0.000000},{1.000000, 0.450980, 0.000000},{1.000000, 0.436456, 0.000000},{1.000000, 0.421932, 0.000000},{1.000000, 0.407407, 0.000000},{1.000000, 0.392883, 0.000000},{1.000000, 0.378359, 0.000000},{1.000000, 0.363834, 0.000000},{1.000000, 0.349310, 0.000000},{1.000000, 0.334786, 0.000000},{1.000000, 0.320261, 0.000000},{1.000000, 0.305737, 0.000000},{1.000000, 0.291213, 0.000000},{1.000000, 0.276688, 0.000000},{1.000000, 0.262164, 0.000000},{1.000000, 0.247640, 0.000000},{1.000000, 0.233115, 0.000000},{1.000000, 0.218591, 0.000000},{1.000000, 0.204067, 0.000000},{1.000000, 0.189542, 0.000000},{1.000000, 0.175018, 0.000000},{1.000000, 0.160494, 0.000000},{1.000000, 0.145969, 0.000000},{1.000000, 0.131445, 0.000000},{1.000000, 0.116921, 0.000000},{1.000000, 0.102397, 0.000000},{1.000000, 0.087872, 0.000000},{0.999109, 0.073348, 0.000000},{0.981283, 0.058824, 0.000000},{0.963458, 0.044299, 0.000000},{0.945633, 0.029775, 0.000000},{0.927807, 0.015251, 0.000000},{0.909982, 0.000726, 0.000000},{0.892157, 0.000000, 0.000000},{0.874332, 0.000000, 0.000000},{0.856506, 0.000000, 0.000000},{0.838681, 0.000000, 0.000000},{0.820856, 0.000000, 0.000000},{0.803030, 0.000000, 0.000000},{0.785205, 0.000000, 0.000000},{0.767380, 0.000000, 0.000000},{0.749554, 0.000000, 0.000000},{0.731729, 0.000000, 0.000000},{0.713904, 0.000000, 0.000000},{0.696078, 0.000000, 0.000000},{0.678253, 0.000000, 0.000000},{0.660428, 0.000000, 0.000000},{0.642602, 0.000000, 0.000000},{0.624777, 0.000000, 0.000000},{0.606952, 0.000000, 0.000000},{0.589127, 0.000000, 0.000000},{0.571301, 0.000000, 0.000000},{0.553476, 0.000000, 0.000000},{0.535651, 0.000000, 0.000000},{0.517825, 0.000000, 0.000000},{0.500000, 0.000000, 0.000000}}}, + {"cmrmap", {{0.000000, 0.000000, 0.000000},{0.004706, 0.004706, 0.015686},{0.009412, 0.009412, 0.031373},{0.014118, 0.014118, 0.047059},{0.018824, 0.018824, 0.062745},{0.023529, 0.023529, 0.078431},{0.028235, 0.028235, 0.094118},{0.032941, 0.032941, 0.109804},{0.037647, 0.037647, 0.125490},{0.042353, 0.042353, 0.141176},{0.047059, 0.047059, 0.156863},{0.051765, 0.051765, 0.172549},{0.056471, 0.056471, 0.188235},{0.061176, 0.061176, 0.203922},{0.065882, 0.065882, 0.219608},{0.070588, 0.070588, 0.235294},{0.075294, 0.075294, 0.250980},{0.080000, 0.080000, 0.266667},{0.084706, 0.084706, 0.282353},{0.089412, 0.089412, 0.298039},{0.094118, 0.094118, 0.313725},{0.098824, 0.098824, 0.329412},{0.103529, 0.103529, 0.345098},{0.108235, 0.108235, 0.360784},{0.112941, 0.112941, 0.376471},{0.117647, 0.117647, 0.392157},{0.122353, 0.122353, 0.407843},{0.127059, 0.127059, 0.423529},{0.131765, 0.131765, 0.439216},{0.136471, 0.136471, 0.454902},{0.141176, 0.141176, 0.470588},{0.145882, 0.145882, 0.486275},{0.150588, 0.150000, 0.500980},{0.155294, 0.150000, 0.508824},{0.160000, 0.150000, 0.516667},{0.164706, 0.150000, 0.524510},{0.169412, 0.150000, 0.532353},{0.174118, 0.150000, 0.540196},{0.178824, 0.150000, 0.548039},{0.183529, 0.150000, 0.555882},{0.188235, 0.150000, 0.563725},{0.192941, 0.150000, 0.571569},{0.197647, 0.150000, 0.579412},{0.202353, 0.150000, 0.587255},{0.207059, 0.150000, 0.595098},{0.211765, 0.150000, 0.602941},{0.216471, 0.150000, 0.610784},{0.221176, 0.150000, 0.618627},{0.225882, 0.150000, 0.626471},{0.230588, 0.150000, 0.634314},{0.235294, 0.150000, 0.642157},{0.240000, 0.150000, 0.650000},{0.244706, 0.150000, 0.657843},{0.249412, 0.150000, 0.665686},{0.254118, 0.150000, 0.673529},{0.258824, 0.150000, 0.681373},{0.263529, 0.150000, 0.689216},{0.268235, 0.150000, 0.697059},{0.272941, 0.150000, 0.704902},{0.277647, 0.150000, 0.712745},{0.282353, 0.150000, 0.720588},{0.287059, 0.150000, 0.728431},{0.291765, 0.150000, 0.736275},{0.296471, 0.150000, 0.744118},{0.302353, 0.150392, 0.748039},{0.311765, 0.151961, 0.740196},{0.321176, 0.153529, 0.732353},{0.330588, 0.155098, 0.724510},{0.340000, 0.156667, 0.716667},{0.349412, 0.158235, 0.708824},{0.358824, 0.159804, 0.700980},{0.368235, 0.161373, 0.693137},{0.377647, 0.162941, 0.685294},{0.387059, 0.164510, 0.677451},{0.396471, 0.166078, 0.669608},{0.405882, 0.167647, 0.661765},{0.415294, 0.169216, 0.653922},{0.424706, 0.170784, 0.646078},{0.434118, 0.172353, 0.638235},{0.443529, 0.173922, 0.630392},{0.452941, 0.175490, 0.622549},{0.462353, 0.177059, 0.614706},{0.471765, 0.178627, 0.606863},{0.481176, 0.180196, 0.599020},{0.490588, 0.181765, 0.591176},{0.500000, 0.183333, 0.583333},{0.509412, 0.184902, 0.575490},{0.518824, 0.186471, 0.567647},{0.528235, 0.188039, 0.559804},{0.537647, 0.189608, 0.551961},{0.547059, 0.191176, 0.544118},{0.556471, 0.192745, 0.536275},{0.565882, 0.194314, 0.528431},{0.575294, 0.195882, 0.520588},{0.584706, 0.197451, 0.512745},{0.594118, 0.199020, 0.504902},{0.604706, 0.200588, 0.495882},{0.617255, 0.202157, 0.484902},{0.629804, 0.203725, 0.473922},{0.642353, 0.205294, 0.462941},{0.654902, 0.206863, 0.451961},{0.667451, 0.208431, 0.440980},{0.680000, 0.210000, 0.430000},{0.692549, 0.211569, 0.419020},{0.705098, 0.213137, 0.408039},{0.717647, 0.214706, 0.397059},{0.730196, 0.216275, 0.386078},{0.742745, 0.217843, 0.375098},{0.755294, 0.219412, 0.364118},{0.767843, 0.220980, 0.353137},{0.780392, 0.222549, 0.342157},{0.792941, 0.224118, 0.331176},{0.805490, 0.225686, 0.320196},{0.818039, 0.227255, 0.309216},{0.830588, 0.228824, 0.298235},{0.843137, 0.230392, 0.287255},{0.855686, 0.231961, 0.276275},{0.868235, 0.233529, 0.265294},{0.880784, 0.235098, 0.254314},{0.893333, 0.236667, 0.243333},{0.905882, 0.238235, 0.232353},{0.918431, 0.239804, 0.221373},{0.930980, 0.241373, 0.210392},{0.943529, 0.242941, 0.199412},{0.956078, 0.244510, 0.188431},{0.968627, 0.246078, 0.177451},{0.981176, 0.247647, 0.166471},{0.993725, 0.249216, 0.155490},{0.998431, 0.253922, 0.147647},{0.995294, 0.261765, 0.142941},{0.992157, 0.269608, 0.138235},{0.989020, 0.277451, 0.133529},{0.985882, 0.285294, 0.128824},{0.982745, 0.293137, 0.124118},{0.979608, 0.300980, 0.119412},{0.976471, 0.308824, 0.114706},{0.973333, 0.316667, 0.110000},{0.970196, 0.324510, 0.105294},{0.967059, 0.332353, 0.100588},{0.963922, 0.340196, 0.095882},{0.960784, 0.348039, 0.091176},{0.957647, 0.355882, 0.086471},{0.954510, 0.363725, 0.081765},{0.951373, 0.371569, 0.077059},{0.948235, 0.379412, 0.072353},{0.945098, 0.387255, 0.067647},{0.941961, 0.395098, 0.062941},{0.938824, 0.402941, 0.058235},{0.935686, 0.410784, 0.053529},{0.932549, 0.418627, 0.048824},{0.929412, 0.426471, 0.044118},{0.926275, 0.434314, 0.039412},{0.923137, 0.442157, 0.034706},{0.920000, 0.450000, 0.030000},{0.916863, 0.457843, 0.025294},{0.913725, 0.465686, 0.020588},{0.910588, 0.473529, 0.015882},{0.907451, 0.481373, 0.011176},{0.904314, 0.489216, 0.006471},{0.901176, 0.497059, 0.001765},{0.900000, 0.504902, 0.001961},{0.900000, 0.512745, 0.005098},{0.900000, 0.520588, 0.008235},{0.900000, 0.528431, 0.011373},{0.900000, 0.536275, 0.014510},{0.900000, 0.544118, 0.017647},{0.900000, 0.551961, 0.020784},{0.900000, 0.559804, 0.023922},{0.900000, 0.567647, 0.027059},{0.900000, 0.575490, 0.030196},{0.900000, 0.583333, 0.033333},{0.900000, 0.591176, 0.036471},{0.900000, 0.599020, 0.039608},{0.900000, 0.606863, 0.042745},{0.900000, 0.614706, 0.045882},{0.900000, 0.622549, 0.049020},{0.900000, 0.630392, 0.052157},{0.900000, 0.638235, 0.055294},{0.900000, 0.646078, 0.058431},{0.900000, 0.653922, 0.061569},{0.900000, 0.661765, 0.064706},{0.900000, 0.669608, 0.067843},{0.900000, 0.677451, 0.070980},{0.900000, 0.685294, 0.074118},{0.900000, 0.693137, 0.077255},{0.900000, 0.700980, 0.080392},{0.900000, 0.708824, 0.083529},{0.900000, 0.716667, 0.086667},{0.900000, 0.724510, 0.089804},{0.900000, 0.732353, 0.092941},{0.900000, 0.740196, 0.096078},{0.900000, 0.748039, 0.099216},{0.900000, 0.753529, 0.109412},{0.900000, 0.758235, 0.121961},{0.900000, 0.762941, 0.134510},{0.900000, 0.767647, 0.147059},{0.900000, 0.772353, 0.159608},{0.900000, 0.777059, 0.172157},{0.900000, 0.781765, 0.184706},{0.900000, 0.786471, 0.197255},{0.900000, 0.791176, 0.209804},{0.900000, 0.795882, 0.222353},{0.900000, 0.800588, 0.234902},{0.900000, 0.805294, 0.247451},{0.900000, 0.810000, 0.260000},{0.900000, 0.814706, 0.272549},{0.900000, 0.819412, 0.285098},{0.900000, 0.824118, 0.297647},{0.900000, 0.828824, 0.310196},{0.900000, 0.833529, 0.322745},{0.900000, 0.838235, 0.335294},{0.900000, 0.842941, 0.347843},{0.900000, 0.847647, 0.360392},{0.900000, 0.852353, 0.372941},{0.900000, 0.857059, 0.385490},{0.900000, 0.861765, 0.398039},{0.900000, 0.866471, 0.410588},{0.900000, 0.871176, 0.423137},{0.900000, 0.875882, 0.435686},{0.900000, 0.880588, 0.448235},{0.900000, 0.885294, 0.460784},{0.900000, 0.890000, 0.473333},{0.900000, 0.894706, 0.485882},{0.900000, 0.899412, 0.498431},{0.902745, 0.902745, 0.513725},{0.905882, 0.905882, 0.529412},{0.909020, 0.909020, 0.545098},{0.912157, 0.912157, 0.560784},{0.915294, 0.915294, 0.576471},{0.918431, 0.918431, 0.592157},{0.921569, 0.921569, 0.607843},{0.924706, 0.924706, 0.623529},{0.927843, 0.927843, 0.639216},{0.930980, 0.930980, 0.654902},{0.934118, 0.934118, 0.670588},{0.937255, 0.937255, 0.686275},{0.940392, 0.940392, 0.701961},{0.943529, 0.943529, 0.717647},{0.946667, 0.946667, 0.733333},{0.949804, 0.949804, 0.749020},{0.952941, 0.952941, 0.764706},{0.956078, 0.956078, 0.780392},{0.959216, 0.959216, 0.796078},{0.962353, 0.962353, 0.811765},{0.965490, 0.965490, 0.827451},{0.968627, 0.968627, 0.843137},{0.971765, 0.971765, 0.858824},{0.974902, 0.974902, 0.874510},{0.978039, 0.978039, 0.890196},{0.981176, 0.981176, 0.905882},{0.984314, 0.984314, 0.921569},{0.987451, 0.987451, 0.937255},{0.990588, 0.990588, 0.952941},{0.993725, 0.993725, 0.968627},{0.996863, 0.996863, 0.984314},{1.000000, 1.000000, 1.000000}}}, + {"ocean", {{0.000000, 0.500000, 0.000000},{0.000000, 0.494118, 0.003922},{0.000000, 0.488235, 0.007843},{0.000000, 0.482353, 0.011765},{0.000000, 0.476471, 0.015686},{0.000000, 0.470588, 0.019608},{0.000000, 0.464706, 0.023529},{0.000000, 0.458824, 0.027451},{0.000000, 0.452941, 0.031373},{0.000000, 0.447059, 0.035294},{0.000000, 0.441176, 0.039216},{0.000000, 0.435294, 0.043137},{0.000000, 0.429412, 0.047059},{0.000000, 0.423529, 0.050980},{0.000000, 0.417647, 0.054902},{0.000000, 0.411765, 0.058824},{0.000000, 0.405882, 0.062745},{0.000000, 0.400000, 0.066667},{0.000000, 0.394118, 0.070588},{0.000000, 0.388235, 0.074510},{0.000000, 0.382353, 0.078431},{0.000000, 0.376471, 0.082353},{0.000000, 0.370588, 0.086275},{0.000000, 0.364706, 0.090196},{0.000000, 0.358824, 0.094118},{0.000000, 0.352941, 0.098039},{0.000000, 0.347059, 0.101961},{0.000000, 0.341176, 0.105882},{0.000000, 0.335294, 0.109804},{0.000000, 0.329412, 0.113725},{0.000000, 0.323529, 0.117647},{0.000000, 0.317647, 0.121569},{0.000000, 0.311765, 0.125490},{0.000000, 0.305882, 0.129412},{0.000000, 0.300000, 0.133333},{0.000000, 0.294118, 0.137255},{0.000000, 0.288235, 0.141176},{0.000000, 0.282353, 0.145098},{0.000000, 0.276471, 0.149020},{0.000000, 0.270588, 0.152941},{0.000000, 0.264706, 0.156863},{0.000000, 0.258824, 0.160784},{0.000000, 0.252941, 0.164706},{0.000000, 0.247059, 0.168627},{0.000000, 0.241176, 0.172549},{0.000000, 0.235294, 0.176471},{0.000000, 0.229412, 0.180392},{0.000000, 0.223529, 0.184314},{0.000000, 0.217647, 0.188235},{0.000000, 0.211765, 0.192157},{0.000000, 0.205882, 0.196078},{0.000000, 0.200000, 0.200000},{0.000000, 0.194118, 0.203922},{0.000000, 0.188235, 0.207843},{0.000000, 0.182353, 0.211765},{0.000000, 0.176471, 0.215686},{0.000000, 0.170588, 0.219608},{0.000000, 0.164706, 0.223529},{0.000000, 0.158824, 0.227451},{0.000000, 0.152941, 0.231373},{0.000000, 0.147059, 0.235294},{0.000000, 0.141176, 0.239216},{0.000000, 0.135294, 0.243137},{0.000000, 0.129412, 0.247059},{0.000000, 0.123529, 0.250980},{0.000000, 0.117647, 0.254902},{0.000000, 0.111765, 0.258824},{0.000000, 0.105882, 0.262745},{0.000000, 0.100000, 0.266667},{0.000000, 0.094118, 0.270588},{0.000000, 0.088235, 0.274510},{0.000000, 0.082353, 0.278431},{0.000000, 0.076471, 0.282353},{0.000000, 0.070588, 0.286275},{0.000000, 0.064706, 0.290196},{0.000000, 0.058824, 0.294118},{0.000000, 0.052941, 0.298039},{0.000000, 0.047059, 0.301961},{0.000000, 0.041176, 0.305882},{0.000000, 0.035294, 0.309804},{0.000000, 0.029412, 0.313725},{0.000000, 0.023529, 0.317647},{0.000000, 0.017647, 0.321569},{0.000000, 0.011765, 0.325490},{0.000000, 0.005882, 0.329412},{0.000000, 0.000000, 0.333333},{0.000000, 0.005882, 0.337255},{0.000000, 0.011765, 0.341176},{0.000000, 0.017647, 0.345098},{0.000000, 0.023529, 0.349020},{0.000000, 0.029412, 0.352941},{0.000000, 0.035294, 0.356863},{0.000000, 0.041176, 0.360784},{0.000000, 0.047059, 0.364706},{0.000000, 0.052941, 0.368627},{0.000000, 0.058824, 0.372549},{0.000000, 0.064706, 0.376471},{0.000000, 0.070588, 0.380392},{0.000000, 0.076471, 0.384314},{0.000000, 0.082353, 0.388235},{0.000000, 0.088235, 0.392157},{0.000000, 0.094118, 0.396078},{0.000000, 0.100000, 0.400000},{0.000000, 0.105882, 0.403922},{0.000000, 0.111765, 0.407843},{0.000000, 0.117647, 0.411765},{0.000000, 0.123529, 0.415686},{0.000000, 0.129412, 0.419608},{0.000000, 0.135294, 0.423529},{0.000000, 0.141176, 0.427451},{0.000000, 0.147059, 0.431373},{0.000000, 0.152941, 0.435294},{0.000000, 0.158824, 0.439216},{0.000000, 0.164706, 0.443137},{0.000000, 0.170588, 0.447059},{0.000000, 0.176471, 0.450980},{0.000000, 0.182353, 0.454902},{0.000000, 0.188235, 0.458824},{0.000000, 0.194118, 0.462745},{0.000000, 0.200000, 0.466667},{0.000000, 0.205882, 0.470588},{0.000000, 0.211765, 0.474510},{0.000000, 0.217647, 0.478431},{0.000000, 0.223529, 0.482353},{0.000000, 0.229412, 0.486275},{0.000000, 0.235294, 0.490196},{0.000000, 0.241176, 0.494118},{0.000000, 0.247059, 0.498039},{0.000000, 0.252941, 0.501961},{0.000000, 0.258824, 0.505882},{0.000000, 0.264706, 0.509804},{0.000000, 0.270588, 0.513725},{0.000000, 0.276471, 0.517647},{0.000000, 0.282353, 0.521569},{0.000000, 0.288235, 0.525490},{0.000000, 0.294118, 0.529412},{0.000000, 0.300000, 0.533333},{0.000000, 0.305882, 0.537255},{0.000000, 0.311765, 0.541176},{0.000000, 0.317647, 0.545098},{0.000000, 0.323529, 0.549020},{0.000000, 0.329412, 0.552941},{0.000000, 0.335294, 0.556863},{0.000000, 0.341176, 0.560784},{0.000000, 0.347059, 0.564706},{0.000000, 0.352941, 0.568627},{0.000000, 0.358824, 0.572549},{0.000000, 0.364706, 0.576471},{0.000000, 0.370588, 0.580392},{0.000000, 0.376471, 0.584314},{0.000000, 0.382353, 0.588235},{0.000000, 0.388235, 0.592157},{0.000000, 0.394118, 0.596078},{0.000000, 0.400000, 0.600000},{0.000000, 0.405882, 0.603922},{0.000000, 0.411765, 0.607843},{0.000000, 0.417647, 0.611765},{0.000000, 0.423529, 0.615686},{0.000000, 0.429412, 0.619608},{0.000000, 0.435294, 0.623529},{0.000000, 0.441176, 0.627451},{0.000000, 0.447059, 0.631373},{0.000000, 0.452941, 0.635294},{0.000000, 0.458824, 0.639216},{0.000000, 0.464706, 0.643137},{0.000000, 0.470588, 0.647059},{0.000000, 0.476471, 0.650980},{0.000000, 0.482353, 0.654902},{0.000000, 0.488235, 0.658824},{0.000000, 0.494118, 0.662745},{0.000000, 0.500000, 0.666667},{0.011765, 0.505882, 0.670588},{0.023529, 0.511765, 0.674510},{0.035294, 0.517647, 0.678431},{0.047059, 0.523529, 0.682353},{0.058824, 0.529412, 0.686275},{0.070588, 0.535294, 0.690196},{0.082353, 0.541176, 0.694118},{0.094118, 0.547059, 0.698039},{0.105882, 0.552941, 0.701961},{0.117647, 0.558824, 0.705882},{0.129412, 0.564706, 0.709804},{0.141176, 0.570588, 0.713725},{0.152941, 0.576471, 0.717647},{0.164706, 0.582353, 0.721569},{0.176471, 0.588235, 0.725490},{0.188235, 0.594118, 0.729412},{0.200000, 0.600000, 0.733333},{0.211765, 0.605882, 0.737255},{0.223529, 0.611765, 0.741176},{0.235294, 0.617647, 0.745098},{0.247059, 0.623529, 0.749020},{0.258824, 0.629412, 0.752941},{0.270588, 0.635294, 0.756863},{0.282353, 0.641176, 0.760784},{0.294118, 0.647059, 0.764706},{0.305882, 0.652941, 0.768627},{0.317647, 0.658824, 0.772549},{0.329412, 0.664706, 0.776471},{0.341176, 0.670588, 0.780392},{0.352941, 0.676471, 0.784314},{0.364706, 0.682353, 0.788235},{0.376471, 0.688235, 0.792157},{0.388235, 0.694118, 0.796078},{0.400000, 0.700000, 0.800000},{0.411765, 0.705882, 0.803922},{0.423529, 0.711765, 0.807843},{0.435294, 0.717647, 0.811765},{0.447059, 0.723529, 0.815686},{0.458824, 0.729412, 0.819608},{0.470588, 0.735294, 0.823529},{0.482353, 0.741176, 0.827451},{0.494118, 0.747059, 0.831373},{0.505882, 0.752941, 0.835294},{0.517647, 0.758824, 0.839216},{0.529412, 0.764706, 0.843137},{0.541176, 0.770588, 0.847059},{0.552941, 0.776471, 0.850980},{0.564706, 0.782353, 0.854902},{0.576471, 0.788235, 0.858824},{0.588235, 0.794118, 0.862745},{0.600000, 0.800000, 0.866667},{0.611765, 0.805882, 0.870588},{0.623529, 0.811765, 0.874510},{0.635294, 0.817647, 0.878431},{0.647059, 0.823529, 0.882353},{0.658824, 0.829412, 0.886275},{0.670588, 0.835294, 0.890196},{0.682353, 0.841176, 0.894118},{0.694118, 0.847059, 0.898039},{0.705882, 0.852941, 0.901961},{0.717647, 0.858824, 0.905882},{0.729412, 0.864706, 0.909804},{0.741176, 0.870588, 0.913725},{0.752941, 0.876471, 0.917647},{0.764706, 0.882353, 0.921569},{0.776471, 0.888235, 0.925490},{0.788235, 0.894118, 0.929412},{0.800000, 0.900000, 0.933333},{0.811765, 0.905882, 0.937255},{0.823529, 0.911765, 0.941176},{0.835294, 0.917647, 0.945098},{0.847059, 0.923529, 0.949020},{0.858824, 0.929412, 0.952941},{0.870588, 0.935294, 0.956863},{0.882353, 0.941176, 0.960784},{0.894118, 0.947059, 0.964706},{0.905882, 0.952941, 0.968627},{0.917647, 0.958824, 0.972549},{0.929412, 0.964706, 0.976471},{0.941176, 0.970588, 0.980392},{0.952941, 0.976471, 0.984314},{0.964706, 0.982353, 0.988235},{0.976471, 0.988235, 0.992157},{0.988235, 0.994118, 0.996078},{1.000000, 1.000000, 1.000000}}}, + {"gist_earth", {{0.000000, 0.000000, 0.000000},{0.002613, 0.000000, 0.168692},{0.005227, 0.000000, 0.221664},{0.007840, 0.000000, 0.263805},{0.010454, 0.000000, 0.305947},{0.013067, 0.000000, 0.348089},{0.015681, 0.000000, 0.390231},{0.018294, 0.000000, 0.432373},{0.020908, 0.008907, 0.454745},{0.023521, 0.017927, 0.455606},{0.026135, 0.026948, 0.456317},{0.028748, 0.035968, 0.457028},{0.031361, 0.044988, 0.457739},{0.033975, 0.054008, 0.458449},{0.036588, 0.063028, 0.459160},{0.039202, 0.072048, 0.459871},{0.041815, 0.081068, 0.460582},{0.044429, 0.090088, 0.461293},{0.047042, 0.099108, 0.462004},{0.049656, 0.108128, 0.462715},{0.052269, 0.117148, 0.463426},{0.054883, 0.126168, 0.464136},{0.057496, 0.135189, 0.464847},{0.060109, 0.144209, 0.465558},{0.062723, 0.153229, 0.466269},{0.065336, 0.162249, 0.466980},{0.067950, 0.171269, 0.467691},{0.070563, 0.180289, 0.468402},{0.073177, 0.189308, 0.469113},{0.075790, 0.197466, 0.469824},{0.078404, 0.205623, 0.470534},{0.081017, 0.213780, 0.471245},{0.083631, 0.221938, 0.471956},{0.086244, 0.230095, 0.472667},{0.088857, 0.238253, 0.473378},{0.091471, 0.246410, 0.474089},{0.094084, 0.254568, 0.474800},{0.096698, 0.262725, 0.475511},{0.099311, 0.270882, 0.476221},{0.101925, 0.279040, 0.476932},{0.104538, 0.287197, 0.477643},{0.107152, 0.295355, 0.478354},{0.109765, 0.303511, 0.479065},{0.112378, 0.310845, 0.479776},{0.114992, 0.318178, 0.480487},{0.117605, 0.325512, 0.481198},{0.120219, 0.332845, 0.481909},{0.122832, 0.340179, 0.482619},{0.125446, 0.347513, 0.483330},{0.128059, 0.354846, 0.484041},{0.130673, 0.362180, 0.484752},{0.133286, 0.369513, 0.485463},{0.135900, 0.376847, 0.486174},{0.138513, 0.384168, 0.486885},{0.141126, 0.390366, 0.487596},{0.143740, 0.396564, 0.488306},{0.146353, 0.402761, 0.489017},{0.148967, 0.408959, 0.489728},{0.151580, 0.415157, 0.490439},{0.154194, 0.421355, 0.491150},{0.156807, 0.427552, 0.491861},{0.159421, 0.433750, 0.492572},{0.162034, 0.439948, 0.493283},{0.164648, 0.446146, 0.493994},{0.167261, 0.452344, 0.494704},{0.169874, 0.458541, 0.495415},{0.172488, 0.464739, 0.496126},{0.175101, 0.470937, 0.496837},{0.177715, 0.477135, 0.497548},{0.180328, 0.483332, 0.498259},{0.182942, 0.489530, 0.498970},{0.185555, 0.495728, 0.499681},{0.188169, 0.501926, 0.500391},{0.190027, 0.504230, 0.495657},{0.191877, 0.506488, 0.490857},{0.193727, 0.508745, 0.486057},{0.195576, 0.511003, 0.481256},{0.197426, 0.513261, 0.476456},{0.199276, 0.515518, 0.471655},{0.201125, 0.517776, 0.466855},{0.202975, 0.520033, 0.462055},{0.204824, 0.522291, 0.457254},{0.206674, 0.524548, 0.452454},{0.208524, 0.526806, 0.447654},{0.210373, 0.529063, 0.442853},{0.212223, 0.531321, 0.438053},{0.214073, 0.533578, 0.433253},{0.215922, 0.535836, 0.428452},{0.217772, 0.538093, 0.423652},{0.219621, 0.540351, 0.418852},{0.221471, 0.542608, 0.414051},{0.223321, 0.544866, 0.409251},{0.225170, 0.547123, 0.404450},{0.227020, 0.549381, 0.399650},{0.228870, 0.551638, 0.394850},{0.230719, 0.553896, 0.390049},{0.232569, 0.556154, 0.385249},{0.234419, 0.558411, 0.380449},{0.236268, 0.560669, 0.375648},{0.238118, 0.562926, 0.370848},{0.239967, 0.565184, 0.366048},{0.241817, 0.567441, 0.361247},{0.243667, 0.569699, 0.356447},{0.245516, 0.571956, 0.351647},{0.247366, 0.574214, 0.346846},{0.249216, 0.576471, 0.342046},{0.251065, 0.578729, 0.337245},{0.252915, 0.580986, 0.332445},{0.254764, 0.583244, 0.327645},{0.256614, 0.585501, 0.322844},{0.258464, 0.587759, 0.318044},{0.260313, 0.590016, 0.313244},{0.262163, 0.592274, 0.308443},{0.264013, 0.594531, 0.303643},{0.265862, 0.596789, 0.298843},{0.267712, 0.599046, 0.294042},{0.269561, 0.601304, 0.289242},{0.271452, 0.603562, 0.284442},{0.280169, 0.605819, 0.279641},{0.288886, 0.608077, 0.274841},{0.297603, 0.610334, 0.277066},{0.306320, 0.612592, 0.279352},{0.315037, 0.614849, 0.281638},{0.323754, 0.617107, 0.283924},{0.332471, 0.619364, 0.286210},{0.341188, 0.621622, 0.288496},{0.349905, 0.623879, 0.290782},{0.358622, 0.626137, 0.293068},{0.367339, 0.628394, 0.295354},{0.376056, 0.630652, 0.297640},{0.384773, 0.632909, 0.299926},{0.393490, 0.635167, 0.302212},{0.402207, 0.637424, 0.304497},{0.410924, 0.639682, 0.306783},{0.419642, 0.641407, 0.309069},{0.428359, 0.643128, 0.311355},{0.437076, 0.644848, 0.313641},{0.445793, 0.646569, 0.315927},{0.454510, 0.648290, 0.318213},{0.463227, 0.650010, 0.320499},{0.471932, 0.651731, 0.321739},{0.478399, 0.653452, 0.322978},{0.484866, 0.655172, 0.324218},{0.491332, 0.656893, 0.325457},{0.497799, 0.658614, 0.326697},{0.504266, 0.660334, 0.327936},{0.510732, 0.662055, 0.329175},{0.517199, 0.663776, 0.330415},{0.523665, 0.665497, 0.331654},{0.530132, 0.667217, 0.332894},{0.536599, 0.668938, 0.334133},{0.543065, 0.670659, 0.335372},{0.549532, 0.672379, 0.336612},{0.555999, 0.674100, 0.337851},{0.562465, 0.675821, 0.339091},{0.568932, 0.677541, 0.340330},{0.575399, 0.679262, 0.341570},{0.581865, 0.680983, 0.342809},{0.588332, 0.682703, 0.344048},{0.594798, 0.684424, 0.345288},{0.601265, 0.686145, 0.346527},{0.607732, 0.687866, 0.347767},{0.614198, 0.689586, 0.349006},{0.620665, 0.691307, 0.350246},{0.627132, 0.693028, 0.351485},{0.633598, 0.694748, 0.352724},{0.640065, 0.696469, 0.353964},{0.646532, 0.698190, 0.355203},{0.652998, 0.699910, 0.356443},{0.659465, 0.701631, 0.357682},{0.665932, 0.703352, 0.358922},{0.672398, 0.705072, 0.360161},{0.678865, 0.706793, 0.361400},{0.685331, 0.708514, 0.362640},{0.691798, 0.710234, 0.363879},{0.698265, 0.711955, 0.365119},{0.704731, 0.713676, 0.366358},{0.711198, 0.715397, 0.367598},{0.717616, 0.717066, 0.368837},{0.719255, 0.713679, 0.370076},{0.720895, 0.710293, 0.371316},{0.722534, 0.706906, 0.372555},{0.724173, 0.703519, 0.373795},{0.725812, 0.700132, 0.375034},{0.727451, 0.696745, 0.376274},{0.729090, 0.693358, 0.377513},{0.730729, 0.689972, 0.378752},{0.732368, 0.686585, 0.379992},{0.734007, 0.683198, 0.381231},{0.735646, 0.679811, 0.382471},{0.737285, 0.676424, 0.383710},{0.738924, 0.673038, 0.384950},{0.740563, 0.669651, 0.386189},{0.742202, 0.666264, 0.387428},{0.743841, 0.662877, 0.388668},{0.745480, 0.659490, 0.389907},{0.747119, 0.656104, 0.391147},{0.748759, 0.652717, 0.392386},{0.750398, 0.649330, 0.393625},{0.752037, 0.645943, 0.394865},{0.753676, 0.642556, 0.396134},{0.755339, 0.639219, 0.405741},{0.759726, 0.641277, 0.415349},{0.764112, 0.642991, 0.424956},{0.768498, 0.644700, 0.434564},{0.772885, 0.646409, 0.444171},{0.777271, 0.648137, 0.453779},{0.781657, 0.651513, 0.463386},{0.786044, 0.654888, 0.472994},{0.790430, 0.658287, 0.482601},{0.794816, 0.661685, 0.492208},{0.799203, 0.665084, 0.501816},{0.803589, 0.668483, 0.511423},{0.807975, 0.671882, 0.521031},{0.812361, 0.675280, 0.530638},{0.816748, 0.678679, 0.540246},{0.821134, 0.682078, 0.549853},{0.825520, 0.685476, 0.559461},{0.829907, 0.688875, 0.569068},{0.834293, 0.692274, 0.578676},{0.838679, 0.695672, 0.588283},{0.843066, 0.699071, 0.597891},{0.847452, 0.704683, 0.607498},{0.851838, 0.710314, 0.617106},{0.856225, 0.715923, 0.626713},{0.860611, 0.721533, 0.636321},{0.864997, 0.726915, 0.645928},{0.869383, 0.732295, 0.655536},{0.873770, 0.737674, 0.665153},{0.878156, 0.743077, 0.676973},{0.882542, 0.750747, 0.688794},{0.886929, 0.758418, 0.700614},{0.891315, 0.766089, 0.712434},{0.895701, 0.773759, 0.724254},{0.900088, 0.781430, 0.736075},{0.904474, 0.789101, 0.747895},{0.908860, 0.796771, 0.759715},{0.913246, 0.804442, 0.771535},{0.917633, 0.812113, 0.783356},{0.922019, 0.819783, 0.795176},{0.926405, 0.827454, 0.806996},{0.930792, 0.836438, 0.818816},{0.935178, 0.845430, 0.830637},{0.939564, 0.854422, 0.842457},{0.943951, 0.863415, 0.854277},{0.948337, 0.872514, 0.866097},{0.952723, 0.881617, 0.877918},{0.957110, 0.893070, 0.889738},{0.961496, 0.904522, 0.901558},{0.965882, 0.915975, 0.913378},{0.970268, 0.927427, 0.925199},{0.974655, 0.938880, 0.937019},{0.979041, 0.950332, 0.948839},{0.983427, 0.961785, 0.960659},{0.987814, 0.973237, 0.972480},{0.992200, 0.984300, 0.984300}}}, + {"terrain", {{0.200000, 0.200000, 0.600000},{0.194771, 0.210458, 0.610458},{0.189542, 0.220915, 0.620915},{0.184314, 0.231373, 0.631373},{0.179085, 0.241830, 0.641830},{0.173856, 0.252288, 0.652288},{0.168627, 0.262745, 0.662745},{0.163399, 0.273203, 0.673203},{0.158170, 0.283660, 0.683660},{0.152941, 0.294118, 0.694118},{0.147712, 0.304575, 0.704575},{0.142484, 0.315033, 0.715033},{0.137255, 0.325490, 0.725490},{0.132026, 0.335948, 0.735948},{0.126797, 0.346405, 0.746405},{0.121569, 0.356863, 0.756863},{0.116340, 0.367320, 0.767320},{0.111111, 0.377778, 0.777778},{0.105882, 0.388235, 0.788235},{0.100654, 0.398693, 0.798693},{0.095425, 0.409150, 0.809150},{0.090196, 0.419608, 0.819608},{0.084967, 0.430065, 0.830065},{0.079739, 0.440523, 0.840523},{0.074510, 0.450980, 0.850980},{0.069281, 0.461438, 0.861438},{0.064052, 0.471895, 0.871895},{0.058824, 0.482353, 0.882353},{0.053595, 0.492810, 0.892810},{0.048366, 0.503268, 0.903268},{0.043137, 0.513725, 0.913725},{0.037908, 0.524183, 0.924183},{0.032680, 0.534641, 0.934641},{0.027451, 0.545098, 0.945098},{0.022222, 0.555556, 0.955556},{0.016993, 0.566013, 0.966013},{0.011765, 0.576471, 0.976471},{0.006536, 0.586928, 0.986928},{0.001307, 0.597386, 0.997386},{0.000000, 0.605882, 0.982353},{0.000000, 0.613725, 0.958824},{0.000000, 0.621569, 0.935294},{0.000000, 0.629412, 0.911765},{0.000000, 0.637255, 0.888235},{0.000000, 0.645098, 0.864706},{0.000000, 0.652941, 0.841176},{0.000000, 0.660784, 0.817647},{0.000000, 0.668627, 0.794118},{0.000000, 0.676471, 0.770588},{0.000000, 0.684314, 0.747059},{0.000000, 0.692157, 0.723529},{0.000000, 0.700000, 0.700000},{0.000000, 0.707843, 0.676471},{0.000000, 0.715686, 0.652941},{0.000000, 0.723529, 0.629412},{0.000000, 0.731373, 0.605882},{0.000000, 0.739216, 0.582353},{0.000000, 0.747059, 0.558824},{0.000000, 0.754902, 0.535294},{0.000000, 0.762745, 0.511765},{0.000000, 0.770588, 0.488235},{0.000000, 0.778431, 0.464706},{0.000000, 0.786275, 0.441176},{0.000000, 0.794118, 0.417647},{0.003922, 0.800784, 0.400784},{0.019608, 0.803922, 0.403922},{0.035294, 0.807059, 0.407059},{0.050980, 0.810196, 0.410196},{0.066667, 0.813333, 0.413333},{0.082353, 0.816471, 0.416471},{0.098039, 0.819608, 0.419608},{0.113725, 0.822745, 0.422745},{0.129412, 0.825882, 0.425882},{0.145098, 0.829020, 0.429020},{0.160784, 0.832157, 0.432157},{0.176471, 0.835294, 0.435294},{0.192157, 0.838431, 0.438431},{0.207843, 0.841569, 0.441569},{0.223529, 0.844706, 0.444706},{0.239216, 0.847843, 0.447843},{0.254902, 0.850980, 0.450980},{0.270588, 0.854118, 0.454118},{0.286275, 0.857255, 0.457255},{0.301961, 0.860392, 0.460392},{0.317647, 0.863529, 0.463529},{0.333333, 0.866667, 0.466667},{0.349020, 0.869804, 0.469804},{0.364706, 0.872941, 0.472941},{0.380392, 0.876078, 0.476078},{0.396078, 0.879216, 0.479216},{0.411765, 0.882353, 0.482353},{0.427451, 0.885490, 0.485490},{0.443137, 0.888627, 0.488627},{0.458824, 0.891765, 0.491765},{0.474510, 0.894902, 0.494902},{0.490196, 0.898039, 0.498039},{0.505882, 0.901176, 0.501176},{0.521569, 0.904314, 0.504314},{0.537255, 0.907451, 0.507451},{0.552941, 0.910588, 0.510588},{0.568627, 0.913725, 0.513725},{0.584314, 0.916863, 0.516863},{0.600000, 0.920000, 0.520000},{0.615686, 0.923137, 0.523137},{0.631373, 0.926275, 0.526275},{0.647059, 0.929412, 0.529412},{0.662745, 0.932549, 0.532549},{0.678431, 0.935686, 0.535686},{0.694118, 0.938824, 0.538824},{0.709804, 0.941961, 0.541961},{0.725490, 0.945098, 0.545098},{0.741176, 0.948235, 0.548235},{0.756863, 0.951373, 0.551373},{0.772549, 0.954510, 0.554510},{0.788235, 0.957647, 0.557647},{0.803922, 0.960784, 0.560784},{0.819608, 0.963922, 0.563922},{0.835294, 0.967059, 0.567059},{0.850980, 0.970196, 0.570196},{0.866667, 0.973333, 0.573333},{0.882353, 0.976471, 0.576471},{0.898039, 0.979608, 0.579608},{0.913725, 0.982745, 0.582745},{0.929412, 0.985882, 0.585882},{0.945098, 0.989020, 0.589020},{0.960784, 0.992157, 0.592157},{0.976471, 0.995294, 0.595294},{0.992157, 0.998431, 0.598431},{0.996078, 0.994980, 0.597882},{0.988235, 0.984941, 0.593647},{0.980392, 0.974902, 0.589412},{0.972549, 0.964863, 0.585176},{0.964706, 0.954824, 0.580941},{0.956863, 0.944784, 0.576706},{0.949020, 0.934745, 0.572471},{0.941176, 0.924706, 0.568235},{0.933333, 0.914667, 0.564000},{0.925490, 0.904627, 0.559765},{0.917647, 0.894588, 0.555529},{0.909804, 0.884549, 0.551294},{0.901961, 0.874510, 0.547059},{0.894118, 0.864471, 0.542824},{0.886275, 0.854431, 0.538588},{0.878431, 0.844392, 0.534353},{0.870588, 0.834353, 0.530118},{0.862745, 0.824314, 0.525882},{0.854902, 0.814275, 0.521647},{0.847059, 0.804235, 0.517412},{0.839216, 0.794196, 0.513176},{0.831373, 0.784157, 0.508941},{0.823529, 0.774118, 0.504706},{0.815686, 0.764078, 0.500471},{0.807843, 0.754039, 0.496235},{0.800000, 0.744000, 0.492000},{0.792157, 0.733961, 0.487765},{0.784314, 0.723922, 0.483529},{0.776471, 0.713882, 0.479294},{0.768627, 0.703843, 0.475059},{0.760784, 0.693804, 0.470824},{0.752941, 0.683765, 0.466588},{0.745098, 0.673725, 0.462353},{0.737255, 0.663686, 0.458118},{0.729412, 0.653647, 0.453882},{0.721569, 0.643608, 0.449647},{0.713725, 0.633569, 0.445412},{0.705882, 0.623529, 0.441176},{0.698039, 0.613490, 0.436941},{0.690196, 0.603451, 0.432706},{0.682353, 0.593412, 0.428471},{0.674510, 0.583373, 0.424235},{0.666667, 0.573333, 0.420000},{0.658824, 0.563294, 0.415765},{0.650980, 0.553255, 0.411529},{0.643137, 0.543216, 0.407294},{0.635294, 0.533176, 0.403059},{0.627451, 0.523137, 0.398824},{0.619608, 0.513098, 0.394588},{0.611765, 0.503059, 0.390353},{0.603922, 0.493020, 0.386118},{0.596078, 0.482980, 0.381882},{0.588235, 0.472941, 0.377647},{0.580392, 0.462902, 0.373412},{0.572549, 0.452863, 0.369176},{0.564706, 0.442824, 0.364941},{0.556863, 0.432784, 0.360706},{0.549020, 0.422745, 0.356471},{0.541176, 0.412706, 0.352235},{0.533333, 0.402667, 0.348000},{0.525490, 0.392627, 0.343765},{0.517647, 0.382588, 0.339529},{0.509804, 0.372549, 0.335294},{0.501961, 0.362510, 0.331059},{0.505882, 0.367529, 0.337882},{0.513725, 0.377569, 0.348392},{0.521569, 0.387608, 0.358902},{0.529412, 0.397647, 0.369412},{0.537255, 0.407686, 0.379922},{0.545098, 0.417725, 0.390431},{0.552941, 0.427765, 0.400941},{0.560784, 0.437804, 0.411451},{0.568627, 0.447843, 0.421961},{0.576471, 0.457882, 0.432471},{0.584314, 0.467922, 0.442980},{0.592157, 0.477961, 0.453490},{0.600000, 0.488000, 0.464000},{0.607843, 0.498039, 0.474510},{0.615686, 0.508078, 0.485020},{0.623529, 0.518118, 0.495529},{0.631373, 0.528157, 0.506039},{0.639216, 0.538196, 0.516549},{0.647059, 0.548235, 0.527059},{0.654902, 0.558275, 0.537569},{0.662745, 0.568314, 0.548078},{0.670588, 0.578353, 0.558588},{0.678431, 0.588392, 0.569098},{0.686275, 0.598431, 0.579608},{0.694118, 0.608471, 0.590118},{0.701961, 0.618510, 0.600627},{0.709804, 0.628549, 0.611137},{0.717647, 0.638588, 0.621647},{0.725490, 0.648627, 0.632157},{0.733333, 0.658667, 0.642667},{0.741176, 0.668706, 0.653176},{0.749020, 0.678745, 0.663686},{0.756863, 0.688784, 0.674196},{0.764706, 0.698824, 0.684706},{0.772549, 0.708863, 0.695216},{0.780392, 0.718902, 0.705725},{0.788235, 0.728941, 0.716235},{0.796078, 0.738980, 0.726745},{0.803922, 0.749020, 0.737255},{0.811765, 0.759059, 0.747765},{0.819608, 0.769098, 0.758275},{0.827451, 0.779137, 0.768784},{0.835294, 0.789176, 0.779294},{0.843137, 0.799216, 0.789804},{0.850980, 0.809255, 0.800314},{0.858824, 0.819294, 0.810824},{0.866667, 0.829333, 0.821333},{0.874510, 0.839373, 0.831843},{0.882353, 0.849412, 0.842353},{0.890196, 0.859451, 0.852863},{0.898039, 0.869490, 0.863373},{0.905882, 0.879529, 0.873882},{0.913725, 0.889569, 0.884392},{0.921569, 0.899608, 0.894902},{0.929412, 0.909647, 0.905412},{0.937255, 0.919686, 0.915922},{0.945098, 0.929725, 0.926431},{0.952941, 0.939765, 0.936941},{0.960784, 0.949804, 0.947451},{0.968627, 0.959843, 0.957961},{0.976471, 0.969882, 0.968471},{0.984314, 0.979922, 0.978980},{0.992157, 0.989961, 0.989490},{1.000000, 1.000000, 1.000000}}}, + {"twilight", {{0.885750, 0.850009, 0.887974},{0.881722, 0.851276, 0.886381},{0.877249, 0.851870, 0.884341},{0.872331, 0.851802, 0.881897},{0.866960, 0.851090, 0.879098},{0.861102, 0.849768, 0.875992},{0.854726, 0.847875, 0.872628},{0.847807, 0.845455, 0.869040},{0.840304, 0.842561, 0.865251},{0.832227, 0.839235, 0.861276},{0.823574, 0.835525, 0.857132},{0.814390, 0.831466, 0.852838},{0.804692, 0.827098, 0.848424},{0.794543, 0.822451, 0.843922},{0.783991, 0.817554, 0.839367},{0.773084, 0.812435, 0.834802},{0.761869, 0.807119, 0.830266},{0.750403, 0.801627, 0.825797},{0.738738, 0.795977, 0.821429},{0.726922, 0.790185, 0.817192},{0.715004, 0.784265, 0.813111},{0.703030, 0.778229, 0.809206},{0.691046, 0.772086, 0.805488},{0.679096, 0.765845, 0.801965},{0.667215, 0.759511, 0.798637},{0.655437, 0.753092, 0.795503},{0.643791, 0.746592, 0.792557},{0.632303, 0.740017, 0.789789},{0.620992, 0.733369, 0.787190},{0.609875, 0.726654, 0.784748},{0.598967, 0.719874, 0.782453},{0.588276, 0.713032, 0.780291},{0.577812, 0.706131, 0.778253},{0.567581, 0.699173, 0.776327},{0.557589, 0.692159, 0.774504},{0.547841, 0.685091, 0.772774},{0.538340, 0.677971, 0.771127},{0.529091, 0.670798, 0.769556},{0.520096, 0.663574, 0.768051},{0.511360, 0.656299, 0.766607},{0.502885, 0.648972, 0.765214},{0.494676, 0.641595, 0.763867},{0.486736, 0.634166, 0.762558},{0.479068, 0.626687, 0.761280},{0.471676, 0.619155, 0.760027},{0.464564, 0.611572, 0.758792},{0.457734, 0.603936, 0.757569},{0.451189, 0.596248, 0.756351},{0.444932, 0.588506, 0.755131},{0.438966, 0.580710, 0.753903},{0.433290, 0.572859, 0.752659},{0.427907, 0.564954, 0.751394},{0.422815, 0.556994, 0.750101},{0.418014, 0.548978, 0.748772},{0.413502, 0.540906, 0.747401},{0.409277, 0.532777, 0.745980},{0.405333, 0.524592, 0.744504},{0.401667, 0.516350, 0.742964},{0.398272, 0.508052, 0.741355},{0.395141, 0.499696, 0.739668},{0.392265, 0.491283, 0.737898},{0.389636, 0.482813, 0.736038},{0.387243, 0.474286, 0.734080},{0.385076, 0.465703, 0.732019},{0.383122, 0.457063, 0.729846},{0.381369, 0.448367, 0.727557},{0.379804, 0.439616, 0.725143},{0.378414, 0.430809, 0.722599},{0.377184, 0.421947, 0.719918},{0.376100, 0.413031, 0.717094},{0.375148, 0.404061, 0.714119},{0.374313, 0.395039, 0.710988},{0.373581, 0.385965, 0.707693},{0.372936, 0.376840, 0.704228},{0.372364, 0.367665, 0.700585},{0.371851, 0.358441, 0.696757},{0.371381, 0.349171, 0.692737},{0.370942, 0.339856, 0.688517},{0.370517, 0.330497, 0.684089},{0.370095, 0.321098, 0.679444},{0.369660, 0.311661, 0.674573},{0.369198, 0.302189, 0.669468},{0.368697, 0.292687, 0.664116},{0.368141, 0.283159, 0.658509},{0.367517, 0.273611, 0.652634},{0.366811, 0.264049, 0.646480},{0.366009, 0.254480, 0.640034},{0.365096, 0.244915, 0.633282},{0.364057, 0.235365, 0.626210},{0.362876, 0.225841, 0.618804},{0.361538, 0.216361, 0.611049},{0.360027, 0.206941, 0.602928},{0.358325, 0.197603, 0.594428},{0.356414, 0.188371, 0.585532},{0.354275, 0.179274, 0.576228},{0.351892, 0.170343, 0.566503},{0.349245, 0.161615, 0.556348},{0.346315, 0.153128, 0.545760},{0.343086, 0.144925, 0.534737},{0.339542, 0.137048, 0.523288},{0.335670, 0.129541, 0.511428},{0.331462, 0.122442, 0.499183},{0.326914, 0.115787, 0.486586},{0.322029, 0.109601, 0.473685},{0.316816, 0.103899, 0.460534},{0.311294, 0.098685, 0.447193},{0.305487, 0.093953, 0.433728},{0.299425, 0.089682, 0.420216},{0.293146, 0.085851, 0.406722},{0.286692, 0.082430, 0.393312},{0.280106, 0.079390, 0.380050},{0.273437, 0.076703, 0.366996},{0.266732, 0.074344, 0.354203},{0.260039, 0.072295, 0.341718},{0.253407, 0.070534, 0.329595},{0.246882, 0.069054, 0.317870},{0.240508, 0.067857, 0.306571},{0.234331, 0.066936, 0.295740},{0.228392, 0.066290, 0.285411},{0.222727, 0.065934, 0.275597},{0.217378, 0.065860, 0.266342},{0.212374, 0.066085, 0.257652},{0.207744, 0.066615, 0.249546},{0.203520, 0.067444, 0.242056},{0.199716, 0.068593, 0.235171},{0.196083, 0.070321, 0.228747},{0.191994, 0.073183, 0.222434},{0.187392, 0.077102, 0.216189},{0.187745, 0.077252, 0.213874},{0.193155, 0.073607, 0.215424},{0.198193, 0.070975, 0.216910},{0.202904, 0.069435, 0.218332},{0.207857, 0.068484, 0.220001},{0.213331, 0.067830, 0.222070},{0.219305, 0.067466, 0.224510},{0.225754, 0.067382, 0.227290},{0.232663, 0.067557, 0.230376},{0.239996, 0.067985, 0.233734},{0.247721, 0.068653, 0.237329},{0.255821, 0.069532, 0.241122},{0.264255, 0.070617, 0.245078},{0.272997, 0.071884, 0.249158},{0.282017, 0.073316, 0.253328},{0.291285, 0.074899, 0.257551},{0.300773, 0.076618, 0.261793},{0.310455, 0.078457, 0.266020},{0.320300, 0.080413, 0.270203},{0.330292, 0.082468, 0.274309},{0.340402, 0.084622, 0.278313},{0.350607, 0.086876, 0.282188},{0.360888, 0.089227, 0.285911},{0.371225, 0.091680, 0.289459},{0.381602, 0.094239, 0.292811},{0.391999, 0.096916, 0.295952},{0.402397, 0.099723, 0.298867},{0.412780, 0.102673, 0.301542},{0.423132, 0.105781, 0.303968},{0.433436, 0.109064, 0.306138},{0.443674, 0.112539, 0.308050},{0.453830, 0.116222, 0.309704},{0.463891, 0.120126, 0.311103},{0.473841, 0.124264, 0.312255},{0.483666, 0.128647, 0.313172},{0.493355, 0.133281, 0.313866},{0.502895, 0.138171, 0.314357},{0.512278, 0.143317, 0.314663},{0.521497, 0.148715, 0.314808},{0.530544, 0.154362, 0.314813},{0.539417, 0.160248, 0.314703},{0.548114, 0.166363, 0.314501},{0.556632, 0.172697, 0.314230},{0.564973, 0.179237, 0.313914},{0.573138, 0.185970, 0.313571},{0.581129, 0.192885, 0.313224},{0.588949, 0.199970, 0.312889},{0.596599, 0.207213, 0.312585},{0.604084, 0.214603, 0.312327},{0.611406, 0.222131, 0.312129},{0.618569, 0.229788, 0.312005},{0.625574, 0.237565, 0.311967},{0.632425, 0.245456, 0.312028},{0.639124, 0.253454, 0.312197},{0.645673, 0.261552, 0.312487},{0.652074, 0.269747, 0.312906},{0.658328, 0.278032, 0.313464},{0.664436, 0.286405, 0.314172},{0.670400, 0.294861, 0.315038},{0.676219, 0.303398, 0.316072},{0.681895, 0.312011, 0.317284},{0.687427, 0.320700, 0.318681},{0.692815, 0.329460, 0.320275},{0.698061, 0.338290, 0.322075},{0.703162, 0.347187, 0.324089},{0.708121, 0.356150, 0.326329},{0.712935, 0.365176, 0.328803},{0.717604, 0.374263, 0.331521},{0.722130, 0.383409, 0.334495},{0.726511, 0.392611, 0.337733},{0.730748, 0.401869, 0.341245},{0.734841, 0.411178, 0.345042},{0.738791, 0.420537, 0.349133},{0.742599, 0.429943, 0.353527},{0.746266, 0.439392, 0.358234},{0.749794, 0.448883, 0.363263},{0.753186, 0.458412, 0.368622},{0.756443, 0.467975, 0.374319},{0.759571, 0.477569, 0.380361},{0.762573, 0.487189, 0.386753},{0.765455, 0.496832, 0.393503},{0.768222, 0.506494, 0.400613},{0.770881, 0.516169, 0.408087},{0.773440, 0.525853, 0.415927},{0.775908, 0.535542, 0.424134},{0.778293, 0.545231, 0.432707},{0.780608, 0.554913, 0.441643},{0.782862, 0.564585, 0.450940},{0.785068, 0.574242, 0.460591},{0.787239, 0.583877, 0.470590},{0.789387, 0.593488, 0.480929},{0.791528, 0.603067, 0.491597},{0.793676, 0.612610, 0.502582},{0.795843, 0.622114, 0.513871},{0.798044, 0.631573, 0.525451},{0.800294, 0.640982, 0.537305},{0.802605, 0.650338, 0.549417},{0.804991, 0.659635, 0.561767},{0.807461, 0.668870, 0.574337},{0.810028, 0.678037, 0.587106},{0.812699, 0.687130, 0.600052},{0.815481, 0.696145, 0.613152},{0.818379, 0.705074, 0.626382},{0.821395, 0.713911, 0.639718},{0.824527, 0.722646, 0.653131},{0.827772, 0.731270, 0.666596},{0.831122, 0.739772, 0.680081},{0.834566, 0.748138, 0.693557},{0.838091, 0.756353, 0.706986},{0.841678, 0.764401, 0.720333},{0.845298, 0.772263, 0.733564},{0.848922, 0.779920, 0.746637},{0.852519, 0.787349, 0.759499},{0.856040, 0.794530, 0.772106},{0.859435, 0.801439, 0.784398},{0.862656, 0.808055, 0.796283},{0.865649, 0.814355, 0.807670},{0.868400, 0.820309, 0.818416},{0.870938, 0.825869, 0.828385},{0.873352, 0.830967, 0.837489},{0.875715, 0.835530, 0.845755},{0.878023, 0.839502, 0.853306},{0.880193, 0.842852, 0.860274},{0.882115, 0.845570, 0.866738},{0.883691, 0.847649, 0.872741},{0.884851, 0.849084, 0.878282},{0.885547, 0.849872, 0.883362},{0.885712, 0.850022, 0.885725}}}, + {"ylgn", {{1.000000, 1.000000, 0.898039},{0.999016, 0.999631, 0.892626},{0.998032, 0.999262, 0.887213},{0.997047, 0.998893, 0.881799},{0.996063, 0.998524, 0.876386},{0.995079, 0.998155, 0.870973},{0.994095, 0.997785, 0.865559},{0.993110, 0.997416, 0.860146},{0.992126, 0.997047, 0.854733},{0.991142, 0.996678, 0.849319},{0.990158, 0.996309, 0.843906},{0.989173, 0.995940, 0.838493},{0.988189, 0.995571, 0.833080},{0.987205, 0.995202, 0.827666},{0.986221, 0.994833, 0.822253},{0.985236, 0.994464, 0.816840},{0.984252, 0.994095, 0.811426},{0.983268, 0.993725, 0.806013},{0.982284, 0.993356, 0.800600},{0.981300, 0.992987, 0.795186},{0.980315, 0.992618, 0.789773},{0.979331, 0.992249, 0.784360},{0.978347, 0.991880, 0.778947},{0.977363, 0.991511, 0.773533},{0.976378, 0.991142, 0.768120},{0.975394, 0.990773, 0.762707},{0.974410, 0.990404, 0.757293},{0.973426, 0.990035, 0.751880},{0.972441, 0.989666, 0.746467},{0.971457, 0.989296, 0.741053},{0.970473, 0.988927, 0.735640},{0.969489, 0.988558, 0.730227},{0.968166, 0.988051, 0.725152},{0.964475, 0.986574, 0.722445},{0.960784, 0.985098, 0.719739},{0.957093, 0.983622, 0.717032},{0.953403, 0.982145, 0.714325},{0.949712, 0.980669, 0.711619},{0.946021, 0.979193, 0.708912},{0.942330, 0.977716, 0.706205},{0.938639, 0.976240, 0.703499},{0.934948, 0.974764, 0.700792},{0.931257, 0.973287, 0.698085},{0.927566, 0.971811, 0.695379},{0.923875, 0.970334, 0.692672},{0.920185, 0.968858, 0.689965},{0.916494, 0.967382, 0.687259},{0.912803, 0.965905, 0.684552},{0.909112, 0.964429, 0.681845},{0.905421, 0.962953, 0.679139},{0.901730, 0.961476, 0.676432},{0.898039, 0.960000, 0.673725},{0.894348, 0.958524, 0.671019},{0.890657, 0.957047, 0.668312},{0.886967, 0.955571, 0.665606},{0.883276, 0.954095, 0.662899},{0.879585, 0.952618, 0.660192},{0.875894, 0.951142, 0.657486},{0.872203, 0.949666, 0.654779},{0.868512, 0.948189, 0.652072},{0.864821, 0.946713, 0.649366},{0.861130, 0.945236, 0.646659},{0.857439, 0.943760, 0.643952},{0.853749, 0.942284, 0.641246},{0.849627, 0.940592, 0.638570},{0.844214, 0.938255, 0.635986},{0.838800, 0.935917, 0.633403},{0.833387, 0.933579, 0.630819},{0.827974, 0.931242, 0.628235},{0.822561, 0.928904, 0.625652},{0.817147, 0.926567, 0.623068},{0.811734, 0.924229, 0.620484},{0.806321, 0.921892, 0.617901},{0.800907, 0.919554, 0.615317},{0.795494, 0.917216, 0.612734},{0.790081, 0.914879, 0.610150},{0.784667, 0.912541, 0.607566},{0.779254, 0.910204, 0.604983},{0.773841, 0.907866, 0.602399},{0.768428, 0.905529, 0.599815},{0.763014, 0.903191, 0.597232},{0.757601, 0.900854, 0.594648},{0.752188, 0.898516, 0.592065},{0.746774, 0.896178, 0.589481},{0.741361, 0.893841, 0.586897},{0.735948, 0.891503, 0.584314},{0.730534, 0.889166, 0.581730},{0.725121, 0.886828, 0.579146},{0.719708, 0.884491, 0.576563},{0.714295, 0.882153, 0.573979},{0.708881, 0.879815, 0.571396},{0.703468, 0.877478, 0.568812},{0.698055, 0.875140, 0.566228},{0.692641, 0.872803, 0.563645},{0.687228, 0.870465, 0.561061},{0.681815, 0.868128, 0.558478},{0.675986, 0.865606, 0.555894},{0.669466, 0.862776, 0.553310},{0.662945, 0.859946, 0.550727},{0.656424, 0.857116, 0.548143},{0.649904, 0.854287, 0.545559},{0.643383, 0.851457, 0.542976},{0.636863, 0.848627, 0.540392},{0.630342, 0.845798, 0.537809},{0.623822, 0.842968, 0.535225},{0.617301, 0.840138, 0.532641},{0.610780, 0.837309, 0.530058},{0.604260, 0.834479, 0.527474},{0.597739, 0.831649, 0.524890},{0.591219, 0.828820, 0.522307},{0.584698, 0.825990, 0.519723},{0.578178, 0.823160, 0.517140},{0.571657, 0.820331, 0.514556},{0.565136, 0.817501, 0.511972},{0.558616, 0.814671, 0.509389},{0.552095, 0.811842, 0.506805},{0.545575, 0.809012, 0.504221},{0.539054, 0.806182, 0.501638},{0.532534, 0.803353, 0.499054},{0.526013, 0.800523, 0.496471},{0.519493, 0.797693, 0.493887},{0.512972, 0.794864, 0.491303},{0.506451, 0.792034, 0.488720},{0.499931, 0.789204, 0.486136},{0.493410, 0.786374, 0.483552},{0.486890, 0.783545, 0.480969},{0.480369, 0.780715, 0.478385},{0.473849, 0.777885, 0.475802},{0.467205, 0.774810, 0.472787},{0.460438, 0.771488, 0.469343},{0.453672, 0.768166, 0.465898},{0.446905, 0.764844, 0.462453},{0.440138, 0.761522, 0.459008},{0.433372, 0.758201, 0.455563},{0.426605, 0.754879, 0.452118},{0.419839, 0.751557, 0.448674},{0.413072, 0.748235, 0.445229},{0.406305, 0.744913, 0.441784},{0.399539, 0.741592, 0.438339},{0.392772, 0.738270, 0.434894},{0.386005, 0.734948, 0.431449},{0.379239, 0.731626, 0.428005},{0.372472, 0.728304, 0.424560},{0.365705, 0.724983, 0.421115},{0.358939, 0.721661, 0.417670},{0.352172, 0.718339, 0.414225},{0.345406, 0.715017, 0.410780},{0.338639, 0.711696, 0.407336},{0.331872, 0.708374, 0.403891},{0.325106, 0.705052, 0.400446},{0.318339, 0.701730, 0.397001},{0.311572, 0.698408, 0.393556},{0.304806, 0.695087, 0.390111},{0.298039, 0.691765, 0.386667},{0.291273, 0.688443, 0.383222},{0.284506, 0.685121, 0.379777},{0.277739, 0.681799, 0.376332},{0.270973, 0.678478, 0.372887},{0.264206, 0.675156, 0.369443},{0.257439, 0.671834, 0.365998},{0.252595, 0.667589, 0.362707},{0.248904, 0.662791, 0.359508},{0.245213, 0.657993, 0.356309},{0.241522, 0.653195, 0.353110},{0.237832, 0.648397, 0.349912},{0.234141, 0.643599, 0.346713},{0.230450, 0.638800, 0.343514},{0.226759, 0.634002, 0.340315},{0.223068, 0.629204, 0.337116},{0.219377, 0.624406, 0.333918},{0.215686, 0.619608, 0.330719},{0.211995, 0.614810, 0.327520},{0.208304, 0.610012, 0.324321},{0.204614, 0.605213, 0.321123},{0.200923, 0.600415, 0.317924},{0.197232, 0.595617, 0.314725},{0.193541, 0.590819, 0.311526},{0.189850, 0.586021, 0.308328},{0.186159, 0.581223, 0.305129},{0.182468, 0.576424, 0.301930},{0.178777, 0.571626, 0.298731},{0.175087, 0.566828, 0.295532},{0.171396, 0.562030, 0.292334},{0.167705, 0.557232, 0.289135},{0.164014, 0.552434, 0.285936},{0.160323, 0.547636, 0.282737},{0.156632, 0.542837, 0.279539},{0.152941, 0.538039, 0.276340},{0.149250, 0.533241, 0.273141},{0.145559, 0.528443, 0.269942},{0.141869, 0.523645, 0.266744},{0.138178, 0.518847, 0.263545},{0.134025, 0.515063, 0.261638},{0.129719, 0.511619, 0.260161},{0.125413, 0.508174, 0.258685},{0.121107, 0.504729, 0.257209},{0.116801, 0.501284, 0.255732},{0.112495, 0.497839, 0.254256},{0.108189, 0.494394, 0.252780},{0.103883, 0.490950, 0.251303},{0.099577, 0.487505, 0.249827},{0.095271, 0.484060, 0.248351},{0.090965, 0.480615, 0.246874},{0.086659, 0.477170, 0.245398},{0.082353, 0.473725, 0.243922},{0.078047, 0.470281, 0.242445},{0.073741, 0.466836, 0.240969},{0.069435, 0.463391, 0.239493},{0.065129, 0.459946, 0.238016},{0.060823, 0.456501, 0.236540},{0.056517, 0.453057, 0.235063},{0.052211, 0.449612, 0.233587},{0.047905, 0.446167, 0.232111},{0.043599, 0.442722, 0.230634},{0.039293, 0.439277, 0.229158},{0.034987, 0.435832, 0.227682},{0.030681, 0.432388, 0.226205},{0.026374, 0.428943, 0.224729},{0.022068, 0.425498, 0.223253},{0.017762, 0.422053, 0.221776},{0.013456, 0.418608, 0.220300},{0.009150, 0.415163, 0.218824},{0.004844, 0.411719, 0.217347},{0.000538, 0.408274, 0.215871},{0.000000, 0.404075, 0.214179},{0.000000, 0.399769, 0.212457},{0.000000, 0.395463, 0.210734},{0.000000, 0.391157, 0.209012},{0.000000, 0.386851, 0.207290},{0.000000, 0.382545, 0.205567},{0.000000, 0.378239, 0.203845},{0.000000, 0.373933, 0.202122},{0.000000, 0.369627, 0.200400},{0.000000, 0.365321, 0.198677},{0.000000, 0.361015, 0.196955},{0.000000, 0.356709, 0.195233},{0.000000, 0.352403, 0.193510},{0.000000, 0.348097, 0.191788},{0.000000, 0.343791, 0.190065},{0.000000, 0.339485, 0.188343},{0.000000, 0.335179, 0.186621},{0.000000, 0.330873, 0.184898},{0.000000, 0.326567, 0.183176},{0.000000, 0.322261, 0.181453},{0.000000, 0.317955, 0.179731},{0.000000, 0.313649, 0.178008},{0.000000, 0.309343, 0.176286},{0.000000, 0.305037, 0.174564},{0.000000, 0.300730, 0.172841},{0.000000, 0.296424, 0.171119},{0.000000, 0.292118, 0.169396},{0.000000, 0.287812, 0.167674},{0.000000, 0.283506, 0.165952},{0.000000, 0.279200, 0.164229},{0.000000, 0.274894, 0.162507},{0.000000, 0.270588, 0.160784}}}, + {"ylgnbu", {{1.000000, 1.000000, 0.850980},{0.997785, 0.999139, 0.846059},{0.995571, 0.998278, 0.841138},{0.993356, 0.997416, 0.836217},{0.991142, 0.996555, 0.831296},{0.988927, 0.995694, 0.826374},{0.986713, 0.994833, 0.821453},{0.984498, 0.993972, 0.816532},{0.982284, 0.993110, 0.811611},{0.980069, 0.992249, 0.806690},{0.977855, 0.991388, 0.801769},{0.975640, 0.990527, 0.796847},{0.973426, 0.989666, 0.791926},{0.971211, 0.988804, 0.787005},{0.968997, 0.987943, 0.782084},{0.966782, 0.987082, 0.777163},{0.964567, 0.986221, 0.772241},{0.962353, 0.985359, 0.767320},{0.960138, 0.984498, 0.762399},{0.957924, 0.983637, 0.757478},{0.955709, 0.982776, 0.752557},{0.953495, 0.981915, 0.747636},{0.951280, 0.981053, 0.742714},{0.949066, 0.980192, 0.737793},{0.946851, 0.979331, 0.732872},{0.944637, 0.978470, 0.727951},{0.942422, 0.977609, 0.723030},{0.940208, 0.976747, 0.718108},{0.937993, 0.975886, 0.713187},{0.935779, 0.975025, 0.708266},{0.933564, 0.974164, 0.703345},{0.931349, 0.973303, 0.698424},{0.928827, 0.972318, 0.694164},{0.924152, 0.970473, 0.694533},{0.919477, 0.968627, 0.694902},{0.914802, 0.966782, 0.695271},{0.910127, 0.964937, 0.695640},{0.905452, 0.963091, 0.696009},{0.900777, 0.961246, 0.696378},{0.896101, 0.959400, 0.696747},{0.891426, 0.957555, 0.697116},{0.886751, 0.955709, 0.697486},{0.882076, 0.953864, 0.697855},{0.877401, 0.952018, 0.698224},{0.872726, 0.950173, 0.698593},{0.868051, 0.948328, 0.698962},{0.863376, 0.946482, 0.699331},{0.858700, 0.944637, 0.699700},{0.854025, 0.942791, 0.700069},{0.849350, 0.940946, 0.700438},{0.844675, 0.939100, 0.700807},{0.840000, 0.937255, 0.701176},{0.835325, 0.935409, 0.701546},{0.830650, 0.933564, 0.701915},{0.825975, 0.931719, 0.702284},{0.821300, 0.929873, 0.702653},{0.816624, 0.928028, 0.703022},{0.811949, 0.926182, 0.703391},{0.807274, 0.924337, 0.703760},{0.802599, 0.922491, 0.704129},{0.797924, 0.920646, 0.704498},{0.793249, 0.918800, 0.704867},{0.788574, 0.916955, 0.705236},{0.783899, 0.915110, 0.705606},{0.778178, 0.912864, 0.706098},{0.769319, 0.909419, 0.706959},{0.760461, 0.905975, 0.707820},{0.751603, 0.902530, 0.708681},{0.742745, 0.899085, 0.709542},{0.733887, 0.895640, 0.710404},{0.725029, 0.892195, 0.711265},{0.716171, 0.888750, 0.712126},{0.707313, 0.885306, 0.712987},{0.698454, 0.881861, 0.713849},{0.689596, 0.878416, 0.714710},{0.680738, 0.874971, 0.715571},{0.671880, 0.871526, 0.716432},{0.663022, 0.868082, 0.717293},{0.654164, 0.864637, 0.718155},{0.645306, 0.861192, 0.719016},{0.636448, 0.857747, 0.719877},{0.627589, 0.854302, 0.720738},{0.618731, 0.850857, 0.721599},{0.609873, 0.847413, 0.722461},{0.601015, 0.843968, 0.723322},{0.592157, 0.840523, 0.724183},{0.583299, 0.837078, 0.725044},{0.574441, 0.833633, 0.725905},{0.565582, 0.830188, 0.726767},{0.556724, 0.826744, 0.727628},{0.547866, 0.823299, 0.728489},{0.539008, 0.819854, 0.729350},{0.530150, 0.816409, 0.730211},{0.521292, 0.812964, 0.731073},{0.512434, 0.809519, 0.731934},{0.503576, 0.806075, 0.732795},{0.495179, 0.802860, 0.733749},{0.487551, 0.800031, 0.734856},{0.479923, 0.797201, 0.735963},{0.472295, 0.794371, 0.737070},{0.464667, 0.791542, 0.738178},{0.457040, 0.788712, 0.739285},{0.449412, 0.785882, 0.740392},{0.441784, 0.783053, 0.741499},{0.434156, 0.780223, 0.742607},{0.426528, 0.777393, 0.743714},{0.418900, 0.774564, 0.744821},{0.411273, 0.771734, 0.745928},{0.403645, 0.768904, 0.747036},{0.396017, 0.766075, 0.748143},{0.388389, 0.763245, 0.749250},{0.380761, 0.760415, 0.750358},{0.373133, 0.757586, 0.751465},{0.365506, 0.754756, 0.752572},{0.357878, 0.751926, 0.753679},{0.350250, 0.749097, 0.754787},{0.342622, 0.746267, 0.755894},{0.334994, 0.743437, 0.757001},{0.327366, 0.740607, 0.758108},{0.319739, 0.737778, 0.759216},{0.312111, 0.734948, 0.760323},{0.304483, 0.732118, 0.761430},{0.296855, 0.729289, 0.762537},{0.289227, 0.726459, 0.763645},{0.281599, 0.723629, 0.764752},{0.273972, 0.720800, 0.765859},{0.266344, 0.717970, 0.766967},{0.258716, 0.715140, 0.768074},{0.252687, 0.711449, 0.768381},{0.248258, 0.706897, 0.767889},{0.243829, 0.702345, 0.767397},{0.239400, 0.697793, 0.766905},{0.234971, 0.693241, 0.766413},{0.230542, 0.688689, 0.765921},{0.226113, 0.684137, 0.765429},{0.221684, 0.679585, 0.764937},{0.217255, 0.675033, 0.764444},{0.212826, 0.670481, 0.763952},{0.208397, 0.665928, 0.763460},{0.203968, 0.661376, 0.762968},{0.199539, 0.656824, 0.762476},{0.195110, 0.652272, 0.761984},{0.190681, 0.647720, 0.761492},{0.186251, 0.643168, 0.761000},{0.181822, 0.638616, 0.760507},{0.177393, 0.634064, 0.760015},{0.172964, 0.629512, 0.759523},{0.168535, 0.624960, 0.759031},{0.164106, 0.620408, 0.758539},{0.159677, 0.615855, 0.758047},{0.155248, 0.611303, 0.757555},{0.150819, 0.606751, 0.757063},{0.146390, 0.602199, 0.756571},{0.141961, 0.597647, 0.756078},{0.137532, 0.593095, 0.755586},{0.133103, 0.588543, 0.755094},{0.128674, 0.583991, 0.754602},{0.124245, 0.579439, 0.754110},{0.119815, 0.574887, 0.753618},{0.115386, 0.570334, 0.753126},{0.114110, 0.564706, 0.751096},{0.114725, 0.558431, 0.748143},{0.115340, 0.552157, 0.745190},{0.115955, 0.545882, 0.742238},{0.116571, 0.539608, 0.739285},{0.117186, 0.533333, 0.736332},{0.117801, 0.527059, 0.733379},{0.118416, 0.520784, 0.730427},{0.119031, 0.514510, 0.727474},{0.119646, 0.508235, 0.724521},{0.120261, 0.501961, 0.721569},{0.120877, 0.495686, 0.718616},{0.121492, 0.489412, 0.715663},{0.122107, 0.483137, 0.712710},{0.122722, 0.476863, 0.709758},{0.123337, 0.470588, 0.706805},{0.123952, 0.464314, 0.703852},{0.124567, 0.458039, 0.700900},{0.125183, 0.451765, 0.697947},{0.125798, 0.445490, 0.694994},{0.126413, 0.439216, 0.692042},{0.127028, 0.432941, 0.689089},{0.127643, 0.426667, 0.686136},{0.128258, 0.420392, 0.683183},{0.128874, 0.414118, 0.680231},{0.129489, 0.407843, 0.677278},{0.130104, 0.401569, 0.674325},{0.130719, 0.395294, 0.671373},{0.131334, 0.389020, 0.668420},{0.131949, 0.382745, 0.665467},{0.132564, 0.376471, 0.662514},{0.133180, 0.370196, 0.659562},{0.133610, 0.364752, 0.656978},{0.133979, 0.359585, 0.654517},{0.134348, 0.354418, 0.652057},{0.134717, 0.349250, 0.649596},{0.135087, 0.344083, 0.647136},{0.135456, 0.338916, 0.644675},{0.135825, 0.333749, 0.642215},{0.136194, 0.328581, 0.639754},{0.136563, 0.323414, 0.637293},{0.136932, 0.318247, 0.634833},{0.137301, 0.313080, 0.632372},{0.137670, 0.307912, 0.629912},{0.138039, 0.302745, 0.627451},{0.138408, 0.297578, 0.624990},{0.138777, 0.292411, 0.622530},{0.139146, 0.287243, 0.620069},{0.139516, 0.282076, 0.617609},{0.139885, 0.276909, 0.615148},{0.140254, 0.271742, 0.612687},{0.140623, 0.266574, 0.610227},{0.140992, 0.261407, 0.607766},{0.141361, 0.256240, 0.605306},{0.141730, 0.251073, 0.602845},{0.142099, 0.245905, 0.600384},{0.142468, 0.240738, 0.597924},{0.142837, 0.235571, 0.595463},{0.143206, 0.230404, 0.593003},{0.143576, 0.225236, 0.590542},{0.143945, 0.220069, 0.588082},{0.144314, 0.214902, 0.585621},{0.144683, 0.209735, 0.583160},{0.145052, 0.204567, 0.580700},{0.141976, 0.201446, 0.573933},{0.138408, 0.198616, 0.566551},{0.134840, 0.195786, 0.559170},{0.131273, 0.192957, 0.551788},{0.127705, 0.190127, 0.544406},{0.124137, 0.187297, 0.537024},{0.120569, 0.184468, 0.529642},{0.117001, 0.181638, 0.522261},{0.113433, 0.178808, 0.514879},{0.109865, 0.175978, 0.507497},{0.106298, 0.173149, 0.500115},{0.102730, 0.170319, 0.492734},{0.099162, 0.167489, 0.485352},{0.095594, 0.164660, 0.477970},{0.092026, 0.161830, 0.470588},{0.088458, 0.159000, 0.463206},{0.084890, 0.156171, 0.455825},{0.081323, 0.153341, 0.448443},{0.077755, 0.150511, 0.441061},{0.074187, 0.147682, 0.433679},{0.070619, 0.144852, 0.426298},{0.067051, 0.142022, 0.418916},{0.063483, 0.139193, 0.411534},{0.059915, 0.136363, 0.404152},{0.056348, 0.133533, 0.396770},{0.052780, 0.130704, 0.389389},{0.049212, 0.127874, 0.382007},{0.045644, 0.125044, 0.374625},{0.042076, 0.122215, 0.367243},{0.038508, 0.119385, 0.359862},{0.034940, 0.116555, 0.352480},{0.031373, 0.113725, 0.345098}}}, + {"piyg", {{0.556863, 0.003922, 0.321569},{0.565321, 0.007920, 0.328181},{0.573779, 0.011918, 0.334794},{0.582238, 0.015917, 0.341407},{0.590696, 0.019915, 0.348020},{0.599154, 0.023914, 0.354633},{0.607612, 0.027912, 0.361246},{0.616071, 0.031911, 0.367859},{0.624529, 0.035909, 0.374471},{0.632987, 0.039908, 0.381084},{0.641446, 0.043906, 0.387697},{0.649904, 0.047905, 0.394310},{0.658362, 0.051903, 0.400923},{0.666820, 0.055902, 0.407536},{0.675279, 0.059900, 0.414148},{0.683737, 0.063899, 0.420761},{0.692195, 0.067897, 0.427374},{0.700654, 0.071895, 0.433987},{0.709112, 0.075894, 0.440600},{0.717570, 0.079892, 0.447213},{0.726028, 0.083891, 0.453825},{0.734487, 0.087889, 0.460438},{0.742945, 0.091888, 0.467051},{0.751403, 0.095886, 0.473664},{0.759862, 0.099885, 0.480277},{0.768320, 0.103883, 0.486890},{0.774471, 0.112957, 0.493964},{0.778316, 0.127105, 0.501499},{0.782161, 0.141253, 0.509035},{0.786005, 0.155402, 0.516571},{0.789850, 0.169550, 0.524106},{0.793695, 0.183699, 0.531642},{0.797539, 0.197847, 0.539177},{0.801384, 0.211995, 0.546713},{0.805229, 0.226144, 0.554248},{0.809073, 0.240292, 0.561784},{0.812918, 0.254441, 0.569319},{0.816763, 0.268589, 0.576855},{0.820607, 0.282737, 0.584391},{0.824452, 0.296886, 0.591926},{0.828297, 0.311034, 0.599462},{0.832141, 0.325183, 0.606997},{0.835986, 0.339331, 0.614533},{0.839831, 0.353479, 0.622068},{0.843676, 0.367628, 0.629604},{0.847520, 0.381776, 0.637140},{0.851365, 0.395925, 0.644675},{0.855210, 0.410073, 0.652211},{0.859054, 0.424221, 0.659746},{0.862899, 0.438370, 0.667282},{0.866744, 0.452518, 0.674817},{0.870588, 0.466667, 0.682353},{0.873510, 0.476355, 0.689120},{0.876432, 0.486044, 0.695886},{0.879354, 0.495732, 0.702653},{0.882276, 0.505421, 0.709419},{0.885198, 0.515110, 0.716186},{0.888120, 0.524798, 0.722953},{0.891042, 0.534487, 0.729719},{0.893964, 0.544175, 0.736486},{0.896886, 0.553864, 0.743253},{0.899808, 0.563552, 0.750019},{0.902730, 0.573241, 0.756786},{0.905652, 0.582930, 0.763552},{0.908574, 0.592618, 0.770319},{0.911496, 0.602307, 0.777086},{0.914418, 0.611995, 0.783852},{0.917339, 0.621684, 0.790619},{0.920261, 0.631373, 0.797386},{0.923183, 0.641061, 0.804152},{0.926105, 0.650750, 0.810919},{0.929027, 0.660438, 0.817686},{0.931949, 0.670127, 0.824452},{0.934871, 0.679815, 0.831219},{0.937793, 0.689504, 0.837985},{0.940715, 0.699193, 0.844752},{0.943637, 0.708881, 0.851519},{0.946021, 0.716955, 0.856517},{0.947866, 0.723414, 0.859746},{0.949712, 0.729873, 0.862976},{0.951557, 0.736332, 0.866205},{0.953403, 0.742791, 0.869435},{0.955248, 0.749250, 0.872664},{0.957093, 0.755709, 0.875894},{0.958939, 0.762168, 0.879123},{0.960784, 0.768627, 0.882353},{0.962630, 0.775087, 0.885582},{0.964475, 0.781546, 0.888812},{0.966321, 0.788005, 0.892042},{0.968166, 0.794464, 0.895271},{0.970012, 0.800923, 0.898501},{0.971857, 0.807382, 0.901730},{0.973702, 0.813841, 0.904960},{0.975548, 0.820300, 0.908189},{0.977393, 0.826759, 0.911419},{0.979239, 0.833218, 0.914648},{0.981084, 0.839677, 0.917878},{0.982930, 0.846136, 0.921107},{0.984775, 0.852595, 0.924337},{0.986621, 0.859054, 0.927566},{0.988466, 0.865513, 0.930796},{0.990311, 0.871972, 0.934025},{0.992157, 0.878431, 0.937255},{0.991234, 0.881968, 0.938485},{0.990311, 0.885506, 0.939715},{0.989389, 0.889043, 0.940946},{0.988466, 0.892580, 0.942176},{0.987543, 0.896117, 0.943406},{0.986621, 0.899654, 0.944637},{0.985698, 0.903191, 0.945867},{0.984775, 0.906728, 0.947097},{0.983852, 0.910265, 0.948328},{0.982930, 0.913802, 0.949558},{0.982007, 0.917339, 0.950788},{0.981084, 0.920877, 0.952018},{0.980161, 0.924414, 0.953249},{0.979239, 0.927951, 0.954479},{0.978316, 0.931488, 0.955709},{0.977393, 0.935025, 0.956940},{0.976471, 0.938562, 0.958170},{0.975548, 0.942099, 0.959400},{0.974625, 0.945636, 0.960631},{0.973702, 0.949173, 0.961861},{0.972780, 0.952710, 0.963091},{0.971857, 0.956248, 0.964321},{0.970934, 0.959785, 0.965552},{0.970012, 0.963322, 0.966782},{0.969089, 0.966859, 0.968012},{0.967320, 0.968474, 0.965629},{0.964706, 0.968166, 0.959631},{0.962092, 0.967859, 0.953633},{0.959477, 0.967551, 0.947636},{0.956863, 0.967243, 0.941638},{0.954248, 0.966936, 0.935640},{0.951634, 0.966628, 0.929642},{0.949020, 0.966321, 0.923645},{0.946405, 0.966013, 0.917647},{0.943791, 0.965705, 0.911649},{0.941176, 0.965398, 0.905652},{0.938562, 0.965090, 0.899654},{0.935948, 0.964783, 0.893656},{0.933333, 0.964475, 0.887659},{0.930719, 0.964168, 0.881661},{0.928105, 0.963860, 0.875663},{0.925490, 0.963552, 0.869666},{0.922876, 0.963245, 0.863668},{0.920261, 0.962937, 0.857670},{0.917647, 0.962630, 0.851672},{0.915033, 0.962322, 0.845675},{0.912418, 0.962015, 0.839677},{0.909804, 0.961707, 0.833679},{0.907190, 0.961399, 0.827682},{0.904575, 0.961092, 0.821684},{0.901961, 0.960784, 0.815686},{0.894887, 0.957709, 0.804306},{0.887812, 0.954633, 0.792926},{0.880738, 0.951557, 0.781546},{0.873664, 0.948481, 0.770165},{0.866590, 0.945406, 0.758785},{0.859516, 0.942330, 0.747405},{0.852441, 0.939254, 0.736025},{0.845367, 0.936178, 0.724644},{0.838293, 0.933103, 0.713264},{0.831219, 0.930027, 0.701884},{0.824145, 0.926951, 0.690504},{0.817070, 0.923875, 0.679123},{0.809996, 0.920800, 0.667743},{0.802922, 0.917724, 0.656363},{0.795848, 0.914648, 0.644983},{0.788774, 0.911572, 0.633602},{0.781699, 0.908497, 0.622222},{0.774625, 0.905421, 0.610842},{0.767551, 0.902345, 0.599462},{0.760477, 0.899270, 0.588082},{0.753403, 0.896194, 0.576701},{0.746328, 0.893118, 0.565321},{0.739254, 0.890042, 0.553941},{0.732180, 0.886967, 0.542561},{0.725106, 0.883891, 0.531180},{0.717186, 0.879508, 0.520185},{0.708420, 0.873818, 0.509573},{0.699654, 0.868128, 0.498962},{0.690888, 0.862438, 0.488351},{0.682122, 0.856747, 0.477739},{0.673356, 0.851057, 0.467128},{0.664591, 0.845367, 0.456517},{0.655825, 0.839677, 0.445905},{0.647059, 0.833987, 0.435294},{0.638293, 0.828297, 0.424683},{0.629527, 0.822607, 0.414072},{0.620761, 0.816917, 0.403460},{0.611995, 0.811226, 0.392849},{0.603230, 0.805536, 0.382238},{0.594464, 0.799846, 0.371626},{0.585698, 0.794156, 0.361015},{0.576932, 0.788466, 0.350404},{0.568166, 0.782776, 0.339792},{0.559400, 0.777086, 0.329181},{0.550634, 0.771396, 0.318570},{0.541869, 0.765705, 0.307958},{0.533103, 0.760015, 0.297347},{0.524337, 0.754325, 0.286736},{0.515571, 0.748635, 0.276125},{0.506805, 0.742945, 0.265513},{0.498039, 0.737255, 0.254902},{0.490350, 0.730796, 0.249981},{0.482661, 0.724337, 0.245060},{0.474971, 0.717878, 0.240138},{0.467282, 0.711419, 0.235217},{0.459592, 0.704960, 0.230296},{0.451903, 0.698501, 0.225375},{0.444214, 0.692042, 0.220454},{0.436524, 0.685582, 0.215532},{0.428835, 0.679123, 0.210611},{0.421146, 0.672664, 0.205690},{0.413456, 0.666205, 0.200769},{0.405767, 0.659746, 0.195848},{0.398078, 0.653287, 0.190927},{0.390388, 0.646828, 0.186005},{0.382699, 0.640369, 0.181084},{0.375010, 0.633910, 0.176163},{0.367320, 0.627451, 0.171242},{0.359631, 0.620992, 0.166321},{0.351942, 0.614533, 0.161399},{0.344252, 0.608074, 0.156478},{0.336563, 0.601615, 0.151557},{0.328874, 0.595156, 0.146636},{0.321184, 0.588697, 0.141715},{0.313495, 0.582238, 0.136794},{0.305805, 0.575779, 0.131872},{0.299039, 0.569012, 0.128797},{0.293195, 0.561938, 0.127566},{0.287351, 0.554864, 0.126336},{0.281507, 0.547789, 0.125106},{0.275663, 0.540715, 0.123875},{0.269819, 0.533641, 0.122645},{0.263975, 0.526567, 0.121415},{0.258131, 0.519493, 0.120185},{0.252288, 0.512418, 0.118954},{0.246444, 0.505344, 0.117724},{0.240600, 0.498270, 0.116494},{0.234756, 0.491196, 0.115263},{0.228912, 0.484121, 0.114033},{0.223068, 0.477047, 0.112803},{0.217224, 0.469973, 0.111572},{0.211380, 0.462899, 0.110342},{0.205536, 0.455825, 0.109112},{0.199692, 0.448750, 0.107882},{0.193849, 0.441676, 0.106651},{0.188005, 0.434602, 0.105421},{0.182161, 0.427528, 0.104191},{0.176317, 0.420454, 0.102960},{0.170473, 0.413379, 0.101730},{0.164629, 0.406305, 0.100500},{0.158785, 0.399231, 0.099270},{0.152941, 0.392157, 0.098039}}}, + {"bwr", {{0.000000, 0.000000, 1.000000},{0.007843, 0.007843, 1.000000},{0.015686, 0.015686, 1.000000},{0.023529, 0.023529, 1.000000},{0.031373, 0.031373, 1.000000},{0.039216, 0.039216, 1.000000},{0.047059, 0.047059, 1.000000},{0.054902, 0.054902, 1.000000},{0.062745, 0.062745, 1.000000},{0.070588, 0.070588, 1.000000},{0.078431, 0.078431, 1.000000},{0.086275, 0.086275, 1.000000},{0.094118, 0.094118, 1.000000},{0.101961, 0.101961, 1.000000},{0.109804, 0.109804, 1.000000},{0.117647, 0.117647, 1.000000},{0.125490, 0.125490, 1.000000},{0.133333, 0.133333, 1.000000},{0.141176, 0.141176, 1.000000},{0.149020, 0.149020, 1.000000},{0.156863, 0.156863, 1.000000},{0.164706, 0.164706, 1.000000},{0.172549, 0.172549, 1.000000},{0.180392, 0.180392, 1.000000},{0.188235, 0.188235, 1.000000},{0.196078, 0.196078, 1.000000},{0.203922, 0.203922, 1.000000},{0.211765, 0.211765, 1.000000},{0.219608, 0.219608, 1.000000},{0.227451, 0.227451, 1.000000},{0.235294, 0.235294, 1.000000},{0.243137, 0.243137, 1.000000},{0.250980, 0.250980, 1.000000},{0.258824, 0.258824, 1.000000},{0.266667, 0.266667, 1.000000},{0.274510, 0.274510, 1.000000},{0.282353, 0.282353, 1.000000},{0.290196, 0.290196, 1.000000},{0.298039, 0.298039, 1.000000},{0.305882, 0.305882, 1.000000},{0.313725, 0.313725, 1.000000},{0.321569, 0.321569, 1.000000},{0.329412, 0.329412, 1.000000},{0.337255, 0.337255, 1.000000},{0.345098, 0.345098, 1.000000},{0.352941, 0.352941, 1.000000},{0.360784, 0.360784, 1.000000},{0.368627, 0.368627, 1.000000},{0.376471, 0.376471, 1.000000},{0.384314, 0.384314, 1.000000},{0.392157, 0.392157, 1.000000},{0.400000, 0.400000, 1.000000},{0.407843, 0.407843, 1.000000},{0.415686, 0.415686, 1.000000},{0.423529, 0.423529, 1.000000},{0.431373, 0.431373, 1.000000},{0.439216, 0.439216, 1.000000},{0.447059, 0.447059, 1.000000},{0.454902, 0.454902, 1.000000},{0.462745, 0.462745, 1.000000},{0.470588, 0.470588, 1.000000},{0.478431, 0.478431, 1.000000},{0.486275, 0.486275, 1.000000},{0.494118, 0.494118, 1.000000},{0.501961, 0.501961, 1.000000},{0.509804, 0.509804, 1.000000},{0.517647, 0.517647, 1.000000},{0.525490, 0.525490, 1.000000},{0.533333, 0.533333, 1.000000},{0.541176, 0.541176, 1.000000},{0.549020, 0.549020, 1.000000},{0.556863, 0.556863, 1.000000},{0.564706, 0.564706, 1.000000},{0.572549, 0.572549, 1.000000},{0.580392, 0.580392, 1.000000},{0.588235, 0.588235, 1.000000},{0.596078, 0.596078, 1.000000},{0.603922, 0.603922, 1.000000},{0.611765, 0.611765, 1.000000},{0.619608, 0.619608, 1.000000},{0.627451, 0.627451, 1.000000},{0.635294, 0.635294, 1.000000},{0.643137, 0.643137, 1.000000},{0.650980, 0.650980, 1.000000},{0.658824, 0.658824, 1.000000},{0.666667, 0.666667, 1.000000},{0.674510, 0.674510, 1.000000},{0.682353, 0.682353, 1.000000},{0.690196, 0.690196, 1.000000},{0.698039, 0.698039, 1.000000},{0.705882, 0.705882, 1.000000},{0.713725, 0.713725, 1.000000},{0.721569, 0.721569, 1.000000},{0.729412, 0.729412, 1.000000},{0.737255, 0.737255, 1.000000},{0.745098, 0.745098, 1.000000},{0.752941, 0.752941, 1.000000},{0.760784, 0.760784, 1.000000},{0.768627, 0.768627, 1.000000},{0.776471, 0.776471, 1.000000},{0.784314, 0.784314, 1.000000},{0.792157, 0.792157, 1.000000},{0.800000, 0.800000, 1.000000},{0.807843, 0.807843, 1.000000},{0.815686, 0.815686, 1.000000},{0.823529, 0.823529, 1.000000},{0.831373, 0.831373, 1.000000},{0.839216, 0.839216, 1.000000},{0.847059, 0.847059, 1.000000},{0.854902, 0.854902, 1.000000},{0.862745, 0.862745, 1.000000},{0.870588, 0.870588, 1.000000},{0.878431, 0.878431, 1.000000},{0.886275, 0.886275, 1.000000},{0.894118, 0.894118, 1.000000},{0.901961, 0.901961, 1.000000},{0.909804, 0.909804, 1.000000},{0.917647, 0.917647, 1.000000},{0.925490, 0.925490, 1.000000},{0.933333, 0.933333, 1.000000},{0.941176, 0.941176, 1.000000},{0.949020, 0.949020, 1.000000},{0.956863, 0.956863, 1.000000},{0.964706, 0.964706, 1.000000},{0.972549, 0.972549, 1.000000},{0.980392, 0.980392, 1.000000},{0.988235, 0.988235, 1.000000},{0.996078, 0.996078, 1.000000},{1.000000, 0.996078, 0.996078},{1.000000, 0.988235, 0.988235},{1.000000, 0.980392, 0.980392},{1.000000, 0.972549, 0.972549},{1.000000, 0.964706, 0.964706},{1.000000, 0.956863, 0.956863},{1.000000, 0.949020, 0.949020},{1.000000, 0.941176, 0.941176},{1.000000, 0.933333, 0.933333},{1.000000, 0.925490, 0.925490},{1.000000, 0.917647, 0.917647},{1.000000, 0.909804, 0.909804},{1.000000, 0.901961, 0.901961},{1.000000, 0.894118, 0.894118},{1.000000, 0.886275, 0.886275},{1.000000, 0.878431, 0.878431},{1.000000, 0.870588, 0.870588},{1.000000, 0.862745, 0.862745},{1.000000, 0.854902, 0.854902},{1.000000, 0.847059, 0.847059},{1.000000, 0.839216, 0.839216},{1.000000, 0.831373, 0.831373},{1.000000, 0.823529, 0.823529},{1.000000, 0.815686, 0.815686},{1.000000, 0.807843, 0.807843},{1.000000, 0.800000, 0.800000},{1.000000, 0.792157, 0.792157},{1.000000, 0.784314, 0.784314},{1.000000, 0.776471, 0.776471},{1.000000, 0.768627, 0.768627},{1.000000, 0.760784, 0.760784},{1.000000, 0.752941, 0.752941},{1.000000, 0.745098, 0.745098},{1.000000, 0.737255, 0.737255},{1.000000, 0.729412, 0.729412},{1.000000, 0.721569, 0.721569},{1.000000, 0.713725, 0.713725},{1.000000, 0.705882, 0.705882},{1.000000, 0.698039, 0.698039},{1.000000, 0.690196, 0.690196},{1.000000, 0.682353, 0.682353},{1.000000, 0.674510, 0.674510},{1.000000, 0.666667, 0.666667},{1.000000, 0.658824, 0.658824},{1.000000, 0.650980, 0.650980},{1.000000, 0.643137, 0.643137},{1.000000, 0.635294, 0.635294},{1.000000, 0.627451, 0.627451},{1.000000, 0.619608, 0.619608},{1.000000, 0.611765, 0.611765},{1.000000, 0.603922, 0.603922},{1.000000, 0.596078, 0.596078},{1.000000, 0.588235, 0.588235},{1.000000, 0.580392, 0.580392},{1.000000, 0.572549, 0.572549},{1.000000, 0.564706, 0.564706},{1.000000, 0.556863, 0.556863},{1.000000, 0.549020, 0.549020},{1.000000, 0.541176, 0.541176},{1.000000, 0.533333, 0.533333},{1.000000, 0.525490, 0.525490},{1.000000, 0.517647, 0.517647},{1.000000, 0.509804, 0.509804},{1.000000, 0.501961, 0.501961},{1.000000, 0.494118, 0.494118},{1.000000, 0.486275, 0.486275},{1.000000, 0.478431, 0.478431},{1.000000, 0.470588, 0.470588},{1.000000, 0.462745, 0.462745},{1.000000, 0.454902, 0.454902},{1.000000, 0.447059, 0.447059},{1.000000, 0.439216, 0.439216},{1.000000, 0.431373, 0.431373},{1.000000, 0.423529, 0.423529},{1.000000, 0.415686, 0.415686},{1.000000, 0.407843, 0.407843},{1.000000, 0.400000, 0.400000},{1.000000, 0.392157, 0.392157},{1.000000, 0.384314, 0.384314},{1.000000, 0.376471, 0.376471},{1.000000, 0.368627, 0.368627},{1.000000, 0.360784, 0.360784},{1.000000, 0.352941, 0.352941},{1.000000, 0.345098, 0.345098},{1.000000, 0.337255, 0.337255},{1.000000, 0.329412, 0.329412},{1.000000, 0.321569, 0.321569},{1.000000, 0.313725, 0.313725},{1.000000, 0.305882, 0.305882},{1.000000, 0.298039, 0.298039},{1.000000, 0.290196, 0.290196},{1.000000, 0.282353, 0.282353},{1.000000, 0.274510, 0.274510},{1.000000, 0.266667, 0.266667},{1.000000, 0.258824, 0.258824},{1.000000, 0.250980, 0.250980},{1.000000, 0.243137, 0.243137},{1.000000, 0.235294, 0.235294},{1.000000, 0.227451, 0.227451},{1.000000, 0.219608, 0.219608},{1.000000, 0.211765, 0.211765},{1.000000, 0.203922, 0.203922},{1.000000, 0.196078, 0.196078},{1.000000, 0.188235, 0.188235},{1.000000, 0.180392, 0.180392},{1.000000, 0.172549, 0.172549},{1.000000, 0.164706, 0.164706},{1.000000, 0.156863, 0.156863},{1.000000, 0.149020, 0.149020},{1.000000, 0.141176, 0.141176},{1.000000, 0.133333, 0.133333},{1.000000, 0.125490, 0.125490},{1.000000, 0.117647, 0.117647},{1.000000, 0.109804, 0.109804},{1.000000, 0.101961, 0.101961},{1.000000, 0.094118, 0.094118},{1.000000, 0.086275, 0.086275},{1.000000, 0.078431, 0.078431},{1.000000, 0.070588, 0.070588},{1.000000, 0.062745, 0.062745},{1.000000, 0.054902, 0.054902},{1.000000, 0.047059, 0.047059},{1.000000, 0.039216, 0.039216},{1.000000, 0.031373, 0.031373},{1.000000, 0.023529, 0.023529},{1.000000, 0.015686, 0.015686},{1.000000, 0.007843, 0.007843},{1.000000, 0.000000, 0.000000}}}, + {"seismic", {{0.000000, 0.000000, 0.300000},{0.000000, 0.000000, 0.310980},{0.000000, 0.000000, 0.321961},{0.000000, 0.000000, 0.332941},{0.000000, 0.000000, 0.343922},{0.000000, 0.000000, 0.354902},{0.000000, 0.000000, 0.365882},{0.000000, 0.000000, 0.376863},{0.000000, 0.000000, 0.387843},{0.000000, 0.000000, 0.398824},{0.000000, 0.000000, 0.409804},{0.000000, 0.000000, 0.420784},{0.000000, 0.000000, 0.431765},{0.000000, 0.000000, 0.442745},{0.000000, 0.000000, 0.453725},{0.000000, 0.000000, 0.464706},{0.000000, 0.000000, 0.475686},{0.000000, 0.000000, 0.486667},{0.000000, 0.000000, 0.497647},{0.000000, 0.000000, 0.508627},{0.000000, 0.000000, 0.519608},{0.000000, 0.000000, 0.530588},{0.000000, 0.000000, 0.541569},{0.000000, 0.000000, 0.552549},{0.000000, 0.000000, 0.563529},{0.000000, 0.000000, 0.574510},{0.000000, 0.000000, 0.585490},{0.000000, 0.000000, 0.596471},{0.000000, 0.000000, 0.607451},{0.000000, 0.000000, 0.618431},{0.000000, 0.000000, 0.629412},{0.000000, 0.000000, 0.640392},{0.000000, 0.000000, 0.651373},{0.000000, 0.000000, 0.662353},{0.000000, 0.000000, 0.673333},{0.000000, 0.000000, 0.684314},{0.000000, 0.000000, 0.695294},{0.000000, 0.000000, 0.706275},{0.000000, 0.000000, 0.717255},{0.000000, 0.000000, 0.728235},{0.000000, 0.000000, 0.739216},{0.000000, 0.000000, 0.750196},{0.000000, 0.000000, 0.761176},{0.000000, 0.000000, 0.772157},{0.000000, 0.000000, 0.783137},{0.000000, 0.000000, 0.794118},{0.000000, 0.000000, 0.805098},{0.000000, 0.000000, 0.816078},{0.000000, 0.000000, 0.827059},{0.000000, 0.000000, 0.838039},{0.000000, 0.000000, 0.849020},{0.000000, 0.000000, 0.860000},{0.000000, 0.000000, 0.870980},{0.000000, 0.000000, 0.881961},{0.000000, 0.000000, 0.892941},{0.000000, 0.000000, 0.903922},{0.000000, 0.000000, 0.914902},{0.000000, 0.000000, 0.925882},{0.000000, 0.000000, 0.936863},{0.000000, 0.000000, 0.947843},{0.000000, 0.000000, 0.958824},{0.000000, 0.000000, 0.969804},{0.000000, 0.000000, 0.980784},{0.000000, 0.000000, 0.991765},{0.003922, 0.003922, 1.000000},{0.019608, 0.019608, 1.000000},{0.035294, 0.035294, 1.000000},{0.050980, 0.050980, 1.000000},{0.066667, 0.066667, 1.000000},{0.082353, 0.082353, 1.000000},{0.098039, 0.098039, 1.000000},{0.113725, 0.113725, 1.000000},{0.129412, 0.129412, 1.000000},{0.145098, 0.145098, 1.000000},{0.160784, 0.160784, 1.000000},{0.176471, 0.176471, 1.000000},{0.192157, 0.192157, 1.000000},{0.207843, 0.207843, 1.000000},{0.223529, 0.223529, 1.000000},{0.239216, 0.239216, 1.000000},{0.254902, 0.254902, 1.000000},{0.270588, 0.270588, 1.000000},{0.286275, 0.286275, 1.000000},{0.301961, 0.301961, 1.000000},{0.317647, 0.317647, 1.000000},{0.333333, 0.333333, 1.000000},{0.349020, 0.349020, 1.000000},{0.364706, 0.364706, 1.000000},{0.380392, 0.380392, 1.000000},{0.396078, 0.396078, 1.000000},{0.411765, 0.411765, 1.000000},{0.427451, 0.427451, 1.000000},{0.443137, 0.443137, 1.000000},{0.458824, 0.458824, 1.000000},{0.474510, 0.474510, 1.000000},{0.490196, 0.490196, 1.000000},{0.505882, 0.505882, 1.000000},{0.521569, 0.521569, 1.000000},{0.537255, 0.537255, 1.000000},{0.552941, 0.552941, 1.000000},{0.568627, 0.568627, 1.000000},{0.584314, 0.584314, 1.000000},{0.600000, 0.600000, 1.000000},{0.615686, 0.615686, 1.000000},{0.631373, 0.631373, 1.000000},{0.647059, 0.647059, 1.000000},{0.662745, 0.662745, 1.000000},{0.678431, 0.678431, 1.000000},{0.694118, 0.694118, 1.000000},{0.709804, 0.709804, 1.000000},{0.725490, 0.725490, 1.000000},{0.741176, 0.741176, 1.000000},{0.756863, 0.756863, 1.000000},{0.772549, 0.772549, 1.000000},{0.788235, 0.788235, 1.000000},{0.803922, 0.803922, 1.000000},{0.819608, 0.819608, 1.000000},{0.835294, 0.835294, 1.000000},{0.850980, 0.850980, 1.000000},{0.866667, 0.866667, 1.000000},{0.882353, 0.882353, 1.000000},{0.898039, 0.898039, 1.000000},{0.913725, 0.913725, 1.000000},{0.929412, 0.929412, 1.000000},{0.945098, 0.945098, 1.000000},{0.960784, 0.960784, 1.000000},{0.976471, 0.976471, 1.000000},{0.992157, 0.992157, 1.000000},{1.000000, 0.992157, 0.992157},{1.000000, 0.976471, 0.976471},{1.000000, 0.960784, 0.960784},{1.000000, 0.945098, 0.945098},{1.000000, 0.929412, 0.929412},{1.000000, 0.913725, 0.913725},{1.000000, 0.898039, 0.898039},{1.000000, 0.882353, 0.882353},{1.000000, 0.866667, 0.866667},{1.000000, 0.850980, 0.850980},{1.000000, 0.835294, 0.835294},{1.000000, 0.819608, 0.819608},{1.000000, 0.803922, 0.803922},{1.000000, 0.788235, 0.788235},{1.000000, 0.772549, 0.772549},{1.000000, 0.756863, 0.756863},{1.000000, 0.741176, 0.741176},{1.000000, 0.725490, 0.725490},{1.000000, 0.709804, 0.709804},{1.000000, 0.694118, 0.694118},{1.000000, 0.678431, 0.678431},{1.000000, 0.662745, 0.662745},{1.000000, 0.647059, 0.647059},{1.000000, 0.631373, 0.631373},{1.000000, 0.615686, 0.615686},{1.000000, 0.600000, 0.600000},{1.000000, 0.584314, 0.584314},{1.000000, 0.568627, 0.568627},{1.000000, 0.552941, 0.552941},{1.000000, 0.537255, 0.537255},{1.000000, 0.521569, 0.521569},{1.000000, 0.505882, 0.505882},{1.000000, 0.490196, 0.490196},{1.000000, 0.474510, 0.474510},{1.000000, 0.458824, 0.458824},{1.000000, 0.443137, 0.443137},{1.000000, 0.427451, 0.427451},{1.000000, 0.411765, 0.411765},{1.000000, 0.396078, 0.396078},{1.000000, 0.380392, 0.380392},{1.000000, 0.364706, 0.364706},{1.000000, 0.349020, 0.349020},{1.000000, 0.333333, 0.333333},{1.000000, 0.317647, 0.317647},{1.000000, 0.301961, 0.301961},{1.000000, 0.286275, 0.286275},{1.000000, 0.270588, 0.270588},{1.000000, 0.254902, 0.254902},{1.000000, 0.239216, 0.239216},{1.000000, 0.223529, 0.223529},{1.000000, 0.207843, 0.207843},{1.000000, 0.192157, 0.192157},{1.000000, 0.176471, 0.176471},{1.000000, 0.160784, 0.160784},{1.000000, 0.145098, 0.145098},{1.000000, 0.129412, 0.129412},{1.000000, 0.113725, 0.113725},{1.000000, 0.098039, 0.098039},{1.000000, 0.082353, 0.082353},{1.000000, 0.066667, 0.066667},{1.000000, 0.050980, 0.050980},{1.000000, 0.035294, 0.035294},{1.000000, 0.019608, 0.019608},{1.000000, 0.003922, 0.003922},{0.994118, 0.000000, 0.000000},{0.986275, 0.000000, 0.000000},{0.978431, 0.000000, 0.000000},{0.970588, 0.000000, 0.000000},{0.962745, 0.000000, 0.000000},{0.954902, 0.000000, 0.000000},{0.947059, 0.000000, 0.000000},{0.939216, 0.000000, 0.000000},{0.931373, 0.000000, 0.000000},{0.923529, 0.000000, 0.000000},{0.915686, 0.000000, 0.000000},{0.907843, 0.000000, 0.000000},{0.900000, 0.000000, 0.000000},{0.892157, 0.000000, 0.000000},{0.884314, 0.000000, 0.000000},{0.876471, 0.000000, 0.000000},{0.868627, 0.000000, 0.000000},{0.860784, 0.000000, 0.000000},{0.852941, 0.000000, 0.000000},{0.845098, 0.000000, 0.000000},{0.837255, 0.000000, 0.000000},{0.829412, 0.000000, 0.000000},{0.821569, 0.000000, 0.000000},{0.813725, 0.000000, 0.000000},{0.805882, 0.000000, 0.000000},{0.798039, 0.000000, 0.000000},{0.790196, 0.000000, 0.000000},{0.782353, 0.000000, 0.000000},{0.774510, 0.000000, 0.000000},{0.766667, 0.000000, 0.000000},{0.758824, 0.000000, 0.000000},{0.750980, 0.000000, 0.000000},{0.743137, 0.000000, 0.000000},{0.735294, 0.000000, 0.000000},{0.727451, 0.000000, 0.000000},{0.719608, 0.000000, 0.000000},{0.711765, 0.000000, 0.000000},{0.703922, 0.000000, 0.000000},{0.696078, 0.000000, 0.000000},{0.688235, 0.000000, 0.000000},{0.680392, 0.000000, 0.000000},{0.672549, 0.000000, 0.000000},{0.664706, 0.000000, 0.000000},{0.656863, 0.000000, 0.000000},{0.649020, 0.000000, 0.000000},{0.641176, 0.000000, 0.000000},{0.633333, 0.000000, 0.000000},{0.625490, 0.000000, 0.000000},{0.617647, 0.000000, 0.000000},{0.609804, 0.000000, 0.000000},{0.601961, 0.000000, 0.000000},{0.594118, 0.000000, 0.000000},{0.586275, 0.000000, 0.000000},{0.578431, 0.000000, 0.000000},{0.570588, 0.000000, 0.000000},{0.562745, 0.000000, 0.000000},{0.554902, 0.000000, 0.000000},{0.547059, 0.000000, 0.000000},{0.539216, 0.000000, 0.000000},{0.531373, 0.000000, 0.000000},{0.523529, 0.000000, 0.000000},{0.515686, 0.000000, 0.000000},{0.507843, 0.000000, 0.000000},{0.500000, 0.000000, 0.000000}}}, + {"turbo", {{0.18995,0.07176,0.23217},{0.19483,0.08339,0.26149},{0.19956,0.09498,0.29024},{0.20415,0.10652,0.31844},{0.20860,0.11802,0.34607},{0.21291,0.12947,0.37314},{0.21708,0.14087,0.39964},{0.22111,0.15223,0.42558},{0.22500,0.16354,0.45096},{0.22875,0.17481,0.47578},{0.23236,0.18603,0.50004},{0.23582,0.19720,0.52373},{0.23915,0.20833,0.54686},{0.24234,0.21941,0.56942},{0.24539,0.23044,0.59142},{0.24830,0.24143,0.61286},{0.25107,0.25237,0.63374},{0.25369,0.26327,0.65406},{0.25618,0.27412,0.67381},{0.25853,0.28492,0.69300},{0.26074,0.29568,0.71162},{0.26280,0.30639,0.72968},{0.26473,0.31706,0.74718},{0.26652,0.32768,0.76412},{0.26816,0.33825,0.78050},{0.26967,0.34878,0.79631},{0.27103,0.35926,0.81156},{0.27226,0.36970,0.82624},{0.27334,0.38008,0.84037},{0.27429,0.39043,0.85393},{0.27509,0.40072,0.86692},{0.27576,0.41097,0.87936},{0.27628,0.42118,0.89123},{0.27667,0.43134,0.90254},{0.27691,0.44145,0.91328},{0.27701,0.45152,0.92347},{0.27698,0.46153,0.93309},{0.27680,0.47151,0.94214},{0.27648,0.48144,0.95064},{0.27603,0.49132,0.95857},{0.27543,0.50115,0.96594},{0.27469,0.51094,0.97275},{0.27381,0.52069,0.97899},{0.27273,0.53040,0.98461},{0.27106,0.54015,0.98930},{0.26878,0.54995,0.99303},{0.26592,0.55979,0.99583},{0.26252,0.56967,0.99773},{0.25862,0.57958,0.99876},{0.25425,0.58950,0.99896},{0.24946,0.59943,0.99835},{0.24427,0.60937,0.99697},{0.23874,0.61931,0.99485},{0.23288,0.62923,0.99202},{0.22676,0.63913,0.98851},{0.22039,0.64901,0.98436},{0.21382,0.65886,0.97959},{0.20708,0.66866,0.97423},{0.20021,0.67842,0.96833},{0.19326,0.68812,0.96190},{0.18625,0.69775,0.95498},{0.17923,0.70732,0.94761},{0.17223,0.71680,0.93981},{0.16529,0.72620,0.93161},{0.15844,0.73551,0.92305},{0.15173,0.74472,0.91416},{0.14519,0.75381,0.90496},{0.13886,0.76279,0.89550},{0.13278,0.77165,0.88580},{0.12698,0.78037,0.87590},{0.12151,0.78896,0.86581},{0.11639,0.79740,0.85559},{0.11167,0.80569,0.84525},{0.10738,0.81381,0.83484},{0.10357,0.82177,0.82437},{0.10026,0.82955,0.81389},{0.09750,0.83714,0.80342},{0.09532,0.84455,0.79299},{0.09377,0.85175,0.78264},{0.09287,0.85875,0.77240},{0.09267,0.86554,0.76230},{0.09320,0.87211,0.75237},{0.09451,0.87844,0.74265},{0.09662,0.88454,0.73316},{0.09958,0.89040,0.72393},{0.10342,0.89600,0.71500},{0.10815,0.90142,0.70599},{0.11374,0.90673,0.69651},{0.12014,0.91193,0.68660},{0.12733,0.91701,0.67627},{0.13526,0.92197,0.66556},{0.14391,0.92680,0.65448},{0.15323,0.93151,0.64308},{0.16319,0.93609,0.63137},{0.17377,0.94053,0.61938},{0.18491,0.94484,0.60713},{0.19659,0.94901,0.59466},{0.20877,0.95304,0.58199},{0.22142,0.95692,0.56914},{0.23449,0.96065,0.55614},{0.24797,0.96423,0.54303},{0.26180,0.96765,0.52981},{0.27597,0.97092,0.51653},{0.29042,0.97403,0.50321},{0.30513,0.97697,0.48987},{0.32006,0.97974,0.47654},{0.33517,0.98234,0.46325},{0.35043,0.98477,0.45002},{0.36581,0.98702,0.43688},{0.38127,0.98909,0.42386},{0.39678,0.99098,0.41098},{0.41229,0.99268,0.39826},{0.42778,0.99419,0.38575},{0.44321,0.99551,0.37345},{0.45854,0.99663,0.36140},{0.47375,0.99755,0.34963},{0.48879,0.99828,0.33816},{0.50362,0.99879,0.32701},{0.51822,0.99910,0.31622},{0.53255,0.99919,0.30581},{0.54658,0.99907,0.29581},{0.56026,0.99873,0.28623},{0.57357,0.99817,0.27712},{0.58646,0.99739,0.26849},{0.59891,0.99638,0.26038},{0.61088,0.99514,0.25280},{0.62233,0.99366,0.24579},{0.63323,0.99195,0.23937},{0.64362,0.98999,0.23356},{0.65394,0.98775,0.22835},{0.66428,0.98524,0.22370},{0.67462,0.98246,0.21960},{0.68494,0.97941,0.21602},{0.69525,0.97610,0.21294},{0.70553,0.97255,0.21032},{0.71577,0.96875,0.20815},{0.72596,0.96470,0.20640},{0.73610,0.96043,0.20504},{0.74617,0.95593,0.20406},{0.75617,0.95121,0.20343},{0.76608,0.94627,0.20311},{0.77591,0.94113,0.20310},{0.78563,0.93579,0.20336},{0.79524,0.93025,0.20386},{0.80473,0.92452,0.20459},{0.81410,0.91861,0.20552},{0.82333,0.91253,0.20663},{0.83241,0.90627,0.20788},{0.84133,0.89986,0.20926},{0.85010,0.89328,0.21074},{0.85868,0.88655,0.21230},{0.86709,0.87968,0.21391},{0.87530,0.87267,0.21555},{0.88331,0.86553,0.21719},{0.89112,0.85826,0.21880},{0.89870,0.85087,0.22038},{0.90605,0.84337,0.22188},{0.91317,0.83576,0.22328},{0.92004,0.82806,0.22456},{0.92666,0.82025,0.22570},{0.93301,0.81236,0.22667},{0.93909,0.80439,0.22744},{0.94489,0.79634,0.22800},{0.95039,0.78823,0.22831},{0.95560,0.78005,0.22836},{0.96049,0.77181,0.22811},{0.96507,0.76352,0.22754},{0.96931,0.75519,0.22663},{0.97323,0.74682,0.22536},{0.97679,0.73842,0.22369},{0.98000,0.73000,0.22161},{0.98289,0.72140,0.21918},{0.98549,0.71250,0.21650},{0.98781,0.70330,0.21358},{0.98986,0.69382,0.21043},{0.99163,0.68408,0.20706},{0.99314,0.67408,0.20348},{0.99438,0.66386,0.19971},{0.99535,0.65341,0.19577},{0.99607,0.64277,0.19165},{0.99654,0.63193,0.18738},{0.99675,0.62093,0.18297},{0.99672,0.60977,0.17842},{0.99644,0.59846,0.17376},{0.99593,0.58703,0.16899},{0.99517,0.57549,0.16412},{0.99419,0.56386,0.15918},{0.99297,0.55214,0.15417},{0.99153,0.54036,0.14910},{0.98987,0.52854,0.14398},{0.98799,0.51667,0.13883},{0.98590,0.50479,0.13367},{0.98360,0.49291,0.12849},{0.98108,0.48104,0.12332},{0.97837,0.46920,0.11817},{0.97545,0.45740,0.11305},{0.97234,0.44565,0.10797},{0.96904,0.43399,0.10294},{0.96555,0.42241,0.09798},{0.96187,0.41093,0.09310},{0.95801,0.39958,0.08831},{0.95398,0.38836,0.08362},{0.94977,0.37729,0.07905},{0.94538,0.36638,0.07461},{0.94084,0.35566,0.07031},{0.93612,0.34513,0.06616},{0.93125,0.33482,0.06218},{0.92623,0.32473,0.05837},{0.92105,0.31489,0.05475},{0.91572,0.30530,0.05134},{0.91024,0.29599,0.04814},{0.90463,0.28696,0.04516},{0.89888,0.27824,0.04243},{0.89298,0.26981,0.03993},{0.88691,0.26152,0.03753},{0.88066,0.25334,0.03521},{0.87422,0.24526,0.03297},{0.86760,0.23730,0.03082},{0.86079,0.22945,0.02875},{0.85380,0.22170,0.02677},{0.84662,0.21407,0.02487},{0.83926,0.20654,0.02305},{0.83172,0.19912,0.02131},{0.82399,0.19182,0.01966},{0.81608,0.18462,0.01809},{0.80799,0.17753,0.01660},{0.79971,0.17055,0.01520},{0.79125,0.16368,0.01387},{0.78260,0.15693,0.01264},{0.77377,0.15028,0.01148},{0.76476,0.14374,0.01041},{0.75556,0.13731,0.00942},{0.74617,0.13098,0.00851},{0.73661,0.12477,0.00769},{0.72686,0.11867,0.00695},{0.71692,0.11268,0.00629},{0.70680,0.10680,0.00571},{0.69650,0.10102,0.00522},{0.68602,0.09536,0.00481},{0.67535,0.08980,0.00449},{0.66449,0.08436,0.00424},{0.65345,0.07902,0.00408},{0.64223,0.07380,0.00401},{0.63082,0.06868,0.00401},{0.61923,0.06367,0.00410},{0.60746,0.05878,0.00427},{0.59550,0.05399,0.00453},{0.58336,0.04931,0.00486},{0.57103,0.04474,0.00529},{0.55852,0.04028,0.00579},{0.54583,0.03593,0.00638},{0.53295,0.03169,0.00705},{0.51989,0.02756,0.00780},{0.50664,0.02354,0.00863},{0.49321,0.01963,0.00955},{0.47960,0.01583,0.01055}}}, + {"default", {{1.0, 0, 1}, {0.9803921568627452, 0, 1}, {0.9607843137254901, 0, 1}, {0.9411764705882353, 0, 1}, {0.9215686274509804, 0, 1}, {0.9019607843137254, 0, 1}, {0.8823529411764706, 0, 1}, {0.8627450980392157, 0, 1}, {0.8431372549019609, 0, 1}, {0.8235294117647058, 0, 1}, {0.803921568627451, 0, 1}, {0.784313725490196, 0, 1}, {0.7647058823529411, 0, 1}, {0.7450980392156863, 0, 1}, {0.7254901960784315, 0, 1}, {0.7058823529411764, 0, 1}, {0.6862745098039216, 0, 1}, {0.6666666666666667, 0, 1}, {0.6470588235294117, 0, 1}, {0.6274509803921569, 0, 1}, {0.607843137254902, 0, 1}, {0.5882352941176472, 0, 1}, {0.5686274509803921, 0, 1}, {0.5490196078431373, 0, 1}, {0.5294117647058822, 0, 1}, {0.5098039215686274, 0, 1}, {0.4901960784313726, 0, 1}, {0.47058823529411775, 0, 1}, {0.4509803921568627, 0, 1}, {0.43137254901960786, 0, 1}, {0.4117647058823528, 0, 1}, {0.392156862745098, 0, 1}, {0.37254901960784315, 0, 1}, {0.3529411764705883, 0, 1}, {0.3333333333333335, 0, 1}, {0.3137254901960784, 0, 1}, {0.2941176470588236, 0, 1}, {0.27450980392156854, 0, 1}, {0.2549019607843137, 0, 1}, {0.23529411764705888, 0, 1}, {0.21568627450980404, 0, 1}, {0.196078431372549, 0, 1}, {0.17647058823529416, 0, 1}, {0.1568627450980391, 0, 1}, {0.13725490196078427, 0, 1}, {0.11764705882352944, 0, 1}, {0.0980392156862746, 0, 1}, {0.07843137254901955, 0, 1}, {0.05882352941176472, 0, 1}, {0.039215686274509665, 0, 1}, {0.019607843137254832, 0, 1}, {0, 0.0, 1}, {0, 0.019607843137254832, 1}, {0, 0.039215686274509665, 1}, {0, 0.0588235294117645, 1}, {0, 0.07843137254901977, 1}, {0, 0.0980392156862746, 1}, {0, 0.11764705882352944, 1}, {0, 0.13725490196078427, 1}, {0, 0.15686274509803955, 1}, {0, 0.17647058823529438, 1}, {0, 0.1960784313725492, 1}, {0, 0.21568627450980404, 1}, {0, 0.23529411764705888, 1}, {0, 0.2549019607843137, 1}, {0, 0.27450980392156854, 1}, {0, 0.2941176470588234, 1}, {0, 0.3137254901960782, 1}, {0, 0.33333333333333304, 1}, {0, 0.35294117647058787, 1}, {0, 0.37254901960784315, 1}, {0, 0.392156862745098, 1}, {0, 0.4117647058823528, 1}, {0, 0.43137254901960764, 1}, {0, 0.4509803921568629, 1}, {0, 0.47058823529411775, 1}, {0, 0.4901960784313726, 1}, {0, 0.5098039215686274, 1}, {0, 0.5294117647058822, 1}, {0, 0.5490196078431371, 1}, {0, 0.5686274509803919, 1}, {0, 0.5882352941176467, 1}, {0, 0.607843137254902, 1}, {0, 0.6274509803921569, 1}, {0, 0.6470588235294117, 1}, {0, 0.6666666666666665, 1}, {0, 0.6862745098039218, 1}, {0, 0.7058823529411766, 1}, {0, 0.7254901960784315, 1}, {0, 0.7450980392156863, 1}, {0, 0.7647058823529411, 1}, {0, 0.784313725490196, 1}, {0, 0.8039215686274508, 1}, {0, 0.8235294117647056, 1}, {0, 0.8431372549019609, 1}, {0, 0.8627450980392157, 1}, {0, 0.8823529411764706, 1}, {0, 0.9019607843137254, 1}, {0, 0.9215686274509807, 1}, {0, 0.9411764705882355, 1}, {0, 0.9607843137254903, 1}, {0, 0.9803921568627452, 1}, {0, 1, 1.0}, {0, 1, 0.9803921568627452}, {0, 1, 0.9607843137254903}, {0, 1, 0.9411764705882355}, {0, 1, 0.9215686274509802}, {0, 1, 0.9019607843137254}, {0, 1, 0.8823529411764706}, {0, 1, 0.8627450980392157}, {0, 1, 0.8431372549019605}, {0, 1, 0.8235294117647056}, {0, 1, 0.8039215686274508}, {0, 1, 0.784313725490196}, {0, 1, 0.7647058823529411}, {0, 1, 0.7450980392156863}, {0, 1, 0.7254901960784315}, {0, 1, 0.7058823529411766}, {0, 1, 0.6862745098039214}, {0, 1, 0.6666666666666665}, {0, 1, 0.6470588235294117}, {0, 1, 0.6274509803921569}, {0, 1, 0.607843137254902}, {0, 1, 0.5882352941176472}, {0, 1, 0.5686274509803924}, {0, 1, 0.5490196078431375}, {0, 1, 0.5294117647058822}, {0, 1, 0.5098039215686274}, {0, 1, 0.4901960784313726}, {0, 1, 0.47058823529411775}, {0, 1, 0.4509803921568629}, {0, 1, 0.4313725490196081}, {0, 1, 0.4117647058823528}, {0, 1, 0.392156862745098}, {0, 1, 0.37254901960784315}, {0, 1, 0.3529411764705883}, {0, 1, 0.3333333333333335}, {0, 1, 0.31372549019607865}, {0, 1, 0.2941176470588238}, {0, 1, 0.274509803921569}, {0, 1, 0.2549019607843137}, {0, 1, 0.23529411764705888}, {0, 1, 0.21568627450980404}, {0, 1, 0.1960784313725492}, {0, 1, 0.17647058823529438}, {0, 1, 0.15686274509803955}, {0, 1, 0.13725490196078471}, {0, 1, 0.11764705882352988}, {0, 1, 0.09803921568627416}, {0, 1, 0.07843137254901933}, {0, 1, 0.0588235294117645}, {0, 1, 0.039215686274509665}, {0, 1, 0.019607843137254832}, {0.0, 1, 0}, {0.019607843137254832, 1, 0}, {0.039215686274509665, 1, 0}, {0.0588235294117645, 1, 0}, {0.07843137254901933, 1, 0}, {0.09803921568627416, 1, 0}, {0.117647058823529, 1, 0}, {0.13725490196078383, 1, 0}, {0.15686274509803866, 1, 0}, {0.1764705882352935, 1, 0}, {0.19607843137254832, 1, 0}, {0.21568627450980404, 1, 0}, {0.23529411764705888, 1, 0}, {0.2549019607843137, 1, 0}, {0.27450980392156854, 1, 0}, {0.2941176470588234, 1, 0}, {0.3137254901960782, 1, 0}, {0.33333333333333304, 1, 0}, {0.35294117647058787, 1, 0}, {0.3725490196078436, 1, 0}, {0.3921568627450984, 1, 0}, {0.41176470588235325, 1, 0}, {0.4313725490196081, 1, 0}, {0.4509803921568629, 1, 0}, {0.47058823529411775, 1, 0}, {0.4901960784313726, 1, 0}, {0.5098039215686274, 1, 0}, {0.5294117647058822, 1, 0}, {0.5490196078431371, 1, 0}, {0.5686274509803919, 1, 0}, {0.5882352941176467, 1, 0}, {0.6078431372549016, 1, 0}, {0.6274509803921564, 1, 0}, {0.6470588235294112, 1, 0}, {0.6666666666666661, 1, 0}, {0.6862745098039218, 1, 0}, {0.7058823529411766, 1, 0}, {0.7254901960784315, 1, 0}, {0.7450980392156863, 1, 0}, {0.7647058823529411, 1, 0}, {0.784313725490196, 1, 0}, {0.8039215686274508, 1, 0}, {0.8235294117647056, 1, 0}, {0.8431372549019613, 1, 0}, {0.8627450980392162, 1, 0}, {0.882352941176471, 1, 0}, {0.9019607843137258, 1, 0}, {0.9215686274509807, 1, 0}, {0.9411764705882355, 1, 0}, {0.9607843137254903, 1, 0}, {0.9803921568627452, 1, 0}, {1, 1.0, 0}, {1, 0.9803921568627452, 0}, {1, 0.9607843137254903, 0}, {1, 0.9411764705882355, 0}, {1, 0.9215686274509807, 0}, {1, 0.9019607843137258, 0}, {1, 0.882352941176471, 0}, {1, 0.8627450980392162, 0}, {1, 0.8431372549019605, 0}, {1, 0.8235294117647056, 0}, {1, 0.8039215686274508, 0}, {1, 0.784313725490196, 0}, {1, 0.7647058823529411, 0}, {1, 0.7450980392156863, 0}, {1, 0.7254901960784315, 0}, {1, 0.7058823529411766, 0}, {1, 0.6862745098039209, 0}, {1, 0.6666666666666661, 0}, {1, 0.6470588235294112, 0}, {1, 0.6274509803921564, 0}, {1, 0.6078431372549016, 0}, {1, 0.5882352941176467, 0}, {1, 0.5686274509803919, 0}, {1, 0.5490196078431371, 0}, {1, 0.5294117647058822, 0}, {1, 0.5098039215686274, 0}, {1, 0.4901960784313726, 0}, {1, 0.47058823529411775, 0}, {1, 0.4509803921568629, 0}, {1, 0.4313725490196081, 0}, {1, 0.41176470588235325, 0}, {1, 0.3921568627450984, 0}, {1, 0.3725490196078427, 0}, {1, 0.35294117647058787, 0}, {1, 0.33333333333333304, 0}, {1, 0.3137254901960782, 0}, {1, 0.2941176470588234, 0}, {1, 0.27450980392156854, 0}, {1, 0.2549019607843137, 0}, {1, 0.23529411764705888, 0}, {1, 0.21568627450980404, 0}, {1, 0.1960784313725492, 0}, {1, 0.17647058823529438, 0}, {1, 0.15686274509803955, 0}, {1, 0.13725490196078471, 0}, {1, 0.11764705882352988, 0}, {1, 0.09803921568627505, 0}, {1, 0.07843137254902022, 0}, {1, 0.0588235294117645, 0}, {1, 0.039215686274509665, 0}, {1, 0.019607843137254832, 0}, {1, 0.0, 0}}}, + {"anybotics", {{1.00000, 0.32549, 0.29020},{1.00000, 0.33333, 0.29412},{1.00000, 0.33725, 0.29804},{0.99608, 0.34510, 0.29804},{0.99608, 0.35294, 0.30196},{0.99608, 0.36078, 0.30588},{0.99216, 0.36863, 0.30980},{0.99216, 0.37647, 0.31373},{0.98824, 0.38431, 0.31373},{0.98824, 0.38824, 0.31765},{0.98431, 0.39608, 0.32157},{0.98431, 0.40392, 0.32549},{0.98431, 0.41176, 0.32549},{0.98039, 0.41961, 0.32941},{0.98039, 0.42745, 0.33333},{0.97647, 0.43529, 0.33725},{0.97647, 0.44314, 0.33725},{0.97255, 0.44706, 0.34118},{0.97255, 0.45490, 0.34510},{0.96863, 0.46275, 0.34902},{0.96863, 0.47059, 0.34902},{0.96863, 0.47843, 0.35294},{0.96471, 0.48627, 0.35686},{0.96471, 0.49412, 0.36078},{0.96078, 0.49804, 0.36471},{0.96078, 0.50588, 0.36471},{0.95686, 0.51373, 0.36863},{0.95686, 0.52157, 0.37255},{0.95686, 0.52941, 0.37647},{0.95294, 0.53725, 0.37647},{0.95294, 0.54510, 0.38039},{0.94902, 0.55294, 0.38431},{0.94902, 0.55686, 0.38824},{0.94510, 0.56471, 0.38824},{0.94510, 0.57255, 0.39216},{0.94510, 0.58039, 0.39608},{0.94118, 0.58824, 0.40000},{0.94118, 0.59608, 0.40000},{0.93725, 0.60392, 0.40392},{0.93725, 0.60784, 0.40784},{0.93333, 0.61569, 0.41176},{0.93333, 0.62353, 0.41176},{0.92941, 0.63137, 0.41569},{0.92941, 0.63922, 0.41961},{0.92941, 0.64706, 0.42353},{0.92549, 0.65490, 0.42745},{0.92549, 0.66275, 0.42745},{0.92157, 0.66667, 0.43137},{0.92157, 0.67451, 0.43529},{0.91765, 0.68235, 0.43922},{0.91765, 0.69020, 0.43922},{0.91765, 0.69804, 0.44314},{0.91373, 0.70588, 0.44706},{0.91373, 0.71373, 0.45098},{0.90980, 0.71765, 0.45098},{0.90980, 0.72549, 0.45490},{0.90588, 0.73333, 0.45882},{0.90588, 0.74118, 0.46275},{0.90196, 0.74902, 0.46275},{0.90196, 0.75686, 0.46667},{0.90196, 0.76471, 0.47059},{0.89804, 0.77255, 0.47451},{0.89804, 0.77647, 0.47843},{0.89412, 0.78431, 0.47843},{0.89412, 0.79216, 0.48235},{0.89412, 0.79216, 0.48235},{0.89412, 0.79216, 0.48627},{0.89412, 0.79216, 0.48627},{0.89804, 0.79608, 0.49020},{0.89804, 0.79608, 0.49020},{0.89804, 0.79608, 0.49412},{0.89804, 0.79608, 0.49412},{0.89804, 0.79608, 0.49804},{0.89804, 0.79608, 0.49804},{0.89804, 0.80000, 0.50196},{0.89804, 0.80000, 0.50196},{0.90196, 0.80000, 0.50588},{0.90196, 0.80000, 0.50588},{0.90196, 0.80000, 0.50588},{0.90196, 0.80392, 0.50980},{0.90196, 0.80392, 0.50980},{0.90196, 0.80392, 0.51373},{0.90196, 0.80392, 0.51373},{0.90588, 0.80392, 0.51765},{0.90588, 0.80784, 0.51765},{0.90588, 0.80784, 0.52157},{0.90588, 0.80784, 0.52157},{0.90588, 0.80784, 0.52549},{0.90588, 0.80784, 0.52549},{0.90588, 0.81176, 0.52549},{0.90588, 0.81176, 0.52941},{0.90980, 0.81176, 0.52941},{0.90980, 0.81176, 0.53333},{0.90980, 0.81176, 0.53333},{0.90980, 0.81569, 0.53725},{0.90980, 0.81569, 0.53725},{0.90980, 0.81569, 0.54118},{0.90980, 0.81569, 0.54118},{0.91373, 0.81569, 0.54510},{0.91373, 0.81961, 0.54510},{0.91373, 0.81961, 0.54902},{0.91373, 0.81961, 0.54902},{0.91373, 0.81961, 0.54902},{0.91373, 0.81961, 0.55294},{0.91373, 0.81961, 0.55294},{0.91373, 0.82353, 0.55686},{0.91765, 0.82353, 0.55686},{0.91765, 0.82353, 0.56078},{0.91765, 0.82353, 0.56078},{0.91765, 0.82353, 0.56471},{0.91765, 0.82745, 0.56471},{0.91765, 0.82745, 0.56863},{0.91765, 0.82745, 0.56863},{0.92157, 0.82745, 0.57255},{0.92157, 0.82745, 0.57255},{0.92157, 0.83137, 0.57255},{0.92157, 0.83137, 0.57647},{0.92157, 0.83137, 0.57647},{0.92157, 0.83137, 0.58039},{0.92157, 0.83137, 0.58039},{0.92157, 0.83529, 0.58431},{0.92549, 0.83529, 0.58431},{0.92549, 0.83529, 0.58824},{0.92549, 0.83529, 0.58824},{0.92549, 0.83529, 0.59216},{0.92549, 0.83922, 0.59216},{0.92549, 0.83922, 0.59608},{0.92549, 0.83922, 0.59608},{0.92549, 0.83529, 0.59608},{0.91765, 0.83137, 0.59608},{0.90980, 0.82745, 0.59608},{0.90196, 0.81961, 0.59216},{0.89412, 0.81569, 0.59216},{0.88627, 0.81176, 0.59216},{0.87843, 0.80392, 0.58824},{0.87059, 0.80000, 0.58824},{0.86275, 0.79608, 0.58824},{0.85882, 0.78824, 0.58431},{0.85098, 0.78431, 0.58431},{0.84314, 0.78039, 0.58431},{0.83529, 0.77255, 0.58431},{0.82745, 0.76863, 0.58039},{0.81961, 0.76471, 0.58039},{0.81176, 0.75686, 0.58039},{0.80392, 0.75294, 0.57647},{0.80000, 0.74902, 0.57647},{0.79216, 0.74118, 0.57647},{0.78431, 0.73725, 0.57647},{0.77647, 0.72941, 0.57255},{0.76863, 0.72549, 0.57255},{0.76078, 0.72157, 0.57255},{0.75294, 0.71373, 0.56863},{0.74510, 0.70980, 0.56863},{0.74118, 0.70588, 0.56863},{0.73333, 0.69804, 0.56863},{0.72549, 0.69412, 0.56471},{0.71765, 0.69020, 0.56471},{0.70980, 0.68235, 0.56471},{0.70196, 0.67843, 0.56078},{0.69412, 0.67451, 0.56078},{0.68627, 0.66667, 0.56078},{0.68235, 0.66275, 0.55686},{0.67451, 0.65882, 0.55686},{0.66667, 0.65098, 0.55686},{0.65882, 0.64706, 0.55686},{0.65098, 0.64314, 0.55294},{0.64314, 0.63529, 0.55294},{0.63529, 0.63137, 0.55294},{0.62745, 0.62745, 0.54902},{0.61961, 0.61961, 0.54902},{0.61569, 0.61569, 0.54902},{0.60784, 0.61176, 0.54902},{0.60000, 0.60392, 0.54510},{0.59216, 0.60000, 0.54510},{0.58431, 0.59216, 0.54510},{0.57647, 0.58824, 0.54118},{0.56863, 0.58431, 0.54118},{0.56078, 0.57647, 0.54118},{0.55686, 0.57255, 0.54118},{0.54902, 0.56863, 0.53725},{0.54118, 0.56078, 0.53725},{0.53333, 0.55686, 0.53725},{0.52549, 0.55294, 0.53333},{0.51765, 0.54510, 0.53333},{0.50980, 0.54118, 0.53333},{0.50196, 0.53725, 0.53333},{0.49804, 0.52941, 0.52941},{0.49020, 0.52549, 0.52941},{0.48235, 0.52157, 0.52941},{0.47451, 0.51373, 0.52549},{0.46667, 0.50980, 0.52549},{0.45882, 0.50588, 0.52549},{0.45098, 0.49804, 0.51765},{0.44706, 0.49020, 0.51373},{0.43922, 0.48627, 0.50588},{0.43529, 0.47843, 0.50196},{0.42745, 0.47451, 0.49412},{0.42353, 0.46667, 0.49020},{0.41569, 0.45882, 0.48235},{0.40784, 0.45490, 0.47451},{0.40392, 0.44706, 0.47059},{0.39608, 0.43922, 0.46275},{0.39216, 0.43529, 0.45882},{0.38431, 0.42745, 0.45098},{0.38039, 0.41961, 0.44314},{0.37255, 0.41569, 0.43922},{0.36471, 0.40784, 0.43137},{0.36078, 0.40000, 0.42745},{0.35294, 0.39608, 0.41961},{0.34902, 0.38824, 0.41176},{0.34118, 0.38431, 0.40784},{0.33725, 0.37647, 0.40000},{0.32941, 0.36863, 0.39608},{0.32157, 0.36471, 0.38824},{0.31765, 0.35686, 0.38431},{0.30980, 0.34902, 0.37647},{0.30588, 0.34510, 0.36863},{0.29804, 0.33725, 0.36471},{0.29412, 0.33333, 0.35686},{0.28627, 0.32549, 0.35294},{0.27843, 0.31765, 0.34510},{0.27451, 0.31373, 0.33725},{0.26667, 0.30588, 0.33333},{0.26275, 0.29804, 0.32549},{0.25490, 0.29412, 0.32157},{0.24706, 0.28627, 0.31373},{0.24314, 0.27843, 0.30980},{0.23529, 0.27451, 0.30196},{0.23137, 0.26667, 0.29412},{0.22353, 0.25882, 0.29020},{0.21961, 0.25490, 0.28235},{0.21176, 0.24706, 0.27843},{0.20392, 0.24314, 0.27059},{0.20000, 0.23529, 0.26275},{0.19216, 0.22745, 0.25882},{0.18824, 0.22353, 0.25098},{0.18039, 0.21569, 0.24706},{0.17647, 0.20784, 0.23922},{0.16863, 0.20392, 0.23137},{0.16078, 0.19608, 0.22745},{0.15686, 0.19216, 0.21961},{0.14902, 0.18431, 0.21569},{0.14510, 0.17647, 0.20784},{0.13725, 0.17255, 0.20392},{0.13333, 0.16471, 0.19608},{0.12549, 0.15686, 0.18824},{0.11765, 0.15294, 0.18431},{0.11373, 0.14510, 0.17647},{0.10588, 0.13725, 0.17255},{0.10196, 0.13333, 0.16471},{0.09412, 0.12549, 0.15686},{0.09020, 0.12157, 0.15294},{0.08235, 0.11373, 0.14510},{0.07451, 0.10588, 0.14118},{0.07059, 0.10196, 0.13333},{0.06275, 0.09412, 0.12549}}}, +}; + +} diff --git a/grid_map_rviz_plugin/src/GridMapDisplay.cpp b/grid_map_rviz_plugin/src/GridMapDisplay.cpp index ab3f99d1e..872b28e1a 100644 --- a/grid_map_rviz_plugin/src/GridMapDisplay.cpp +++ b/grid_map_rviz_plugin/src/GridMapDisplay.cpp @@ -8,6 +8,8 @@ #include "grid_map_rviz_plugin/GridMapVisual.hpp" #include "grid_map_rviz_plugin/GridMapDisplay.hpp" +#include "grid_map_rviz_plugin/GridMapColorMaps.hpp" + // The following replaces #include "grid_map_rviz_plugin/modified/frame_manager.h" @@ -28,6 +30,8 @@ namespace grid_map_rviz_plugin { GridMapDisplay::GridMapDisplay() { + qRegisterMetaType("grid_map_msgs::GridMap::ConstPtr"); + alphaProperty_ = new rviz::FloatProperty("Alpha", 1.0, "0 is fully transparent, 1.0 is fully opaque.", this, SLOT(updateVisualization())); @@ -36,9 +40,13 @@ GridMapDisplay::GridMapDisplay() "Number of prior grid maps to display.", this, SLOT(updateHistoryLength())); - showGridLinesProperty_ = new rviz::BoolProperty( - "Show Grid Lines", true, "Whether to show the lines connecting the grid cells.", this, - SLOT(updateVisualization())); + showGridLinesProperty_ = new rviz::BoolProperty("Show Grid Lines", true, "Whether to show the lines connecting the grid cells.", this, + SLOT(updateGridLines())); + + gridLinesThicknessProperty_ = + new rviz::FloatProperty("Grid Line Thickness", 0.1, "Set thickness for the grid lines.", this, SLOT(updateVisualization())); + gridCellDecimationProperty_ = new rviz::IntProperty("Grid Cell Decimation", 1, "Decimation factor for the grid map cell display.", this, + SLOT(updateVisualization())); heightModeProperty_ = new rviz::EnumProperty("Height Transformer", "GridMapLayer", "Select the transformer to use to set the height.", @@ -62,19 +70,23 @@ GridMapDisplay::GridMapDisplay() "Color Layer", "elevation", "Select the grid map layer to compute the color.", this, SLOT(updateVisualization())); + colorMapProperty_ = new rviz::EditableEnumProperty( + "ColorMap", "default", "Select the colormap to be used.", this, + SLOT(updateVisualization())); + colorProperty_ = new rviz::ColorProperty("Color", QColor(200, 200, 200), "Color to draw the mesh.", this, SLOT(updateVisualization())); colorProperty_->hide(); - useRainbowProperty_ = new rviz::BoolProperty( - "Use Rainbow", true, - "Whether to use a rainbow of colors or to interpolate between two colors.", this, - SLOT(updateUseRainbow())); + useColorMapProperty_ = new rviz::BoolProperty( + "Use ColorMap", true, + "Whether to use a colormap or to interpolate between two colors.", this, + SLOT(updateUseColorMap())); - invertRainbowProperty_ = new rviz::BoolProperty( - "Invert Rainbow", false, - "Whether to invert the rainbow colors.", this, + invertColorMapProperty_ = new rviz::BoolProperty( + "Invert ColorMap", false, + "Whether to invert the colormap colors.", this, SLOT(updateVisualization())); minColorProperty_ = new rviz::ColorProperty( @@ -118,12 +130,29 @@ void GridMapDisplay::onInitialize() { MFDClass::onInitialize(); // "MFDClass" = typedef of "MessageFilterDisplay" updateHistoryLength(); + updateColorMapList(); +} + +void GridMapDisplay::onEnable() +{ + isEnabled_ = true; + connect(this, &GridMapDisplay::process, this, &GridMapDisplay::onProcessMessage); + MessageFilterDisplay::onEnable(); +} + +void GridMapDisplay::onDisable(){ + MFDClass::onDisable(); + isEnabled_ = false; } void GridMapDisplay::reset() { + disconnect(this, &GridMapDisplay::process, this, &GridMapDisplay::onProcessMessage); MFDClass::reset(); visuals_.clear(); + if(isEnabled_){ + onEnable(); + } } void GridMapDisplay::updateHistoryLength() @@ -146,24 +175,32 @@ void GridMapDisplay::updateColorMode() bool none = colorModeProperty_->getOptionInt() == 3; colorProperty_->setHidden(!flatColor); colorTransformerProperty_->setHidden(flatColor || none); - useRainbowProperty_->setHidden(!intensityColor); - invertRainbowProperty_->setHidden(!intensityColor); + useColorMapProperty_->setHidden(!intensityColor); + invertColorMapProperty_->setHidden(!intensityColor); autocomputeIntensityBoundsProperty_->setHidden(!intensityColor); - bool useRainbow = useRainbowProperty_->getBool(); - minColorProperty_->setHidden(!intensityColor || useRainbow); - maxColorProperty_->setHidden(!intensityColor || useRainbow); + bool useColorMap = useColorMapProperty_->getBool(); + minColorProperty_->setHidden(!intensityColor || useColorMap); + maxColorProperty_->setHidden(!intensityColor || useColorMap); bool autocomputeIntensity = autocomputeIntensityBoundsProperty_->getBool(); minIntensityProperty_->setHidden(!intensityColor || autocomputeIntensity); minIntensityProperty_->setHidden(!intensityColor || autocomputeIntensity); } -void GridMapDisplay::updateUseRainbow() +void GridMapDisplay::updateUseColorMap() +{ + updateVisualization(); + bool useColorMap = useColorMapProperty_->getBool(); + minColorProperty_->setHidden(useColorMap); + maxColorProperty_->setHidden(useColorMap); + invertColorMapProperty_->setHidden(!useColorMap); +} + +void GridMapDisplay::updateGridLines() { updateVisualization(); - bool useRainbow = useRainbowProperty_->getBool(); - minColorProperty_->setHidden(useRainbow); - maxColorProperty_->setHidden(useRainbow); - invertRainbowProperty_->setHidden(!useRainbow); + const bool isShowGridLines = showGridLinesProperty_->getBool(); + gridLinesThicknessProperty_->setHidden(!isShowGridLines); + gridCellDecimationProperty_->setHidden(!isShowGridLines); } void GridMapDisplay::updateAutocomputeIntensityBounds() @@ -184,23 +221,36 @@ void GridMapDisplay::updateVisualization() bool noColor = colorModeProperty_->getOptionInt() == 3; Ogre::ColourValue meshColor = colorProperty_->getOgreColor(); std::string colorLayer = colorTransformerProperty_->getStdString(); - bool useRainbow = useRainbowProperty_->getBool(); - bool invertRainbow = invertRainbowProperty_->getBool(); + std::string colorMap = colorMapProperty_->getStdString(); + bool useColorMap = useColorMapProperty_->getBool(); + bool invertColorMap = invertColorMapProperty_->getBool(); Ogre::ColourValue minColor = minColorProperty_->getOgreColor(); Ogre::ColourValue maxColor = maxColorProperty_->getOgreColor(); bool autocomputeIntensity = autocomputeIntensityBoundsProperty_->getBool(); float minIntensity = minIntensityProperty_->getFloat(); float maxIntensity = maxIntensityProperty_->getFloat(); + const float gridLineThickness = gridLinesThicknessProperty_->getFloat(); + const int gridCellDecimation = gridCellDecimationProperty_->getInt(); for (size_t i = 0; i < visuals_.size(); i++) { - visuals_[i]->computeVisualization(alpha, showGridLines, flatTerrain, heightLayer, flatColor, noColor, meshColor, - mapLayerColor, colorLayer, useRainbow, invertRainbow, minColor, maxColor, - autocomputeIntensity, minIntensity, maxIntensity); + visuals_[i]->computeVisualization(alpha, showGridLines, flatTerrain, heightLayer, flatColor, noColor, meshColor, mapLayerColor, + colorLayer, colorMap, useColorMap, invertColorMap, minColor, maxColor, autocomputeIntensity, minIntensity, + maxIntensity, gridLineThickness, gridCellDecimation); } } void GridMapDisplay::processMessage(const grid_map_msgs::GridMap::ConstPtr& msg) { + process(msg); +} + +void GridMapDisplay::onProcessMessage(const grid_map_msgs::GridMap::ConstPtr& msg) +{ + // Check if the display was already reset. + if (!isEnabled_) { + return; + } + // Check if transform between the message's frame and the fixed frame exists. Ogre::Quaternion orientation; Ogre::Vector3 position; @@ -226,10 +276,12 @@ void GridMapDisplay::processMessage(const grid_map_msgs::GridMap::ConstPtr& msg) heightModeProperty_->getOptionInt() == 1, heightTransformerProperty_->getStdString(), colorModeProperty_->getOptionInt() == 2, colorModeProperty_->getOptionInt() == 3, colorProperty_->getOgreColor(), colorModeProperty_->getOptionInt() == 1, - colorTransformerProperty_->getStdString(), useRainbowProperty_->getBool(), - invertRainbowProperty_->getBool(), minColorProperty_->getOgreColor(), - maxColorProperty_->getOgreColor(), autocomputeIntensityBoundsProperty_->getBool(), - minIntensityProperty_->getFloat(), maxIntensityProperty_->getFloat()); + colorTransformerProperty_->getStdString(), colorMapProperty_->getStdString(), + useColorMapProperty_->getBool(), invertColorMapProperty_->getBool(), + minColorProperty_->getOgreColor(), maxColorProperty_->getOgreColor(), + autocomputeIntensityBoundsProperty_->getBool(), minIntensityProperty_->getFloat(), + maxIntensityProperty_->getFloat(), + gridLinesThicknessProperty_->getFloat(), gridCellDecimationProperty_->getInt()); std::vector layer_names = visual->getLayerNames(); heightTransformerProperty_->clearOptions(); @@ -242,6 +294,15 @@ void GridMapDisplay::processMessage(const grid_map_msgs::GridMap::ConstPtr& msg) visuals_.push_back(visual); } +void GridMapDisplay::updateColorMapList() +{ + updateVisualization(); + for (auto cmap : getColorMapNames()) { + colorMapProperty_->addOptionStd(cmap); + } +} + + } // end namespace grid_map_rviz_plugin #include diff --git a/grid_map_rviz_plugin/src/GridMapVisual.cpp b/grid_map_rviz_plugin/src/GridMapVisual.cpp index 5b85f7026..5dc88857e 100644 --- a/grid_map_rviz_plugin/src/GridMapVisual.cpp +++ b/grid_map_rviz_plugin/src/GridMapVisual.cpp @@ -8,6 +8,9 @@ #include "grid_map_rviz_plugin/GridMapVisual.hpp" +#include +#include + #include #include #include @@ -22,6 +25,8 @@ #include #include +#include "grid_map_rviz_plugin/GridMapColorMaps.hpp" + namespace grid_map_rviz_plugin { GridMapVisual::GridMapVisual(Ogre::SceneManager* sceneManager, Ogre::SceneNode* parentNode) : manualObject_(0), haveMap_(false) { @@ -33,10 +38,12 @@ GridMapVisual::GridMapVisual(Ogre::SceneManager* sceneManager, Ogre::SceneNode* } GridMapVisual::~GridMapVisual() { - // Destroy the ManualObject. - sceneManager_->destroyManualObject(manualObject_); - material_->unload(); - Ogre::MaterialManager::getSingleton().remove(material_->getName()); + // Destroy the ManualObject if it was created. + if (manualObject_) { + sceneManager_->destroyManualObject(manualObject_); + material_->unload(); + Ogre::MaterialManager::getSingleton().remove(material_->getName()); + } // Destroy the frame node. sceneManager_->destroySceneNode(frameNode_); @@ -50,8 +57,9 @@ void GridMapVisual::setMessage(const grid_map_msgs::GridMap::ConstPtr& msg) { void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool flatTerrain, std::string heightLayer, bool flatColor, bool noColor, Ogre::ColourValue meshColor, bool mapLayerColor, std::string colorLayer, - bool useRainbow, bool invertRainbow, Ogre::ColourValue minColor, Ogre::ColourValue maxColor, - bool autocomputeIntensity, float minIntensity, float maxIntensity) { + std::string colorMap, bool useColorMap, bool invertColorMap, Ogre::ColourValue minColor, + Ogre::ColourValue maxColor, bool autocomputeIntensity, float minIntensity, float maxIntensity, float gridLineThickness, + int gridCellDecimation) { const auto startTime = std::chrono::high_resolution_clock::now(); if (!haveMap_) { ROS_DEBUG("Unable to visualize grid map, no map data. Use setMessage() first!"); @@ -91,8 +99,10 @@ void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool f // Reset the mesh lines. meshLines_->clear(); if (showGridLines) { - initializeMeshLines(cols, rows, resolution, alpha); + initializeMeshLines(cols, rows, resolution, alpha, gridLineThickness); } + // Make sure gridCellDecimation is within a valid range + gridCellDecimation = std::max(gridCellDecimation, 1); // Compute a mask of valid cells. auto basicLayers = map_.getBasicLayers(); @@ -101,6 +111,9 @@ void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool f } const MaskArray isValid = computeIsValidMask(basicLayers); + // Check and warn the user if any basic layers are not present in the grid map. + printMissingBasicLayers(basicLayers); + // Compute the display heights for each cell. Eigen::ArrayXXf heightOrFlatData; if (flatTerrain) { @@ -111,18 +124,20 @@ void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool f // Compute the color data for each cell. ColoringMethod coloringMethod; - if (flatColor) { + if (flatColor || noColor) { coloringMethod = ColoringMethod::FLAT; - } else if (!useRainbow) { + } else if(mapLayerColor) { + coloringMethod = ColoringMethod::COLOR_LAYER; + } else if (!useColorMap) { coloringMethod = ColoringMethod::INTENSITY_LAYER_MANUAL; - } else if (!invertRainbow) { - coloringMethod = ColoringMethod::INTENSITY_LAYER_RAINBOW; + } else if (!invertColorMap) { + coloringMethod = ColoringMethod::INTENSITY_LAYER_COLORMAP; } else { - coloringMethod = ColoringMethod::INTENSITY_LAYER_INVERTED_RAINBOW; + coloringMethod = ColoringMethod::INTENSITY_LAYER_INVERTED_COLORMAP; } - const auto colorValues = computeColorValues(heightData, colorData, coloringMethod, meshColor, minIntensity, maxIntensity, - autocomputeIntensity, minColor, maxColor); + const auto colorValues = computeColorValues(heightData, colorData, coloringMethod, colorMap, meshColor, + minIntensity, maxIntensity, autocomputeIntensity, minColor, maxColor); // Initialize loop constants. grid_map::Position topLeft; @@ -136,81 +151,92 @@ void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool f // Add vertices for mesh. for (size_t i = 0; i < rows; ++i) { for (size_t j = 0; j < cols; ++j) { - std::vector vertices; - std::vector colors; - - // Add the vertex to the scene - grid_map::Index index(i, j); - if (!isValid(index(0), index(1))) { - continue; - } - grid_map::Position position = topLeft.array() - index.cast() * resolution; - manualObject_->position(position(0), position(1), heightOrFlatData(index(0), index(1))); + if(!noColor) { + std::vector vertices; + std::vector colors; + + // Add the vertex to the scene + grid_map::Index index(i, j); + if (!isValid(index(0), index(1))) { + continue; + } + grid_map::Position position = topLeft.array() - index.cast() * resolution; + manualObject_->position(position(0), position(1), heightOrFlatData(index(0), index(1))); - const Ogre::ColourValue& color = colorValues(index(0), index(1)); - manualObject_->colour(color.r, color.g, color.b, alpha); + const Ogre::ColourValue& color = colorValues(index(0), index(1)); + manualObject_->colour(color.r, color.g, color.b, alpha); - indexToOgreIndex(index(0), index(1)) = ogreIndex; - ogreIndex++; + indexToOgreIndex(index(0), index(1)) = ogreIndex; + ogreIndex++; - // We can only add triangles to the top left side of the current vertex if we have data. - if (i == 0 || j == 0) { - continue; - } + // We can only add triangles to the top left side of the current vertex if we have data. + if (i == 0 || j == 0) { + continue; + } - // Add triangles and grid to scene. - std::vector vertexIndices; - std::vector vertexPositions; - for (size_t k = 0; k < 2; k++) { - for (size_t l = 0; l < 2; l++) { - grid_map::Index index(i - k, j - l); - if (!isValid(index(0), index(1))) { - continue; + // Add triangles and grid to scene. + std::vector vertexIndices; + for (size_t k = 0; k < 2; k++) { + for (size_t l = 0; l < 2; l++) { + grid_map::Index index(i - k, j - l); + if (!isValid(index(0), index(1))) { + continue; + } + vertexIndices.emplace_back(indexToOgreIndex(index(0), index(1))); } - const grid_map::Position position = topLeft.array() - index.cast() * resolution; - vertexIndices.emplace_back(indexToOgreIndex(index(0), index(1))); - vertexPositions.emplace_back(position(0), position(1), heightOrFlatData(index(0), index(1))); } - } - // Plot triangles if we have enough vertices. - if (vertexIndices.size() > 2) { - // Create one or two triangles from the vertices depending on how many vertices we have. - if (!noColor) { + // Plot triangles if we have enough vertices. + if (vertexIndices.size() > 2) { + // Create one or two triangles from the vertices depending on how many vertices we have. if (vertexIndices.size() == 3) { manualObject_->triangle(vertexIndices[0], vertexIndices[1], vertexIndices[2]); } else { manualObject_->quad(vertexIndices[0], vertexIndices[2], vertexIndices[3], vertexIndices[1]); } } + } - // plot grid lines - if (showGridLines) { - meshLines_->addPoint(vertexPositions[0]); - meshLines_->addPoint(vertexPositions[1]); - meshLines_->newLine(); - - if (vertexIndices.size() == 3) { - meshLines_->addPoint(vertexPositions[1]); - meshLines_->addPoint(vertexPositions[2]); - meshLines_->newLine(); - } else { - meshLines_->addPoint(vertexPositions[1]); - meshLines_->addPoint(vertexPositions[3]); - meshLines_->newLine(); + // compute grid lines vertices + const bool isNthRow{i % gridCellDecimation == 0}; + const bool isNthCol{j % gridCellDecimation == 0}; + const bool isLastRow{i == rows - 1}; + const bool isLastCol{j == cols - 1}; + const bool isDrawMeshLines{(isNthRow && isNthCol) || (isLastRow && isNthCol) || (isLastCol && isNthRow) || (isLastRow && isLastCol)}; - meshLines_->addPoint(vertexPositions[3]); - meshLines_->addPoint(vertexPositions[2]); - meshLines_->newLine(); - } + if (!showGridLines || !isDrawMeshLines) { + continue; + } + std::vector meshLineVertices = computeMeshLineVertices(i, j, gridCellDecimation, isNthRow, isNthCol, isLastRow, + isLastCol, resolution, topLeft, heightOrFlatData, isValid); + + // plot grid lines if we have enough points + if (meshLineVertices.size() > 2) { + meshLines_->addPoint(meshLineVertices[0]); + meshLines_->addPoint(meshLineVertices[1]); + meshLines_->newLine(); + + if (meshLineVertices.size() == 3) { + meshLines_->addPoint(meshLineVertices[1]); + meshLines_->addPoint(meshLineVertices[2]); + meshLines_->newLine(); + } else { + meshLines_->addPoint(meshLineVertices[1]); + meshLines_->addPoint(meshLineVertices[3]); + meshLines_->newLine(); - meshLines_->addPoint(vertexPositions[2]); - meshLines_->addPoint(vertexPositions[0]); + meshLines_->addPoint(meshLineVertices[3]); + meshLines_->addPoint(meshLineVertices[2]); meshLines_->newLine(); } + + meshLines_->addPoint(meshLineVertices[2]); + meshLines_->addPoint(meshLineVertices[0]); + meshLines_->newLine(); } - } - } + + } // end for loop cols + } // end for loop rows manualObject_->end(); material_->getTechnique(0)->setLightingEnabled(false); @@ -228,6 +254,29 @@ void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool f ROS_DEBUG_STREAM("Visualization of grid_map took: " << elapsedTime.count() << " ms."); } +std::vector GridMapVisual::computeMeshLineVertices(int i, int j, int gridCellDecimation, bool isNthRow, bool isNthCol, + bool isLastRow, bool isLastCol, double resolution, + const grid_map::Position& topLeft, + const Eigen::ArrayXXf& heightOrFlatData, const MaskArray& isValid) const { + std::vector meshLineVertices; + meshLineVertices.reserve(4); + for (int k = 0; k < 2; ++k) { + for (int l = 0; l < 2; ++l) { + const int strideX = isLastRow ? (i % gridCellDecimation + int(isNthRow) * gridCellDecimation) : gridCellDecimation; + const int strideY = isLastCol ? (j % gridCellDecimation + int(isNthCol) * gridCellDecimation) : gridCellDecimation; + const int x = i - k * strideX; + const int y = j - l * strideY; + grid_map::Index index(x > 0 ? x : 0, y > 0 ? y : 0); + if (!isValid(index(0), index(1))) { + continue; + } + const grid_map::Position position = topLeft.array() - index.cast() * resolution; + meshLineVertices.emplace_back(position(0), position(1), heightOrFlatData(index(0), index(1))); + } + } + return meshLineVertices; +} + void GridMapVisual::initializeAndBeginManualObject(size_t nVertices) { if (!manualObject_) { static uint32_t count = 0; @@ -250,20 +299,22 @@ void GridMapVisual::initializeAndBeginManualObject(size_t nVertices) { manualObject_->begin(materialName_, Ogre::RenderOperation::OT_TRIANGLE_LIST); } -GridMapVisual::ColorArray GridMapVisual::computeColorValues(Eigen::Ref heightData, - Eigen::Ref colorData, - GridMapVisual::ColoringMethod coloringMethod, Ogre::ColourValue flatColor, - double minIntensity, double maxIntensity, bool autocomputeIntensity, - Ogre::ColourValue minColor, Ogre::ColourValue maxColor) { +GridMapVisual::ColorArray GridMapVisual::computeColorValues(GridMapVisual::MatrixConstRef heightData, + GridMapVisual::MatrixConstRef colorData, + GridMapVisual::ColoringMethod coloringMethod, std::string cmap, + Ogre::ColourValue flatColor, double minIntensity, double maxIntensity, + bool autocomputeIntensity, Ogre::ColourValue minColor, Ogre::ColourValue maxColor) { // Determine max and min intensity. - bool isIntensityColoringMethod = coloringMethod == ColoringMethod::INTENSITY_LAYER_INVERTED_RAINBOW || - coloringMethod == ColoringMethod::INTENSITY_LAYER_RAINBOW || + bool isIntensityColoringMethod = coloringMethod == ColoringMethod::INTENSITY_LAYER_INVERTED_COLORMAP || + coloringMethod == ColoringMethod::INTENSITY_LAYER_COLORMAP || coloringMethod == ColoringMethod::INTENSITY_LAYER_MANUAL; if (autocomputeIntensity && isIntensityColoringMethod) { minIntensity = colorData.minCoeffOfFinites(); maxIntensity = minIntensity + std::max(colorData.maxCoeffOfFinites() - minIntensity, 0.2); // regularize the intensity range. } + const std::vector>& ctable = colorMap.at(cmap); + switch (coloringMethod) { case ColoringMethod::FLAT: return ColorArray::Constant(heightData.rows(), heightData.cols(), flatColor); @@ -278,15 +329,15 @@ GridMapVisual::ColorArray GridMapVisual::computeColorValues(Eigen::RefsetColor(0.0, 0.0, 0.0, alpha); - meshLines_->setLineWidth(resolution / 10.0); + meshLines_->setLineWidth(resolution * lineWidth); meshLines_->setMaxPointsPerLine(2); // In the algorithm below, we have to account for max. 4 lines per cell. const size_t nLines = 2 * (rows * (cols - 1) + cols * (rows - 1)); @@ -306,11 +357,26 @@ void GridMapVisual::initializeMeshLines(size_t cols, size_t rows, double resolut GridMapVisual::MaskArray GridMapVisual::computeIsValidMask(std::vector basicLayers) { MaskArray isValid = MaskArray::Ones(map_.getSize()(0), map_.getSize()(1)); for (const std::string& layer : basicLayers) { - isValid = isValid && map_.get(layer).array().unaryExpr([](float v) { return std::isfinite(v); }); + if (map_.exists(layer)) { + isValid = isValid && map_.get(layer).array().unaryExpr([](float v) { return std::isfinite(v); }); + } } return isValid; } +void GridMapVisual::printMissingBasicLayers(const std::vector& basicLayers) const { + std::stringstream missingBasicLayers{}; + std::copy_if(basicLayers.cbegin(), basicLayers.cend(), std::ostream_iterator(missingBasicLayers, "\n"), + [&](const std::string& basicLayer) { return !map_.exists(basicLayer); }); + + if (missingBasicLayers.str().empty()) { + return; + } + + ROS_WARN_STREAM_THROTTLE(warningMessageThrottlePeriod_, "The following basic layers are missing from the grid map:\n" + << missingBasicLayers.str()); +} + void GridMapVisual::setFramePosition(const Ogre::Vector3& position) { frameNode_->setPosition(position); } @@ -367,4 +433,4 @@ Ogre::ColourValue GridMapVisual::getInterpolatedColor(float intensity, Ogre::Col return color; } -} // namespace grid_map_rviz_plugin \ No newline at end of file +} // namespace grid_map_rviz_plugin diff --git a/grid_map_sdf/CHANGELOG.rst b/grid_map_sdf/CHANGELOG.rst index 96b334ecc..bd9e5fde1 100644 --- a/grid_map_sdf/CHANGELOG.rst +++ b/grid_map_sdf/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog for package grid_map_sdf ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ +* Release of new implementation to convert grid maps to signed distance fields. +* Contributors: Ruben Grandia + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_sdf/CMakeLists.txt b/grid_map_sdf/CMakeLists.txt index ff34075de..88eda8145 100644 --- a/grid_map_sdf/CMakeLists.txt +++ b/grid_map_sdf/CMakeLists.txt @@ -2,13 +2,12 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_sdf) set(CMAKE_CXX_FLAGS "-std=c++14 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries find_package(catkin REQUIRED COMPONENTS grid_map_core - pcl_ros ) ################################### @@ -27,7 +26,6 @@ catkin_package( ${PROJECT_NAME} CATKIN_DEPENDS grid_map_core - pcl_ros DEPENDS ) @@ -45,6 +43,7 @@ include_directories( ## Declare a cpp library add_library(${PROJECT_NAME} + src/SignedDistance2d.cpp src/SignedDistanceField.cpp ) @@ -68,7 +67,9 @@ install( install( DIRECTORY include/${PROJECT_NAME}/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} - FILES_MATCHING PATTERN "*.hpp" + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.h" ) ############# @@ -80,11 +81,16 @@ if(CATKIN_ENABLE_TESTING) ## Add gtest based cpp test target and link libraries catkin_add_gtest(${PROJECT_NAME}-test - test/SignedDistanceFieldTest.cpp + test/test3dLookup.cpp test/test_grid_map_sdf.cpp + test/testDerivatives.cpp + test/testPixelBorderDistance.cpp + test/testSignedDistance2d.cpp + test/testSignedDistance3d.cpp ) target_include_directories(${PROJECT_NAME}-test PRIVATE include + test/include ) target_include_directories(${PROJECT_NAME}-test SYSTEM PUBLIC ${catkin_INCLUDE_DIRS} @@ -114,4 +120,4 @@ if(cmake_clang_tools_FOUND) add_default_clang_tooling( DISABLE_CLANG_FORMAT ) -endif(cmake_clang_tools_FOUND) \ No newline at end of file +endif(cmake_clang_tools_FOUND) diff --git a/grid_map_sdf/doc/anymal_sdf_demo.gif b/grid_map_sdf/doc/anymal_sdf_demo.gif new file mode 100644 index 000000000..1e3fe5d84 Binary files /dev/null and b/grid_map_sdf/doc/anymal_sdf_demo.gif differ diff --git a/grid_map_sdf/include/grid_map_sdf/DistanceDerivatives.hpp b/grid_map_sdf/include/grid_map_sdf/DistanceDerivatives.hpp new file mode 100644 index 000000000..20fe2d166 --- /dev/null +++ b/grid_map_sdf/include/grid_map_sdf/DistanceDerivatives.hpp @@ -0,0 +1,62 @@ +/* + * DistanceDerivatives.h + * + * Created on: Aug 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +#include + +namespace grid_map { +namespace signed_distance_field { + +/** + * Takes the columnwise central difference of a matrix. Uses single sided difference at the boundaries. + * diff = (col(i+1) - col(i-1)) / (2 * resolution) + * + * @param data [in] : data to take the difference of. + * @param centralDifference [out] : matrix to store the result in. + * @param resolution [in] : scaling of the distance between two columns. + */ +inline void columnwiseCentralDifference(const Matrix& data, Matrix& centralDifference, float resolution) { + assert(data.cols() >= 2); // Minimum size to take finite differences. + + const Eigen::Index m{data.cols()}; + const float resInv{1.0F / resolution}; + const float doubleResInv{1.0F / (2.0F * resolution)}; + + // First column + centralDifference.col(0) = resInv * (data.col(1) - data.col(0)); + + // All the middle columns + for (Eigen::Index i = 1; i + 1 < m; ++i) { + centralDifference.col(i) = doubleResInv * (data.col(i + 1) - data.col(i - 1)); + } + + // Last column + centralDifference.col(m - 1) = resInv * (data.col(m - 1) - data.col(m - 2)); +} + +/** + * Takes the finite difference between layers + * result = (data_{k+1} - data{k}) / resolution + */ +inline void layerFiniteDifference(const Matrix& data_k, const Matrix& data_kp1, Matrix& result, float resolution) { + const float resInv{1.0F / resolution}; + result = resInv * (data_kp1 - data_k); +} + +/** + * Takes the central difference between layers + * result = (data_{k+1} - data{k-1}) / (2.0 * resolution) + */ +inline void layerCentralDifference(const Matrix& data_km1, const Matrix& data_kp1, Matrix& result, float resolution) { + const float doubleResInv{1.0F / (2.0F * resolution)}; + result = doubleResInv * (data_kp1 - data_km1); +} + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/Gridmap3dLookup.hpp b/grid_map_sdf/include/grid_map_sdf/Gridmap3dLookup.hpp new file mode 100644 index 000000000..1ef1f5427 --- /dev/null +++ b/grid_map_sdf/include/grid_map_sdf/Gridmap3dLookup.hpp @@ -0,0 +1,105 @@ +/* + * Gridmap3dLookup.h + * + * Created on: Aug 13, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +#include + +namespace grid_map { +namespace signed_distance_field { + +/** + * Stores 3 dimensional grid information and provides methods to convert between position - 3d Index - linear index. + * + * As with the 2D GridMap, the X-Y position is opposite to the row-col-index: (X,Y) is highest at (0,0) and lowest at (n, m). + * The z-position is increasing with the layer-index. + */ +struct Gridmap3dLookup { + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + /// size_t in 3 dimensions + struct size_t_3d { + size_t x{0}; + size_t y{0}; + size_t z{0}; + + size_t_3d() = default; + size_t_3d(size_t x, size_t y, size_t z) : x(x), y(y), z(z) {} + }; + + //! 3D size of the grid + size_t_3d gridsize_{0, 0, 0}; + + //! Origin position of the grid + Position3 gridOrigin_{0.0, 0.0, 0.0}; + + //! Maximum index per dimension stored as double + Position3 gridMaxIndexAsDouble_{0.0, 0.0, 0.0}; + + //! Grid resolution + double resolution_{1.0}; + + /** Default constructor: creates an empty grid */ + Gridmap3dLookup() = default; + + /** + * Constructor + * @param gridsize : x, y, z size of the grid + * @param gridOrigin : position at x=y=z=0 + * @param resolution : (>0.0) size of 1 voxel + */ + Gridmap3dLookup(const size_t_3d& gridsize, const Position3& gridOrigin, double resolution) + : gridsize_(gridsize), + gridOrigin_(gridOrigin), + gridMaxIndexAsDouble_(static_cast(gridsize_.x - 1), static_cast(gridsize_.y - 1), + static_cast(gridsize_.z - 1)), + resolution_(resolution) { + assert(resolution_ > 0.0); + assert(gridsize_.x > 0); + assert(gridsize_.y > 0); + assert(gridsize_.z > 0); + }; + + /** Returns the 3d index of the grid node closest to the query position */ + size_t_3d nearestNode(const Position3& position) const noexcept { + const double resInv{1.0 / resolution_}; + Position3 subpixelVector{(gridOrigin_.x() - position.x()) * resInv, (gridOrigin_.y() - position.y()) * resInv, + (position.z() - gridOrigin_.z()) * resInv}; + return {getNearestPositiveInteger(subpixelVector.x(), gridMaxIndexAsDouble_.x()), + getNearestPositiveInteger(subpixelVector.y(), gridMaxIndexAsDouble_.y()), + getNearestPositiveInteger(subpixelVector.z(), gridMaxIndexAsDouble_.z())}; + } + + /** Returns the 3d node position from a 3d index */ + Position3 nodePosition(const size_t_3d& index) const noexcept { + assert(index.x < gridsize_.x); + assert(index.y < gridsize_.y); + assert(index.z < gridsize_.z); + return {gridOrigin_.x() - index.x * resolution_, gridOrigin_.y() - index.y * resolution_, gridOrigin_.z() + index.z * resolution_}; + } + + /** Returns the linear node index from a 3d node index */ + size_t linearIndex(const size_t_3d& index) const noexcept { + assert(index.x < gridsize_.x); + assert(index.y < gridsize_.y); + assert(index.z < gridsize_.z); + return (index.z * gridsize_.y + index.y) * gridsize_.x + index.x; + } + + /** Linear size */ + size_t linearSize() const noexcept { return gridsize_.x * gridsize_.y * gridsize_.z; } + + /** rounds subindex value and clamps it to [0, max] */ + static size_t getNearestPositiveInteger(double val, double max) noexcept { + // Comparing bounds as double prevents underflow/overflow of size_t + return static_cast(std::max(0.0, std::min(std::round(val), max))); + } +}; + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/PixelBorderDistance.hpp b/grid_map_sdf/include/grid_map_sdf/PixelBorderDistance.hpp new file mode 100644 index 000000000..5cd2baebf --- /dev/null +++ b/grid_map_sdf/include/grid_map_sdf/PixelBorderDistance.hpp @@ -0,0 +1,104 @@ +/* + * PixelBorderDistance.h + * + * Created on: Aug 7, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +#include +#include +#include + +namespace grid_map { +namespace signed_distance_field { + +/** + * Returns distance between the center of a pixel and the border of an other pixel. + * Returns zero if the center is inside the other pixel. + * Pixels are assumed to have size 1.0F + * @param i : location of pixel 1 + * @param j : location of pixel 2 + * @return : absolute distance between center of pixel 1 and the border of pixel 2 + */ +inline float pixelBorderDistance(float i, float j) { + return std::max(std::abs(i - j) - 0.5F, 0.0F); +} + +/** + * Returns square pixelBorderDistance, adding offset f. + */ +inline float squarePixelBorderDistance(float i, float j, float f) { + const float d{pixelBorderDistance(i, j)}; + return d * d + f; +} + +namespace internal { + +/** + * Return equidistancepoint between origin and pixel p (with p > 0) with offset fp + */ +inline float intersectionPointRightSideOfOrigin(float p, float fp) { + /* + * There are 5 different solutions + * In decreasing order: + * sol 1 in [p^2, inf] + * sol 2 in [bound, p^2] + * sol 3 in [-bound, bound] + * sol 4 in [-p^2, -bound] + * sol 5 in [-inf, -p^2] + */ + const float pSquared{p * p}; + if (fp > pSquared) { + return (pSquared + p + fp) / (2.0F * p); // sol 1 + } else if (fp < -pSquared) { + return (pSquared - p + fp) / (2.0F * p); // sol 5 + } else { + const float bound{pSquared - 2.0F * p + 1.0F}; // Always positive because (p > 0) + if (fp > bound) { + return 0.5F + std::sqrt(fp); // sol 2 + } else if (fp < -bound) { + return p - 0.5F - std::sqrt(-fp); // sol 4 + } else { + return (pSquared - p + fp) / (2.0F * p - 2.0F); // sol 3 + } + } +} + +/** + * Return equidistancepoint between origin and pixel p with offset fp + */ +inline float intersectionOffsetFromOrigin(float p, float fp) { + if (p > 0.0F) { + return intersectionPointRightSideOfOrigin(p, fp); + } else { + // call with negative p and flip the result + return -intersectionPointRightSideOfOrigin(-p, fp); + } +} + +} // namespace internal + +/** + * Return the point s in pixel space that is equally far from p and q (taking into account offsets fp, and fq) + * It is the solution to the following equation: + * squarePixelBorderDistance(s, q, fq) == squarePixelBorderDistance(s, p, fp) + */ +inline float equidistancePoint(float q, float fq, float p, float fp) { + assert(q == p || + std::abs(q - p) >= 1.0F); // Check that q and p are proper pixel locations: either the same pixel or non-overlapping pixels + assert((q == p) ? fp == fq : true); // Check when q and p are equal, the offsets are also equal + + if (fp == fq) { // quite common case when both pixels are of the same class (occupied / free) + return 0.5F * (p + q); + } else { + float df{fp - fq}; + float dr{p - q}; + return internal::intersectionOffsetFromOrigin(dr, df) + q; + } +} + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/SignedDistance2d.hpp b/grid_map_sdf/include/grid_map_sdf/SignedDistance2d.hpp new file mode 100644 index 000000000..1646d0bbb --- /dev/null +++ b/grid_map_sdf/include/grid_map_sdf/SignedDistance2d.hpp @@ -0,0 +1,59 @@ +/* + * SignedDistance2d.h + * + * Created on: Jul 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +#include + +#include + +#include "Utils.hpp" + +namespace grid_map { +namespace signed_distance_field { + +/** + * Computes the signed distance field at a specified height for a given elevation map. + * + * @param elevationMap : elevation data. + * @param height : height to generate the signed distance at. + * @param resolution : resolution of the elevation map. (The true distance [m] between cells in world frame) + * @param minHeight : the lowest height contained in elevationMap + * @param maxHeight : the maximum height contained in elevationMap + * @return The signed distance field at the query height. + */ +Matrix signedDistanceAtHeight(const Matrix& elevationMap, float height, float resolution, float minHeight, float maxHeight); + +/** + * Same as above, but returns the sdf in transposed form. + * Also takes temporary variables from outside to prevent memory allocation. + * + * @param elevationMap : elevation data. + * @param sdfTranspose : [output] resulting sdf in transposed form (automatically allocated if of wrong size) + * @param tmp : temporary of size elevationMap (automatically allocated if of wrong size) + * @param tmpTranspose : temporary of size elevationMap transpose (automatically allocated if of wrong size) + * @param height : height to generate the signed distance at. + * @param resolution : resolution of the elevation map. (The true distance [m] between cells in world frame) + * @param minHeight : the lowest height contained in elevationMap + * @param maxHeight : the maximum height contained in elevationMap + */ +void signedDistanceAtHeightTranspose(const Matrix& elevationMap, Matrix& sdfTranspose, Matrix& tmp, Matrix& tmpTranspose, float height, + float resolution, float minHeight, float maxHeight); + +/** + * Gets the 2D signed distance from an occupancy grid. + * Returns +INF if there are no obstacles, and -INF if there are only obstacles + * + * @param occupancyGrid : occupancy grid with true = obstacle, false = free space + * @param resolution : resolution of the grid. + * @return signed distance for each point in the grid to the occupancy border. + */ +Matrix signedDistanceFromOccupancy(const Eigen::Matrix& occupancyGrid, float resolution); + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/SignedDistanceField.hpp b/grid_map_sdf/include/grid_map_sdf/SignedDistanceField.hpp index f1105da55..cfcd8ef81 100644 --- a/grid_map_sdf/include/grid_map_sdf/SignedDistanceField.hpp +++ b/grid_map_sdf/include/grid_map_sdf/SignedDistanceField.hpp @@ -1,45 +1,141 @@ /* - * SignedDistanceField.hpp + * SignedDistanceField.h * - * Created on: Aug 16, 2017 - * Authors: Takahiro Miki, Peter Fankhauser - * Institute: ETH Zurich, ANYbotics + * Created on: Jul 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich */ #pragma once -#include +#include -#include -#include +#include -#include -#include +#include +#include + +#include "Gridmap3dLookup.hpp" namespace grid_map { -class SignedDistanceField -{ +/** + * This class creates a dense 3D signed distance field grid for a given elevation map. + * The 3D grid uses the same resolution as the 2D map. i.e. all voxels are of size (resolution x resolution x resolution). + * The size of the 3D grid is the same as the map in XY direction. The size in Z direction is specified in the constructor. + * + * During the creation of the class all values and derivatives are pre-computed in one go. This makes repeated lookups very cheap. + * Querying a point outside of the constructed grid will result in linear extrapolation. + * + * The distance value and derivatives (dx,dy,dz) per voxel are stored next to each other in memory to support fast lookup during + * interpolation, where we need all 4 values simultaneously. The entire dense grid is stored as a flat vector, with the indexing outsourced + * to the Gridmap3dLookup class. + */ +class SignedDistanceField { public: - SignedDistanceField(); - virtual ~SignedDistanceField(); + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + using Derivative3 = Eigen::Vector3d; + + /** + * Create a signed distance field and its derivative for an elevation layer in the grid map. + * + * @param gridMap : Input map to create the SDF for. + * @param elevationLayer : Name of the elevation layer. + * @param minHeight : Desired starting height of the 3D SDF grid. + * @param maxHeight : Desired ending height of the 3D SDF grid. (Will be rounded up to match the resolution) + */ + SignedDistanceField(const GridMap& gridMap, const std::string& elevationLayer, double minHeight, double maxHeight); + + /** + * Get the signed distance value at a 3D position. + * @param position : 3D position in the frame of the gridmap. + * @return signed distance to the elevation surface. + */ + double value(const Position3& position) const noexcept; + + /** + * Get the signed distance derivative at a 3D position. + * @param position : 3D position in the frame of the gridmap. + * @return derivative of the signed distance field. + */ + Derivative3 derivative(const Position3& position) const noexcept; + + /** + * Get both the signed distance value and derivative at a 3D position. + * @param position : 3D position in the frame of the gridmap. + * @return {value, derivative} of the signed distance field. + */ + std::pair valueAndDerivative(const Position3& position) const noexcept; + + size_t size() const noexcept; - void calculateSignedDistanceField(const GridMap& gridMap, const std::string& layer, const double heightClearance); - double getDistanceAt(const Position3& position) const; - Vector3 getDistanceGradientAt(const Position3& position) const; - double getInterpolatedDistanceAt(const Position3& position) const; - void convertToPointCloud(pcl::PointCloud& points) const; + const std::string& getFrameId() const noexcept; + + Time getTime() const noexcept; + + /** + * Calls a function on each point in the signed distance field. The points are processed in the order they are stored in memory. + * @param func : function taking the node position, signed distance value, and signed distance derivative. + * @param decimation : specifies how many points are returned. 1: all points, 2: every second point, etc. + */ + void filterPoints(std::function func, size_t decimation = 1) const; private: - Matrix getPlanarSignedDistanceField(Eigen::Matrix& data) const; - - double resolution_; - Size size_; - Position position_; - std::vector data_; - float zIndexStartHeight_; - float maxDistance_; - const float lowestHeight_; + /** + * Implementation of the signed distance field computation in this class. + * @param elevation + */ + void computeSignedDistance(const Matrix& elevation); + + /** + * Simultaneously compute the signed distance and derivative in x direction at a given height + * @param elevation [in] : elevation data + * @param currentLayer [out] : signed distance values + * @param dxTranspose [out] : derivative in x direction (the matrix is transposed) + * @param sdfTranspose [tmp] : temporary variable to reuse between calls (allocated on first use) + * @param tmp [tmp] : temporary variable (allocated on first use) + * @param tmpTranspose [tmp] : temporary variable (allocated on first use) + * @param height [in] : height the signed distance is created for. + * @param resolution [in] : resolution of the map. + * @param minHeight [in] : smallest height value in the elevation data. + * @param maxHeight [in] : largest height value in the elevation data. + */ + void computeLayerSdfandDeltaX(const Matrix& elevation, Matrix& currentLayer, Matrix& dxTranspose, Matrix& sdfTranspose, Matrix& tmp, + Matrix& tmpTranspose, float height, float resolution, float minHeight, float maxHeight) const; + + /** + * Add the computed signed distance values and derivatives at a particular height to the grid. + * Adds the layer to the back of data_ + * @param signedDistance : signed distance values. + * @param dxTranspose : x components of the derivative (matrix is transposed). + * @param dy : y components of the derivative. + * @param dz : z components of the derivative. + */ + void emplacebackLayerData(const Matrix& signedDistance, const Matrix& dxTranspose, const Matrix& dy, const Matrix& dz); + + //! Data structure to store together {signed distance value, derivative}. + using node_data_t = std::array; + + /** Helper function to extract the sdf value */ + static double distance(const node_data_t& nodeData) noexcept { return nodeData[0]; } + + /** Helper function to extract the sdf value as float */ + static float distanceFloat(const node_data_t& nodeData) noexcept { return nodeData[0]; } + + /** Helper function to extract the sdf derivative */ + static Derivative3 derivative(const node_data_t& nodeData) noexcept { return {nodeData[1], nodeData[2], nodeData[3]}; } + + //! Object encoding the 3D grid. + signed_distance_field::Gridmap3dLookup gridmap3DLookup_; + + //! Object encoding the signed distance value and derivative in the grid. + std::vector data_; + + //! Frame id of the grid map. + std::string frameId_; + + //! Timestamp of the grid map (nanoseconds). + Time timestamp_; }; -} /* namespace */ +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/Utils.hpp b/grid_map_sdf/include/grid_map_sdf/Utils.hpp new file mode 100644 index 000000000..32d03c452 --- /dev/null +++ b/grid_map_sdf/include/grid_map_sdf/Utils.hpp @@ -0,0 +1,21 @@ +/* + * Utils.h + * + * Created on: Jul 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +namespace grid_map { +namespace signed_distance_field { + +// Check existence of inf +static_assert(std::numeric_limits::has_infinity, "float does not support infinity"); + +//! Distance value that is considered infinite. +constexpr float INF = std::numeric_limits::infinity(); + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/COPYING b/grid_map_sdf/include/grid_map_sdf/distance_transform/COPYING deleted file mode 100755 index d511905c1..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/dt.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/dt.h deleted file mode 100755 index 7a00eab49..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/dt.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* distance transform */ - -#pragma once - -#include - -#include "image.h" -#include "imconv.h" - -namespace distance_transform -{ - const double INF = 1E20; - - /* dt of 1d function using squared distance */ - static float *dt(float *f, int n) { - float *d = new float[n]; - int *v = new int[n]; - float *z = new float[n+1]; - int k = 0; - v[0] = 0; - z[0] = -INF; - z[1] = +INF; - for (int q = 1; q <= n-1; q++) { - float s = ((f[q]+square(q))-(f[v[k]]+square(v[k])))/(2*q-2*v[k]); - while (s <= z[k]) { - k--; - s = ((f[q]+square(q))-(f[v[k]]+square(v[k])))/(2*q-2*v[k]); - } - k++; - v[k] = q; - z[k] = s; - z[k+1] = +INF; - } - - k = 0; - for (int q = 0; q <= n-1; q++) { - while (z[k+1] < q) - k++; - d[q] = square(q-v[k]) + f[v[k]]; - } - - delete [] v; - delete [] z; - return d; - } - - /* dt of 2d function using squared distance */ - static void dt(image *im) { - int width = im->width(); - int height = im->height(); - float *f = new float[std::max(width,height)]; - - // transform along columns - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - f[y] = imRef(im, x, y); - } - float *d = dt(f, height); - for (int y = 0; y < height; y++) { - imRef(im, x, y) = d[y]; - } - delete [] d; - } - - // transform along rows - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - f[x] = imRef(im, x, y); - } - float *d = dt(f, width); - for (int x = 0; x < width; x++) { - imRef(im, x, y) = d[x]; - } - delete [] d; - } - - delete [] f; - } - - /* dt of binary image using squared distance */ - static image *dt(image *im, uchar on = 1) { - int width = im->width(); - int height = im->height(); - - image *out = new image(width, height, false); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - if (imRef(im, x, y) == on) - imRef(out, x, y) = 0; - else - imRef(out, x, y) = INF; - } - } - - dt(out); - return out; - } - -} // namespace diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/image.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/image.h deleted file mode 100755 index 47cada090..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/image.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* a simple image class */ - -#pragma once - -#include - -namespace distance_transform -{ - - template - class image { - public: - /* create an image */ - image(const int width, const int height, const bool init = true); - - /* delete an image */ - ~image(); - - /* init an image */ - void init(const T &val); - - /* copy an image */ - image *copy() const; - - /* get the width of an image. */ - int width() const { return w; } - - /* get the height of an image. */ - int height() const { return h; } - - /* image data. */ - T *data; - - /* row pointers. */ - T **access; - - private: - int w, h; - }; - - /* use imRef to access image data. */ -#define imRef(im, x, y) (im->access[y][x]) - - /* use imPtr to get pointer to image data. */ -#define imPtr(im, x, y) &(im->access[y][x]) - - template - image::image(const int width, const int height, const bool init) { - w = width; - h = height; - data = new T[w * h]; // allocate space for image data - access = new T*[h]; // allocate space for row pointers - - // initialize row pointers - for (int i = 0; i < h; i++) - access[i] = data + (i * w); - - if (init) - memset(data, 0, w * h * sizeof(T)); - } - - template - image::~image() { - delete [] data; - delete [] access; - } - - template - void image::init(const T &val) { - T *ptr = imPtr(this, 0, 0); - T *end = imPtr(this, w-1, h-1); - while (ptr <= end) - *ptr++ = val; - } - - template - image *image::copy() const { - image *im = new image(w, h, false); - memcpy(im->data, data, w * h * sizeof(T)); - return im; - } - -} // namespace diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/imconv.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/imconv.h deleted file mode 100755 index b4f27ff36..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/imconv.h +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* image conversion */ - -#pragma once - -#include - -#include "image.h" -#include "imutil.h" -#include "misc.h" - -namespace distance_transform -{ - const double RED_WEIGHT = 0.299; - const double GREEN_WEIGHT = 0.584; - const double BLUE_WEIGHT = 0.114; - - static image *imageRGBtoGRAY(image *input) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = (uchar) - (imRef(input, x, y).r * RED_WEIGHT + - imRef(input, x, y).g * GREEN_WEIGHT + - imRef(input, x, y).b * BLUE_WEIGHT); - } - } - return output; - } - - static image *imageGRAYtoRGB(image *input) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y).r = imRef(input, x, y); - imRef(output, x, y).g = imRef(input, x, y); - imRef(output, x, y).b = imRef(input, x, y); - } - } - return output; - } - - static image *imageUCHARtoFLOAT(image *input) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; - } - - static image *imageINTtoFLOAT(image *input) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; - } - - static image *imageFLOATtoUCHAR(image *input, - float min, float max) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; - } - - static image *imageFLOATtoUCHAR(image *input) { - float min, max; - min_max(input, &min, &max); - return imageFLOATtoUCHAR(input, min, max); - } - - static image *imageUCHARtoLONG(image *input) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(output, x, y) = imRef(input, x, y); - } - } - return output; - } - - static image *imageLONGtoUCHAR(image *input, long min, long max) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (float)(max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; - } - - static image *imageLONGtoUCHAR(image *input) { - long min, max; - min_max(input, &min, &max); - return imageLONGtoUCHAR(input, min, max); - } - - static image *imageSHORTtoUCHAR(image *input, - short min, short max) { - int width = input->width(); - int height = input->height(); - image *output = new image(width, height, false); - - if (max == min) - return output; - - float scale = UCHAR_MAX / (float)(max - min); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uchar val = (uchar)((imRef(input, x, y) - min) * scale); - imRef(output, x, y) = bound(val, (uchar)0, (uchar)UCHAR_MAX); - } - } - return output; - } - - static image *imageSHORTtoUCHAR(image *input) { - short min, max; - min_max(input, &min, &max); - return imageSHORTtoUCHAR(input, min, max); - } - -} // namespace diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/imutil.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/imutil.h deleted file mode 100755 index ad09d1f8e..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/imutil.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* some image utilities */ - -#pragma once - -#include "image.h" -#include "misc.h" - -namespace distance_transform -{ - - /* compute minimum and maximum value in an image */ - template - void min_max(image *im, T *ret_min, T *ret_max) { - int width = im->width(); - int height = im->height(); - - T min = imRef(im, 0, 0); - T max = imRef(im, 0, 0); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - T val = imRef(im, x, y); - if (min > val) - min = val; - if (max < val) - max = val; - } - } - - *ret_min = min; - *ret_max = max; - } - - /* threshold image */ - template - image *threshold(image *src, int t) { - int width = src->width(); - int height = src->height(); - image *dst = new image(width, height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - imRef(dst, x, y) = (imRef(src, x, y) >= t); - } - } - - return dst; - } - -} // namespace diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/misc.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/misc.h deleted file mode 100755 index a9514d1f4..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/misc.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* random stuff */ - -#pragma once - -#include - -namespace distance_transform -{ - typedef unsigned char uchar; - - typedef struct { uchar r, g, b; } rgb; - - inline bool operator==(const rgb &a, const rgb &b) { - return ((a.r == b.r) && (a.g == b.g) && (a.b == b.b)); - } - - template - inline T abs(const T &x) { return (x > 0 ? x : -x); }; - - template - inline int sign(const T &x) { return (x >= 0 ? 1 : -1); }; - - template - inline T square(const T &x) { return x*x; }; - - template - inline T bound(const T &x, const T &min, const T &max) { - return (x < min ? min : (x > max ? max : x)); - } - - template - inline bool check_bound(const T &x, const T&min, const T &max) { - return ((x < min) || (x > max)); - } - - inline int vlib_round(float x) { return (int)(x + 0.5F); } - - inline int vlib_round(double x) { return (int)(x + 0.5); } - - inline double gaussian(double val, double sigma) { - return exp(-square(val/sigma)/2)/(sqrt(2*M_PI)*sigma); - } - -} // namespace diff --git a/grid_map_sdf/include/grid_map_sdf/distance_transform/pnmfile.h b/grid_map_sdf/include/grid_map_sdf/distance_transform/pnmfile.h deleted file mode 100755 index 282843284..000000000 --- a/grid_map_sdf/include/grid_map_sdf/distance_transform/pnmfile.h +++ /dev/null @@ -1,214 +0,0 @@ -/* -Copyright (C) 2006 Pedro Felzenszwalb - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* basic image I/O */ - -#pragma once - -#include -#include -#include -#include - -#include "image.h" -#include "misc.h" - -namespace distance_transform -{ - - const short BUF_SIZE = 256; - - class pnm_error { }; - - static void read_packed(unsigned char *data, int size, std::ifstream &f) { - unsigned char c = 0; - - int bitshift = -1; - for (int pos = 0; pos < size; pos++) { - if (bitshift == -1) { - c = f.get(); - bitshift = 7; - } - data[pos] = (c >> bitshift) & 1; - bitshift--; - } - } - - static void write_packed(unsigned char *data, int size, std::ofstream &f) { - unsigned char c = 0; - - int bitshift = 7; - for (int pos = 0; pos < size; pos++) { - c = c | (data[pos] << bitshift); - bitshift--; - if ((bitshift == -1) || (pos == size-1)) { - f.put(c); - bitshift = 7; - c = 0; - } - } - } - - /* read PNM field, skipping comments */ - static void pnm_read(std::ifstream &file, char *buf) { - char doc[BUF_SIZE]; - char c; - - file >> c; - while (c == '#') { - file.getline(doc, BUF_SIZE); - file >> c; - } - file.putback(c); - - file.width(BUF_SIZE); - file >> buf; - file.ignore(); - } - - static image *loadPBM(const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P4", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - /* read data */ - image *im = new image(width, height); - for (int i = 0; i < height; i++) - read_packed(imPtr(im, 0, i), width, file); - - return im; - } - - static void savePBM(image *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P4\n" << width << " " << height << "\n"; - for (int i = 0; i < height; i++) - write_packed(imPtr(im, 0, i), width, file); - } - - static image *loadPGM(const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P5", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - pnm_read(file, buf); - if (atoi(buf) > UCHAR_MAX) - throw pnm_error(); - - /* read data */ - image *im = new image(width, height); - file.read((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); - - return im; - } - - static void savePGM(image *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P5\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(uchar)); - } - - static image *loadPPM(const char *name) { - char buf[BUF_SIZE], doc[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "P6", 2)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - pnm_read(file, buf); - if (atoi(buf) > UCHAR_MAX) - throw pnm_error(); - - /* read data */ - image *im = new image(width, height); - file.read((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); - - return im; - } - - static void savePPM(image *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "P6\n" << width << " " << height << "\n" << UCHAR_MAX << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(rgb)); - } - - template - void load_image(image **im, const char *name) { - char buf[BUF_SIZE]; - - /* read header */ - std::ifstream file(name, std::ios::in | std::ios::binary); - pnm_read(file, buf); - if (strncmp(buf, "VLIB", 9)) - throw pnm_error(); - - pnm_read(file, buf); - int width = atoi(buf); - pnm_read(file, buf); - int height = atoi(buf); - - /* read data */ - *im = new image(width, height); - file.read((char *)imPtr((*im), 0, 0), width * height * sizeof(T)); - } - - template - void save_image(image *im, const char *name) { - int width = im->width(); - int height = im->height(); - std::ofstream file(name, std::ios::out | std::ios::binary); - - file << "VLIB\n" << width << " " << height << "\n"; - file.write((char *)imPtr(im, 0, 0), width * height * sizeof(T)); - } - -} // namespace diff --git a/grid_map_sdf/package.xml b/grid_map_sdf/package.xml index de2016e48..f75f43eaf 100644 --- a/grid_map_sdf/package.xml +++ b/grid_map_sdf/package.xml @@ -1,10 +1,10 @@ grid_map_sdf - 1.6.2 + 1.7.18 Generates signed distance fields from grid maps. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Mathieu Agostinucci BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues @@ -13,7 +13,6 @@ catkin grid_map_core - pcl_ros gtest diff --git a/grid_map_sdf/src/SignedDistance2d.cpp b/grid_map_sdf/src/SignedDistance2d.cpp new file mode 100644 index 000000000..c3ee519f5 --- /dev/null +++ b/grid_map_sdf/src/SignedDistance2d.cpp @@ -0,0 +1,290 @@ +/* +* SignedDistance2d.cpp +* +* Created on: Jul 10, 2020 +* Author: Ruben Grandia +* Institute: ETH Zurich +*/ + +#include "grid_map_sdf/SignedDistance2d.hpp" + +#include "grid_map_sdf/PixelBorderDistance.hpp" + +namespace grid_map { +namespace signed_distance_field { + +namespace internal { +struct DistanceLowerBound { + float v; // origin of bounding function + float f; // functional offset at the origin + float z_lhs; // lhs of interval where this bound holds + float z_rhs; // rhs of interval where this lower bound holds +}; + +/** +* 1D Euclidean Distance Transform based on: http://cs.brown.edu/people/pfelzens/dt/ +* Adapted to work on Eigen objects directly +* Optimized computation of s. +* Some optimization to not keep track of bounds that lie fully outside the grid. +*/ +std::vector::iterator fillLowerBounds(const Eigen::Ref& squareDistance1d, + std::vector& lowerBounds, Eigen::Index start) { + const auto n = squareDistance1d.size(); + const auto nFloat = static_cast(n); + const auto startFloat = static_cast(start); + + // Initialize + auto rhsBoundIt = lowerBounds.begin(); + *rhsBoundIt = DistanceLowerBound{startFloat, squareDistance1d[start], -INF, INF}; + auto firstBoundIt = lowerBounds.begin(); + + // Compute bounds to the right of minimum + float qFloat = rhsBoundIt->v + 1.0F; + for (Eigen::Index q = start + 1; q < n; ++q) { + // Storing this by value gives better performance (removed indirection?) + const float fq = squareDistance1d[q]; + + float s = equidistancePoint(qFloat, fq, rhsBoundIt->v, rhsBoundIt->f); + if (s < nFloat) { // Can ignore the lower bound derived from this point if it is only active outsize of [start, n] + // Search backwards in bounds until s is within [z_lhs, z_rhs] + while (s < rhsBoundIt->z_lhs) { + --rhsBoundIt; + s = equidistancePoint(qFloat, fq, rhsBoundIt->v, rhsBoundIt->f); + } + if (s > startFloat) { // Intersection is within [start, n]. Adjust current lowerbound and insert the new one after + rhsBoundIt->z_rhs = s; // Update the bound that comes before + ++rhsBoundIt; // insert new bound after. + *rhsBoundIt = DistanceLowerBound{qFloat, fq, s, INF}; // Valid from s till infinity + } else { // Intersection is outside [0, n]. This means that the new bound dominates all previous bounds + *rhsBoundIt = DistanceLowerBound{qFloat, fq, -INF, INF}; + firstBoundIt = rhsBoundIt; // No need to keep other bounds, so update the first bound iterator to this one. + } + } + + // Increment to follow loop counter as float + qFloat += 1.0F; + } + + return firstBoundIt; +} + +void extractSquareDistances(Eigen::Ref squareDistance1d, std::vector::const_iterator lowerBoundIt, + Eigen::Index start) { + const auto n = squareDistance1d.size(); + + // Store active bound by value to remove indirection + auto lastz = lowerBoundIt->z_rhs; + + auto qFloat = static_cast(start); + for (Eigen::Index q = start; q < n; ++q) { + if (squareDistance1d[q] > 0.0F) { // Observe that if squareDistance1d[q] == 0.0, this is already the minimum and it will stay 0.0 + // Find the new active lower bound if q no longer belongs to current interval + if (qFloat > lastz) { + do { + ++lowerBoundIt; + } while (lowerBoundIt->z_rhs < qFloat); + lastz = lowerBoundIt->z_rhs; + } + + squareDistance1d[q] = squarePixelBorderDistance(qFloat, lowerBoundIt->v, lowerBoundIt->f); + } + + qFloat += 1.0F; + } +} + +/** +* Same as extractSquareDistances, but takes the sqrt as a final step. +* Because several cells will have a value of 0.0 (obstacle / free space label), we can skip the sqrt computation for those. +*/ +void extractDistances(Eigen::Ref squareDistance1d, + std::vector::const_iterator lowerBoundIt, Eigen::Index start) { + const auto n = squareDistance1d.size(); + + // Store active bound by value to remove indirection + auto lastz = lowerBoundIt->z_rhs; + + auto qFloat = static_cast(start); + for (Eigen::Index q = start; q < n; ++q) { + if (squareDistance1d[q] > 0.0F) { // Observe that if squareDistance1d[q] == 0.0, this is already the minimum and it will stay 0.0 + // Find the new active lower bound if q no longer belongs to current interval + if (qFloat > lastz) { + do { + ++lowerBoundIt; + } while (lowerBoundIt->z_rhs < qFloat); + lastz = lowerBoundIt->z_rhs; + } + + squareDistance1d[q] = std::sqrt(squarePixelBorderDistance(qFloat, lowerBoundIt->v, lowerBoundIt->f)); + } + + qFloat += 1.0F; + } +} + +/** +* Find the location of the last zero value from the front +*/ +Eigen::Index lastZeroFromFront(const Eigen::Ref& squareDistance1d) { + const auto n = squareDistance1d.size(); + + for (Eigen::Index q = 0; q < n; ++q) { + if (squareDistance1d[q] > 0.0F) { + if (q > 0) { + return q - 1; + } else { + return 0; + } + } + } + return n; +} + +inline void squaredDistanceTransform_1d_inplace(Eigen::Ref squareDistance1d, + std::vector& lowerBounds) { + auto start = lastZeroFromFront(squareDistance1d); + + // Only need to process line if there are nonzero elements. Also the first zeros stay untouched. + if (start < squareDistance1d.size()) { + auto startIt = fillLowerBounds(squareDistance1d, lowerBounds, start); + extractSquareDistances(squareDistance1d, startIt, start); + } +} + +/** +* Same as above, but takes sqrt as final step (within the same loop) +* @param squareDistance1d : input as squared distance, output is the distance after sqrt. +* @param lowerBounds : work vector +*/ +inline void distanceTransform_1d_inplace(Eigen::Ref squareDistance1d, std::vector& lowerBounds) { + auto start = lastZeroFromFront(squareDistance1d); + + // Only need to process line if there are nonzero elements. Also the first zeros stay untouched. + if (start < squareDistance1d.size()) { + auto startIt = fillLowerBounds(squareDistance1d, lowerBounds, start); + extractDistances(squareDistance1d, startIt, start); + } +} + +void computePixelDistance2dTranspose(Matrix& input, Matrix& distanceTranspose) { + const auto n = input.rows(); + const auto m = input.cols(); + + // Allocate a buffer big enough for processing both rowise and columnwise + std::vector lowerBounds(std::max(n, m)); + + // Process columns + for (Eigen::Index i = 0; i < m; ++i) { + squaredDistanceTransform_1d_inplace(input.col(i), lowerBounds); + } + + // Process rows (= columns after transpose). + distanceTranspose = input.transpose(); + for (Eigen::Index i = 0; i < n; ++i) { + // Fuses square distance algorithm and taking sqrt. + distanceTransform_1d_inplace(distanceTranspose.col(i), lowerBounds); + } +} + +// Initialize with square distance in height direction in pixel units if above the surface +void initializeObstacleDistance(const Matrix& elevationMap, Matrix& result, float height, float resolution) { + /* Vectorized implementation of: + * if (height > elevation) { + * const auto diff = (height - elevation) / resolution; + * return diff * diff; + * } else { + * return 0.0F; + * } + */ + result = ((1.0F / resolution) * (height - elevationMap.array()).cwiseMax(0.0F)).square(); +} + +// Initialize with square distance in height direction in pixel units if below the surface +void initializeObstacleFreeDistance(const Matrix& elevationMap, Matrix& result, float height, float resolution) { + /* Vectorized implementation of: + * if (height < elevation) { + * const auto diff = (height - elevation) / resolution; + * return diff * diff; + * } else { + * return 0.0F; + * } + */ + result = ((1.0F / resolution) * (height - elevationMap.array()).cwiseMin(0.0F)).square(); +} + +void pixelDistanceToFreeSpaceTranspose(const Matrix& elevationMap, Matrix& sdfObstacleFree, Matrix& tmp, float height, float resolution) { + internal::initializeObstacleFreeDistance(elevationMap, tmp, height, resolution); + internal::computePixelDistance2dTranspose(tmp, sdfObstacleFree); +} + +void pixelDistanceToObstacleTranspose(const Matrix& elevationMap, Matrix& sdfObstacleTranspose, Matrix& tmp, float height, + float resolution) { + internal::initializeObstacleDistance(elevationMap, tmp, height, resolution); + internal::computePixelDistance2dTranspose(tmp, sdfObstacleTranspose); +} + +Matrix signedDistanceFromOccupancyTranspose(const Eigen::Matrix& occupancyGrid, float resolution) { + // Compute pixel distance to obstacles + Matrix sdfObstacle; + Matrix init = occupancyGrid.unaryExpr([=](bool val) { return (val) ? 0.0F : INF; }); + internal::computePixelDistance2dTranspose(init, sdfObstacle); + + // Compute pixel distance to obstacle free space + Matrix sdfObstacleFree; + init = occupancyGrid.unaryExpr([=](bool val) { return (val) ? INF : 0.0F; }); + internal::computePixelDistance2dTranspose(init, sdfObstacleFree); + + return resolution * (sdfObstacle - sdfObstacleFree); +} + +} // namespace internal + +void signedDistanceAtHeightTranspose(const Matrix& elevationMap, Matrix& sdfTranspose, Matrix& tmp, Matrix& tmpTranspose, float height, + float resolution, float minHeight, float maxHeight) { + const bool allPixelsAreObstacles = height < minHeight; + const bool allPixelsAreFreeSpace = height > maxHeight; + + if (allPixelsAreObstacles) { + internal::pixelDistanceToFreeSpaceTranspose(elevationMap, sdfTranspose, tmp, height, resolution); + + sdfTranspose *= -resolution; + } else if (allPixelsAreFreeSpace) { + internal::pixelDistanceToObstacleTranspose(elevationMap, sdfTranspose, tmp, height, resolution); + + sdfTranspose *= resolution; + } else { // This layer contains a mix of obstacles and free space + internal::pixelDistanceToObstacleTranspose(elevationMap, sdfTranspose, tmp, height, resolution); + internal::pixelDistanceToFreeSpaceTranspose(elevationMap, tmpTranspose, tmp, height, resolution); + + sdfTranspose = resolution * (sdfTranspose - tmpTranspose); + } +} + +Matrix signedDistanceAtHeight(const Matrix& elevationMap, float height, float resolution, float minHeight, float maxHeight) { + Matrix sdfTranspose; + Matrix tmp; + Matrix tmpTranspose; + + signedDistanceAtHeightTranspose(elevationMap, sdfTranspose, tmp, tmpTranspose, height, resolution, minHeight, maxHeight); + return sdfTranspose.transpose(); +} + +Matrix signedDistanceFromOccupancy(const Eigen::Matrix& occupancyGrid, float resolution) { + auto obstacleCount = occupancyGrid.count(); + bool hasObstacles = obstacleCount > 0; + if (hasObstacles) { + bool hasFreeSpace = obstacleCount < occupancyGrid.size(); + if (hasFreeSpace) { + return internal::signedDistanceFromOccupancyTranspose(occupancyGrid, resolution).transpose(); + } else { + // Only obstacles -> distance is minus infinity everywhere + return Matrix::Constant(occupancyGrid.rows(), occupancyGrid.cols(), -INF); + } + } else { + // No obstacles -> planar distance is infinite + return Matrix::Constant(occupancyGrid.rows(), occupancyGrid.cols(), INF); + } +} + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/src/SignedDistanceField.cpp b/grid_map_sdf/src/SignedDistanceField.cpp index 163dc3a2b..16ecd3906 100644 --- a/grid_map_sdf/src/SignedDistanceField.cpp +++ b/grid_map_sdf/src/SignedDistanceField.cpp @@ -1,184 +1,198 @@ /* - * SignedDistanceField.hpp + * SignedDistanceField.cpp * - * Created on: Aug 16, 2017 - * Authors: Takahiro Miki, Peter Fankhauser - * Institute: ETH Zurich, ANYbotics + * Created on: Jul 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich */ -#include -#include +#include "grid_map_sdf/SignedDistanceField.hpp" -#include +#include -#include - -using namespace distance_transform; +#include "grid_map_sdf/DistanceDerivatives.hpp" +#include "grid_map_sdf/SignedDistance2d.hpp" namespace grid_map { -SignedDistanceField::SignedDistanceField() - : maxDistance_(std::numeric_limits::max()), - zIndexStartHeight_(0.0), - resolution_(0.0), - lowestHeight_(-1e5) // We need some precision. -{ -} +// Import from the signed_distance_field namespace +using signed_distance_field::columnwiseCentralDifference; +using signed_distance_field::Gridmap3dLookup; +using signed_distance_field::layerCentralDifference; +using signed_distance_field::layerFiniteDifference; +using signed_distance_field::signedDistanceAtHeightTranspose; + +SignedDistanceField::SignedDistanceField(const GridMap& gridMap, const std::string& elevationLayer, double minHeight, double maxHeight) + : frameId_(gridMap.getFrameId()), timestamp_(gridMap.getTimestamp()) { + assert(maxHeight >= minHeight); + + // Determine origin of the 3D grid + Position mapOriginXY; + gridMap.getPosition(Eigen::Vector2i(0, 0), mapOriginXY); + Position3 gridOrigin(mapOriginXY.x(), mapOriginXY.y(), minHeight); + + // Round up the Z-discretization. We need a minimum of two layers to enable finite difference in Z direction + const auto numZLayers = static_cast(std::max(std::ceil((maxHeight - minHeight) / gridMap.getResolution()), 2.0)); + const size_t numXrows = gridMap.getSize().x(); + const size_t numYrows = gridMap.getSize().y(); + Gridmap3dLookup::size_t_3d gridsize = {numXrows, numYrows, numZLayers}; + + // Initialize 3D lookup + gridmap3DLookup_ = Gridmap3dLookup(gridsize, gridOrigin, gridMap.getResolution()); + + // Allocate the internal data structure + data_.reserve(gridmap3DLookup_.linearSize()); + + // Check for NaN + const auto& elevationData = gridMap.get(elevationLayer); + if (elevationData.hasNaN()) { + std::cerr + << "[grid_map_sdf::SignedDistanceField] elevation data contains NaN. The generated SDF will be invalid! Apply inpainting first" + << std::endl; + } -SignedDistanceField::~SignedDistanceField() -{ + // Compute the SDF + computeSignedDistance(elevationData); } -void SignedDistanceField::calculateSignedDistanceField(const GridMap& gridMap, const std::string& layer, - const double heightClearance) -{ - data_.clear(); - resolution_ = gridMap.getResolution(); - position_ = gridMap.getPosition(); - size_ = gridMap.getSize(); - Matrix map = gridMap.get(layer); // Copy! - - float minHeight = map.minCoeffOfFinites(); - if (!std::isfinite(minHeight)) minHeight = lowestHeight_; - float maxHeight = map.maxCoeffOfFinites(); - if (!std::isfinite(maxHeight)) maxHeight = lowestHeight_; - - const float valueForEmptyCells = lowestHeight_; // maxHeight, minHeight (TODO Make this an option). - for (size_t i = 0; i < map.size(); ++i) { - if (std::isnan(map(i))) map(i) = valueForEmptyCells; - } +double SignedDistanceField::value(const Position3& position) const noexcept { + const auto nodeIndex = gridmap3DLookup_.nearestNode(position); + const auto nodePosition = gridmap3DLookup_.nodePosition(nodeIndex); + const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)]; + const auto jacobian = derivative(nodeData); + return distance(nodeData) + jacobian.dot(position - nodePosition); +} - // Height range of the signed distance field is higher than the max height. - maxHeight += heightClearance; - - Matrix sdfElevationAbove = Matrix::Ones(map.rows(), map.cols()) * maxDistance_; - Matrix sdfLayer = Matrix::Zero(map.rows(), map.cols()); - std::vector sdf; - zIndexStartHeight_ = minHeight; - - // Calculate signed distance field from bottom. - for (float h = minHeight; h < maxHeight; h += resolution_) { - Eigen::Matrix obstacleFreeField = map.array() < h; - Eigen::Matrix obstacleField = obstacleFreeField.array() < 1; - Matrix sdfObstacle = getPlanarSignedDistanceField(obstacleField); - Matrix sdfObstacleFree = getPlanarSignedDistanceField(obstacleFreeField); - Matrix sdf2d; - // If 2d sdfObstacleFree calculation failed, neglect this SDF - // to avoid extreme small distances (-INF). - if ((sdfObstacleFree.array() >= INF).any()) sdf2d = sdfObstacle; - else sdf2d = sdfObstacle - sdfObstacleFree; - sdf2d *= resolution_; - for (size_t i = 0; i < sdfElevationAbove.size(); ++i) { - if(sdfElevationAbove(i) == maxDistance_ && map(i) <= h) sdfElevationAbove(i) = h - map(i); - else if(sdfElevationAbove(i) != maxDistance_ && map(i) <= h) sdfElevationAbove(i) = sdfLayer(i) + resolution_; - if (sdf2d(i) == 0) sdfLayer(i) = h - map(i); - else if (sdf2d(i) < 0) sdfLayer(i) = -std::min(fabs(sdf2d(i)), fabs(map(i) - h)); - else sdfLayer(i) = std::min(sdf2d(i), sdfElevationAbove(i)); - } - data_.push_back(sdfLayer); - } +SignedDistanceField::Derivative3 SignedDistanceField::derivative(const Position3& position) const noexcept { + const auto nodeIndex = gridmap3DLookup_.nearestNode(position); + const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)]; + return derivative(nodeData); } -grid_map::Matrix SignedDistanceField::getPlanarSignedDistanceField(Eigen::Matrix& data) const -{ - image *input = new image(data.rows(), data.cols(), true); +std::pair SignedDistanceField::valueAndDerivative(const Position3& position) const noexcept { + const auto nodeIndex = gridmap3DLookup_.nearestNode(position); + const auto nodePosition = gridmap3DLookup_.nodePosition(nodeIndex); + const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)]; + const auto jacobian = derivative(nodeData); + return {distance(nodeData) + jacobian.dot(position - nodePosition), jacobian}; +} - for (int y = 0; y < input->height(); y++) { - for (int x = 0; x < input->width(); x++) { - imRef(input, x, y) = data(x, y); - } +void SignedDistanceField::computeSignedDistance(const Matrix& elevation) { + const auto gridOriginZ = static_cast(gridmap3DLookup_.gridOrigin_.z()); + const auto resolution = static_cast(gridmap3DLookup_.resolution_); + const auto minHeight = elevation.minCoeff(); + const auto maxHeight = elevation.maxCoeff(); + + /* + * General strategy to reduce the amount of transposing: + * - SDF at a height is in transposed form after computing it. + * - Take the finite difference in dx, now that this data is continuous in memory. + * - Transpose the SDF + * - Take other finite differences. Now dy is efficient. + * - When writing to the 3D structure, keep in mind that dx is still transposed. + */ + + // Memory needed to compute the SDF at a layer + Matrix tmp; // allocated on first use + Matrix tmpTranspose; // allocated on first use + Matrix sdfTranspose; // allocated on first use + + // Memory needed to keep a buffer of layers. We need 3 due to the central difference + Matrix currentLayer; // allocated on first use + Matrix nextLayer; // allocated on first use + Matrix previousLayer; // allocated on first use + + // Memory needed to compute finite differences + Matrix dxTranspose = Matrix::Zero(elevation.cols(), elevation.rows()); + Matrix dxNextTranspose = Matrix::Zero(elevation.cols(), elevation.rows()); + Matrix dy = Matrix::Zero(elevation.rows(), elevation.cols()); + Matrix dz = Matrix::Zero(elevation.rows(), elevation.cols()); + + // Compute SDF of first layer + computeLayerSdfandDeltaX(elevation, currentLayer, dxTranspose, sdfTranspose, tmp, tmpTranspose, gridOriginZ, resolution, minHeight, + maxHeight); + + // Compute SDF of second layer + computeLayerSdfandDeltaX(elevation, nextLayer, dxNextTranspose, sdfTranspose, tmp, tmpTranspose, gridOriginZ + resolution, resolution, + minHeight, maxHeight); + + // First layer: forward difference in z + layerFiniteDifference(currentLayer, nextLayer, dz, resolution); // dz / layer = +resolution + columnwiseCentralDifference(currentLayer, dy, -resolution); // dy / dcol = -resolution + + emplacebackLayerData(currentLayer, dxTranspose, dy, dz); + + // Middle layers: central difference in z + for (size_t layerZ = 1; layerZ + 1 < gridmap3DLookup_.gridsize_.z; ++layerZ) { + // Circulate layer buffers + previousLayer.swap(currentLayer); + currentLayer.swap(nextLayer); + dxTranspose.swap(dxNextTranspose); + + // Compute SDF of next layer + computeLayerSdfandDeltaX(elevation, nextLayer, dxNextTranspose, sdfTranspose, tmp, tmpTranspose, + gridOriginZ + (layerZ + 1) * resolution, resolution, minHeight, maxHeight); + + // Compute other finite differences + layerCentralDifference(previousLayer, nextLayer, dz, resolution); + columnwiseCentralDifference(currentLayer, dy, -resolution); + + // Add the data to the 3D structure + emplacebackLayerData(currentLayer, dxTranspose, dy, dz); } - // Compute dt. - image *out = dt(input); + // Circulate layer buffers one last time + previousLayer.swap(currentLayer); + currentLayer.swap(nextLayer); + dxTranspose.swap(dxNextTranspose); - Matrix result(data.rows(), data.cols()); + // Last layer: backward difference in z + layerFiniteDifference(previousLayer, currentLayer, dz, resolution); + columnwiseCentralDifference(currentLayer, dy, -resolution); + + // Add the data to the 3D structure + emplacebackLayerData(currentLayer, dxTranspose, dy, dz); +} +void SignedDistanceField::computeLayerSdfandDeltaX(const Matrix& elevation, Matrix& currentLayer, Matrix& dxTranspose, Matrix& sdfTranspose, + Matrix& tmp, Matrix& tmpTranspose, float height, float resolution, float minHeight, + float maxHeight) const { + // Compute SDF + dx of layer: compute sdfTranspose -> take dxTranspose -> transpose to get sdf + signedDistanceAtHeightTranspose(elevation, sdfTranspose, tmp, tmpTranspose, height, resolution, minHeight, maxHeight); + columnwiseCentralDifference(sdfTranspose, dxTranspose, -resolution); // dx / drow = -resolution + currentLayer = sdfTranspose.transpose(); +} - // Take square roots. - for (int y = 0; y < out->height(); y++) { - for (int x = 0; x < out->width(); x++) { - result(x, y) = sqrt(imRef(out, x, y)); +void SignedDistanceField::emplacebackLayerData(const Matrix& signedDistance, const Matrix& dxTranspose, const Matrix& dy, + const Matrix& dz) { + for (size_t colY = 0; colY < gridmap3DLookup_.gridsize_.y; ++colY) { + for (size_t rowX = 0; rowX < gridmap3DLookup_.gridsize_.x; ++rowX) { + data_.emplace_back(node_data_t{signedDistance(rowX, colY), dxTranspose(colY, rowX), dy(rowX, colY), dz(rowX, colY)}); } } - return result; } -double SignedDistanceField::getDistanceAt(const Position3& position) const -{ - double xCenter = size_.x() / 2.0; - double yCenter = size_.y() / 2.0; - int i = std::round(xCenter - (position.x() - position_.x()) / resolution_); - int j = std::round(yCenter - (position.y() - position_.y()) / resolution_); - int k = std::round((position.z() - zIndexStartHeight_) / resolution_); - i = std::max(i, 0); - i = std::min(i, size_.x() - 1); - j = std::max(j, 0); - j = std::min(j, size_.y() - 1); - k = std::max(k, 0); - k = std::min(k, (int)data_.size() - 1); - return data_[k](i, j); +size_t SignedDistanceField::size() const noexcept { + return gridmap3DLookup_.linearSize(); } -double SignedDistanceField::getInterpolatedDistanceAt(const Position3& position) const -{ - double xCenter = size_.x() / 2.0; - double yCenter = size_.y() / 2.0; - int i = std::round(xCenter - (position.x() - position_.x()) / resolution_); - int j = std::round(yCenter - (position.y() - position_.y()) / resolution_); - int k = std::round((position.z() - zIndexStartHeight_) / resolution_); - i = std::max(i, 0); - i = std::min(i, size_.x() - 1); - j = std::max(j, 0); - j = std::min(j, size_.y() - 1); - k = std::max(k, 0); - k = std::min(k, (int)data_.size() - 1); - Vector3 gradient = getDistanceGradientAt(position); - double xp = position_.x() + ((size_.x() - i) - xCenter) * resolution_; - double yp = position_.y() + ((size_.y() - j) - yCenter) * resolution_; - double zp = zIndexStartHeight_ + k * resolution_; - Vector3 error = position - Vector3(xp, yp, zp); - return data_[k](i, j) + gradient.dot(error); +const std::string& SignedDistanceField::getFrameId() const noexcept { + return frameId_; } -Vector3 SignedDistanceField::getDistanceGradientAt(const Position3& position) const -{ - double xCenter = size_.x() / 2.0; - double yCenter = size_.y() / 2.0; - int i = std::round(xCenter - (position.x() - position_.x()) / resolution_); - int j = std::round(yCenter - (position.y() - position_.y()) / resolution_); - int k = std::round((position.z() - zIndexStartHeight_) / resolution_); - i = std::max(i, 1); - i = std::min(i, size_.x() - 2); - j = std::max(j, 1); - j = std::min(j, size_.y() - 2); - k = std::max(k, 1); - k = std::min(k, (int)data_.size() - 2); - double dx = (data_[k](i - 1, j) - data_[k](i + 1, j)) / (2 * resolution_); - double dy = (data_[k](i, j - 1) - data_[k](i, j + 1)) / (2 * resolution_); - double dz = (data_[k + 1](i, j) - data_[k - 1](i, j)) / (2 * resolution_); - return Vector3(dx, dy, dz); +Time SignedDistanceField::getTime() const noexcept { + return timestamp_; } -void SignedDistanceField::convertToPointCloud(pcl::PointCloud& points) const -{ - double xCenter = size_.x() / 2.0; - double yCenter = size_.y() / 2.0; - for (int z = 0; z < data_.size(); z++){ - for (int y = 0; y < size_.y(); y++) { - for (int x = 0; x < size_.x(); x++) { - double xp = position_.x() + ((size_.x() - x) - xCenter) * resolution_; - double yp = position_.y() + ((size_.y() - y) - yCenter) * resolution_; - double zp = zIndexStartHeight_ + z * resolution_; - pcl::PointXYZI p; - p.x = xp; - p.y = yp; - p.z = zp; - p.intensity = data_[z](x, y); - points.push_back(p); +void SignedDistanceField::filterPoints(std::function func, size_t decimation) const { + for (size_t layerZ = 0; layerZ < gridmap3DLookup_.gridsize_.z; layerZ += decimation) { + for (size_t colY = 0; colY < gridmap3DLookup_.gridsize_.y; colY += decimation) { + for (size_t rowX = 0; rowX < gridmap3DLookup_.gridsize_.x; rowX += decimation) { + const Gridmap3dLookup::size_t_3d index3d = {rowX, colY, layerZ}; + const auto index = gridmap3DLookup_.linearIndex(index3d); + func(gridmap3DLookup_.nodePosition(index3d), distanceFloat(data_[index]), derivative(data_[index])); } } } - return; } -} /* namespace */ +} // namespace grid_map diff --git a/grid_map_sdf/test/SignedDistanceFieldTest.cpp b/grid_map_sdf/test/SignedDistanceFieldTest.cpp deleted file mode 100644 index c66adbcc0..000000000 --- a/grid_map_sdf/test/SignedDistanceFieldTest.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * SignedDistanceFieldTest.cpp - * - * Created on: Aug 25, 2017 - * Author: Takahiro Miki, Peter Fankhauser - * Institute: ETH Zurich, ANYbotics - */ - -#include "grid_map_core/GridMap.hpp" -#include "grid_map_sdf/SignedDistanceField.hpp" - -#include -#include - -using namespace std; -using namespace grid_map; - -TEST(SignedDistanceField, EmptyMap) -{ - GridMap map({"layer"}); - map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.0, 0.0)); - - SignedDistanceField sdf; - sdf.calculateSignedDistanceField(map, "layer", 1.0); - Position3 position(0.0, 0.0, 0.0); - - EXPECT_NO_THROW(sdf.getDistanceAt(position)); - EXPECT_NO_THROW(sdf.getInterpolatedDistanceAt(position)); - EXPECT_NO_THROW(sdf.getDistanceGradientAt(position)); -} - -TEST(SignedDistanceField, GetDistanceFlat) -{ - GridMap map({"layer"}); - map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.1, 0.2)); - map["layer"].setConstant(1.0); - map.at("layer", Index(0,0)) = -1.0; - - SignedDistanceField sdf; - sdf.calculateSignedDistanceField(map, "layer", 2.5); - Position pos; - map.getPosition(Index(9, 9), pos); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.0)), -1.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.1)), -0.9, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.2)), -0.8, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.3)), -0.7, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.4)), -0.6, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.5)), -0.5, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.6)), -0.4, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.7)), -0.3, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.8)), -0.2, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.9)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.1)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.2)), 0.2, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.3)), 0.3, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.4)), 0.4, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.5)), 0.5, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.6)), 0.6, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.7)), 0.7, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.8, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.9)), 0.9, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.0)), 1.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.1)), 1.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.2)), 1.2, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.3)), 1.3, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.4)), 1.4, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.5)), 1.5, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 10.0)), 2.5, 0.0001); -} - - -TEST(SignedDistanceField, GetDistance) -{ - GridMap map({"layer"}); - map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.15, 0.25)); - map["layer"].setConstant(1.0); - - map.at("layer", Index(3,4)) = 2.0; - map.at("layer", Index(3,5)) = 2.0; - map.at("layer", Index(3,6)) = 2.0; - map.at("layer", Index(4,4)) = 2.0; - map.at("layer", Index(4,5)) = 2.0; - map.at("layer", Index(4,6)) = 2.0; - map.at("layer", Index(5,4)) = 2.0; - map.at("layer", Index(5,5)) = 2.0; - map.at("layer", Index(5,6)) = 2.0; - map.at("layer", Index(6,4)) = 2.0; - map.at("layer", Index(6,5)) = 2.0; - map.at("layer", Index(6,6)) = 2.0; - map.at("layer", Index(7,4)) = 2.0; - map.at("layer", Index(7,5)) = 2.0; - map.at("layer", Index(7,6)) = 2.0; - - SignedDistanceField sdf; - sdf.calculateSignedDistanceField(map, "layer", 1.5); - Position pos; - - map.getPosition(Index(5, 5), pos); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.0)), -1.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.5)), -1.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.0)), -1.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.1)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.2)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.3)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.4)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.5)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.6)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.7)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.1)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.2)), 0.2, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.3)), 0.3, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.4)), 0.4, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.5)), 0.5, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 10.0)), 1.5, 0.0001); - - map.getPosition(Index(5, 2), pos); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 0.5)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.1)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.2)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.3)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.4)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.5)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.6)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.7)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 1.9)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.0)), 0.2, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.1)), 0.3, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.2)), 0.4, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.3)), 0.5, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.4)), 0.6, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 2.5)), 0.7, 0.0001); - EXPECT_NEAR(sdf.getDistanceAt(Vector3(pos.x(), pos.y(), 10.0)), 1.7, 0.0001); -} - -TEST(SignedDistanceField, GetDistanceGradient) -{ - GridMap map({"layer"}); - map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.15, 0.25)); - map["layer"].setConstant(1.0); - - map.at("layer", Index(3,4)) = 2.0; - map.at("layer", Index(3,5)) = 2.0; - map.at("layer", Index(3,6)) = 2.0; - map.at("layer", Index(4,4)) = 2.0; - map.at("layer", Index(4,5)) = 2.0; - map.at("layer", Index(4,6)) = 2.0; - map.at("layer", Index(5,4)) = 2.0; - map.at("layer", Index(5,5)) = 2.0; - map.at("layer", Index(5,6)) = 2.0; - map.at("layer", Index(6,4)) = 2.0; - map.at("layer", Index(6,5)) = 2.0; - map.at("layer", Index(6,6)) = 2.0; - map.at("layer", Index(7,4)) = 2.0; - map.at("layer", Index(7,5)) = 2.0; - map.at("layer", Index(7,6)) = 2.0; - - SignedDistanceField sdf; - sdf.calculateSignedDistanceField(map, "layer", 1.5); - Position pos; - Vector3 gradient; - - map.getPosition(Index(5, 6), pos); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 0.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), -1, 0.0001); - EXPECT_NEAR(gradient.z(), 0.5, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 1.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), -1, 0.0001); - EXPECT_NEAR(gradient.z(), 0.5, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 2.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), -1.5, 0.0001); - EXPECT_NEAR(gradient.z(), 1, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 2.2)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), -1.5, 0.0001); - EXPECT_NEAR(gradient.z(), 1.0, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 10.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), -1.5, 0.0001); - EXPECT_NEAR(gradient.z(), 1.0, 0.0001); - map.getPosition(Index(2, 2), pos); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 1.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), 1, 0.0001); - EXPECT_NEAR(gradient.z(), 0.5, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 2.0)); - EXPECT_NEAR(gradient.x(), 0.207107, 0.0001); - EXPECT_NEAR(gradient.y(), 1.5, 0.0001); - EXPECT_NEAR(gradient.z(), 1, 0.0001); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 2.2)); - EXPECT_NEAR(gradient.x(), 0.207107, 0.0001); - EXPECT_NEAR(gradient.y(), 1.5, 0.0001); - EXPECT_NEAR(gradient.z(), 1, 0.0001); - map.getPosition(Index(12, 22), pos); - gradient = sdf.getDistanceGradientAt(Vector3(pos.x(), pos.y(), 1.0)); - EXPECT_NEAR(gradient.x(), 0.0, 0.0001); - EXPECT_NEAR(gradient.y(), 1.0, 0.0001); - EXPECT_NEAR(gradient.z(), 0.5, 0.0001); -} - -TEST(SignedDistanceField, GetInterpolatedDistance) -{ - GridMap map({"layer"}); - map.setGeometry(Length(1.0, 2.0), 0.1, Position(0.15, 0.25)); - map["layer"].setConstant(1.0); - - map.at("layer", Index(3,3)) = 2.0; - map.at("layer", Index(3,4)) = 2.0; - map.at("layer", Index(3,5)) = 2.0; - map.at("layer", Index(3,6)) = 2.0; - map.at("layer", Index(3,7)) = 2.0; - map.at("layer", Index(4,3)) = 2.0; - map.at("layer", Index(4,4)) = 2.0; - map.at("layer", Index(4,5)) = 2.0; - map.at("layer", Index(4,6)) = 2.0; - map.at("layer", Index(4,7)) = 2.0; - map.at("layer", Index(5,3)) = 2.0; - map.at("layer", Index(5,4)) = 2.0; - map.at("layer", Index(5,5)) = 2.0; - map.at("layer", Index(5,6)) = 2.0; - map.at("layer", Index(5,7)) = 2.0; - map.at("layer", Index(6,3)) = 2.0; - map.at("layer", Index(6,4)) = 2.0; - map.at("layer", Index(6,5)) = 2.0; - map.at("layer", Index(6,6)) = 2.0; - map.at("layer", Index(6,7)) = 2.0; - map.at("layer", Index(7,3)) = 2.0; - map.at("layer", Index(7,4)) = 2.0; - map.at("layer", Index(7,5)) = 2.0; - map.at("layer", Index(7,6)) = 2.0; - map.at("layer", Index(7,7)) = 2.0; - - SignedDistanceField sdf; - sdf.calculateSignedDistanceField(map, "layer", 1.5); - Position pos; - - map.getPosition(Index(5, 5), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 0.0)), -5.05, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 0.5)), -3.05, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.0)), -1.05, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.1)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.2)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.3)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.4)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.5)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.6)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.7)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.25, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.9)), -0.1, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.1)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.2)), 0.2, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.3)), 0.3, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.4)), 0.4, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.5)), 0.5, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 10.0)), 8, 0.0001); - - map.getPosition(Index(5, 10), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 0.0)), -1.0, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 0.5)), -0.5, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.0)), 0.0, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.1)), 0.1, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.2)), 0.2, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.3)), 0.3, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.5)), 0.35, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.7)), 0.35, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.9)), 0.35, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.0)), 0.45, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.1)), 0.55, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.2)), 0.65, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.3)), 0.75, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.4)), 0.85, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 2.5)), 0.95, 0.0001); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 10.0)), 8.45, 0.0001); - - map.getPosition(Index(5, 0), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.25, 0.0001); - map.getPosition(Index(5, 1), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.175, 0.0001); - map.getPosition(Index(5, 2), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.025, 0.0001); - map.getPosition(Index(5, 3), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.2, 0.0001); - map.getPosition(Index(5, 4), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.225, 0.0001); - map.getPosition(Index(5, 5), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.25, 0.0001); - map.getPosition(Index(5, 6), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), -0.175, 0.0001); - map.getPosition(Index(5, 7), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.025, 0.0001); - map.getPosition(Index(5, 8), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.15, 0.0001); - map.getPosition(Index(5, 9), pos); - EXPECT_NEAR(sdf.getInterpolatedDistanceAt(Vector3(pos.x(), pos.y(), 1.8)), 0.25, 0.0001); -} diff --git a/grid_map_sdf/test/include/naiveSignedDistance.hpp b/grid_map_sdf/test/include/naiveSignedDistance.hpp new file mode 100644 index 000000000..c3957a0b2 --- /dev/null +++ b/grid_map_sdf/test/include/naiveSignedDistance.hpp @@ -0,0 +1,116 @@ +/* + * naiveSignedDistance.h + * + * Created on: Aug 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#pragma once + +namespace grid_map { +namespace signed_distance_field { + +inline Eigen::Matrix occupancyAtHeight(const Matrix& elevationMap, float height) { + Eigen::Matrix occupancy = elevationMap.unaryExpr([=](float val) { return val > height; }); + return occupancy; +} + +inline bool isEqualSdf(const Matrix& sdf0, const Matrix& sdf1, float tol) { + if (sdf0.cols() != sdf1.cols() || sdf0.rows() != sdf1.rows()) { + return false; + } + + for (Eigen::Index col = 0; col < sdf0.cols(); ++col) { + for (Eigen::Index row = 0; row < sdf0.rows(); ++row) { + if (sdf0(row, col) == sdf1(row, col) || std::abs(sdf0(row, col) - sdf1(row, col)) < tol) { + continue; + } else { + return false; + } + } + } + return true; +} + +// N^2 naive implementation, for testing purposes +inline Matrix naiveSignedDistanceAtHeight(const Matrix& elevationMap, float height, float resolution) { + Matrix signedDistance(elevationMap.rows(), elevationMap.cols()); + + // For each point + for (Eigen::Index col = 0; col < elevationMap.cols(); ++col) { + for (Eigen::Index row = 0; row < elevationMap.rows(); ++row) { + if (elevationMap(row, col) >= height) { // point in the SDF is below surface + signedDistance(row, col) = -INF; + // find closest open space over all other points + for (Eigen::Index j = 0; j < elevationMap.cols(); ++j) { + for (Eigen::Index i = 0; i < elevationMap.rows(); ++i) { + // Compute distance to free cube at location (i, j) + const float dx{resolution * pixelBorderDistance(i, row)}; + const float dy{resolution * pixelBorderDistance(j, col)}; + const float dz{std::max(elevationMap(i, j) - height, 0.0F)}; + const float currentSignedDistance{-std::sqrt(dx * dx + dy * dy + dz * dz)}; + signedDistance(row, col) = std::max(signedDistance(row, col), currentSignedDistance); + } + } + } else { // point in the SDF is above surface + signedDistance(row, col) = INF; + // find closest object over all other points + for (Eigen::Index j = 0; j < elevationMap.cols(); ++j) { + for (Eigen::Index i = 0; i < elevationMap.rows(); ++i) { + // Compute distance to occupied cube at location (i, j) + const float dx{resolution * pixelBorderDistance(i, row)}; + const float dy{resolution * pixelBorderDistance(j, col)}; + const float dz{std::max(height - elevationMap(i, j), 0.0F)}; + const float currentSignedDistance{std::sqrt(dx * dx + dy * dy + dz * dz)}; + signedDistance(row, col) = std::min(signedDistance(row, col), currentSignedDistance); + } + } + } + } + } + + return signedDistance; +} + +inline Matrix naiveSignedDistanceFromOccupancy(const Eigen::Matrix& occupancyGrid, float resolution) { + Matrix signedDistance(occupancyGrid.rows(), occupancyGrid.cols()); + + // For each point + for (Eigen::Index col = 0; col < occupancyGrid.cols(); ++col) { + for (Eigen::Index row = 0; row < occupancyGrid.rows(); ++row) { + if (occupancyGrid(row, col)) { // This point is an obstable + signedDistance(row, col) = -INF; + // find closest open space over all other points + for (Eigen::Index j = 0; j < occupancyGrid.cols(); ++j) { + for (Eigen::Index i = 0; i < occupancyGrid.rows(); ++i) { + if (!occupancyGrid(i, j)) { + const float dx{resolution * pixelBorderDistance(i, row)}; + const float dy{resolution * pixelBorderDistance(j, col)}; + const float currentSignedDistance{-std::sqrt(dx * dx + dy * dy)}; + signedDistance(row, col) = std::max(signedDistance(row, col), currentSignedDistance); + } + } + } + } else { // This point is in free space + signedDistance(row, col) = INF; + // find closest object over all other points + for (Eigen::Index j = 0; j < occupancyGrid.cols(); ++j) { + for (Eigen::Index i = 0; i < occupancyGrid.rows(); ++i) { + if (occupancyGrid(i, j)) { + const float dx{resolution * pixelBorderDistance(i, row)}; + const float dy{resolution * pixelBorderDistance(j, col)}; + const float currentSignedDistance{std::sqrt(dx * dx + dy * dy)}; + signedDistance(row, col) = std::min(signedDistance(row, col), currentSignedDistance); + } + } + } + } + } + } + + return signedDistance; +} + +} // namespace signed_distance_field +} // namespace grid_map \ No newline at end of file diff --git a/grid_map_sdf/test/test3dLookup.cpp b/grid_map_sdf/test/test3dLookup.cpp new file mode 100644 index 000000000..254a6cbd5 --- /dev/null +++ b/grid_map_sdf/test/test3dLookup.cpp @@ -0,0 +1,59 @@ +/* + * test3dLookup.cpp + * + * Created on: Aug 18, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#include + +#include "grid_map_sdf/Gridmap3dLookup.hpp" + +using namespace grid_map; +using namespace signed_distance_field; +using size_t_3d = Gridmap3dLookup::size_t_3d; + +TEST(testGridmap3dLookup, nearestNode) { + const size_t_3d gridsize{8, 9, 10}; + const Position3 gridOrigin{-0.1, -0.2, -0.4}; + const double resolution = 0.1; + + Gridmap3dLookup gridmap3DLookup(gridsize, gridOrigin, resolution); + + // Retrieve origin + const size_t_3d originNode = gridmap3DLookup.nearestNode(gridOrigin); + ASSERT_EQ(originNode.x, 0); + ASSERT_EQ(originNode.y, 0); + ASSERT_EQ(originNode.z, 0); + + // test underflow + const size_t_3d originNodeProjected = gridmap3DLookup.nearestNode(gridOrigin + Position3{1e20, 1e20, -1e20}); + ASSERT_EQ(originNodeProjected.x, 0); + ASSERT_EQ(originNodeProjected.y, 0); + ASSERT_EQ(originNodeProjected.z, 0); + + // test overflow + const size_t_3d maxNodeProjected = gridmap3DLookup.nearestNode(gridOrigin + Position3{-1e20, -1e20, +1e20}); + ASSERT_EQ(maxNodeProjected.x, gridsize.x - 1); + ASSERT_EQ(maxNodeProjected.y, gridsize.y - 1); + ASSERT_EQ(maxNodeProjected.z, gridsize.z - 1); + + // Nearest neighbour + const size_t_3d nodeIndex{3, 4, 5}; + const Position3 nodePosition = gridmap3DLookup.nodePosition(nodeIndex); + const size_t_3d closestNodeIndex = gridmap3DLookup.nearestNode(nodePosition + 0.49 * Position3::Constant(resolution)); + ASSERT_EQ(closestNodeIndex.x, nodeIndex.x); + ASSERT_EQ(closestNodeIndex.y, nodeIndex.y); + ASSERT_EQ(closestNodeIndex.z, nodeIndex.z); +} + +TEST(testGridmap3dLookup, linearIndex) { + const size_t_3d gridsize{8, 9, 10}; + const Position3 gridOrigin{-0.1, -0.2, -0.4}; + const double resolution = 0.1; + + Gridmap3dLookup gridmap3DLookup(gridsize, gridOrigin, resolution); + ASSERT_EQ(gridmap3DLookup.linearIndex({0, 0, 0}), 0); + ASSERT_EQ(gridmap3DLookup.linearIndex({gridsize.x - 1, gridsize.y - 1, gridsize.z - 1}), gridmap3DLookup.linearSize() - 1); +} \ No newline at end of file diff --git a/grid_map_sdf/test/testDerivatives.cpp b/grid_map_sdf/test/testDerivatives.cpp new file mode 100644 index 000000000..5fa890412 --- /dev/null +++ b/grid_map_sdf/test/testDerivatives.cpp @@ -0,0 +1,31 @@ +/* + * testDerivatives.cpp + * + * Created on: Aug 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#include + +#include "grid_map_sdf/DistanceDerivatives.hpp" + +using namespace grid_map; +using namespace signed_distance_field; + +TEST(testDerivatives, columnwise) { + Matrix data(2, 3); + data << 1.0, 2.0, 4.0, 2.0, 4.0, 6.0; + + float resolution{0.1F}; + float doubleResolution{2.0F * resolution}; + + Matrix manualDifference(2, 3); + manualDifference << 1.0 / resolution, 3.0 / doubleResolution, 2.0 / resolution, 2.0 / resolution, 4.0 / doubleResolution, + 2.0 / resolution; + + Matrix computedDifference = Matrix::Zero(data.rows(), data.cols()); + columnwiseCentralDifference(data, computedDifference, resolution); + + ASSERT_TRUE(manualDifference.isApprox(computedDifference)); +} diff --git a/grid_map_sdf/test/testPixelBorderDistance.cpp b/grid_map_sdf/test/testPixelBorderDistance.cpp new file mode 100644 index 000000000..38b1a441c --- /dev/null +++ b/grid_map_sdf/test/testPixelBorderDistance.cpp @@ -0,0 +1,76 @@ +/* + * testPixelBorderDistance.cpp + * + * Created on: Aug 7, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#include + +#include "grid_map_sdf/PixelBorderDistance.hpp" +#include "grid_map_sdf/Utils.hpp" + +using namespace grid_map; +using namespace signed_distance_field; + +TEST(testPixelBorderDistance, distanceFunction) { + // Basic properties of the distance function + ASSERT_TRUE(pixelBorderDistance(0, 0) == 0.0F); + ASSERT_FLOAT_EQ(pixelBorderDistance(0, 1), 0.5); + ASSERT_FLOAT_EQ(pixelBorderDistance(0, 2), 1.5); + ASSERT_TRUE(pixelBorderDistance(0, 1) == pixelBorderDistance(1, 0)); + ASSERT_TRUE(pixelBorderDistance(-10, 42) == pixelBorderDistance(42, -10)); +} + +TEST(testPixelBorderDistance, equidistantPoint) { + int pixelRange = 10; + float offsetRange = 20.0; + float offsetStep = 0.25; + float tol = 1e-4; + + for (int p = -pixelRange; p < pixelRange; ++p) { + for (float fp = -offsetRange; fp < offsetRange; fp += offsetStep) { + for (int q = -pixelRange; q < pixelRange; ++q) { + for (float fq = -offsetRange; fq < offsetRange; fq += offsetStep) { + // Fix that offset is the same if pixels are the same + if (p == q) { + fp = fq; + } + // Check symmetry of the equidistant point computation + float s0 = equidistancePoint(q, fq, p, fp); + float s1 = equidistancePoint(p, fp, q, fq); + ASSERT_LT(std::abs(s0 - s1), tol); + + // Check that the distance from s0 to p and q is indeed equal + float dp = squarePixelBorderDistance(s0, p, fp); + float dq = squarePixelBorderDistance(s0, q, fq); + ASSERT_LT(std::abs(dp - dq), tol) << "p: " << p << ", q: " << q << ", fp: " << fp << ", fq: " << fq; + } + } + } + } +} + +TEST(testPixelBorderDistance, equidistantPointInfCases) { + const float pixelTestDistance{1e6}; // Pick a very high pixel index + // With one of the cells at +INF, the intersection will always be +- INF on the side of that cell + // Here the intersection is at the left = -INF + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, INF, pixelTestDistance, std::numeric_limits::max()), -INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, INF, pixelTestDistance, 0.0F), -INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, INF, pixelTestDistance, std::numeric_limits::lowest()), -INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, INF, pixelTestDistance, -INF), -INF); + // Here the intersection is at the right = +INF + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, std::numeric_limits::max(), pixelTestDistance, INF), INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, 0.0F, pixelTestDistance, INF), INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, std::numeric_limits::lowest(), pixelTestDistance, INF), INF); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, -INF, pixelTestDistance, INF), INF); + // Except when both are infinite, then the intersection is in the middle + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, INF, pixelTestDistance, INF), 0.0F); + EXPECT_FLOAT_EQ( + equidistancePoint(-pixelTestDistance, std::numeric_limits::max(), pixelTestDistance, std::numeric_limits::max()), 0.0F); + EXPECT_FLOAT_EQ( + equidistancePoint(-pixelTestDistance, std::numeric_limits::lowest(), pixelTestDistance, std::numeric_limits::lowest()), + 0.0F); + EXPECT_FLOAT_EQ(equidistancePoint(-pixelTestDistance, -INF, pixelTestDistance, -INF), 0.0F); +} diff --git a/grid_map_sdf/test/testSignedDistance2d.cpp b/grid_map_sdf/test/testSignedDistance2d.cpp new file mode 100644 index 000000000..621f96c17 --- /dev/null +++ b/grid_map_sdf/test/testSignedDistance2d.cpp @@ -0,0 +1,112 @@ +/* + * testSignedDistance2d.cpp + * + * Created on: Jul 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#include + +#include "grid_map_sdf/PixelBorderDistance.hpp" +#include "grid_map_sdf/SignedDistance2d.hpp" + +#include "naiveSignedDistance.hpp" + +using namespace grid_map; +using namespace signed_distance_field; + +TEST(testSignedDistance2d, signedDistance2d_noObstacles) { + const int n = 3; + const int m = 4; + const float resolution = 0.1; + const Matrix map = Matrix::Ones(n, m); + + const auto occupancy = occupancyAtHeight(map, 2.0); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + + ASSERT_TRUE((signedDistance.array() == INF).all()); +} + +TEST(testSignedDistance2d, signedDistance2d_allObstacles) { + const int n = 3; + const int m = 4; + const float resolution = 0.1; + const Matrix map = Matrix::Ones(n, m); + + const auto occupancy = occupancyAtHeight(map, 0.0); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + + ASSERT_TRUE((signedDistance.array() == -INF).all()); +} + +TEST(testSignedDistance2d, signedDistance2d_mixed) { + const int n = 2; + const int m = 3; + const float resolution = 1.0; + Matrix map(n, m); + map << 0.0, 1.0, 1.0, 0.0, 0.0, 1.0; + const auto occupancy = occupancyAtHeight(map, 0.5); + + const auto naiveSignedDistance = naiveSignedDistanceFromOccupancy(occupancy, resolution); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)); +} + +TEST(testSignedDistance2d, signedDistance2d_oneObstacle) { + const int n = 20; + const int m = 30; + const float resolution = 0.1; + Matrix map = Matrix::Zero(n, m); + map(n / 2, m / 2) = 1.0; + const auto occupancy = occupancyAtHeight(map, 0.5); + + const auto naiveSignedDistance = naiveSignedDistanceFromOccupancy(occupancy, resolution); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)); +} + +TEST(testSignedDistance2d, signedDistance2d_oneFreeSpace) { + const int n = 20; + const int m = 30; + const float resolution = 0.1; + Matrix map = Matrix::Ones(n, m); + map(n / 2, m / 2) = 0.0; + + const auto occupancy = occupancyAtHeight(map, 0.5); + + const auto naiveSignedDistance = naiveSignedDistanceFromOccupancy(occupancy, resolution); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)); +} + +TEST(testSignedDistance2d, signedDistance2d_debugcase) { + const int n = 3; + const int m = 3; + const float resolution = 1.0; + Matrix map(n, m); + map << 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0; + + const auto occupancy = occupancyAtHeight(map, 0.5); + + const auto naiveSignedDistance = naiveSignedDistanceFromOccupancy(occupancy, resolution); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)); +} + +TEST(testSignedDistance2d, signedDistance2d_random) { + const int n = 20; + const int m = 30; + const float resolution = 1.0; + Matrix map = Matrix::Random(n, m); // random [-1.0, 1.0] + + // Check at different heights, resulting in different levels of sparsity. + float heightStep = 0.1; + for (float height = -1.0 - heightStep; height < 1.0 + heightStep; height += heightStep) { + const auto occupancy = occupancyAtHeight(map, height); + + const auto naiveSignedDistance = naiveSignedDistanceFromOccupancy(occupancy, resolution); + const auto signedDistance = signedDistanceFromOccupancy(occupancy, resolution); + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)) << "height: " << height; + } +} \ No newline at end of file diff --git a/grid_map_sdf/test/testSignedDistance3d.cpp b/grid_map_sdf/test/testSignedDistance3d.cpp new file mode 100644 index 000000000..49219e890 --- /dev/null +++ b/grid_map_sdf/test/testSignedDistance3d.cpp @@ -0,0 +1,193 @@ +/* + * testSignedDistance3d.cpp + * + * Created on: Aug 10, 2020 + * Author: Ruben Grandia + * Institute: ETH Zurich + */ + +#include + +#include "grid_map_sdf/PixelBorderDistance.hpp" +#include "grid_map_sdf/SignedDistance2d.hpp" +#include "grid_map_sdf/SignedDistanceField.hpp" + +#include "naiveSignedDistance.hpp" + +using namespace grid_map; +using namespace signed_distance_field; + +TEST(testSignedDistance3d, flatTerrain) { + const int n = 3; + const int m = 4; + const float resolution = 0.1; + const float terrainHeight = 0.5; + const Matrix map = Matrix::Constant(n, m, terrainHeight); + const float minHeight = map.minCoeff(); + const float maxHeight = map.maxCoeff(); + + const float testHeightAboveTerrain = 3.0; + const auto naiveSignedDistanceAbove = naiveSignedDistanceAtHeight(map, testHeightAboveTerrain, resolution); + const auto signedDistanceAbove = signedDistanceAtHeight(map, testHeightAboveTerrain, resolution, minHeight, maxHeight); + ASSERT_TRUE(isEqualSdf(signedDistanceAbove, naiveSignedDistanceAbove, 1e-4)); + + const float testHeightBelowTerrain = -3.0; + const auto naiveSignedDistanceBelow = naiveSignedDistanceAtHeight(map, testHeightBelowTerrain, resolution); + const auto signedDistanceBelow = signedDistanceAtHeight(map, testHeightBelowTerrain, resolution, minHeight, maxHeight); + ASSERT_TRUE(isEqualSdf(signedDistanceBelow, naiveSignedDistanceBelow, 1e-4)); +} + +TEST(testSignedDistance3d, randomTerrain) { + const int n = 20; + const int m = 30; + const float resolution = 0.1; + Matrix map = Matrix::Random(n, m); // random [-1.0, 1.0] + const float minHeight = map.minCoeff(); + const float maxHeight = map.maxCoeff(); + + // Check at different heights, resulting in different levels of sparsity. + float heightStep = 0.1; + for (float height = -1.0 - heightStep; height < 1.0 + heightStep; height += heightStep) { + const auto naiveSignedDistance = naiveSignedDistanceAtHeight(map, height, resolution); + const auto signedDistance = signedDistanceAtHeight(map, height, resolution, minHeight, maxHeight); + + ASSERT_TRUE(isEqualSdf(signedDistance, naiveSignedDistance, 1e-4)) << "height: " << height; + } +} + +TEST(testSignedDistance3d, randomTerrainInterpolation) { + const int n = 20; + const int m = 30; + const float resolution = 0.1; + GridMap map; + map.setGeometry({n * resolution, m * resolution}, resolution); + map.add("elevation"); + map.get("elevation").setRandom(); // random [-1.0, 1.0] + const Matrix mapData = map.get("elevation"); + const float minHeight = mapData.minCoeff(); + const float maxHeight = mapData.maxCoeff(); + + SignedDistanceField sdf(map, "elevation", minHeight, maxHeight); + + // Check at different heights/ + for (float height = minHeight; height < maxHeight; height += resolution) { + const auto naiveSignedDistance = naiveSignedDistanceAtHeight(mapData, height, resolution); + + for (int i = 0; i < mapData.rows(); ++i) { + for (int j = 0; j < mapData.rows(); ++j) { + Position position2d; + map.getPosition({i, j}, position2d); + + const auto sdfValue = sdf.value({position2d.x(), position2d.y(), height}); + const auto sdfCheck = naiveSignedDistance(i, j); + ASSERT_LT(std::abs(sdfValue - sdfCheck), 1e-4); + } + } + } +} + +TEST(testSignedDistance3d, randomTerrainDerivative) { + const int n = 10; + const int m = 20; + const float resolution = 0.1; + GridMap map; + map.setGeometry({n * resolution, m * resolution}, resolution); + map.add("elevation"); + map.get("elevation").setRandom(); // random [-1.0, 1.0] + const Matrix mapData = map.get("elevation"); + const float minHeight = mapData.minCoeff(); + const float maxHeight = mapData.maxCoeff(); + + SignedDistanceField sdf(map, "elevation", minHeight, maxHeight); + + // Check at different heights/ + int numLayers = (maxHeight - minHeight) / resolution; + for (int k = 0; k <= numLayers; ++k) { + const float height = minHeight + k * resolution; + const auto naiveSignedDistance = naiveSignedDistanceAtHeight(mapData, height, resolution); + const auto naiveSignedDistanceNext = naiveSignedDistanceAtHeight(mapData, height + resolution, resolution); + const auto naiveSignedDistancePrevious = naiveSignedDistanceAtHeight(mapData, height - resolution, resolution); + + for (int i = 0; i < mapData.rows(); ++i) { + for (int j = 0; j < mapData.rows(); ++j) { + Position position2d; + map.getPosition({i, j}, position2d); + const auto sdfderivative = sdf.valueAndDerivative(Position3{position2d.x(), position2d.y(), height}); + const auto sdfCheck = naiveSignedDistance(i, j); + ASSERT_LT(std::abs(sdfderivative.first - sdfCheck), 1e-4); + + // Check finite difference + float dx = 0.0; + if (i > 0) { + if (i + 1 < mapData.rows()) { + dx = (naiveSignedDistance(i + 1, j) - naiveSignedDistance(i - 1, j)) / (-2.0 * resolution); + } else { + dx = (naiveSignedDistance(i, j) - naiveSignedDistance(i - 1, j)) / (-resolution); + } + } else { + dx = (naiveSignedDistance(i + 1, j) - naiveSignedDistance(i, j)) / (-resolution); + } + ASSERT_LT(std::abs(dx - sdfderivative.second.x()), 1e-4); + + float dy = 0.0; + if (j > 0) { + if (j + 1 < mapData.cols()) { + dy = (naiveSignedDistance(i, j + 1) - naiveSignedDistance(i, j - 1)) / (-2.0 * resolution); + } else { + dy = (naiveSignedDistance(i, j) - naiveSignedDistance(i, j - 1)) / (-resolution); + } + } else { + dy = (naiveSignedDistance(i, j + 1) - naiveSignedDistance(i, j)) / (-resolution); + } + ASSERT_LT(std::abs(dy - sdfderivative.second.y()), 1e-4); + + float dz = 0.0; + if (k > 0) { + if (k < numLayers) { + dz = (naiveSignedDistanceNext(i, j) - naiveSignedDistancePrevious(i, j)) / (2.0 * resolution); + } else { + dz = (naiveSignedDistance(i, j) - naiveSignedDistancePrevious(i, j)) / (resolution); + } + } else { + dz = (naiveSignedDistanceNext(i, j) - naiveSignedDistance(i, j)) / (resolution); + } + ASSERT_LT(std::abs(dz - sdfderivative.second.z()), 1e-4); + } + } + } +} + +TEST(testSignedDistance3d, extrapolation) { + const int n = 20; + const int m = 30; + const float resolution = 0.1; + const float h = 0.5; + GridMap map; + map.setGeometry({n * resolution, m * resolution}, resolution); + map.add("elevation"); + map.get("elevation").setConstant(h); // random [-1.0, 1.0] + const Matrix mapData = map.get("elevation"); + const float minHeight = h - resolution; + const float maxHeight = h + resolution; + + SignedDistanceField sdf(map, "elevation", minHeight, maxHeight); + + // Check at different heights/ + for (float height = h - 1.0; height < h + 1.0; height += resolution) { + const auto naiveSignedDistance = naiveSignedDistanceAtHeight(mapData, height, resolution); + + for (int i = 0; i < mapData.rows(); ++i) { + for (int j = 0; j < mapData.rows(); ++j) { + Position position2d; + map.getPosition({i, j}, position2d); + + const auto sdfValueAndDerivative = sdf.valueAndDerivative({position2d.x(), position2d.y(), height}); + const auto sdfCheck = naiveSignedDistance(i, j); + ASSERT_LT(std::abs(sdfValueAndDerivative.first - sdfCheck), 1e-4); + + // Constant terrain, derivative should be up everywhere + ASSERT_LT((sdfValueAndDerivative.second - SignedDistanceField::Derivative3::UnitZ()).norm(), 1e-4); + } + } + } +} \ No newline at end of file diff --git a/grid_map_visualization/CHANGELOG.rst b/grid_map_visualization/CHANGELOG.rst index 40798aa98..2dc865c06 100644 --- a/grid_map_visualization/CHANGELOG.rst +++ b/grid_map_visualization/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog for package grid_map_visualization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1.7.0 (2022-03-17) +------------------ + +1.6.4 (2020-12-04) +------------------ + 1.6.2 (2019-10-14) ------------------ diff --git a/grid_map_visualization/CMakeLists.txt b/grid_map_visualization/CMakeLists.txt index 8a72cb7fc..c506948ba 100644 --- a/grid_map_visualization/CMakeLists.txt +++ b/grid_map_visualization/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5.1) project(grid_map_visualization) set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") - +add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ## Find catkin macros and libraries diff --git a/grid_map_visualization/package.xml b/grid_map_visualization/package.xml index 984992924..f7a3fd2d4 100644 --- a/grid_map_visualization/package.xml +++ b/grid_map_visualization/package.xml @@ -1,10 +1,10 @@ grid_map_visualization - 1.6.2 + 1.7.18 Configurable tool to visualize grid maps in RViz. - Maximilian Wulf - Yoshua Nava + Magnus Gaertner + Guoxiang Zhou BSD http://github.com/anybotics/grid_map http://github.com/anybotics/grid_map/issues diff --git a/grid_map_visualization/src/GridMapVisualization.cpp b/grid_map_visualization/src/GridMapVisualization.cpp index 2301e9818..8c6e110d8 100644 --- a/grid_map_visualization/src/GridMapVisualization.cpp +++ b/grid_map_visualization/src/GridMapVisualization.cpp @@ -62,7 +62,7 @@ bool GridMapVisualization::readParameters() } // Iterate over all visualizations (may be just one), - for (unsigned int i = 0; i < config.size(); ++i) { + for (int i = 0; i < config.size(); ++i) { if (config[i].getType() != XmlRpc::XmlRpcValue::TypeStruct) { ROS_ERROR("%s: Visualizations must be specified as maps, but they are XmlRpcType:%d", visualizationsParameter_.c_str(), config[i].getType()); diff --git a/grid_map_visualization/src/GridMapVisualizationHelpers.cpp b/grid_map_visualization/src/GridMapVisualizationHelpers.cpp index 9432bad2b..7f9e7d808 100644 --- a/grid_map_visualization/src/GridMapVisualizationHelpers.cpp +++ b/grid_map_visualization/src/GridMapVisualizationHelpers.cpp @@ -60,12 +60,12 @@ void interpolateBetweenColors(std_msgs::ColorRGBA& color, const std_msgs::ColorR setColorChannelFromValue(color.b, value, lowerValueBound, upperValueBound, false, colorForLowerValue.b, colorForUpperValue.b); } -void setSaturationFromValue(std_msgs::ColorRGBA& color, const double value, const double lowerValueBound, +void setSaturationFromValue(std_msgs::ColorRGBA& color, const double value, const double /*lowerValueBound*/, const double upperValueBound, const double maxSaturation, const double minSaturation) { // Based on "changeSaturation" function by Darel Rex Finley. const Eigen::Array3f HspFactors(.299, .587, .114); // see http://alienryderflex.com/hsp.html - float saturationChange = static_cast(computeLinearMapping(value, value, upperValueBound, maxSaturation, minSaturation)); + const float saturationChange{static_cast(computeLinearMapping(value, value, upperValueBound, maxSaturation, minSaturation))}; Vector3f colorVector; getColorVectorFromColorMessage(colorVector, color); float perceivedBrightness = sqrt((colorVector.array().square() * HspFactors).sum());