diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_segment_traits_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_segment_traits_2.h index 13c7e27ba19..676b3c2bc1c 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_segment_traits_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_segment_traits_2.h @@ -712,6 +712,11 @@ public: // Check if we have a single intersection point. const Point_2* ip = std::get_if(&*res); if (ip != nullptr) { + if(!(cv1.is_vertical() ? + m_traits.is_in_y_range_2_object()(cv1, *ip) : + m_traits.is_in_x_range_2_object()(cv1, *ip))){ + std::cout << cv1 << std::endl; + } CGAL_assertion(cv1.is_vertical() ? m_traits.is_in_y_range_2_object()(cv1, *ip) : m_traits.is_in_x_range_2_object()(cv1, *ip)); 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 73c54858bf2..d99ac998b0e 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -406,6 +406,9 @@ CGAL_add_named_parameter(do_not_modify_geometry_t, do_not_modify_geometry, do_no CGAL_add_named_parameter_with_compatibility_ref_only(angles_param_t, angles_param, angles) CGAL_add_named_parameter(maximum_height_t, maximum_height, maximum_height) +//List of named parameters used in Snap_rounding_2 +CGAL_add_named_parameter(compute_intersection_t, compute_intersection, compute_intersection) + // List of named parameters used in the package 'Constrained_triangulation_3' CGAL_add_named_parameter(plc_face_id_t, plc_face_id, plc_face_id) CGAL_add_named_parameter(with_plc_face_id_t, with_plc_face_id, with_plc_face_id) diff --git a/Snap_rounding_2/doc/Snap_rounding_2/Concepts/FloatSnapRoundingTraits_2.h b/Snap_rounding_2/doc/Snap_rounding_2/Concepts/FloatSnapRoundingTraits_2.h new file mode 100644 index 00000000000..e067d0e6d18 --- /dev/null +++ b/Snap_rounding_2/doc/Snap_rounding_2/Concepts/FloatSnapRoundingTraits_2.h @@ -0,0 +1,228 @@ + +/*! +\ingroup PkgSnapRounding2Concepts +\cgalConcept + +The concept `FloatSnapRoundingTraits_2` lists the set of requirements that must be fulfilled by +an instance of the `Traits` template-parameter of +the free functions of TODO. +The list includes the nested types of the geometric primitives used in this class and +some function object types for the required predicates on those primitives. + +\cgalRefines{AosTraits_2} + +\cgalHasModelsBegin +\cgalHasModels{CGAL::Float_snap_rounding_traits_2} +\cgalHasModelsEnd +*/ + +class FloatSnapRoundingTraits_2 { +public: + +/// \name Types +/// @{ + +/*! +The number type. This type must fulfill the requirements on +`FieldNumberType` +*/ +typedef unspecified_type FT; + +/*! +Models the concept `ArrTraits::Point_2`. +*/ +typedef unspecified_type Point_2; + +/*! +Models the concept `ArrTraits::XMonotoneCurve_2`. +*/ +typedef unspecified_type Segment_2; + +/// @} + +/// \name Functor Types +/// @{ + +/*! +Models the concept `Kernel::ConstructSource_2`. +*/ +typedef unspecified_type Construct_source_2; + +/*! +Models the concept `Kernel::ConstructTarget_2`. +*/ +typedef unspecified_type Construct_target_2; + +/*! +Models the concept `Kernel::ConstructSegment_2`. +*/ +typedef unspecified_type Construct_segment_2; + +/*! +Models the concept `Kernel::LessXY_2`. +*/ +typedef unspecified_type Less_xy_2; + +/*! +Models the concept `Kernel::LessY_2`. +*/ +typedef unspecified_type Less_y_2; + +/*! +Models the concept `Kernel::Equal_2`. +*/ +typedef unspecified_type Equal_2; + +/*! +Models the concept `FSRTraits_2::ConstructRoundPoint_2`. +*/ +typedef unspecified_type Construct_round_point_2; + +/*! +Models the concept `FSRTraits_2::SquaredRoundBound_2`. +*/ +typedef unspecified_type Squared_round_bound_2; + +/*! +Models the concept `FSRTraits_2::ConverterToExact`. +*/ +typedef unspecified_type Converter_to_exact; + +/*! +Models the concept `FSRTraits_2::ConverterFromExact`. +*/ +typedef unspecified_type Converter_from_exact; + +/// @} + +/// \name Accessing Functor Objects +/// @{ + +/*! + +*/ +Construct_source_2 construct_source_2_object(); + +/*! + +*/ +Construct_target_2 construct_target_2_object(); + +/*! + +*/ +Construct_segment_2 construct_segment_2_object(); + +/*! + +*/ +Less_xy_2 less_xy_2_object(); + +/*! + +*/ +Less_y_2 less_y_2_object(); + +/*! + +*/ +Construct_round_point_2 construct_round_point_2_object(); + +/*! + +*/ +Squared_round_bound_2 squared_round_bound_2_object(); + +/*! + +*/ +Converter_to_exact converter_to_exact_object(); + +/*! + +*/ +Converter_from_exact converter_from_exact_object(); + + +/// @} + +}; /* end FloatSnapRoundingTraits_2 */ + + +namespace FSRTraits_2{ + +/*! + \ingroup PkgSnapRounding2Concepts + \cgalConcept + \cgalHasModelsBegin + \cgalHasModelsBare{\link FloatSnapRoundingTraits_2::Construct_round_point_2 `Float_snap_rounding_traits_2::Construct_round_point_2` \endlink} + \cgalHasModelsEnd +*/ +class ConstructRoundPoint_2 +{ + public: + + /*! + Given a point, construct its rounded version + */ + Point_2 operator()(Point_2 p); +}; + +/*! + \ingroup PkgSnapRounding2Concepts + \cgalConcept + \cgalHasModelsBegin + \cgalHasModelsBare{\link FloatSnapRoundingTraits_2::Squared_round_bound_2 `Float_snap_rounding_traits_2::Squared_round_bound_2` \endlink} + \cgalHasModelsEnd +*/ +class SquaredRoundBound_2 +{ + public: + + /*! + Given a point, compute an upper bound of the squared distance between its exact coordinates and its rounded coordinates + */ + double operator()(Point_2 p); +}; + +/*! + \ingroup PkgSnapRounding2Concepts + \cgalConcept + \cgalHasModelsBegin + \cgalHasModelsBare{\link FloatSnapRoundingTraits_2::Converter_to_exact `Float_snap_rounding_traits_2::Convertex_to_exact` \endlink} + \cgalHasModelsEnd +*/ +class ConverterToExact +{ + public: + typedef unspecified_type InputPoint_2; + typedef unspecified_type InputSegment_2; + /*! + Convert input points (i.e. segments) into points (i.e. segments) of the type of the traits + */ + Point_2 operator()(InputPoint_2 p); + Segment_2 operator()(InputSegment_2 p); +}; + +/*! + \ingroup PkgSnapRounding2Concepts + \cgalConcept + \cgalHasModelsBegin + \cgalHasModelsBare{\link FloatSnapRoundingTraits_2::Converter_from_exact `Float_snap_rounding_traits_2::Convertex_to_exact` \endlink} + \cgalHasModelsEnd +*/ +class ConverterFromExact +{ + public: + typedef unspecified_type OutputPoint_2; + typedef unspecified_type OutputSegment_2; + /*! + Convert points (i.e. segments) of the type of the traits into points (i.e. segments) of the output + */ + OutputPoint_2 operator()(Point_2 p); + OutputSegment_2 operator()(Segment_2 p); +}; + + + +} /* end of namespace SRTraits_2 */ diff --git a/Snap_rounding_2/doc/Snap_rounding_2/Doxyfile.in b/Snap_rounding_2/doc/Snap_rounding_2/Doxyfile.in index 7c99d29c3b1..0e85668587a 100644 --- a/Snap_rounding_2/doc/Snap_rounding_2/Doxyfile.in +++ b/Snap_rounding_2/doc/Snap_rounding_2/Doxyfile.in @@ -1,3 +1,11 @@ @INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 2D Snap Rounding" + +# custom options for this package +HIDE_UNDOC_CLASSES = true +WARN_IF_UNDOCUMENTED = false + + +INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Float_snap_rounding_2.h \ + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Float_snap_rounding_traits_2.h diff --git a/Snap_rounding_2/examples/Snap_rounding_2/CMakeLists.txt b/Snap_rounding_2/examples/Snap_rounding_2/CMakeLists.txt index e09fa21f5d5..315389de4a2 100644 --- a/Snap_rounding_2/examples/Snap_rounding_2/CMakeLists.txt +++ b/Snap_rounding_2/examples/Snap_rounding_2/CMakeLists.txt @@ -14,3 +14,11 @@ file( foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() + +find_package(TBB QUIET) +include(CGAL_TBB_support) +if(TARGET CGAL::TBB_support) + target_link_libraries(float_snap_rounding PRIVATE CGAL::TBB_support) +else() + message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.") +endif() diff --git a/Snap_rounding_2/examples/Snap_rounding_2/float_snap_rounding.cpp b/Snap_rounding_2/examples/Snap_rounding_2/float_snap_rounding.cpp new file mode 100644 index 00000000000..9ac611ca936 --- /dev/null +++ b/Snap_rounding_2/examples/Snap_rounding_2/float_snap_rounding.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; +typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; +typedef Kernel::Segment_2 Segment_2; +typedef Kernel::Point_2 Point_2; +typedef Kernel::FT FT; +typedef std::vector Polyline_2; + +int main(int argc, char *argv[]) +{ + std::vector< Segment_2 > segs; + + if(argc>1){ + std::cout << "Read segments in " << argv[1] << std::endl; + std::ifstream in(argv[1]); + + int n; + in >> n; + segs.reserve(n); + for (int i=0; i> x1 >> y1 >> x2 >> y2; + if(Point_2(x1,y1)!=Point_2(x2,y2)) + segs.emplace_back(Point_2(x1, y1), Point_2(x2, y2)); + } + } else { + //Example with a non-trivial rounding + FT e(std::pow(2, -60)); + + segs.emplace_back(Point_2(1-e, 1), Point_2(-1-e, -1+2*e)); + segs.emplace_back(Point_2(e/2, e/2), Point_2(1, -1)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(2, 0)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(-2+e, -4)); + segs.emplace_back(Point_2(-2, 2), Point_2(2, 2)); + segs.emplace_back(Point_2(7, 7), Point_2(7+e, 7+e)); + segs.emplace_back(Point_2(5, 7-e), Point_2(9, 7-e)); + } + + std::cout << "Computes the intersections and snaps the segments" << std::endl; + std::vector< Segment_2> out; + CGAL::compute_snapped_subcurves_2(segs.begin(), segs.end(), std::back_inserter(out)); + std::cout << "Does the output intersect: " << CGAL::do_curves_intersect(out.begin(), out.end()) << std::endl; + std::cout << "Size of the output: " << out.size() << std::endl; + + std::string out_path=(argc>2)?argv[2]:"out.segs"; + std::cout << "Write the outputs in " << out_path << std::endl; + std::ofstream outf(out_path); + CGAL::Random r; + + outf << std::setprecision(17); + outf << out.size() << std::endl; + for (auto &seg: out) + { + double x1 = CGAL::to_double(seg.source().x()); + double y1 = CGAL::to_double(seg.source().y()); + double x2 = CGAL::to_double(seg.target().x()); + double y2 = CGAL::to_double(seg.target().y()); + outf << x1 << " " << y1 << " " << x2 << " " << y2 << std::endl; + } + + return 0; +} diff --git a/Snap_rounding_2/include/CGAL/Float_snap_rounding_2.h b/Snap_rounding_2/include/CGAL/Float_snap_rounding_2.h new file mode 100644 index 00000000000..66d1630fdcc --- /dev/null +++ b/Snap_rounding_2/include/CGAL/Float_snap_rounding_2.h @@ -0,0 +1,830 @@ +// Copyright (c) 2025 Geometry Factory. +// 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) : Léo Valque + +#ifndef CGAL_FLOAT_SNAP_ROUNDING_2_H +#define CGAL_FLOAT_SNAP_ROUNDING_2_H + +#ifdef DOUBLE_2D_SNAP_VERBOSE +#include +#endif + +#include + +#include + +#include +#include +#include + +#include + +#include + +namespace CGAL { + +namespace internal{ + /* + Scan the vertices from left to right while maintening the y order of the segments. + Subdivide the segments if there are too close to a vertex + */ +template +void snap_rounding_scan(PointsRange &pts, PolylineRange &polylines, const Traits &traits){ + using Point_2 = typename Traits::Point_2; + using Segment_2 = typename Traits::Segment_2; + + using Polyline = std::remove_cv_t::value_type>; + + using Less_xy_2 = typename Traits::Less_xy_2; + using Less_y_2 = typename Traits::Less_y_2; + using Construct_segment_2 = typename Traits::Construct_segment_2; + using Construct_point_at_x_on_segment_2 = typename Traits::Construct_point_at_x_on_segment_2; + + using Compare_squared_distance_2 = typename Traits::Compare_squared_distance_2; + using Squared_round_bound_2 = typename Traits::Squared_round_bound_2; + + Less_xy_2 less_xy_2 = traits.less_xy_2_object(); + Less_y_2 less_y_2 = traits.less_y_2_object(); + Construct_segment_2 segment_2 = traits.construct_segment_2_object(); + Construct_point_at_x_on_segment_2 point_at_x = traits.construct_point_at_x_on_segment_2_object(); + + Compare_squared_distance_2 csq_dist_2 = traits.compare_squared_distance_2_object(); + Squared_round_bound_2 round_bound = traits.squared_round_bound_2_object(); + + // Precompute round bounds for points + std::vector round_bound_pts; + round_bound_pts.reserve(pts.size()); + for(std::size_t i=0; i b.type; + // Only to have a complete order + return a.li < b.li; + }; + + auto pi_below_li=[&](size_t li, std::pair pair_pi_li){ + std::size_t ind_pi_support = pair_pi_li.second; + std::size_t pi = pair_pi_li.first; + if(ind_pi_support==li) return false; + + const Polyline &pl=polylines[li]; + + Orientation ori=COLLINEAR; + if(pl.front()!=pi && pl.back()!=pi) + ori=orientation(pts[pl.front()], pts[pl.back()], pts[pi]); + // When collinear, pick an other vertex of the line of pi to decide + if(ori==COLLINEAR){ + const Polyline &pi_support = polylines[ind_pi_support]; + std::size_t other_point = (pi_support.front()!=pi) ? pi_support.front() : pi_support.back(); + ori=orientation(pts[pl.front()], pts[pl.back()], pts[other_point]); + assert(ori!=COLLINEAR); + } + return ori==RIGHT_TURN; + }; + +#ifdef DOUBLE_2D_SNAP_FULL_VERBOSE + std::cout << "Input points" << std::endl; + std::size_t i=0; + for(const Point_2 &p: pts) + std::cout << i++ << ": " << p << std::endl; + std::cout << std::endl; + std::cout << "Input polylines" << std::endl; + i=0; + for(const Polyline &pl: polylines){ + std::cout << i++ << ":"; + for(std::size_t pi: pl) + std::cout << " " << pi; + std::cout << std::endl; + } + std::cout << std::endl; +#endif + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Create the Event Queue" << std::endl; +#endif + + // Input polylines are supposed going from left to right + std::vector event_queue; + event_queue.reserve(2*polylines.size()); + for(size_t li=0; li y_order; + y_order.push_back(event_queue[0].li); + for(size_t i=1; i::iterator pos_it = y_order.begin(); + if(!y_order.empty()) + pos_it = std::lower_bound(y_order.begin(), y_order.end(), std::pair(event.pi, event.li), pi_below_li); + if(event.type==EVENT_TYPE::REMOVE){ + assert(*pos_it==event.li); + pos_it = y_order.erase(pos_it); + } + + if(!y_order.empty()){ + auto close_event=[&](std::size_t pi, std::size_t li){ + if((pi==polylines[li].front()) || (pi==polylines[li].back())) + return true; + + const Point_2 &p = pts[pi]; + Polyline &pl=polylines[li]; + Segment_2 seg=segment_2(pts[pl.front()], pts[pl.back()]); + + // (A+B)^2 <= 4*max(A^2,B^2) + double bound=round_bound_pts[pi]; + bound = (std::max)(bound, round_bound_pts[pl.front()]); + bound = (std::max)(bound, round_bound_pts[pl.back()]); + bound*=4; + + if(possibly(csq_dist_2(p, seg, bound)!=CGAL::LARGER)) + { + if constexpr (std::is_same_v) + { + internal::Evaluate evaluate; + // We refine the pts to reduce the rounding shift and check again + evaluate(pts[pi]); + evaluate(pts[pl[pl.size()-2]]); + // The two following call of exact act on seg variables since they appear in its DAG + evaluate(pts[pl.back()]); + evaluate(pts[pl.front()]); + // Update the bounds + round_bound_pts[pi]=round_bound(pts[pi]); + round_bound_pts[pl[pl.size()-2]]=round_bound(pts[pl[pl.size()-2]]); + round_bound_pts[pl.back()]=round_bound(pts[pl.back()]); + round_bound_pts[pl.front()]=round_bound(pts[pl.front()]); + bound=round_bound_pts[pi]; + bound = (std::max)(bound, round_bound_pts[pl[pl.size()-2]]); + bound = (std::max)(bound, round_bound_pts[pl.back()]); + bound*=4; + + // Check if the point and the segment are still too closed for a safe rounding + if(csq_dist_2(p, seg, bound)==CGAL::LARGER) + return false; + } + + // Check if segment was not already subdivided by another point + for(std::size_t i: pl) + if(pts[i].x()==pts[pi].x()) + return false; + + // Create a point on seg at the same x coordinate than p + pts.push_back(point_at_x(seg, pts[event.pi].x())); + std::size_t new_pi=pts.size()-1; + round_bound_pts.emplace_back(round_bound(pts[new_pi])); + // We insert it on pl before the last vertex + pl.insert(pl.end()-1, new_pi); +#ifdef DOUBLE_2D_SNAP_FULL_VERBOSE + std::cout << "Create point " << new_pi << " on " << li << " due to proximity with " << pi << "_____________________________" << std::endl; + std::cout << new_pi <<": " << pts[new_pi] << std::endl; + std::cout << li << ":"; + for(std::size_t i: pl) + std::cout << " " << i; + std::cout << std::endl; +#endif + return true; + } + return false; + }; + + if(event.pi != event_queue[i-1].pi) + { + // Look above segments and creates a point if there too close for a safe rounding + auto above = pos_it; + size_t pi=event.pi; + while(above!=y_order.end() && close_event(pi,*above)){ + if((pi!=polylines[*above].front()) && (pi!=polylines[*above].back())) + pi=pts.size()-1; + ++above; + } + + // same with below segments + auto below = pos_it; + pi=event.pi; + if(below!=y_order.begin()){ + --below; + while(close_event(pi,*(below))){ + if(below==y_order.begin()) + break; + if((pi!=polylines[*below].front()) && (pi!=polylines[*below].back())) + pi=pts.size()-1; + --below; + } + } + } + } + + if(event.type==EVENT_TYPE::INSERT){ + y_order.insert(pos_it, event.li); + } + } + + // We sort the point by y to ensure the order of the point is preserved + // Round value of y coordinates are indirectly modified in the case of a filter failure + auto sort_pi=[&](std::size_t i, std::size_t j){ + return less_y_2(pts[i],pts[j]); + }; + std::vector< std::size_t > indices(pts.size(),0); + std::iota(indices.begin(),indices.end(),0); + std::sort(indices.begin(),indices.end(),sort_pi); + +#ifdef DOUBLE_2D_SNAP_FULL_VERBOSE + std::cout << "Output points" << std::endl; + i=0; + for(const Point_2 &p: pts) + std::cout << i++ << ": " << p << std::endl; + std::cout << std::endl; + std::cout << "Output polylines" << std::endl; + i=0; + for(const Polyline &pl: polylines){ + std::cout << i++ << ":"; + for(std::size_t pi: pl) + std::cout << " " << pi; + std::cout << std::endl; + } + std::cout << std::endl; +#endif +} + +template +void merge_duplicate_points_in_polylines(PointsRange &pts, PolylineRange &polylines, const Traits &traits) +{ + using Point_2 = typename Traits::Point_2; + using Polyline = std::remove_cv_t::value_type>; + + using Less_xy_2 = typename Traits::Less_xy_2; + using Equal_2 = typename Traits::Equal_2; + Equal_2 equal = traits.equal_2_object(); + + auto Less_indexes_xy_2=[&](std::size_t i, std::size_t j){ + return Less_xy_2()(pts[i], pts[j]); + }; + + std::vector< std::size_t > unique_points(pts.size()); + std::iota(unique_points.begin(), unique_points.end(), 0); + std::sort(unique_points.begin(), unique_points.end(), Less_indexes_xy_2); + std::vector new_pts; + std::vector old_to_new_index(pts.size()); + for(std::size_t i=0; i!=pts.size(); ++i){ + if(i==0 || !equal(pts[unique_points[i]],pts[unique_points[i-1]])) + new_pts.push_back(pts[unique_points[i]]); + old_to_new_index[unique_points[i]]=new_pts.size()-1; + } + + std::swap(pts, new_pts); + for (Polyline& polyline : polylines) { + std::vector updated_polyline; + for (std::size_t i=0; i +void snap_post_process(PointsRange &pts, PolylineRange &polylines, const Traits &traits) +{ + using Polyline = std::remove_cv_t::value_type>; + using Less_xy_2 = typename Traits::Less_xy_2; + + Less_xy_2 less_xy_2 = traits.less_xy_2_object(); + + auto Less_indexes_xy_2=[&](std::size_t i, std::size_t j){ + return less_xy_2(pts[i], pts[j]); + }; + + std::set p_sort_by_x(Less_indexes_xy_2); + for(std::size_t i=0; i!=pts.size(); ++i) + p_sort_by_x.insert(i); + + using Iterator_set_x = typename std::set::iterator; + + for(Polyline &poly: polylines){ + std::vector updated_polyline; + updated_polyline.push_back(poly.front()); + for(std::size_t i=1; i!=poly.size(); ++i){ + if(pts[poly[i-1]].x()==pts[poly[i]].x()){ + Iterator_set_x start, end; + // Get all vertices between the two endpoints along x order + if(Less_indexes_xy_2(poly[i-1],poly[i])){ + start=p_sort_by_x.upper_bound(poly[i-1]); + end=p_sort_by_x.lower_bound(poly[i]); + for(auto it=start; it!=end; ++it){ + updated_polyline.push_back(*it); + } + } else { + start=p_sort_by_x.upper_bound(poly[i]); + end=p_sort_by_x.lower_bound(poly[i-1]); + for(auto it=end; it!=start;){ + --it; + updated_polyline.push_back(*it); + } + } + } + updated_polyline.push_back(poly[i]); + } + std::swap(poly, updated_polyline); + } +} + +template +void double_snap_rounding_2_disjoint(PointsRange &pts, PolylineRange &polylines, const Traits &traits) +{ + using Point_2 = typename Traits::Point_2; + using Construct_round_point_2 = typename Traits::Construct_round_point_2; + + Construct_round_point_2 round = traits.construct_round_point_2_object(); + + snap_rounding_scan(pts, polylines, traits); +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Round" << std::endl; +#endif + for(Point_2 &p: pts) + p=round(p); +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Merging points that are collapsed together" << std::endl; +#endif + merge_duplicate_points_in_polylines(pts, polylines, traits); +#ifdef DOUBLE_2D_SNAP_VERBOSE + // The algorithm prevents the a vertex that goes through a segment but a vertex may lie on an horizontal/vertical segments after rounding + std::cout << "Subdivide vertical segments with vertices on them" << std::endl; +#endif + snap_post_process(pts, polylines, traits); +} + +} // end of namespace internal + +/** +* ingroup PkgSnapRounding2Ref +* +* Given a range of segments, compute rounded subsegments that are pairwise disjoint in their interiors, based on the input curves. +* The output is a range of polyline with each polyline corresponding to an input segment. +* +* TODO Currently compute_subcurves have no visitor to obtain a polyline per input segment, thus +* currently the output contain one polyline per ubsegment of the arrangement +* +* @tparam Concurrency_tag allows choosing whether the algorithm runs in parallel or sequentially. +* If `CGAL::Parallel_tag` is specified and CGAL is linked with the Intel TBB library, the algorithm will run in parallel. +* Otherwise, if `CGAL::Sequential_tag` is specified (the default), the algorithm will run sequentially. +* @tparam InputIterator iterator over a range of `Segment_2` +* @tparam OutputContainer inserter over a range of `Polyline`. `Polyline` must be a type that provides a `push_back(Point_2)` function. +* @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" +* \param begin,end the input segment range +* \param out the output container +* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below +* \cgalNamedParamsBegin +* \cgalParamNBegin{concurrency_tag} +* \cgalParamDescription{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, otherwise.} +* \cgalParamType{CGAL::Concurrency_tag} +* \cgalParamDefault{CGAL::Sequential_tag} +* \cgalParamNEnd +* +* \cgalParamNBegin{geom_traits} +* \cgalParamDescription{an instance of a geometric traits class} +* \cgalParamType{The traits class must respect the concept of `FloatSnapRoundingTraits_2`} +* \cgalParamDefault{an instance of `Float_snap_rounding_traits_2`} +* \cgalParamNEnd +*/ +template +typename OutputContainer::iterator double_snap_rounding_2(InputIterator begin, + InputIterator end, + OutputContainer &out, + const NamedParameters &np = parameters::default_values()) +{ + using Concurrency_tag = typename internal_np::Lookup_named_param_def::type; + + using InputKernel = typename Kernel_traits::value_type>>::Kernel; + using DefaultTraits = Float_snap_rounding_traits_2; + using Traits = typename internal_np::Lookup_named_param_def::type; + + using Point_2 = typename Traits::Point_2; + using Segment_2 = typename Traits::Segment_2; + using I2E = typename Traits::Converter_to_exact; + using E2O = typename Traits::Converter_from_exact; + using VectorIterator = typename std::vector::iterator; + + using Polyline = std::remove_cv_t::value_type>; + + using Less_xy_2 = typename Traits::Less_xy_2; + + using parameters::choose_parameter; + using parameters::get_parameter; + + const Traits &traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + + I2E to_exact=traits.converter_to_exact_object(); + E2O from_exact=traits.converter_from_exact_object(); + + std::vector convert_input; + for(InputIterator it=begin; it!=end; ++it) + convert_input.push_back(to_exact(*it)); + std::vector segs; +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Solved intersections" << std::endl; + std::cout << "do intersect? " << do_curves_intersect(convert_input.begin(), convert_input.end()) << std::endl; +#endif + compute_subcurves(convert_input.begin(), convert_input.end(), std::back_inserter(segs)); + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Change format to range of points and indexes" << std::endl; +#endif + std::set unique_point_set; + std::map point_to_index; + std::vector 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 (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; + } + 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(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()]; + if(Less_xy_2()(it->source(), it->target())) + polylines.push_back({index1, index2}); + else + polylines.push_back({index2, index1}); + } + + // Main algorithm + internal::double_snap_rounding_2_disjoint(pts, polylines, traits); + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Build output" << std::endl; +#endif + + // Output polylines + for(auto &poly: polylines){ + Polyline new_line; + for(std::size_t pi: poly) + new_line.push_back(from_exact(pts[pi])); + out.push_back(new_line); + } + + return out.end(); +} + +/** +* ingroup PkgSnapRounding2Ref +* +* Given a range of segments, compute rounded subsegments that are pairwise disjoint in their interior, as induced by the input curves. +* +* @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 NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" +* \param begin,end the input segment range +* \param out the output inserter +* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below +* \cgalNamedParamsBegin +* \cgalParamNBegin{concurrency_tag} +* \cgalParamDescription{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, otherwise.} +* \cgalParamType{CGAL::Concurrency_tag} +* \cgalParamDefault{CGAL::Sequential_tag} +* \cgalParamNEnd +* +* \cgalParamNBegin{geom_traits} +* \cgalParamDescription{an instance of a geometric traits class} +* \cgalParamType{The traits class must respect the concept of `FloatSnapRoundingTraits_2`} +* \cgalParamDefault{an instance of `Float_snap_rounding_traits_2`} +* \cgalParamNEnd +*/ +template +OutputIterator compute_snapped_subcurves_2(InputIterator begin, + InputIterator end, + OutputIterator out, + const NamedParameters &np = parameters::default_values()) +{ + using Concurrency_tag = typename internal_np::Lookup_named_param_def::type; + + using InputKernel = typename Kernel_traits::value_type>>::Kernel; + using DefaultTraits = Float_snap_rounding_traits_2; + using Traits = typename internal_np::Lookup_named_param_def::type; + + using Point_2 = typename Traits::Point_2; + using Segment_2 = typename Traits::Segment_2; + using I2E = typename Traits::Converter_to_exact; + using E2O = typename Traits::Converter_from_exact; + + using SegmentRange = std::vector; + using SegmentRangeIterator = typename SegmentRange::iterator; + + using Less_xy_2 = typename Traits::Less_xy_2; + using Construct_segment_2 = typename Traits::Construct_segment_2; + + using parameters::choose_parameter; + using parameters::get_parameter; + + const Traits &traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + + I2E to_exact=traits.converter_to_exact_object(); + E2O from_exact=traits.converter_from_exact_object(); + + Less_xy_2 less_xy_2 = traits.less_xy_2_object(); + Construct_segment_2 segment_2 = traits.construct_segment_2_object(); + + std::vector convert_input; + for(InputIterator it=begin; it!=end; ++it) + if(it->source()!=it->target()) + convert_input.push_back(to_exact(*it)); + std::vector segs; +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Solved intersections" << std::endl; + std::cout << "do intersect? " << do_curves_intersect(convert_input.begin(), convert_input.end()) << std::endl; +#endif + compute_subcurves(convert_input.begin(), convert_input.end(), std::back_inserter(segs)); + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Change format to range of points and indexes" << std::endl; +#endif + std::set unique_point_set(less_xy_2); + std::map point_to_index; + std::vector pts; + std::vector< std::vector< std::size_t> > polylines; + + // Transform range of the segments in the range of points and polyline of indexes + for(SegmentRangeIterator it=segs.begin(); it!=segs.end(); ++it) + { + const Point_2& p1 = it->source(); + const Point_2& p2 = it->target(); + + 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; + } + 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(SegmentRangeIterator 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()]; + if(less_xy_2(it->source(), it->target())) + polylines.push_back({index1, index2}); + else + polylines.push_back({index2, index1}); + } + + + // Main algorithm + internal::double_snap_rounding_2_disjoint(pts, polylines, traits); + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Build output" << std::endl; +#endif + + // Output a range of segments while removing duplicate ones + std::set< std::pair > set_out_segs; + for(auto &poly: polylines) + for(std::size_t i=1; i +void compute_snapped_polygons_2(InputIterator begin, + InputIterator end, + OutputIterator out, + const NamedParameters &np = parameters::default_values()) +{ + using Concurrency_tag = typename internal_np::Lookup_named_param_def::type; + + using Polygon_2 = typename std::iterator_traits::value_type; + using InputKernel = typename Kernel_traits::Kernel; + using DefaultTraits = Float_snap_rounding_traits_2; + using Traits = typename internal_np::Lookup_named_param_def::type; + + using Less_xy_2 = typename Traits::Less_xy_2; + + using Point_2 = typename Traits::Point_2; + using I2E = typename Traits::Converter_to_exact; + using E2O = typename Traits::Converter_from_exact; + + using parameters::choose_parameter; + using parameters::get_parameter; + + Polygon_2 P=*begin; + + const Traits &traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + const bool compute_intersections = choose_parameter(get_parameter(np, internal_np::compute_intersection), false); + + Less_xy_2 less_xy_2 = traits.less_xy_2_object(); + + 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::vector pts; + std::vector< std::vector< std::size_t> > polylines; + std::vector polygon_index; + + if(compute_intersections){ + // TODO Need to compute_subcurves that output polylines to track the polygons + assert(0); + } else { + // Index of the segments that introduced a new polygon + polygon_index.reserve(std::distance(begin, end)); + for(InputIterator it=begin; it!=end; ++it){ + size_t index_start=polylines.size(); + polygon_index.push_back(polylines.size()); + for(const typename Polygon_2::Point_2 &p: it->vertices()) + pts.push_back(to_exact(p)); + for(size_t i=0; i(pts, polylines, traits); + +#ifdef DOUBLE_2D_SNAP_VERBOSE + std::cout << "Build output" << std::endl; +#endif + + // Output a range of segments while removing duplicate ones + for(std::size_t input_ind=0; input_ind &poly = polylines[pl_ind]; + Polygon_2 P; + for(std::size_t i=1; i +void compute_snapped_polygon_2(const Polygon_2 &P, Polygon_2 &out, const NamedParameters &np = parameters::default_values()) +{ + std::array vec({P}); + std::vector out_vec; + compute_snapped_polygons_2(vec.begin(), vec.end(), std::back_inserter(out_vec), np); + out = out_vec[0]; +} + + + +} //namespace CGAL + +#endif diff --git a/Snap_rounding_2/include/CGAL/Float_snap_rounding_traits_2.h b/Snap_rounding_2/include/CGAL/Float_snap_rounding_traits_2.h new file mode 100644 index 00000000000..1d61398de05 --- /dev/null +++ b/Snap_rounding_2/include/CGAL/Float_snap_rounding_traits_2.h @@ -0,0 +1,116 @@ +// Copyright (c) 2025 Geometry Factory. +// 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) : Léo Valque + +#ifndef CGAL_FLOAT_SNAP_ROUNDING_TRAITS_2_H +#define CGAL_FLOAT_SNAP_ROUNDING_TRAITS_2_H + +#include + +#include +#include + +#include + +#include + +namespace CGAL { + +/*! +\ingroup PkgSnapRounding2Ref + +The class `Float_snap_rounding_traits_2` is a model of the +`FloatSnapRoundingTraits_2` concept, and is the only traits class supplied +with the package. +This class should be instantiated with a kernel with points and segments that are convertible to +`Exact_predicates_exact_constructions_kernel`, the user must specified to used another exact geometric kernel that conforms +to the \cgal kernel-concept, such as `Cartesian`. + +\cgalModels{SnapRoundingTraits_2} + +*/ +template +struct Float_snap_rounding_traits_2: Arr_segment_traits_2{ + typedef Arr_segment_traits_2 Base; + + typedef Exact_Kernel Exact_type; + + typedef typename Base::FT FT; + typedef typename Base::Point_2 Point_2; + typedef typename Base::Segment_2 Segment_2; + + typedef typename Base::Less_xy_2 Less_xy_2; + typedef typename Base::Less_y_2 Less_y_2; + typedef typename Base::Equal_2 Equal_2; + + typedef typename Base::Construct_point_2 Construct_point_2; + typedef typename Base::Construct_source_2 Construct_source_2; + typedef typename Base::Construct_target_2 Construct_target_2; + typedef typename Base::Construct_segment_2 Construct_segment_2; + + typedef Cartesian_converter Converter_to_exact; + typedef Cartesian_converter Converter_from_exact; + + // Return an upperbound of the squared distance between a point and its rounded value + struct Squared_round_bound_2{ + double operator()(const FT &x) const{ + double b=std::nextafter(to_interval(x).second - to_interval(x).first, std::numeric_limits::infinity()); + return b*b; + } + double operator()(const Point_2 &p) const{ + return (*this)(p.x())+(*this)(p.y()); + } + double operator()(const Segment_2 &seg) const{ + return (std::max)( (*this)(Base().construct_source_2_object()(seg)), + (*this)(Base().construct_target_2_object()(seg))); + } + }; + + struct Construct_round_point_2{ + double operator()(const FT &x) const{ + return to_double(x); + } + Point_2 operator()(const Point_2 &p) const{ + return Point_2((*this)(p.x()),(*this)(p.y())); + } + }; + + struct Construct_point_at_x_on_segment_2{ + Point_2 operator()(const Segment_2 &seg, const FT &x) const{ + FT y= (seg.supporting_line().y_at_x(x)); + return Base().construct_point_2_object()(x, y); + } + }; + + Converter_to_exact converter_to_exact_object() const{ + return Converter_to_exact(); + } + + Converter_from_exact converter_from_exact_object() const{ + return Converter_from_exact(); + } + + Squared_round_bound_2 squared_round_bound_2_object() const{ + return Squared_round_bound_2(); + } + + Construct_round_point_2 construct_round_point_2_object() const{ + return Construct_round_point_2(); + } + + Construct_point_at_x_on_segment_2 construct_point_at_x_on_segment_2_object() const{ + return Construct_point_at_x_on_segment_2(); + } +}; + +} //namespace CGAL + +#endif diff --git a/Snap_rounding_2/include/CGAL/Snap_rounding_traits_2.h b/Snap_rounding_2/include/CGAL/Snap_rounding_traits_2.h index 517c56907a3..c7d6d16b7cb 100644 --- a/Snap_rounding_2/include/CGAL/Snap_rounding_traits_2.h +++ b/Snap_rounding_2/include/CGAL/Snap_rounding_traits_2.h @@ -59,6 +59,10 @@ public: public: void operator()(const Point_2& p, NT pixel_size, NT &x, NT &y) { + // TODO a proper and efficient double_ceil was done for snap_rounding_polygon_soup +#if 0 + x = double_ceil((x / pixel_size) - 0.5) * pixel_size; +#else NT x_tmp = p.x() / pixel_size; NT y_tmp = p.y() / pixel_size; @@ -81,6 +85,7 @@ public: x = x_floor * pixel_size + pixel_size / NT(2.0); y = y_floor * pixel_size + pixel_size / NT(2.0); +#endif } }; diff --git a/Snap_rounding_2/test/Snap_rounding_2/CMakeLists.txt b/Snap_rounding_2/test/Snap_rounding_2/CMakeLists.txt index 9add288c3ac..94bc16ced5c 100644 --- a/Snap_rounding_2/test/Snap_rounding_2/CMakeLists.txt +++ b/Snap_rounding_2/test/Snap_rounding_2/CMakeLists.txt @@ -7,6 +7,8 @@ project(Snap_rounding_2_Tests) find_package(CGAL REQUIRED) create_single_source_cgal_program(test_snap_rounding_2.cpp NO_TESTING) +create_single_source_cgal_program(test_float_snap_rounding_2.cpp) +create_single_source_cgal_program(test_float_snap_rounding_2_concept.cpp) function(add_Snap_rounding_tests name) set(data_dir "data") diff --git a/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2.cpp b/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2.cpp new file mode 100644 index 00000000000..421204fbde0 --- /dev/null +++ b/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2.cpp @@ -0,0 +1,331 @@ +#include +#include + +#include +#include +#include +#include + +#include + +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; +typedef CGAL::Float_snap_rounding_traits_2 Traits_2; +typedef Kernel::Segment_2 Segment_2; +typedef Kernel::Point_2 Point_2; +typedef Kernel::Vector_2 Vector_2; +typedef std::vector Polyline_2; +typedef std::vector Polyline_range_2; +typedef Kernel::FT FT; + +typedef CGAL::Arr_segment_traits_2 Arr_segment_traits_2; +typedef Arr_segment_traits_2::Curve_2 Curve_2; + +typedef CGAL::Polygon_2 Polygon_2; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; +typedef std::vector Pwh_vec_2; + +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 +#include +#include +#include +typedef CGAL::Simple_cartesian Naive; +typedef CGAL::Cartesian_converter EK_to_IK; +typedef CGAL::Cartesian_converter IK_to_EK; +#endif + +#ifdef COMPARE_WITH_INTEGER_SNAP_ROUNDING_2 +#include +#include +typedef CGAL::Snap_rounding_traits_2 SnapTraits; +#endif + +//Biggest double with ulp smaller than an integer +const double maxFloat = std::pow(2,23); +const double maxDouble = std::pow(2,52); + +Segment_2 scale_segment(const Segment_2 &s){ + auto scale_value=[](FT x){ + return maxDouble * CGAL::to_double(x); + }; + return Segment_2(Point_2(scale_value(s.start().x()), scale_value(s.start().y())), + Point_2(scale_value(s.end().x()), scale_value(s.end().y()))); +} + +Point_2 random_point(CGAL::Random& r, double m = -1, double M = 1) +{ + return Point_2(FT(r.get_double(m, M)), FT(r.get_double(m, M))); +} + +Point_2 random_noise_point(CGAL::Random& r, double m, double M, Vector_2 v) +{ + return Point_2(FT(r.get_double(m, M) + CGAL::to_double(v.x())), FT(r.get_double(m, M) + CGAL::to_double(v.y()))); +} + +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 +template +void compute_subcurves_and_naive_snap(Iterator begin, Iterator end, OutRange &out){ + EK_to_IK to_inexact; + IK_to_EK to_exact; + std::vector segs; + for(Iterator it=begin; it!=end; ++it) + if(it->source()!=it->target()) + segs.emplace_back(*it); + CGAL::compute_subcurves(segs.begin(), segs.end(), std::back_inserter(out)); + //Naive snap + OutRange out2; + for(Segment_2 &seg: out){ + Segment_2 s = to_exact(to_inexact(seg)); + if(!s.is_degenerate()) + out2.emplace_back(s); + } + std::swap(out,out2); +} +#endif + +void test(const std::vector &segs){ + std::vector out; +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + CGAL::Real_timer t; + std::cout << "Input size: " << segs.size() << std::endl; + t.start(); + compute_subcurves_and_naive_snap(segs.begin(), segs.end(), out); + t.stop(); + std::cout << "Naive snap size: " << out.size() << " ,running time: " << t.time() << " and output do intersect: " << CGAL::do_curves_intersect(out.begin(), out.end()) < out2(out.begin(), out.end()); +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + t.stop(); + std::cout << "Formal snap size: " << out.size() << " ,running time: " << t.time() << std::endl; +#endif + assert(!CGAL::do_curves_intersect(out.begin(), out.end())); +#ifdef COMPARE_WITH_INTEGER_SNAP_ROUNDING_2 + Polyline_range_2 output_list; + t.reset(); + t.start(); + CGAL::snap_rounding_2(segs.begin(), segs.end(), std::back_inserter(output_list), 1./maxDouble); + t.stop(); + std::cout << "Running time with integer 2D snap (scaled at 10^15): " << t.time() << std::endl; +#endif +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + std::cout << "\n"; +#endif +} + +void test_fully_random(CGAL::Random &r, size_t nb_segments){ +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + std::cout << "Test fully random" << std::endl; +#endif + std::vector segs; + for(size_t i=0; i!=nb_segments; ++i) + segs.emplace_back(random_point(r), random_point(r)); + + test(segs); +} + +void test_random_polygons(CGAL::Random &r, size_t nb_polygons, size_t nb_pts){ +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + std::cout << "Test random polygons" << std::endl; +#endif + std::vector polygons; + for(size_t i=0; i!=nb_polygons; ++i){ + Polygon_2 poly; + for(size_t j=0; j!=nb_pts; ++j) + poly.push_back(Point_2(random_point(r), random_point(r))); + polygons.emplace_back(poly); + } + + std::vector out; + CGAL::compute_snapped_polygons_2(polygons.begin(), polygons.end(), std::back_inserter(out)); + + std::vector segs; + for(const Polygon_2 &poly: out) + for(size_t i=1; i segs; + double eps=std::pow(2,-40); + for(size_t i=0; i!=nb_segments; ++i){ + segs.emplace_back(random_noise_point(r, -eps, eps, source), random_noise_point(r, -eps, eps, target)); + } + + test(segs); +} + +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); +#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; + }; + + Polygon_2 scene=random_rotated_square(); + Polygon_2 snap_scene; + Pwh_vec_2 out_intersection; + + for(size_t i=0; i segs; + FT e(std::pow(2, -60)); + + // Disjoint segments + segs.emplace_back(Point_2(0, 1), Point_2(1, 1)); + segs.emplace_back(Point_2(1, 2), Point_2(2, 2)); + test(segs); + segs.clear(); + + // Degenerate segment + segs.emplace_back(Point_2(0, 1), Point_2(2, 1)); + segs.emplace_back(Point_2(1, 0), Point_2(1, 0)); + test(segs); + segs.clear(); + + // Collinear segments + segs.emplace_back(Point_2(0, 0), Point_2(4, 0)); + segs.emplace_back(Point_2(2, 0), Point_2(6, 0)); + test(segs); + segs.clear(); + + // Duplicate segment + segs.emplace_back(Point_2(0, 0), Point_2(4, 0)); + segs.emplace_back(Point_2(0, 0), Point_2(4, 0)); + test(segs); + segs.clear(); + + // Polyline + segs.emplace_back(Point_2(0, 0), Point_2(2, 0)); + segs.emplace_back(Point_2(2, 0), Point_2(1, 1)); + segs.emplace_back(Point_2(1, 1), Point_2(3, 1)); + test(segs); + segs.clear(); + + // Horizontal intersection when round + segs.emplace_back(Point_2(0, 1+e), Point_2(2, 1)); + segs.emplace_back(Point_2(1, 1), Point_2(1, 0)); + test(segs); + segs.clear(); + + // Vertical intersection when round + segs.emplace_back(Point_2(0, 0), Point_2(1-e, 0)); + segs.emplace_back(Point_2(1, 1), Point_2(1, -1)); + test(segs); + segs.clear(); + + // A more complex example + segs.emplace_back(Point_2(1-e, 1), Point_2(-1-e, -1+2*e)); + segs.emplace_back(Point_2(e/2, e/2), Point_2(1, -1)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(2, 0)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(-2+e, -4)); + segs.emplace_back(Point_2(-2, 2), Point_2(2, 2)); + segs.emplace_back(Point_2(7, 7), Point_2(7+e, 7+e)); + segs.emplace_back(Point_2(5, 7-e), Point_2(9, 7-e)); + test(segs); + + // Minimal intersection subsets of a random examples + segs.emplace_back(Point_2(0.99999999999992173,-0.9999999999991368),Point_2(0.99999999999987266,-6.016984285966173e-13)); + segs.emplace_back(Point_2(1.000000000000494,-1.0000000000002354),Point_2(0.99999999999950062,8.0391743541535603e-13)); + segs.emplace_back(Point_2(1.0000000000008489,-0.99999999999951794),Point_2(0.99999999999926925,-2.3642280455149055e-13)); + test(segs); + segs.clear(); + + segs.emplace_back(Point_2(1.0000000000008309,9.0061990813884068e-13), Point_2(0.99999999999981259,0.99999999999938394)); + segs.emplace_back(Point_2(1.0000000000005094,-3.1951552372595695e-13), Point_2(1.0000000000004887,1.0000000000006302)); + segs.emplace_back(Point_2(1.0000000000006171,-7.3034227828590228e-13), Point_2(1.0000000000004181,0.99999999999913269)); + segs.emplace_back(Point_2(1.0000000000008491,6.6531536071947998e-14), Point_2(0.9999999999993564,1.0000000000006497)); + segs.emplace_back(Point_2(1.0000000000006859,3.4155608842536406e-13), Point_2(0.99999999999990252,0.99999999999978451)); + segs.emplace_back(Point_2(1.0000000000007121,-1.6363168568197086e-13), Point_2(1.0000000000004778,0.99999999999927969)); + segs.emplace_back(Point_2(1.000000000000753,8.3769047200168284e-13), Point_2(1.0000000000003773,1.0000000000001725)); + segs.emplace_back(Point_2(1.000000000000574,6.6944274898354285e-13), Point_2(1.000000000000427,0.99999999999998845)); + segs.emplace_back(Point_2(1.0000000000006153,-2.798953111315494e-13), Point_2(1.0000000000003044,1.0000000000008289)); + segs.emplace_back(Point_2(1.0000000000008387,5.8214218612051864e-13), Point_2(1.0000000000002918,0.99999999999943212)); + segs.emplace_back(Point_2(1.0000000000007745,-3.9231414307222458e-13), Point_2(1.000000000000294,1.0000000000003408)); + test(segs); + segs.clear(); +} + +void test_float_snap_rounding(){ +#ifdef BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + std::cout << "Test the other API" << std::endl; +#endif + + std::vector< Segment_2 > segs; + std::vector< Polyline_2 > out; + FT e(std::pow(2, -60)); + segs.emplace_back(Point_2(1-e, 1), Point_2(-1-e, -1+2*e)); + segs.emplace_back(Point_2(e/2, e/2), Point_2(1, -1)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(2, 0)); + segs.emplace_back(Point_2(0, 2-e/2), Point_2(-2+e, -4)); + segs.emplace_back(Point_2(-2, 2), Point_2(2, 2)); + segs.emplace_back(Point_2(7, 7), Point_2(7+e, 7+e)); + segs.emplace_back(Point_2(5, 7-e), Point_2(9, 7-e)); + + double_snap_rounding_2(segs.begin(), segs.end(), out); +} + +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); + fix_test(); + test_float_snap_rounding(); + test_fully_random(r,1000); + test_multi_almost_indentical_segments(r,200); + // test_random_polygons(r,200,10); + // test_iterative_square_intersection(r,2000); + return(0); +} diff --git a/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2_concept.cpp b/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2_concept.cpp new file mode 100644 index 00000000000..518f8d405e4 --- /dev/null +++ b/Snap_rounding_2/test/Snap_rounding_2/test_float_snap_rounding_2_concept.cpp @@ -0,0 +1,42 @@ +// #define DOUBLE_2D_SNAP_VERBOSE +// #define DOUBLE_2D_SNAP_FULL_VERBOSE +#define BENCH_AND_VERBOSE_FLOAT_SNAP_ROUNDING_2 + +#include +#include +#include + +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; +typedef CGAL::Cartesian Rational_Kernel; +typedef CGAL::Float_snap_rounding_traits_2 Rational_Traits; + +template +struct Test{ + typedef typename Traits::Segment_2 Segment_2; + typedef typename Traits::Point_2 Point_2; + + void fix_test(){ + std::vector segs; + segs.emplace_back(Point_2(1, 1), Point_2(-1, -1)); + segs.emplace_back(Point_2(0, 0), Point_2(1, -1)); + segs.emplace_back(Point_2(0, 2), Point_2(2, 0)); + segs.emplace_back(Point_2(0, 2), Point_2(-2, -4)); + segs.emplace_back(Point_2(-2, 2), Point_2(2, 2)); + segs.emplace_back(Point_2(5, 7), Point_2(9, 7)); + segs.emplace_back(Point_2(1,1), Point_2(3,1)); + segs.emplace_back(Point_2(1,2), Point_2(3,0)); + std::vector out; + Traits traits; + CGAL::compute_snapped_subcurves_2(segs.begin(), segs.end(), std::back_inserter(out), CGAL::parameters::geom_traits(traits)); + assert(!CGAL::do_curves_intersect(out.begin(), out.end())); + } +}; + +int main(/*int argc,char *argv[]*/) +{ + Test().fix_test(); + return(0); +} diff --git a/Surface_sweep_2/examples/Surface_sweep_2/plane_sweep.cpp b/Surface_sweep_2/examples/Surface_sweep_2/plane_sweep.cpp index 28f894cd8bf..1493b1c5f63 100644 --- a/Surface_sweep_2/examples/Surface_sweep_2/plane_sweep.cpp +++ b/Surface_sweep_2/examples/Surface_sweep_2/plane_sweep.cpp @@ -6,25 +6,56 @@ #include #include +#include #include typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; typedef Kernel::Point_2 Point_2; -typedef CGAL::Arr_segment_traits_2 Traits_2; -typedef Traits_2::Curve_2 Segment_2; +typedef Kernel::Segment_2 Segment; +typedef Kernel::Point_2 Point; +using Segment_traits = CGAL::Arr_segment_traits_2; +using Traits = CGAL::Arr_polyline_traits_2; +using My_polyline = Traits::Curve_2; int main() { + Traits traits; // Construct the input segments. - Segment_2 segments[] = {Segment_2 (Point_2 (1, 5), Point_2 (8, 5)), - Segment_2 (Point_2 (1, 1), Point_2 (8, 8)), - Segment_2 (Point_2 (3, 1), Point_2 (3, 8)), - Segment_2 (Point_2 (8, 5), Point_2 (8, 8))}; + auto polyline_construct = traits.construct_curve_2_object(); + + Point points1[5]; + points1[0] = Point(0, 0); + points1[1] = Point(2, 4); + points1[2] = Point(3, 0); + points1[3] = Point(4, 4); + points1[4] = Point(6, 0); + auto pi1 = polyline_construct(&points1[0], &points1[5]); + + std::list points2; + points2.push_back(Point(1, 3)); + points2.push_back(Point(0, 2)); + points2.push_back(Point(1, 0)); + points2.push_back(Point(2, 1)); + points2.push_back(Point(3, 0)); + points2.push_back(Point(4, 1)); + points2.push_back(Point(5, 0)); + points2.push_back(Point(6, 2)); + points2.push_back(Point(5, 3)); + points2.push_back(Point(4, 2)); + auto pi2 = polyline_construct(points2.begin(), points2.end()); + + std::vector segs; + segs.push_back(Segment(Point(0, 2), Point(1, 2))); + segs.push_back(Segment(Point(1, 2), Point(3, 6))); + segs.push_back(Segment(Point(3, 6), Point(5, 2))); + auto pi3 = polyline_construct(segs.begin(), segs.end()); + + My_polyline vec[3]={pi1, pi2, pi3}; // Compute all intersection points. std::list pts; - CGAL::compute_intersection_points(segments, segments + 4, + CGAL::compute_intersection_points(vec, vec + 3, std::back_inserter(pts)); // Print the result. @@ -33,14 +64,14 @@ int main() std::ostream_iterator(std::cout, "\n")); // Compute the non-intersecting sub-segments induced by the input segments. - std::list sub_segs; + std::list sub_segs; - CGAL::compute_subcurves(segments, segments + 4, std::back_inserter(sub_segs)); + CGAL::compute_subcurves(vec, vec + 3, std::back_inserter(sub_segs)); std::cout << "Found " << sub_segs.size() << " interior-disjoint sub-segments." << std::endl; - assert(CGAL::do_curves_intersect (segments, segments + 4)); +// assert(CGAL::do_curves_intersect (segments, segments + 4)); return 0; } diff --git a/Surface_sweep_2/include/CGAL/Surface_sweep_2.h b/Surface_sweep_2/include/CGAL/Surface_sweep_2.h index 2c007c5955a..93d46ecc8a9 100644 --- a/Surface_sweep_2/include/CGAL/Surface_sweep_2.h +++ b/Surface_sweep_2/include/CGAL/Surface_sweep_2.h @@ -198,7 +198,7 @@ protected: /*! Compute intersections between the two given curves. * If the two curves intersect, create a new event (or use the event that - * already exits in the intersection point) and insert the curves to the + * already exists in the intersection point) and insert the curves to the * event. * \param curve1 The first curve. * \param curve2 The second curve.