diff --git a/Property_map/doc/Property_map/Doxyfile.in b/Property_map/doc/Property_map/Doxyfile.in index 2167dbca4fe..a079ae055f4 100644 --- a/Property_map/doc/Property_map/Doxyfile.in +++ b/Property_map/doc/Property_map/Doxyfile.in @@ -3,4 +3,4 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - CGAL and Boost Property Maps" INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/property_map.h EXCLUDE += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Dynamic_property_map.h -EXAMPLE_PATH = ${CGAL_Point_set_processing_3_EXAMPLE_DIR} +EXAMPLE_PATH += ${CGAL_Point_set_processing_3_EXAMPLE_DIR} diff --git a/Property_map/doc/Property_map/Property_map.txt b/Property_map/doc/Property_map/Property_map.txt index fef8999a0c6..d2ecf66d1df 100644 --- a/Property_map/doc/Property_map/Property_map.txt +++ b/Property_map/doc/Property_map/Property_map.txt @@ -6,6 +6,8 @@ namespace CGAL { \anchor chapterProperty_map +\cgalAutoToc + \authors Andreas Fabri and Laurent Saboret \section Property_mapA A Short Introduction to the Boost Property Maps Library @@ -55,6 +57,20 @@ The following example reads a point set from an input file and writes it to a fi The following example reads a point set in the `xyz` format and computes the average spacing. %Index, position and color are stored in a tuple and accessed through property maps. \cgalExample{Point_set_processing_3/average_spacing_example.cpp} +\section Property_mapCustom Writing Custom Property Maps + +Property maps are especially useful when using predefined data +structures that are not part of the \cgal library: algorithms written +with property maps can be called on these data structures provided the +user writes the required property maps, without the need to create +deep copies of potentially large data into \cgal formats. + +The following example shows how to write a readable point map and a +read-write normal map to run \cgal normal estimation and orientation +algorithm on raw `double` arrays: +\cgalExample{Property_map/custom_property_map.cpp} + + */ } /* namespace CGAL */ diff --git a/Property_map/doc/Property_map/examples.txt b/Property_map/doc/Property_map/examples.txt index fe5b9e2f71c..30870650f01 100644 --- a/Property_map/doc/Property_map/examples.txt +++ b/Property_map/doc/Property_map/examples.txt @@ -2,4 +2,5 @@ \example Point_set_processing_3/remove_outliers_example.cpp \example Point_set_processing_3/read_write_xyz_point_set_example.cpp \example Point_set_processing_3/average_spacing_example.cpp +\example Property_map/custom_property_map.cpp */ diff --git a/Property_map/examples/Property_map/CMakeLists.txt b/Property_map/examples/Property_map/CMakeLists.txt index 9b183930b64..d5717d3f5d2 100644 --- a/Property_map/examples/Property_map/CMakeLists.txt +++ b/Property_map/examples/Property_map/CMakeLists.txt @@ -32,4 +32,10 @@ endif() create_single_source_cgal_program( "dynamic_properties.cpp" ) +find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater) +if (EIGEN3_FOUND) + create_single_source_cgal_program( "custom_property_map.cpp" ) + CGAL_target_use_Eigen(custom_property_map) +endif() + diff --git a/Property_map/examples/Property_map/custom_property_map.cpp b/Property_map/examples/Property_map/custom_property_map.cpp new file mode 100644 index 00000000000..540bec3cd2d --- /dev/null +++ b/Property_map/examples/Property_map/custom_property_map.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = Kernel::Point_3; +using Vector_3 = Kernel::Vector_3; +using Generator = CGAL::Random_points_on_sphere_3; + +// Example of readable property map to get CGAL::Point_3 objects from +// 3 coordinate arrays +struct Custom_point_map +{ + using key_type = std::size_t; // The iterator's value type is an index + using value_type = Point_3; // The object manipulated by the algorithm is a Point_3 + using reference = Point_3; // The object does not exist in memory, so there's no reference + using category = boost::readable_property_map_tag; // The property map is only used for reading + + double *x, *y, *z; + + Custom_point_map (double* x = nullptr, double* y = nullptr, double* z = nullptr) + : x(x), y(y), z(z) { } + + // The get() function returns the object expected by the algorithm (here, Point_3) + friend Point_3 get (const Custom_point_map& map, std::size_t idx) + { + return Point_3 (map.x[idx], map.y[idx], map.z[idx]); + } +}; + +// Example of read-write property map to get CGAL::Vector_3 objects from +// a buffer array and put CGAL::Vector_3 values in this buffer +struct Custom_normal_map +{ + using key_type = std::size_t; // The iterator's value type is an index + using value_type = Vector_3; // The object manipulated by the algorithm is a Vector_3 + using reference = Vector_3; // The object does not exist in memory, so there's no reference + using category = boost::read_write_property_map_tag; // The property map is used both + // for reading and writing data + double *buffer; + + Custom_normal_map (double* buffer = nullptr) + : buffer (buffer) { } + + // The get() function returns the object expected by the algorithm (here, Vector_3) + friend Vector_3 get (const Custom_normal_map& map, std::size_t idx) + { + return Vector_3 (map.buffer[idx * 3 ], + map.buffer[idx * 3 + 1], + map.buffer[idx * 3 + 2]); + } + + // The put() function updated the user's data structure from the + // object handled by the algorithm (here Vector_3) + friend void put (const Custom_normal_map& map, std::size_t idx, const Vector_3& vector_3) + { + map.buffer[idx * 3 ] = vector_3.x(); + map.buffer[idx * 3 + 1] = vector_3.y(); + map.buffer[idx * 3 + 2] = vector_3.z(); + } +}; + + +int main() +{ + constexpr std::size_t nb_points = 1000; + + // in this example, points are stored as separate coordinate arrays + double x[nb_points]; + double y[nb_points]; + double z[nb_points]; + + // generate random points + Generator generator; + for (std::size_t i = 0; i < nb_points; ++ i) + { + Point_3 p = *(generator ++ ); + x[i] = p.x(); + y[i] = p.y(); + z[i] = p.z(); + } + + // normals are stored as a contiguous double array + double normals[3 *nb_points]; + + // we use a vector of indices to access arrays + std::vector indices; + indices.reserve (nb_points); + for (std::size_t i = 0; i < nb_points; ++ i) + indices.push_back(i); + + // estimate and orient normals using directly user's data structure + // instead of creating deep copies using Point_3 and Vector_3 + CGAL::jet_estimate_normals + (indices, 12, + CGAL::parameters::point_map (Custom_point_map(x,y,z)). + normal_map (Custom_normal_map(normals))); + + CGAL::mst_orient_normals + (indices, 12, + CGAL::parameters::point_map (Custom_point_map(x,y,z)). + normal_map (Custom_normal_map(normals))); + + // Display first 10 points+normals + for (std::size_t i = 0; i < 10; ++ i) + std::cerr << "Point(" << i << ") = " << x[i] << " " << y[i] << " " << z[i] + << "\tNormal(" << i << ") = " + << normals[3*i] << " " << normals[3*i+1] << " " << normals[3*i+2] << std::endl; + + return EXIT_SUCCESS; +}