diff --git a/Generator/doc/Generator/CGAL/point_generators_2.h b/Generator/doc/Generator/CGAL/point_generators_2.h index 5b6f71c135ac..9eb1870ccd71 100644 --- a/Generator/doc/Generator/CGAL/point_generators_2.h +++ b/Generator/doc/Generator/CGAL/point_generators_2.h @@ -184,9 +184,12 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; +/// @} +/// \name Function +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_2` uniformly +creates an input iterator `g` generating points of type `Point_2` uniformly distributed in the open disc with radius \f$ r\f$, i.e.\ \f$ |*g| < r\f$. Two random numbers are needed from `rnd` for each point. @@ -246,10 +249,12 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; - +/// @} +/// \name Function +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_2` uniformly +creates an input iterator `g` generating points of type `Point_2` uniformly distributed in the half-open square with side length \f$ 2 a\f$, centered at the origin, i.e.\ \f$ \forall p = *g: -a \le p.x() < a\f$ and \f$ -a \le p.y() < a\f$. @@ -310,8 +315,13 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; + +/// @} +/// \name Functions +/// @{ + /*! - Creates an input iterator `g` generating points of type `Point_2` uniformly + creates an input iterator `g` generating points of type `Point_2` uniformly distributed inside the triangle with vertices \f$ p, q \f$ and \f$ r \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r \f$, for some \f$ \alpha, \beta, \gamma \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma = 1 \f$. Two random numbers are needed from `rnd` for each point. @@ -321,7 +331,7 @@ typedef const Point_2& reference; get_default_random() ); /*! - Creates an input iterator `g` generating points of type `Point_2` uniformly + creates an input iterator `g` generating points of type `Point_2` uniformly distributed inside a triangle \f$t\f$ with vertices \f$ p, q \f$ and \f$ r \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r \f$, for some \f$ \alpha, \beta, \gamma \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma = 1 \f$. Two random numbers are needed from `rnd` for each point. @@ -390,14 +400,26 @@ typedef const Point_2& reference; */ typedef const Point_2& reference; +/// @} + +/// \name Functions +/// @{ + /*! -Creates an input iterator `g` generating points of type `Point_2` uniformly +creates an input iterator `g` generating points of type `Point_2` uniformly distributed between the triangles of the triangulation. Each triangle has a probability to be chosen to hold the point depending on its area. */ Random_points_in_triangle_mesh_2(const Triangulation& triangulation, Random& rnd = get_default_random() ); +/*! +returns a face containing the last point generated. +\pre a point must have been generated before calling the function +*/ +typename Triangulation::Face_handle +last_item_picked() const; + /// @} }; /* end Random_points_in_triangle_mesh_2 */ @@ -455,8 +477,13 @@ get_default_random() ); */ typedef const Point_2& reference; +/// @} + + /// \name Functions + /// @{ + /*! -Creates an input iterator `g` generating points of type `Point_2` uniformly +creates an input iterator `g` generating points of type `Point_2` uniformly distributed between the triangles of the range. Each triangle has a probability to be chosen to hold the point depending on its area. */ @@ -464,6 +491,13 @@ template Random_points_in_triangles_2(const TriangleRange& triangles, Random& rnd = get_default_random() ); +/*! +returns the address of an input triangle containing the last point generated. +\pre a point must have been generated before calling the function +*/ +const Triangle_2* +last_item_picked() const; + /// @} }; /* end Random_points_in_triangles_2 */ @@ -519,6 +553,9 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_2` uniformly @@ -584,6 +621,9 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_2` uniformly @@ -649,6 +689,9 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_2` uniformly @@ -717,7 +760,9 @@ typedef const Point_2* pointer; */ typedef const Point_2& reference; - +/// @} +/// \name Functions +/// @{ /*! creates an input iterator `g` generating points of type `P` equally diff --git a/Generator/doc/Generator/CGAL/point_generators_3.h b/Generator/doc/Generator/CGAL/point_generators_3.h index 8167d2d506c9..cde6347ab6f7 100644 --- a/Generator/doc/Generator/CGAL/point_generators_3.h +++ b/Generator/doc/Generator/CGAL/point_generators_3.h @@ -81,9 +81,12 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} +/// \name Function +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed in the half-open cube with side length \f$ 2 a\f$, centered at the origin, i.e.\ \f$ \forall p = *g: -a \le p.x(),p.y(),p.z() < a\f$ . Three random numbers are needed from `rnd` for each point. @@ -145,6 +148,9 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_3` uniformly @@ -208,10 +214,12 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; - +/// @} +/// \name Function +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed inside the 3D triangle with vertices \f$ p, q \f$ and \f$ r \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r \f$, for some \f$ \alpha, \beta, \gamma \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma = 1 \f$. Two random numbers are needed from `rnd` for each point. @@ -221,7 +229,7 @@ Random_points_in_triangle_3(Point_3& p, Point_3& q, Point_3& r, Random& rnd = get_default_random()); /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed inside a 3D triangle \f$t\f$ with vertices \f$ p, q \f$ and \f$ r \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r \f$, for some \f$ \alpha, \beta, \gamma \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma = 1 \f$. Two random numbers are needed from `rnd` for each point. @@ -235,6 +243,74 @@ get_default_random()); }; /* end Random_points_in_triangle_3 */ } /* end namespace CGAL */ +namespace CGAL { + +/*! + +The class `Random_points_on_segment_3` is an input iterator creating points uniformly +distributed on a segment. The default `Creator` is +`Creator_uniform_3::Kernel::RT,Point_3>`. + +\cgalModels{InputIterator,PointGenerator} + +\sa `CGAL::Random_points_in_disc_2` +\sa `CGAL::Random_points_in_cube_3` +\sa `CGAL::Random_points_in_tetrahedron_3` +\sa `CGAL::Random_points_on_sphere_3` +\sa `CGAL::Random_points_in_triangle_3` +*/ +template< typename Point_3, typename Creator > +class Random_points_on_segment_3 { +public: + +/// \name Types +/// @{ + +/*! + +*/ +typedef std::input_iterator_tag iterator_category; + +/*! + +*/ +typedef Point_3 value_type; + +/*! + +*/ +typedef std::ptrdiff_t difference_type; + +/*! + +*/ +typedef const Point_3* pointer; + +/*! + +*/ +typedef const Point_3& reference; + +/// @} +/// \name Function +/// @{ + +/*! +creates an input iterator `g` generating points of type `Point_3` uniformly +distributed on the segment from \f$ p\f$ to \f$ q\f$ (excluding \f$ q\f$), +i.e.\ \f$ *g == (1-\lambda)\, p + \lambda q\f$ where \f$ 0 \le\lambda< 1\f$. +A single random number is needed from `rnd` for each point. +The expressions `to_double(p.x())` and `to_double(p.y())` must result in the respective `double` representation of the coordinates of \f$ p\f$, and similarly for \f$ q\f$. +*/ +Random_points_on_segment_3( const Point_3& p, const Point_3& q, +Random& rnd = get_default_random()); + +/// @} + +}; /* end Random_points_on_segment_3 */ +} /* end namespace CGAL */ + + namespace CGAL{ /*! @@ -281,6 +357,9 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_3` uniformly @@ -346,10 +425,12 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; - +/// @} +/// \name Function +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed inside the tetrahedron with vertices \f$ p, q, r \f$ and \f$ s \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r + \delta s \f$, for some \f$ \alpha, \beta, \gamma, \delta \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma + \delta = 1 \f$. Three random numbers are needed from `rnd` for each point. @@ -359,7 +440,7 @@ Random_points_in_tetrahedron_3(Point_3& p, Point_3& q, Point_3& r, Point_3& s, R get_default_random()); /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed inside a tetrahedron \f$t\f$ with vertices \f$ p, q, r \f$ and \f$ s \f$, i.e., \f$*g = \alpha p + \beta q + \gamma r + \delta s \f$, for some \f$ \alpha, \beta, \gamma, \delta \in [0, 1] \f$ and \f$ \alpha + \beta + \gamma + \delta = 1 \f$. Three random numbers are needed from `rnd` for each point. @@ -426,15 +507,26 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} + +/// \name Functions +/// @{ + /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly +creates an input iterator `g` generating points of type `Point_3` uniformly distributed between the triangles of the range. Each triangle has a probability to be chosen to hold the point depending on its area. - */ template Random_points_in_triangles_3(TriangleRange triangulation, Random& rnd = get_default_random() ); +/*! +returns the address of an input triangle containing the last point generated. +\pre a point must have been generated before calling the function +*/ +const Triangle_3* +last_item_picked() const; + /// @} }; /* end Random_points_in_triangles_3 */ @@ -498,18 +590,23 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} +/// \name Functions +/// @{ /*! -Creates an input iterator `g` generating points of type `Point_3` uniformly -distributed in the mesh faces based on `vpm`. Each triangle has a probability to be chosen to hold the point depending on its area. +creates an input iterator `g` generating points of type `Point_3` uniformly +distributed in the faces of `mesh`. Each triangle has a probability to be chosen to hold the point depending on its area. */ -Random_points_in_triangle_mesh_3(const TriangleMesh& mesh, VertexPointMap vpm, Random& rnd = get_default_random() ); +Random_points_in_triangle_mesh_3(const TriangleMesh& mesh, VertexPointMap vpm = get(vertex_point, mesh), Random& rnd = get_default_random() ); /*! -Similar to the previous constructor using `get(vertex_point, mesh)` as vertex point map. +returns a face containing the last point generated. +\pre a point must have been generated before calling the function */ -Random_points_in_triangle_mesh_3(const TriangleMesh& mesh, Random& rnd = get_default_random() ); +typename boost::graph_traits::face_descriptor +last_item_picked() const; /// @} @@ -575,17 +672,28 @@ typedef const value_type* pointer; */ typedef const value_type& reference; +/// @} +/// \name Functions +/// @{ /*! -Creates an input iterator `g` generating points of type `Weighted_point_3` uniformly +creates an input iterator `g` generating points of type `Weighted_point_3` uniformly distributed on the mesh. Each triangle has a probability to be chosen to hold the point depending on its area. */ Random_points_in_tetrahedral_mesh_boundary_3( const C3T3& c3t3,Random& rnd = get_default_random() ); +/*! +returns a facet containing the last point generated. +\pre a point must have been generated before calling the function +*/ +typename C3t3::Triangulation::Facet +last_item_picked() const; + /// @} + }; /* end Random_points_in_tetrahedral_mesh_boundary_3 */ } /* end namespace CGAL */ @@ -647,15 +755,27 @@ typedef const value_type* pointer; */ typedef const value_type& reference; +/// @} +/// \name Functions +/// @{ /*! -Creates an input iterator `g` generating points of type `Weighted_point_3` uniformly +creates an input iterator `g` generating points of type `Weighted_point_3` uniformly distributed inside the tetrahedra of the mesh. Each tetrahedron has a probability to be chosen to hold the point depending on its volume. */ Random_points_in_tetrahedral_mesh_3( const C3T3& c3t3,Random& rnd = get_default_random() ); + +/*! +returns a cell containing the last point generated. +\pre a point must have been generated before calling the function +*/ +typename C3t3::Triangulation::Cell_handle + +last_item_picked() const; + /// @} }; /* end Random_points_in_tetrahedral_mesh_3 */ @@ -680,8 +800,7 @@ rounding errors. \sa `CGAL::Random_points_in_sphere_3` */ template< typename Point_3, typename Creator > -class Random_points_on_sphere_3 { -public: +struct Random_points_on_sphere_3 { /// \name Types /// @{ @@ -711,6 +830,9 @@ typedef const Point_3* pointer; */ typedef const Point_3& reference; +/// @} +/// \name Function +/// @{ /*! creates an input iterator `g` generating points of type `Point_3` uniformly @@ -726,3 +848,169 @@ get_default_random()); }; /* end Random_points_on_sphere_3 */ } /* end namespace CGAL */ + + +namespace CGAL { + +/*! + +The class `Random_points_in_triangle_soup_3` is an input iterator creating points uniformly distributed inside a range of `Triangle_3`. +The triangle range must be valid and unchanged while the iterator is used. Triangle are triple of indices refering to position of points +in the input point range. + +\tparam PointRange a model of the concepts `RandomAccessContainer` with value type begin a point from a \cgal kernel +\tparam Triangle_3 a model of the concept `RandomAccessContainer` with `value_type` being `std::size_t`. + +\cgalModels{InputIterator,PointGenerator} + +\sa `CGAL::Random_points_in_cube_3` +\sa `CGAL::Random_points_in_triangle_3` +\sa `CGAL::Random_points_in_tetrahedron_3` +\sa `CGAL::Random_points_in_triangle_mesh_3` +\sa `CGAL::Random_points_in_tetrahedral_mesh_boundary_3` +\sa `CGAL::Random_points_in_tetrahedral_mesh_3` +\sa `CGAL::Random_points_in_triangles_2` +*/ +template< typename PointRange, + typename Triangle_3 = std::vector, + typename Creator = Creator_uniform_3< + typename Kernel_traits< typename PointRange::value_type >::Kernel::RT, + typename PointRange::value_type> > +struct Random_points_in_triangle_soup_3 { + +/// \name Types +/// @{ + +/*! + +*/ +typedef std::input_iterator_tag iterator_category; + +/*! + +*/ +typedef Point_3 value_type; + +/*! + +*/ +typedef std::ptrdiff_t difference_type; + +/*! + +*/ +typedef const Point_3* pointer; + +/*! + +*/ +typedef const Point_3& reference; + +/// @} + +/// \name Functions +/// @{ + +/*! +creates an input iterator `g` generating points of type `Point_3` uniformly +distributed between the triangles of the range. Each triangle has a probability to be chosen to hold the point depending on its area. +\tparam TriangleRange a model of the concept `RandomAccessContainer` with `value_type` being `std::size_t`. +*/ +template +Random_points_in_triangle_soup_3(const TriangleRange& triangles, + const PointRange& points, Random& rnd = get_default_random() ); + +/*! +returns an input triangle containing the last point generated. +\pre a point must have been generated before calling the function +*/ +const Triangle_3 +last_item_picked() const; + +/// @} + +}; /* end Random_points_in_triangle_soup_3 */ +} /* end namespace CGAL */ + +namespace CGAL { + +/*! + +The class `Random_points_in_triangle_mesh_3` is an input iterator creating points uniformly +distributed inside the faces of a triangle mesh model of `EdgeListGraph`. +The graph must be valid and unchanged while the iterator is used. + +\cgalModels{InputIterator,PointGenerator} + +\sa `CGAL::Random_points_in_disc_2` +\sa `CGAL::Random_points_in_cube_3` +\sa `CGAL::Random_points_in_triangle_3` +\sa `CGAL::Random_points_on_sphere_3` +\sa `CGAL::Random_points_in_triangle_mesh_2` +\sa `CGAL::Random_points_in_tetrahedral_mesh_boundary_3` +\sa `CGAL::Random_points_in_tetrahedral_mesh_3` +\sa `CGAL::Random_points_in_triangles_2` +\sa `CGAL::Random_points_in_triangles_3` + +*/ +template < class EdgeListGraph, + class VertexPointMap = typename boost::property_map::type, + class Creator = Creator_uniform_3< + typename Kernel_traits< typename boost::property_traits::value_type >::Kernel::RT, + typename boost::property_traits::value_type > > +struct Random_points_on_edge_list_graph_3 { + +/// \name Types +/// @{ + +/*! + +*/ +typedef std::input_iterator_tag iterator_category; +typedef typename boost::property_traits::value_type Point_3; + +/*! + +*/ +typedef Point_3 value_type; + +/*! + +*/ +typedef std::ptrdiff_t difference_type; + +/*! + +*/ +typedef const Point_3* pointer; + +/*! + +*/ +typedef const Point_3& reference; + +/// @} + +/// \name Functions +/// @{ + +/*! +creates an input iterator `g` generating points of type `Point_3` uniformly +distributed on the edges of the graph. Each edge has a probability to be chosen to hold the point depending on its length. +*/ +Random_points_on_edge_list_graph_3(const EdgeListGraph& mesh, VertexPointMap vpm = get(vertex_point, mesh), Random& rnd = get_default_random() ); + + +/*! +returns a face containing the last point generated. +\pre a point must have been generated before calling the function +*/ +typename boost::graph_traits::edge_descriptor +last_item_picked() const; + +/// @} + +}; /* end Random_points_on_edge_list_graph_3 */ + +} /* end namespace CGAL */ diff --git a/Generator/doc/Generator/PackageDescription.txt b/Generator/doc/Generator/PackageDescription.txt index 361419d9c33f..947799f16b87 100644 --- a/Generator/doc/Generator/PackageDescription.txt +++ b/Generator/doc/Generator/PackageDescription.txt @@ -56,24 +56,33 @@ achieve random permutations for otherwise regular generators ( - `CGAL::Random` - `CGAL::Points_on_segment_2` -- `CGAL::Random_points_in_ball_d` -- `CGAL::Random_points_in_cube_3` -- `CGAL::Random_points_in_cube_d` -- `CGAL::Random_points_in_disc_2` + +- `CGAL::Random_points_on_segment_2` - `CGAL::Random_points_in_triangle_2` -- `CGAL::Random_points_in_sphere_3` -- `CGAL::Random_points_in_triangle_3` -- `CGAL::Random_points_in_tetrahedron_3` -- `CGAL::Random_points_in_tetrahedral_mesh_3` - `CGAL::Random_points_in_square_2` +- `CGAL::Random_points_in_disc_2` - `CGAL::Random_points_on_circle_2` -- `CGAL::Random_points_in_triangle_mesh_2` -- `CGAL::Random_points_in_triangle_mesh_3` -- `CGAL::Random_points_in_tetrahedral_mesh_boundary_3` -- `CGAL::Random_points_on_segment_2` + +- `CGAL::Random_points_on_segment_3` +- `CGAL::Random_points_in_triangle_3` +- `CGAL::Random_points_in_tetrahedron_3` +- `CGAL::Random_points_in_cube_3` +- `CGAL::Random_points_in_sphere_3` - `CGAL::Random_points_on_sphere_3` + +- `CGAL::Random_points_in_ball_d` +- `CGAL::Random_points_in_cube_d` - `CGAL::Random_points_on_sphere_d` -- `CGAL::Random_points_on_square_2` + +- `CGAL::Random_points_in_triangle_mesh_2` +- `CGAL::Random_points_in_triangles_2` + +- `CGAL::Random_points_in_triangle_mesh_3` +- `CGAL::Random_points_on_edge_list_graph_3` +- `CGAL::Random_points_in_triangles_3` +- `CGAL::Random_points_in_triangle_soup_3` +- `CGAL::Random_points_in_tetrahedral_mesh_3` +- `CGAL::Random_points_in_tetrahedral_mesh_boundary_3` \cgalCRPSection{Traits Class} diff --git a/Generator/include/CGAL/Generator/internal/Generic_random_point_generator.h b/Generator/include/CGAL/Generator/internal/Generic_random_point_generator.h index 2c6bfaf40e6a..a992f99dbe41 100644 --- a/Generator/include/CGAL/Generator/internal/Generic_random_point_generator.h +++ b/Generator/include/CGAL/Generator/internal/Generic_random_point_generator.h @@ -34,6 +34,7 @@ class Generic_random_point_generator : public Random_generator_base

std::vector weights; ObjectFromIdMap object_from_id_map; Random& random; + std::size_t last_picked_id = std::size_t(-1); protected: void generate_point(); @@ -89,13 +90,19 @@ class Generic_random_point_generator : public Random_generator_base

return 0; return weights.back(); } + + Id last_item_picked() const + { + CGAL_assertion(last_picked_id != std::size_t(-1)); + return ids[last_picked_id]; + } }; template < typename Id, class ObjectFromIdMap, class GeneratorOnObject, class P > void Generic_random_point_generator::generate_point() { //shoot a random value in weights - std::size_t target = std::distance( + last_picked_id = std::distance( weights.begin(), std::upper_bound( weights.begin(), @@ -105,7 +112,7 @@ void Generic_random_point_generator: ); // generate the points - GeneratorOnObject pointCreator(object_from_id_map(ids[target]), random); + GeneratorOnObject pointCreator(object_from_id_map(ids[last_picked_id])); this->d_item = *pointCreator; } diff --git a/Generator/include/CGAL/point_generators_3.h b/Generator/include/CGAL/point_generators_3.h index bee67709be1b..7141fcd6f25f 100644 --- a/Generator/include/CGAL/point_generators_3.h +++ b/Generator/include/CGAL/point_generators_3.h @@ -714,7 +714,7 @@ template ::Kernel::RT, typename PointRange::value_type> > -struct Random_points_in_triangle_soup +struct Random_points_in_triangle_soup_3 : public Generic_random_point_generator, Random_points_in_triangle_3, @@ -728,12 +728,12 @@ struct Random_points_in_triangle_soup typedef typename Kernel_traits::Kernel Kernel; typedef Triangle Id; typedef Point_3 result_type; - typedef Random_points_in_triangle_soup This; + typedef Random_points_in_triangle_soup_3 This; template - Random_points_in_triangle_soup(const TriangleRange& triangles, - const PointRange& points, - Random& rnd = get_default_random()) + Random_points_in_triangle_soup_3(const TriangleRange& triangles, + const PointRange& points, + Random& rnd = get_default_random()) : Base(triangles, internal::Triangle_3_from_soup(points), internal::Apply_approx_sqrt::Kernel::Compute_squared_area_3>(), diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 717ff715394f..03e1b0382a97 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -8,6 +8,7 @@ project(Polygon_mesh_processing_Examples) find_package(CGAL REQUIRED) create_single_source_cgal_program("sample_example.cpp" ) +create_single_source_cgal_program("poisson_sampling_sm_example.cpp" ) create_single_source_cgal_program("extrude.cpp" ) create_single_source_cgal_program( "list_non_pure_triangle_meshes.cpp" ) create_single_source_cgal_program("polyhedral_envelope.cpp" ) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/poisson_sampling_sm_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/poisson_sampling_sm_example.cpp new file mode 100644 index 000000000000..1ac2d8ec1e1d --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/poisson_sampling_sm_example.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::Point_3 Point; + +typedef CGAL::Surface_mesh Mesh; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +namespace PMP = CGAL::Polygon_mesh_processing; + +int main(int argc, char* argv[]) +{ + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/eight.off"); + + Mesh mesh; + if(!PMP::IO::read_polygon_mesh(filename, mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + const double sampling_radius = (argc > 2) ? std::atof(argv[2]) : 0.009; + + const std::size_t number_of_darts = (argc > 3) ? std::atof(argv[3]) : 20; + + CGAL::Random random_seed = (argc > 4) ? std::atof(argv[4]) : CGAL::get_default_random(); + + std::vector points; + PMP::sample_triangle_mesh(mesh, + std::back_inserter(points), + CGAL::parameters::use_poisson_disk_sampling_euclidean(true) + .sampling_radius(sampling_radius) + .number_of_darts(number_of_darts) + .random_seed(random_seed)); + + std::ofstream out("sampling.xyz"); + out << std::setprecision(17); + std::copy(points.begin(), points.end(), std::ostream_iterator(out, "\n")); + + std::cout << points.size() << std::endl; + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h index 29d4d5810bb9..49ccf3ecdbf7 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -473,6 +474,33 @@ struct Triangle_structure_sampler_for_triangle_mesh min_sq_edge_length = (std::numeric_limits::max)(); } + void procede() + { + using parameters::choose_parameter; + using parameters::get_parameter; + using parameters::is_default_parameter; + + bool use_pds_e = choose_parameter(get_parameter(this->np, internal_np::use_poisson_disk_sampling_euclidean), false); +// bool use_pds_g = choose_parameter(get_parameter(this->np, internal_np::use_poisson_disk_sampling_geodesic), false); + double sampling_radius = choose_parameter(get_parameter(this->np, internal_np::sampling_radius), 1.); + std::size_t number_of_darts = choose_parameter(get_parameter(this->np, internal_np::number_of_darts),30.); + CGAL::Random random_seed = choose_parameter(get_parameter(this->np, internal_np::random_seed),CGAL::get_default_random()); + + + if (use_pds_e /* || use_pds_g */) + { + std::vector points = /* use_pds_e ? */ + internal::poisson_disk_sampling(tm, sampling_radius,number_of_darts,random_seed) + /* : internal::poisson_disk_sampling(tm, sampling_radius,number_of_darts,random_seed) */; + std::copy(points.begin(), points.end(), this->out); + } + else + { + static_cast(this)->procede(); + } + } + + std::pair get_range() { return std::make_pair(faces(tm).begin(), faces(tm).end()); @@ -599,9 +627,9 @@ struct Triangle_structure_sampler_for_triangle_soup GeomTraits, NamedParameters, typename TriangleRange::const_iterator, - Random_points_in_triangle_soup, + Random_points_in_triangle_soup_3, Creator, Triangle_structure_sampler_for_triangle_soup, + Random_points_in_triangle_soup_3, Creator, Self> Base; typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; - typedef Random_points_in_triangle_soup Randomizer; + typedef Random_points_in_triangle_soup_3 Randomizer; typedef typename TriangleRange::const_iterator TriangleIterator; double min_sq_edge_length; @@ -889,6 +917,36 @@ struct Triangle_structure_sampler_for_triangle_soup * \cgalParamType{unsigned int} * \cgalParamDefault{`0`} * \cgalParamNEnd + * + * \cgalParamNBegin{use_poisson_disk_sampling_euclidean} + * \cgalParamDescription{if `true` is passed, the Euclidean distance is used to compute the distance between sampled points.} + * \cgalParamType{Boolean} + * \cgalParamDefault{`false`} + * \cgalParamNEnd + * + * \cgalParamNBegin{use_poisson_disk_sampling_geodesic} + * \cgalParamDescription{if `true` is passed, the approximate geodesic distance is used to compute the distance between + * sampled points.} + * \cgalParamType{Boolean} + * \cgalParamDefault{`false`} + * \cgalParamExtra{The geodesic distance is approximated using the 'locally_shortest_path' + * function.} + * \cgalParamNEnd + * + * \cgalParamNBegin{sampling_radius} + * \cgalParamDescription{a value used by Poisson disk sampling to specify the minimum allowable distance between + * points in the sample.} + * \cgalParamType{double} + * \cgalParamDefault{`1`} + * \cgalParamNEnd + * + * \cgalParamNBegin{number_of_darts} + * \cgalParamDescription{a value used by Poisson disk sampling to specify the number of attempts to find a point in the annulus + * around a sample point that is sufficiently far from all other points in the sample.} + * \cgalParamType{std::size_t} + * \cgalParamDefault{`30`} + * \cgalParamNEnd + * * \cgalNamedParamsEnd * * @see `CGAL::Polygon_mesh_processing::sample_triangle_soup()` @@ -969,7 +1027,7 @@ sample_triangle_mesh(const TriangleMesh& tm, * of the smallest non-null edge of the soup or the value passed to the named parameter * `grid_spacing`.} * \cgalParamNEnd - * \cgalParamNBegin{use_monte_carlo_sampling} + * \cgalParamNBegin{use_monte_carlo_sampling} * \cgalParamDescription{if `true` is passed, points are generated randomly in each triangle.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/poisson_disk_sampling.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/poisson_disk_sampling.h new file mode 100644 index 000000000000..49ecfb3844a1 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/poisson_disk_sampling.h @@ -0,0 +1,250 @@ +// Copyright (c) 2025 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Sebastien Loriot +// Bradley McCoy + +#ifndef CGAL_POLYGON_MESH_PROCESSING_INTERNAL_POISSON_DISK_SAMPLING_H +#define CGAL_POLYGON_MESH_PROCESSING_INTERNAL_POISSON_DISK_SAMPLING_H + + +#include +#include +#include +#include +#include +#include +#ifdef CGAL_USE_BSURF +#include +#endif + +#include +#include + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace internal { + +enum Distance_version { EUCLIDEAN_DISTANCE, GEODESIC_DISTANCE }; + +template +double euclideanDistancePoints(const typename GeomTraits::Point_3& source, + const typename GeomTraits::Point_3& target) +{ + return sqrt(CGAL::squared_distance(source,target)); +} + +#ifdef CGAL_USE_BSURF +template +double geodesiceApproximation(const Face_location& source, + const Face_location& target, + const TriangleMesh& mesh, + const Dual_geodesic_solver& solver) +{ + std::vector> edge_locations; + CGAL::Polygon_mesh_processing::locally_shortest_path(source, target, mesh, edge_locations, solver); + + return path_length(edge_locations,source,target,mesh); +} +#endif + +//function to switch between geodesic and Euclidean distance +template +double distancePoints(const TriangleMesh& /* mesh */, + const typename GeomTraits::Point_3& source, + const typename GeomTraits::Point_3& target, + const Face_location& /* start */, + const Face_location& /* end */ +#ifdef CGAL_USE_BSURF + , const Dual_geodesic_solver& solver +#endif + ) +{ +#ifdef CGAL_USE_BSURF + if constexpr (V==GEODESIC_DISTANCE) + return geodesiceApproximation(start, end, mesh, solver); +#endif + if constexpr (V==EUCLIDEAN_DISTANCE) + return euclideanDistancePoints(source, target); + return 0; +} + + +template +std::vector::face_descriptor> +faces_in_sub_mesh(const typename GeomTraits::Point_3& c, + typename boost::graph_traits::face_descriptor fc, + const TriangleMesh& mesh, + double minDistance) +{ +// using Point = typename GeomTraits::Point_3; + using Graph_traits = boost::graph_traits; + using face_descriptor = typename Graph_traits::face_descriptor; + using halfedge_descriptor = typename Graph_traits::halfedge_descriptor; + + //Begin flooding + face_descriptor fd = fc; + + std::vector selected(num_faces(mesh), false); + std::vector selection; + selected[fd] = true; + selection.push_back(fd); + + auto do_queue_edge = [&](halfedge_descriptor h) + { + halfedge_descriptor hopp=opposite(h, mesh); + if (is_border(hopp, mesh) || selected[face(hopp, mesh)]) return false; + + typename GeomTraits::Segment_3 edge(mesh.point(source(h,mesh)), mesh.point(target(h,mesh))); + +// return (distancePoints(mesh, mesh.point(source(h,mesh)), c, tree)< 3*minDistance || +// distancePoints(mesh, mesh.point(target(h,mesh)), c, tree)< 3*minDistance); + + + return typename GeomTraits::Compare_squared_distance_3()(c, edge, sqrt(3*minDistance))!=CGAL::LARGER; + }; + + + std::vector queue; + for (halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(fd, mesh), mesh)) + if (do_queue_edge(h)) + queue.push_back(opposite(h, mesh)); + + while (!queue.empty()) + { + halfedge_descriptor h = queue.back(); + face_descriptor f = face(h, mesh); + queue.pop_back(); + if (!selected[f]) + { + selected[f]=true; + selection.push_back(f); + } + + h=next(h, mesh); + if (do_queue_edge(h)) queue.push_back(opposite(h, mesh)); + h=next(h, mesh); + if (do_queue_edge(h)) queue.push_back(opposite(h, mesh)); + } + + return selection; +} + +template +bool +is_far_enough(const typename GeomTraits::Point_3 c, + const Face_location c_location, + const TriangleMesh& mesh, + double minDistance, + std::vector::face_descriptor> selection, + const PointsPerFaceMap& face_points +#ifdef CGAL_USE_BSURF + , const Dual_geodesic_solver& solver +#endif + ) +{ + for (typename boost::graph_traits::face_descriptor f : selection) + { + for(const typename GeomTraits::Point_3& p : face_points[f]) + { + Face_location p_location = locate_in_face(p, f, mesh); + //Todo: Ask why is distancePoints for Euclidean so much slower then just + //calling euclideanDistancePoints? + if (distancePoints(mesh, c, p, c_location, p_location +#ifdef CGAL_USE_BSURF + , solver +#endif + ) < minDistance) + //if (euclideanDistancePoints(c, p) < minDistance) + //if (geodesiceApproximation(c_location, p_location, mesh) < minDistance) + return false; + } + } + return true; +} + +template +std::vector +poisson_disk_sampling(const TriangleMesh& mesh, + double minDistance, + std::size_t kMaxTries, + CGAL::Random& r) +{ + using Point = typename GeomTraits::Point_3; + using Graph_traits = boost::graph_traits; + using face_descriptor = typename Graph_traits::face_descriptor; + using FT = typename GeomTraits::FT; + using Face_location = Face_location; + +#ifdef CGAL_USE_BSURF + Dual_geodesic_solver solver; + if constexpr (V==GEODESIC_DISTANCE) + init_geodesic_dual_solver(solver, mesh); +#endif + + std::vector points; + std::queue> activePoints; + + Random_points_in_triangle_mesh_3 g(mesh, r); + Point c = *g; + face_descriptor fc = g.last_item_picked(); + + std::vector> face_points(num_faces(mesh)); + + face_points[fc].push_back(c); + activePoints.push(std::make_pair(c, fc)); + points.push_back(c); + + Point currentPoint; + face_descriptor currentFace; + while (!activePoints.empty()) + { + std::tie(currentPoint, currentFace) = activePoints.front(); + activePoints.pop(); + + std::vector selection = faces_in_sub_mesh(currentPoint, currentFace, mesh, minDistance); + Face_location current_location = locate_in_face(currentPoint, currentFace, mesh); + if (current_location.second[0]<0) current_location.second[0]=0; + if (current_location.second[1]<0) current_location.second[1]=0; + if (current_location.second[2]<0) current_location.second[2]=0; + std::size_t k = 0; + while (k < kMaxTries) + { + double angle = 2 * CGAL_PI * r.get_double(); + double distance = minDistance + minDistance * r.get_double(); + typename GeomTraits::Vector_2 dir(cos(angle),sin(angle)); + #warning find alternative + CGAL_USE(distance); + std::vector path /* = straightest_geodesic(current_location, dir, distance, mesh) */ ; + Face_location newLocation = path.back(); + Point newPoint = construct_point(path.back(), mesh); + if(is_far_enough(newPoint, newLocation, mesh, minDistance, selection, make_random_access_property_map(face_points) +#ifdef CGAL_USE_BSURF + , solver +#endif + )) + { + face_points[path.back().first].push_back(newPoint); + activePoints.push(std::make_pair(newPoint,path.back().first)); + points.push_back(newPoint); + }else + { + ++k; + } + } + } + + return points; +} + +} } } // CGAL::Polygon_mesh_processing::internal + + +#endif //CGAL_POLYGON_MESH_PROCESSING_INTERNAL_POISSON_DISK_SAMPLING_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 919ac774e0d6..781163695e20 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -74,6 +74,7 @@ create_single_source_cgal_program("test_corefinement_nm_bo.cpp") create_single_source_cgal_program("test_corefinement_cavities.cpp") create_single_source_cgal_program("issue_8730.cpp") create_single_source_cgal_program("issue_7164.cpp") +create_single_source_cgal_program("test_poisson_disk_sampling.cpp") # create_single_source_cgal_program("test_pmp_repair_self_intersections.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_poisson_disk_sampling.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_poisson_disk_sampling.cpp new file mode 100644 index 000000000000..56afc30d9da0 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_poisson_disk_sampling.cpp @@ -0,0 +1,179 @@ +#include +#include +#include +#ifdef CGAL_USE_BSURF +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::Point_3 Point; + +typedef CGAL::Surface_mesh Mesh; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +typedef CGAL::AABB_face_graph_triangle_primitive AABB_face_graph_primitive; +typedef CGAL::AABB_traits_3 AABB_face_graph_traits; + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef PMP::Face_location Face_location; +#ifdef CGAL_USE_BSURF +typedef PMP::Edge_location Edge_location; +#endif + +#ifdef CGAL_USE_BSURF +double geodesiceApproximation(const Face_location source, const Face_location target, Mesh mesh) +{ + PMP::Dual_geodesic_solver solver; + PMP::init_geodesic_dual_solver(solver, mesh); + std::vector edge_locations; + + CGAL::Polygon_mesh_processing::locally_shortest_path(source, target, mesh, edge_locations, solver); + + return PMP::path_length(edge_locations,source,target,mesh); +} +#endif + +int main(int argc, char* argv[]) +{ + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/eight.off"); + + Mesh mesh; + if(!PMP::IO::read_polygon_mesh(filename, mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + const double sampling_radius = (argc > 2) ? std::atof(argv[2]) : 0.07; + + const std::size_t number_of_darts = (argc > 3) ? std::atof(argv[3]) : 30; + + CGAL::Random random_seed = (argc > 4) ? std::atof(argv[4]) : CGAL::get_default_random(); + + std::vector euclidean_points; + PMP::sample_triangle_mesh(mesh, + std::back_inserter(euclidean_points), + CGAL::parameters::use_poisson_disk_sampling_euclidean(true). + sampling_radius(sampling_radius).number_of_darts(number_of_darts).random_seed(random_seed)); + + std::ofstream out1("euclidean-sampling.xyz"); + out1 << std::setprecision(17); + std::copy(euclidean_points.begin(), euclidean_points.end(), std::ostream_iterator(out1, "\n")); + + std::cout << euclidean_points.size() << std::endl; + + //test if euclidean_points are far enough apart. + + bool result=true; + double d; + double c; + + for(std::size_t i=0; i 2*sampling_radius){ + result = false; + } + } + std::cout << "The Euclidean test result is: " << result << std::endl; + + Mesh square; + Mesh::Halfedge_index h = CGAL::make_quad(Point(0,0,0), Point(1,0,0), + Point(1,1,0),Point(0,1,0), + square); + CGAL::Euler::split_face(h, next(next(h,square), square), square); + PMP::isotropic_remeshing(faces(square), 0.05, square); + std::cout << "Square now has " << faces(square).size() << " faces" << std::endl; + PMP::stitch_borders(square); + CGAL::IO::write_polygon_mesh("square.off", square, CGAL::parameters::stream_precision(17)); + std::vector square_points; + + PMP::sample_triangle_mesh(square, + std::back_inserter(square_points), + CGAL::parameters::use_poisson_disk_sampling_euclidean(true). + sampling_radius(sampling_radius).number_of_darts(number_of_darts).random_seed(random_seed)); + + std::cout << "There are: " << square_points.size() << " points in the square." << std::endl; + + std::ofstream out("square.xyz"); + out << std::setprecision(17); + std::copy(square_points.begin(), square_points.end(), std::ostream_iterator(out, "\n")); + + /* + // + +#ifdef TEST_PURPOSE + CGAL::AABB_tree tree; + PMP::build_AABB_tree(mesh, tree); + Face_location query_location_target = PMP::locate_with_AABB_tree(euclidean_points[0], tree, mesh); + Face_location query_location_source = PMP::locate_with_AABB_tree(euclidean_points[1], tree, mesh); + + std::cout << "Euclidean: " << sqrt(CGAL::squared_distance(euclidean_points[0],euclidean_points[1])) << std::endl; + std::cout << "Geodesic: " << geodesiceApproximation(query_location_source, query_location_target, mesh) << std::endl; + + //Test geodesic + //why can't I use geodesic????? + + result=true; + std::vector geodesic_points; + PMP::sample_triangle_mesh(mesh, + std::back_inserter(geodesic_points), + CGAL::parameters::use_poisson_disk_sampling_geodesic(true). + sampling_radius(sampling_radius).number_of_darts(number_of_darts).random_seed(random_seed)); + std::ofstream out("geodesic-sampling.xyz"); + out << std::setprecision(17); + std::copy(geodesic_points.begin(), geodesic_points.end(), std::ostream_iterator(out, "\n")); + std::cout << geodesic_points.size() << std::endl; + + for(std::size_t i=0; i 2*sampling_radius){ + result = false; + } +} + std::cout << "The geodesic test result is: " << result << std::endl; +#endif + + + */ + return 0; +} diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index ffe386d086cb..799e36c09c2e 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -88,6 +88,10 @@ CGAL_add_named_parameter(face_patch_t, face_patch, face_patch_map) CGAL_add_named_parameter(random_uniform_sampling_t, random_uniform_sampling, use_random_uniform_sampling) CGAL_add_named_parameter(grid_sampling_t, grid_sampling, use_grid_sampling) CGAL_add_named_parameter(monte_carlo_sampling_t, monte_carlo_sampling, use_monte_carlo_sampling) +CGAL_add_named_parameter(use_poisson_disk_sampling_euclidean_t, use_poisson_disk_sampling_euclidean, use_poisson_disk_sampling_euclidean) +CGAL_add_named_parameter(use_poisson_disk_sampling_geodesic_t, use_poisson_disk_sampling_geodesic, use_poisson_disk_sampling_geodesic) +CGAL_add_named_parameter(number_of_darts_t, number_of_darts, number_of_darts) +CGAL_add_named_parameter(sampling_radius_t, sampling_radius, sampling_radius) CGAL_add_named_parameter(do_sample_edges_t, do_sample_edges, do_sample_edges) CGAL_add_named_parameter(do_sample_vertices_t, do_sample_vertices, do_sample_vertices) CGAL_add_named_parameter(do_sample_faces_t, do_sample_faces, do_sample_faces)