Snap of polygons

This commit is contained in:
Léo Valque 2025-10-16 15:14:42 +02:00
parent 64922a63bb
commit c380ad2fb6
2 changed files with 79 additions and 124 deletions

View File

@ -476,12 +476,12 @@ typename OutputContainer::iterator compute_snapped_subcurves_2(InputIterator
using I2E = typename Traits::Converter_in;
using E2O = typename Traits::Converter_out;
using VectorIterator = typename std::vector<Segment_2>::iterator;
I2E converter_to_exact=traits.converter_to_exact_object();
E2O converter_from_exact=traits.converter_from_exact_object();
I2E to_exact=traits.converter_to_exact_object();
E2O from_exact=traits.converter_from_exact_object();
std::vector<Segment_2> convert_input;
for(InputIterator it=input_begin; it!=input_end; ++it)
convert_input.push_back(Segment_2(converter_to_exact(*it)));
convert_input.push_back(Segment_2(to_exact(*it)));
std::vector<Segment_2> segs;
#ifdef DOUBLE_2D_SNAP_VERBOSE
std::cout << "Solved intersections" << std::endl;
@ -537,7 +537,7 @@ typename OutputContainer::iterator compute_snapped_subcurves_2(InputIterator
set_out_segs.emplace((std::min)(poly[i-1],poly[i]),(std::max)(poly[i-1],poly[i]));
}
for(auto &pair: set_out_segs){
output.emplace_back(converter_from_exact(pts[pair.first]), converter_from_exact(pts[pair.second]));
output.emplace_back(from_exact(pts[pair.first]), from_exact(pts[pair.second]));
assert(pts[pair.first]!=pts[pair.second]);
}
@ -547,71 +547,67 @@ typename OutputContainer::iterator compute_snapped_subcurves_2(InputIterator
/**
* ingroup
*
* Given a range of segments, compute rounded subsegments that are pairwise disjoint in their interior, as induced by the input curves.
* Given a Polygon_2, compute rounded segments that are pairwise disjoint in their interior, as induced by the input polygon.
* The output is guarantee to be a Polygon but may present pinched section.
*
* @tparam Concurrency_tag That template parameter enables to choose whether the algorithm is to be run in
* parallel, if CGAL::Parallel_tag is specified and CGAL has been linked with the Intel TBB library, or sequentially, if CGAL::Sequential_tag - the default value - is specified.
* @tparam InputIterator iterator of a segment range
* @tparam OutputContainer inserter of a segment range
* @tparam The exact kernel needed for computation (Epeck by default)
*
* @warning The convex property is not necessarly preserved
*/
template <class Concurrency_tag=Sequential_tag, class InputIterator , class OutputContainer, class Traits=Float_snap_rounding_traits_2<typename Kernel_traits<std::remove_cv_t<typename std::iterator_traits<InputIterator>::value_type>>::Kernel> >
typename OutputContainer::iterator snap_polygons_2(InputIterator input_begin,
InputIterator input_end,
OutputContainer& output,
const Traits& traits=Traits())
template <class Concurrency_tag=Sequential_tag, class Polygon_2, class Traits=Float_snap_rounding_traits_2<typename Kernel_traits<typename Polygon_2::Point_2>::Kernel> >
void snap_polygons_2(const Polygon_2 &P,
Polygon_2 &out,
const Traits& traits=Traits(),
bool check_duplicates = false)
{
using Point_2 = typename Traits::Point_2;
using Segment_2 = typename Traits::Segment_2;
using I2E = typename Traits::Converter_in;
using E2O = typename Traits::Converter_out;
using VectorIterator = typename std::vector<Segment_2>::iterator;
I2E converter_to_exact=traits.converter_to_exact_object();
E2O converter_from_exact=traits.converter_from_exact_object();
std::vector<Segment_2> convert_input;
for(InputIterator it=input_begin; it!=input_end; ++it)
convert_input.push_back(Segment_2(converter_to_exact(*it)));
std::vector<Segment_2> segs;
#ifdef DOUBLE_2D_SNAP_VERBOSE
std::cout << "Solved intersections" << std::endl;
#endif
compute_subcurves(convert_input.begin(), convert_input.end(), std::back_inserter(segs));
I2E to_exact=traits.converter_to_exact_object();
E2O from_exact=traits.converter_from_exact_object();
#ifdef DOUBLE_2D_SNAP_VERBOSE
std::cout << "Change format to range of points and indexes" << std::endl;
#endif
std::set<Point_2> unique_point_set;
std::map<Point_2, int> point_to_index;
std::vector<Point_2> pts;
std::vector< std::vector< std::size_t> > polylines;
// Transform range of the segments in the range of points and polyline of indexes
for(VectorIterator it=segs.begin(); it!=segs.end(); ++it)
{
const Point_2& p1 = it->source();
const Point_2& p2 = it->target();
if(check_duplicates){
std::set<Point_2> unique_point_set;
std::map<Point_2, int> point_to_index;
if (unique_point_set.find(p1) == unique_point_set.end()) {
unique_point_set.insert(p1);
pts.push_back(p1);
point_to_index[p1] = pts.size() - 1;
// Transform the polygon in a range of points and polylines of indexes
for(const typename Polygon_2::Point_2 &p_: P.vertices())
{
Point_2 p=to_exact(p_);
if (unique_point_set.find(p) == unique_point_set.end()) {
unique_point_set.insert(p);
pts.push_back(p);
point_to_index[p] = pts.size() - 1;
}
}
if (unique_point_set.find(p2) == unique_point_set.end()) {
unique_point_set.insert(p2);
pts.push_back(p2);
point_to_index[p2] = pts.size() - 1;
for(const typename Polygon_2::Segment_2 &s: P.edges()){
Point_2 p1=to_exact(s.source());
Point_2 p2=to_exact(s.target());
std::size_t index1 = point_to_index[p1];
std::size_t index2 = point_to_index[p2];
polylines.push_back({index1, index2});
}
} else {
for(const typename Polygon_2::Point_2 &p: P.vertices())
pts.push_back(to_exact(p));
for(size_t i=0; i<P.size()-1; ++i)
polylines.push_back({i, i+1});
polylines.push_back({P.size()-1,0});
}
for(VectorIterator it=segs.begin(); it!=segs.end(); ++it)
{
std::size_t index1 = point_to_index[it->source()];
std::size_t index2 = point_to_index[it->target()];
polylines.push_back({index1, index2});
}
// Main algorithm
double_snap_rounding_2_disjoint<Concurrency_tag>(pts, polylines, traits);
@ -620,20 +616,14 @@ typename OutputContainer::iterator snap_polygons_2(InputIterator input_begin,
#endif
// Output a range of segments while removing duplicate ones
std::set< std::pair<std::size_t,std::size_t> > set_out_segs;
output.clear();
for(auto &poly: polylines){
out.clear();
for(auto &poly: polylines)
for(std::size_t i=1; i<poly.size(); ++i)
set_out_segs.emplace((std::min)(poly[i-1],poly[i]),(std::max)(poly[i-1],poly[i]));
}
for(auto &pair: set_out_segs){
output.emplace_back(converter_from_exact(pts[pair.first]), converter_from_exact(pts[pair.second]));
assert(pts[pair.first]!=pts[pair.second]);
}
return output.begin();
out.push_back(from_exact(pts[poly[i]]));
}
} //namespace CGAL
#endif

View File

@ -1,6 +1,4 @@
#define DOUBLE_2D_SNAP_VERBOSE
#define BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2
#define COMPARE_WITH_INTEGER_SNAP_ROUNDING_2
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
@ -129,69 +127,47 @@ void test_almost_indentical_segments(CGAL::Random &r, size_t nb_segments, Vector
}
void test_iterative_square_intersection(CGAL::Random &r, size_t nb_iterations){
auto add_random_rotated_square=[&](std::vector<Segment_2> &segs){
auto random_rotated_square=[&](){
double theta=r.get_double(0, CGAL_PI/2);
double cos_t = std::cos(theta);
double sin_t = std::sin(theta);
Point_2 a( cos_t, sin_t);
Point_2 b( sin_t,-cos_t);
Point_2 c(-cos_t,-sin_t);
Point_2 d(-sin_t, cos_t);
segs.emplace_back(a,b);
segs.emplace_back(b,c);
segs.emplace_back(c,d);
segs.emplace_back(d,a);
#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2
std::cout << "Angle: " << theta << std::endl;
#endif
FT cos_t(std::cos(theta));
FT sin_t(std::sin(theta));
Polygon_2 P;
P.push_back(Point_2( cos_t, sin_t));
P.push_back(Point_2(-sin_t, cos_t));
P.push_back(Point_2(-cos_t,-sin_t));
P.push_back(Point_2( sin_t,-cos_t));
return P;
};
std::vector<Segment_2> segs;
std::vector<Segment_2> out;
std::vector<Curve_2> arr_segs;
Polygon_2 scene=random_rotated_square();
Polygon_2 snap_scene;
Pwh_vec_2 out_intersection;
CGAL::Real_timer t;
for(size_t i=0; i<nb_iterations; ++i){
std::cout << "Iterations " << i << std::endl;
out.clear();
arr_segs.clear();
for(int j=0; j<5; ++j)
add_random_rotated_square(segs);
test(segs);
CGAL::compute_snapped_subcurves_2(segs.begin(), segs.end(), out);
assert(!CGAL::do_curves_intersect(out.begin(), out.end()));
segs.clear();
segs.insert(segs.begin(), out.begin(), out.end());
out_intersection.clear();
CGAL::intersection(random_rotated_square(), scene, std::back_inserter(out_intersection));
assert(out_intersection.size()==1 && out_intersection[0].number_of_holes()==0);
#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2
CGAL::Real_timer t;
t.start();
#endif
snap_polygons_2(out_intersection[0].outer_boundary(), snap_scene);
// snap_scene=out_intersection[0].outer_boundary();
#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2
t.stop();
std::cout << "Iteration " << i << std::endl;
std::cout << "Polygon size: " << out_intersection[0].outer_boundary().size()
<< " , Snapped polygon size: " << snap_scene.size() << std::endl;
std::cout << "is convex: " << snap_scene.is_convex() << std::endl;
std::cout << "Running time: " << t.time() << std::endl;
#endif
scene=snap_scene;
}
}
// void test_iterative_square_intersection(CGAL::Random &r, size_t nb_iterations){
// auto random_rotated_square=[&](){
// double theta=r.get_double(0, CGAL_PI/2);
// double cos_t = std::cos(theta);
// double sin_t = std::sin(theta);
// Polygon_2 P;
// P.push_back( cos_t, sin_t);
// P.push_back( sin_t,-cos_t);
// P.push_back(-cos_t,-sin_t);
// P.push_back(-sin_t, cos_t);
// return P;
// };
// Pwh_vec_2 scene, out_intersection;
// CGAL::intersection(random_rotated_square, random_rotated_square, scene);
// for(size_t i=0; i<nb_iterations; ++i){
// CGAL::intersection(random_rotated_square, random_rotated_square, out_intersection);
// }
// }
void test_multi_almost_indentical_segments(CGAL::Random &r, size_t nb_segments){
for(double x1=-1; x1<=1; ++x1)
for(double y1=-1; y1<=1; ++y1)
@ -222,26 +198,15 @@ void fix_test(){
test(segs);
}
void test_box_intersection(){
std::vector< Segment_2 > segs;
FT e(std::pow(2, -60));
segs.emplace_back(Point_2(0, 0), Point_2(1, 1));
segs.emplace_back(Point_2(0.5+e, 0.5), Point_2(1, -1));
segs.emplace_back(Point_2(0.5-e, 0.5), Point_2(-1, 3));
test(segs);
}
int main(int argc,char *argv[])
{
CGAL::Random rp;
CGAL::Random r(argc==1?rp.get_seed():std::stoi(argv[1]));
std::cout << "random seed = " << r.get_seed() << std::endl;
std::cout << std::setprecision(17);
test_box_intersection();
fix_test();
// test_fully_random(r,1000);
// test_multi_almost_indentical_segments(r,100);
// test_iterative_square_intersection(r,500);
test_iterative_square_intersection(r,2000);
return(0);
}