diff --git a/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies b/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies index fffe4db3c74..d885997679a 100644 --- a/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies +++ b/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies @@ -2,6 +2,7 @@ Advancing_front_surface_reconstruction Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Algebraic_foundations/include/CGAL/Rational_traits.h b/Algebraic_foundations/include/CGAL/Rational_traits.h index a2ac5f1016c..7fcf808e43b 100644 --- a/Algebraic_foundations/include/CGAL/Rational_traits.h +++ b/Algebraic_foundations/include/CGAL/Rational_traits.h @@ -53,7 +53,7 @@ struct Rational_traits_base { private: typedef Fraction_traits FT; - typedef typename FT::Decompose Decomose; + typedef typename FT::Decompose Decompose; typedef typename FT::Compose Compose; public: @@ -61,13 +61,13 @@ public: RT numerator (const Rational& r) const { RT num,den; - Decomose()(r,num,den); + Decompose()(r,num,den); return num; } RT denominator (const Rational& r) const { RT num,den; - Decomose()(r,num,den); + Decompose()(r,num,den); return den; } diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h index bf607e75f15..4cba93085a4 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h @@ -303,7 +303,7 @@ public: Integer alpha_num = Integer(1), int log_denom = 1 ) : alpha_num_(alpha_num), beta_num_((Integer(1) << log_denom) - alpha_num), - half_((log_denom > 0) ? (Integer(1) << log_denom-1) : 0), + half_((log_denom > 0) ? Integer(Integer(1) << log_denom-1) : 0), log_denom_(log_denom) { CGAL_precondition(log_denom_ >= 0); diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h index cba01cdc911..a76e1c6ceca 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h @@ -206,7 +206,7 @@ polynomial_power_to_bernstein_approx( std::vector f(n+1); polynomial_affine_transform_approx_log_denom( first, beyond, f.begin(), - upper_num - lower_num, lower_num, log_denom, + Integer(upper_num - lower_num), lower_num, log_denom, p+q, approx, log, logl ); diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h index 155cfbffc68..8ed8fa5ae0c 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h @@ -27,31 +27,10 @@ #include -#if CGAL_USE_CORE -namespace CORE { class BigInt; } -#endif - namespace CGAL { namespace internal { -#if CGAL_USE_CORE -// bugfix for CORE by Michael Kerber -// why is there a specialized function for CORE? -inline CORE::BigInt shift_integer_by(CORE::BigInt x, long shift){ - if( shift > 0 ){ - while(shift>63) { - x = (x >> 63); - shift-=63; - } - x = (x >> shift); - }else{ - // add 0 bits - x = (x << -shift); - } - return x; -} -#endif template Shiftable shift_integer_by(Shiftable x, long shift){ diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Real_embeddable_extension.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Real_embeddable_extension.h index 31e43a0f2dd..9f06bc83383 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Real_embeddable_extension.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Real_embeddable_extension.h @@ -327,7 +327,7 @@ public: long operator()( CORE::BigFloat x ) const { CGAL_precondition(!CGAL::zero_in(x)); x = CGAL::abs(x); - return CORE::floorLg(x.m()-x.err())+x.exp()*CORE::CHUNK_BIT; + return CORE::floorLg(CORE::BigInt(x.m()-x.err()))+x.exp()*CORE::CHUNK_BIT; } }; @@ -337,7 +337,7 @@ public: // (already commented out in EXACUS)... // NiX_precond(!(NiX::in_zero(x) && NiX::singleton(x))); x = CGAL::abs(x); - return CORE::ceilLg(x.m()+x.err())+x.exp()*CORE::CHUNK_BIT; + return CORE::ceilLg(CORE::BigInt(x.m()+x.err()))+x.exp()*CORE::CHUNK_BIT; } }; diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h index ade0be4a1a5..aaf9185350f 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h @@ -220,7 +220,7 @@ simple_bound_between(const Algebraic_real& a, final_mantissa = final_mantissa << 1; final_mantissa++; y_log--; - x_m = x_m==0 ? 0 : x_m & ((Integer(1) << x_log) - 1); //x_m - CGAL::ipower(Integer(2),x_log); + x_m = x_m==0 ? 0 : Integer(x_m & ((Integer(1) << x_log) - 1)); //x_m - CGAL::ipower(Integer(2),x_log); x_log = x_m==0 ? -1 : CGAL::internal::floor_log2_abs(x_m); } final_mantissa = final_mantissa << 1; diff --git a/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies b/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies index 8d3b893eeb2..2345a7f06d4 100644 --- a/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies +++ b/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_circles Arithmetic_kernel +CGAL_Core Filtered_kernel Installation Interval_support diff --git a/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies b/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies index bfe13a407f4..e9b3846ab79 100644 --- a/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies +++ b/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_spheres Arithmetic_kernel +CGAL_Core Filtered_kernel Installation Interval_support diff --git a/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies b/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies index 6c159737638..d9c0f7d94a8 100644 --- a/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies +++ b/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Alpha_shapes_2 Arithmetic_kernel +CGAL_Core Cartesian_kernel Hash_map Homogeneous_kernel diff --git a/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies b/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies index f0dc76c90d9..516a70a2bde 100644 --- a/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies +++ b/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Alpha_shapes_3 Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Filtered_kernel diff --git a/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies b/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies index 7645e1ba53c..a3f60578532 100644 --- a/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies +++ b/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies @@ -4,6 +4,7 @@ Alpha_wrap_3 Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h b/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h index b1c1b1532e8..1ac38a24234 100644 --- a/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h +++ b/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h @@ -19,6 +19,10 @@ #ifdef CGAL_USE_BOOST_MP +#ifdef CGAL_USE_CORE +#include +#endif + //Currently already included in boost_mp.h //#include //#ifdef CGAL_USE_GMP @@ -26,20 +30,29 @@ //#endif // FIXME: the could be several kernels based on Boost.Multiprecision. - namespace CGAL { /** \ingroup CGAL_Arithmetic_kernel * \brief The Boost.Multiprecision set of exact number types */ + +#if !defined(CGAL_USE_CORE) || defined(CGAL_CORE_USE_GMP_BACKEND) struct BOOST_cpp_arithmetic_kernel : internal::Arithmetic_kernel_base { typedef boost::multiprecision::cpp_int Integer; typedef boost::multiprecision::cpp_rational Rational; }; +#else +typedef CORE_arithmetic_kernel BOOST_cpp_arithmetic_kernel; +#endif + #ifdef CGAL_USE_GMP +#if !defined(CGAL_USE_CORE) || !defined(CGAL_CORE_USE_GMP_BACKEND) struct BOOST_gmp_arithmetic_kernel : internal::Arithmetic_kernel_base { typedef boost::multiprecision::mpz_int Integer; typedef boost::multiprecision::mpq_rational Rational; }; +#else +typedef CORE_arithmetic_kernel BOOST_gmp_arithmetic_kernel; +#endif #endif template diff --git a/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h b/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h index c619f418de0..67586065fa5 100644 --- a/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h +++ b/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h @@ -55,14 +55,6 @@ public: }; -template <> -struct Get_arithmetic_kernel{ - typedef CORE_arithmetic_kernel Arithmetic_kernel; -}; -template <> -struct Get_arithmetic_kernel{ - typedef CORE_arithmetic_kernel Arithmetic_kernel; -}; template <> struct Get_arithmetic_kernel{ typedef CORE_arithmetic_kernel Arithmetic_kernel; @@ -74,6 +66,8 @@ struct Get_arithmetic_kernel{ } //namespace CGAL +#include + #endif // CGAL_USE_CORE #endif // CGAL_CORE_ARITHMETIC_KERNEL_H diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp index df21f6699ab..b6bd521f622 100644 --- a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp @@ -202,7 +202,7 @@ operator()(const Point_2& p, const X_monotone_curve_2& curve) const { auto points = painterOstream.getPointsList(curve); QPoint p_viewport = - view->mapFromScene(QPointF{p.x().doubleValue(), p.y().doubleValue()}); + view->mapFromScene(QPointF{CGAL::to_double(p.x()), CGAL::to_double(p.y())}); double min_dist = (std::numeric_limits::max)(); for (auto& vec : points) { diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_conic_traits_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_conic_traits_2.h index a850f5ef3c8..fd0215a1a1e 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_conic_traits_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_conic_traits_2.h @@ -3316,9 +3316,9 @@ public: // sqrt((r - s)^2 + t^2) // const int or_fact = (cv.orientation() == CLOCKWISE) ? -1 : 1; - const Algebraic r = m_nt_traits->convert(or_fact * cv.r()); - const Algebraic s = m_nt_traits->convert(or_fact * cv.s()); - const Algebraic t = m_nt_traits->convert(or_fact * cv.t()); + const Algebraic r = m_nt_traits->convert(Integer(or_fact * cv.r())); + const Algebraic s = m_nt_traits->convert(Integer(or_fact * cv.s())); + const Algebraic t = m_nt_traits->convert(Integer(or_fact * cv.t())); const Algebraic cos_2phi = (r - s) / m_nt_traits->sqrt((r-s)*(r-s) + t*t); const Algebraic zero = 0; const Algebraic one = 1; @@ -3363,8 +3363,8 @@ public: // 4*r*s - t^2 4*r*s - t^2 // // The denominator (4*r*s - t^2) must be negative for hyperbolas. - const Algebraic u = m_nt_traits->convert(or_fact * cv.u()); - const Algebraic v = m_nt_traits->convert(or_fact * cv.v()); + const Algebraic u = m_nt_traits->convert(Integer(or_fact * cv.u())); + const Algebraic v = m_nt_traits->convert(Integer(or_fact * cv.v())); const Algebraic det = 4*r*s - t*t; Algebraic x0, y0; @@ -3803,9 +3803,9 @@ public: auto u = cv.u(); auto v = cv.v(); auto w = cv.w(); - Algebraic* xs_end = m_nt_traits->solve_quadratic_equation(t*t - four*r*s, - two*t*v - four*s*u, - v*v - four*s*w, + Algebraic* xs_end = m_nt_traits->solve_quadratic_equation(Integer(t*t - four*r*s), + Integer(two*t*v - four*s*u), + Integer(v*v - four*s*w), xs); auto n_xs = static_cast(xs_end - xs); @@ -3816,15 +3816,15 @@ public: if (CGAL::sign(cv.t()) == ZERO) { // The two vertical tangency points have the same y coordinate: - ys[0] = m_nt_traits->convert(-v) / m_nt_traits->convert(two*s); + ys[0] = m_nt_traits->convert(Integer(- v)) / m_nt_traits->convert(Integer(two * s)); n_ys = 1; } else { - ys_end = m_nt_traits->solve_quadratic_equation(four*r*s*s - s*t*t, - four*r*s*v - two*s*t*u, - r*v*v - t*u*v + - t*t*w, + ys_end = m_nt_traits->solve_quadratic_equation(Integer(four*r*s*s - s*t*t), + Integer(four*r*s*v - two*s*t*u), + Integer((r*v*v - t*u*v) + (t*t*w)), ys); + n_ys = static_cast(ys_end - ys); } @@ -3837,7 +3837,7 @@ public: } else { for (int j = 0; j < n_ys; ++j) { - if (CGAL::compare(m_nt_traits->convert(two*s) * ys[j], + if (CGAL::compare(m_nt_traits->convert(Integer(two*s)) * ys[j], -(m_nt_traits->convert(t) * xs[i] + m_nt_traits->convert(v))) == EQUAL) { @@ -3904,10 +3904,11 @@ public: auto u = cv.u(); auto v = cv.v(); auto w = cv.w(); - Algebraic* ys_end = m_nt_traits->solve_quadratic_equation(t*t - four*r*s, - two*t*u - four*r*v, - u*u - four*r*w, - ys); + Algebraic* ys_end = m_nt_traits->template + solve_quadratic_equation(t*t - four*r*s, + two*t*u - four*r*v, + u*u - four*r*w, + ys); auto n = static_cast(ys_end - ys); // Compute the x coordinates and construct the horizontal tangency points. @@ -3915,7 +3916,7 @@ public: // Having computed y, x is the single solution to the quadratic equation // above, and since its discriminant is 0, x is simply given by: Algebraic x = -(m_nt_traits->convert(t)*ys[i] + m_nt_traits->convert(u)) / - m_nt_traits->convert(two*r); + m_nt_traits->convert(Integer(two*r)); ps[i] = Point_2(x, ys[i]); } diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Circle_segment_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Circle_segment_2.h index f46489ba969..dfa75584c53 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Circle_segment_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Circle_segment_2.h @@ -1430,8 +1430,8 @@ protected: { // Actually compare the slopes. const bool swap_res = (sign_denom1 != sign_denom2); - const CoordNT A = (cv.y0() - y0())*p.x() + (y0()*cv.x0() - cv.y0()*x0()); - const CoordNT B = (cv.x0() - x0())*p.y(); + const CoordNT A = NT(cv.y0() - y0())*p.x() + (y0()*cv.x0() - cv.y0()*x0()); + const CoordNT B = NT(cv.x0() - x0())*p.y(); slope_res = CGAL::compare (A, B); diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_arc_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_arc_2.h index db6c0fccfc8..08126475b5a 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_arc_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_arc_2.h @@ -1110,9 +1110,9 @@ protected: // Nt_traits nt_traits; const int or_fact = (m_orient == CLOCKWISE) ? -1 : 1; - const Algebraic r = nt_traits.convert(or_fact * m_r); - const Algebraic s = nt_traits.convert(or_fact * m_s); - const Algebraic t = nt_traits.convert(or_fact * m_t); + const Algebraic r = nt_traits.convert(Integer(or_fact * m_r)); + const Algebraic s = nt_traits.convert(Integer(or_fact * m_s)); + const Algebraic t = nt_traits.convert(Integer(or_fact * m_t)); const Algebraic cos_2phi = (r - s) / nt_traits.sqrt((r-s)*(r-s) + t*t); const Algebraic zero = 0; const Algebraic one = 1; @@ -1158,8 +1158,8 @@ protected: // 4*r*s - t^2 4*r*s - t^2 // // The denominator (4*r*s - t^2) must be negative for hyperbolas. - const Algebraic u = nt_traits.convert(or_fact * m_u); - const Algebraic v = nt_traits.convert(or_fact * m_v); + const Algebraic u = nt_traits.convert(Integer(or_fact * m_u)); + const Algebraic v = nt_traits.convert(Integer(or_fact * m_v)); const Algebraic det = 4*r*s - t*t; Algebraic x0, y0; diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_intersections_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_intersections_2.h index 223fcda24e9..3af830c193d 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_intersections_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_geometry_traits/Conic_intersections_2.h @@ -64,8 +64,9 @@ int compute_resultant_roots(const Nt_traits& nt_traits, } // Act according to the degree of the first conic curve. - const typename Nt_traits::Integer two = 2; - typename Nt_traits::Integer c[5]; + typedef typename Nt_traits::Integer Integer; + const Integer two = 2; + Integer c[5]; unsigned int degree = 4; typename Nt_traits::Algebraic* xs_end; @@ -73,7 +74,7 @@ int compute_resultant_roots(const Nt_traits& nt_traits, // The first curve has no quadratic coefficients, and represents a line. if (CGAL::sign (v1) == ZERO) { // The first line is u1*x + w1 = 0, therefore: - xs[0] = nt_traits.convert(-w1) / nt_traits.convert(u1); + xs[0] = nt_traits.convert(Integer(- w1)) / nt_traits.convert(u1); return 1; } @@ -87,7 +88,7 @@ int compute_resultant_roots(const Nt_traits& nt_traits, // Return if the two lines are parallel if (CGAL::sign (c[1]) == ZERO) return 0; - xs[0] = nt_traits.convert(-c[0]) / nt_traits.convert(c[1]); + xs[0] = nt_traits.convert(Integer(- c[0])) / nt_traits.convert(c[1]); return 1; } diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_rat_arc/Algebraic_point_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_rat_arc/Algebraic_point_2.h index 460bbe432a7..c5b38c13a8c 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_rat_arc/Algebraic_point_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_rat_arc/Algebraic_point_2.h @@ -225,10 +225,11 @@ public: if (CGAL::compare( CGAL::width(y_bfi), CGAL::lower(CGAL::abs(y_bfi)) * eps) - == SMALLER) + == SMALLER){ return std::make_pair( Bound(CGAL::lower(y_bfi)), Bound(CGAL::upper(y_bfi))); + } } else precision*=2; } @@ -287,10 +288,11 @@ private: if (CGAL::zero_in(y_denom_bfi) == false) { BFI y_bfi(y_numer_bfi/y_denom_bfi); - if (CGAL::width(y_bfi) < eps ) + if (CGAL::width(y_bfi) < eps ){ return std::make_pair( Bound(CGAL::lower(y_bfi)), Bound(CGAL::upper(y_bfi))); + } } else precision*=2; diff --git a/Arrangement_on_surface_2/include/CGAL/CORE_algebraic_number_traits.h b/Arrangement_on_surface_2/include/CGAL/CORE_algebraic_number_traits.h index c3fd592a7d6..c3e941fdab8 100644 --- a/Arrangement_on_surface_2/include/CGAL/CORE_algebraic_number_traits.h +++ b/Arrangement_on_surface_2/include/CGAL/CORE_algebraic_number_traits.h @@ -106,7 +106,7 @@ public: ix1 = scaled_x1.BigIntValue(); ix2 = scaled_x2.BigIntValue(); - if (CORE::abs (ix2 - ix1) > one) + if (CGAL::abs (ix2 - ix1) > one) break; // Scale the values by a factor of 2. @@ -179,9 +179,9 @@ public: temp_gcd = numer_gcd; denom_lcm *= denom; - denom_lcm /= CORE::gcd (temp_lcm, denom); + denom_lcm /= CGAL::gcd (temp_lcm, denom); - numer_gcd = CORE::gcd (temp_gcd, numer); + numer_gcd = CGAL::gcd (temp_gcd, numer); } ++q_iter; @@ -246,8 +246,8 @@ public: if (sign_disc == ZERO) { - // We have one real root with mutliplicity 2. - *oi = -Algebraic (b) / Algebraic (2*a); + // We have one real root with multiplicity 2. + *oi = -Algebraic (b) / Algebraic (NT(2*a)); ++oi; } else if (sign_disc == POSITIVE) @@ -255,7 +255,7 @@ public: // We have two distinct real roots. We return them in ascending order. const Algebraic sqrt_disc = CGAL::sqrt (Algebraic (disc)); const Algebraic alg_b = b; - const Algebraic alg_2a = 2*a; + const Algebraic alg_2a = NT(2*a); if (sign_a == POSITIVE) { @@ -334,7 +334,7 @@ public: temp_lcm = denom_lcm; denom_lcm *= denom; - denom_lcm /= CORE::gcd (temp_lcm, denom); + denom_lcm /= CGAL::gcd (temp_lcm, denom); } index--; diff --git a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_2.h b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_2.h index fcf022620d9..f940e49b91b 100644 --- a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_2.h @@ -865,7 +865,7 @@ void draw(const Arc_2& arc, get_pixel_coords(l, y_clip, pix_beg); get_pixel_coords(ptmp->left, it->second ? engine.y_max_r : engine.y_min_r, pix_end); - if(CGAL_ABS(ptmp->left - l) <= engine.pixel_w_r*2) { + if(CGAL_ABS(Rational(ptmp->left - l)) <= engine.pixel_w_r*2) { Coordinate_2 xy(Coordinate_1(pt), *support, arc.arcno()); Rational _; diff --git a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_internals.h b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_internals.h index 636b7919b33..a8a739cc40f 100644 --- a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_internals.h +++ b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_internals.h @@ -695,7 +695,7 @@ bool get_range_QF_1(int var, const NT& l_, const NT& r_, const NT& key, while(der_it != der_begin) { --der_it; - e1 = xsum_abs * e1 + CGAL_ABS(x1 * z1); + e1 = xsum_abs * e1 + CGAL_ABS(NT(x1 * z1)); z1 = x0*z1 + x1*y1; y1 = y1*x0 + x1*y0; y0 = x0*y0 + extract(*cache_it)*(*der_it); @@ -726,7 +726,7 @@ bool get_range_QF_1(int var, const NT& l_, const NT& r_, const NT& key, NT y0 = extract(*cache_it), y1(0), z1(0), e1(0); while(cache_it != begin) { --cache_it; - e1 = xsum_abs * e1 + CGAL_ABS(x1*z1); + e1 = xsum_abs * e1 + CGAL_ABS(NT(x1*z1)); z1 = x0*z1 + x1*y1; y1 = y1*x0 + x1*y0; y0 = x0*y0 + extract(*cache_it); diff --git a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_traits.h b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_traits.h index 5300354df86..7520f9cfad2 100644 --- a/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_traits.h +++ b/Arrangement_on_surface_2/include/CGAL/Curved_kernel_via_analysis_2/gfx/Curve_renderer_traits.h @@ -336,7 +336,7 @@ struct Curve_renderer_traits, CORE::BigRat > : //! Specialization for \c CORE::BigFloat template <> -struct Curve_renderer_traits +struct Curve_renderer_traits : public Curve_renderer_traits_base { @@ -347,7 +347,7 @@ struct Curve_renderer_traits typedef Integer result_type; Integer operator()(const Rational& x) const { - return x.BigIntValue(); + return numerator(x)/denominator(x); } }; @@ -400,7 +400,7 @@ struct Curve_renderer_traits : typedef Integer result_type; Integer operator()(const Rational& x) const { - return x.BigIntValue(); + return numerator(x)/denominator(x); } }; @@ -409,9 +409,7 @@ struct Curve_renderer_traits : typedef std::size_t result_type; inline result_type operator()(const Float& key) const { - const CORE::BigRatRep& rep = key.getRep(); - std::size_t ret = reinterpret_cast(&rep); - return ret; + return std::hash()(key); } }; }; diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/cgal_test.cmake b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/cgal_test.cmake index 59f174e0ba6..bc8f8c10c5c 100644 --- a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/cgal_test.cmake +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/cgal_test.cmake @@ -877,7 +877,7 @@ endfunction() #---------------------------------------------------------------------# function(test_polycurve_conic_traits) # echo polycurve test starting - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_polycurve_conic_traits requires CORE and will not be executed") return() endif() @@ -948,7 +948,7 @@ endfunction() # polycurve bezier traits #---------------------------------------------------------------------# function(test_polycurve_bezier_traits) - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_polycurve_bezier_traits requires CORE and will not be executed") return() endif() @@ -1057,7 +1057,7 @@ endfunction() # conic traits #---------------------------------------------------------------------# function(test_conic_traits) - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_conic_traits requires CORE and will not be executed") return() endif() @@ -1188,7 +1188,7 @@ endfunction() # bezier traits #---------------------------------------------------------------------# function(test_bezier_traits) - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_bezier_traits requires CORE and will not be executed") return() endif() @@ -1235,7 +1235,7 @@ endfunction() # rational arc traits #---------------------------------------------------------------------# function(test_rational_arc_traits) - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_rational_arc_traits requires CORE and will not be executed") return() endif() @@ -1302,7 +1302,7 @@ endfunction() #---------------------------------------------------------------------# function(test_algebraic_traits_core) #TODO: Adapt - if(CGAL_DISABLE_GMP) + if(NOT CGAL_Core_FOUND) MESSAGE(STATUS "test_algebraic_traits_core requires CORE and will not be executed") return() endif() diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/data/polycurves_bezier/points b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/data/polycurves_bezier/points index b81a26dacc1..0382a34de18 100644 --- a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/data/polycurves_bezier/points +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/data/polycurves_bezier/points @@ -7,18 +7,18 @@ # We need the bezier curve (not polycurve) to contruct a bezier point. # # Point ID: 0 -r c 4 0 0 500 200 100 200 900 0 0.5 +r c 4 0 0 500 200 100 200 900 0 1/2 # Point ID: 1 -r c 4 900 0 1200 600 1500 400 900 600 0.3 +r c 4 900 0 1200 600 1500 400 900 600 3/10 # Point id: 2 -r c 4 0 0 100 200 500 200 900 0 0.0 +r c 4 0 0 100 200 500 200 900 0 0 # Point id: 3 -r c 4 900 0 1200 300 1500 400 2000 450 1.0 +r c 4 900 0 1200 300 1500 400 2000 450 1 # Point id: 4 -r c 4 2000 450 2100 300 2200 200 2300 450 0.0 +r c 4 2000 450 2100 300 2200 200 2300 450 0 # Point id: 5 -r c 4 2300 450 2500 500 2800 600 3000 450 1.0 +r c 4 2300 450 2500 500 2800 600 3000 450 1 # Point id: 6 -r c 4 0 0 100 200 500 200 900 0 0.0 +r c 4 0 0 100 200 500 200 900 0 0 # Point id: 7 -r c 4 2300 450 2500 500 2800 600 3000 450 1.0 \ No newline at end of file +r c 4 2300 450 2500 500 2800 600 3000 450 1 \ No newline at end of file diff --git a/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies b/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies index ee1e1d6538a..1ea1e70b883 100644 --- a/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies +++ b/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Barycentric_coordinates_2 +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Boolean_set_operations_2/package_info/Boolean_set_operations_2/dependencies b/Boolean_set_operations_2/package_info/Boolean_set_operations_2/dependencies index a4d2b53c7b3..c5720a8c2b1 100644 --- a/Boolean_set_operations_2/package_info/Boolean_set_operations_2/dependencies +++ b/Boolean_set_operations_2/package_info/Boolean_set_operations_2/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 Boolean_set_operations_2 +CGAL_Core Cartesian_kernel Circular_kernel_2 Circulator diff --git a/CGAL_Core/include/CGAL/CORE/BigFloat.h b/CGAL_Core/include/CGAL/CORE/BigFloat.h index 6c7a8abff4c..de9ed2f5508 100644 --- a/CGAL_Core/include/CGAL/CORE/BigFloat.h +++ b/CGAL_Core/include/CGAL/CORE/BigFloat.h @@ -59,7 +59,7 @@ public: /// constructor for const char* (default base = 10) BigFloat(const char* s) : RCBigFloat(new BigFloatRep(s)) {} /// constructor for std::string(default base = 10) - BigFloat(const std::string& s) : RCBigFloat(new BigFloatRep(s)) {} + BigFloat(const std::string& s) : RCBigFloat(new BigFloatRep(s.c_str())) {} /// constructor for int and long // This is a hack because in Sturm, we need to approximate any @@ -106,6 +106,9 @@ public: explicit BigFloat(BigFloatRep* r, bool) : RCBigFloat(r) { } + operator BigRat() const { + return this->BigRatValue(); + } //@} @@ -510,32 +513,6 @@ inline long minStar(long m, long n) { } /// \name Functions for Compatibility with BigInt (needed by Poly, Curves) //@{ -/// isDivisible(a,b) = "is a divisible by b" -/** Assuming that a and b are in coanonized forms. - Defined to be true if mantissa(b) | mantissa(a) && - exp(b) = min*(exp(b), exp(a)). - * This concepts assume a and b are exact BigFloats. - */ -inline bool isDivisible(const BigFloat& a, const BigFloat& b) { - // assert: a and b are exact BigFloats. - if (sign(a.m()) == 0) return true; - if (sign(b.m()) == 0) return false; - unsigned long bin_a = getBinExpo(a.m()); - unsigned long bin_b = getBinExpo(b.m()); - - BigInt m_a = a.m() >> bin_a; - BigInt m_b = b.m() >> bin_b; - long e_a = bin_a + BigFloatRep::bits(a.exp()); - long e_b = bin_b + BigFloatRep::bits(b.exp()); - long dx = minStar(e_a, e_b); - - return isDivisible(m_a, m_b) && (dx == e_b); -} - -inline bool isDivisible(double x, double y) { - //Are these exact? - return isDivisible(BigFloat(x), BigFloat(y)); -} /// div_exact(x,y) returns the BigFloat quotient of x divided by y /** This is defined only if isDivisible(x,y). @@ -548,7 +525,6 @@ inline bool isDivisible(double x, double y) { // normalizing it then we get zero. inline BigFloat div_exact(const BigFloat& x, const BigFloat& y) { BigInt z; - CGAL_assertion (isDivisible(x,y)); unsigned long bin_x = getBinExpo(x.m()); unsigned long bin_y = getBinExpo(y.m()); @@ -615,15 +591,20 @@ inline BigFloat gcd(const BigFloat& a, const BigFloat& b) { //}// -// constructor BigRat from BigFloat -inline BigRat::BigRat(const BigFloat& f) : RCBigRat(new BigRatRep()){ - *this = f.BigRatValue(); +inline double doubleValue(const BigFloat& bf) +{ + return bf.doubleValue(); } + +inline long longValue(const BigFloat& bf) +{ + return bf.longValue(); +} + } //namespace CORE #ifdef CGAL_HEADER_ONLY #include -#include #endif // CGAL_HEADER_ONLY #include diff --git a/CGAL_Core/include/CGAL/CORE/BigFloatRep.h b/CGAL_Core/include/CGAL/CORE/BigFloatRep.h index 4cb5f461b05..cce0771f06f 100644 --- a/CGAL_Core/include/CGAL/CORE/BigFloatRep.h +++ b/CGAL_Core/include/CGAL/CORE/BigFloatRep.h @@ -317,7 +317,7 @@ inline void BigFloatRep::eliminateTrailingZeroes() { // builtin functions inline extLong BigFloatRep::lMSB() const { if (!isZeroIn()) - return extLong(floorLg(abs(m) - err)) + bits(exp); + return extLong(floorLg(BigInt(abs(m) - err))) + bits(exp); else return extLong(CORE_negInfty); } @@ -327,7 +327,7 @@ inline extLong BigFloatRep::lMSB() const { * Not well-defined if zero is in the interval. */ inline extLong BigFloatRep::uMSB() const { - return extLong(floorLg(abs(m) + err)) + bits(exp); + return extLong(floorLg(BigInt(abs(m) + err))) + bits(exp); } inline extLong BigFloatRep::MSB() const { diff --git a/CGAL_Core/include/CGAL/CORE/BigFloat_impl.h b/CGAL_Core/include/CGAL/CORE/BigFloat_impl.h index 59723f30df8..2fabfd36124 100644 --- a/CGAL_Core/include/CGAL/CORE/BigFloat_impl.h +++ b/CGAL_Core/include/CGAL/CORE/BigFloat_impl.h @@ -194,8 +194,7 @@ void BigFloatRep :: truncM(const BigFloatRep& B, const extLong& r, const extLong err = 2; exp = B.exp + t; } else // t < chunkCeil(clLg(B.err)) - core_error(std::string("BigFloat error: truncM called with stricter") - + "precision than current error.", __FILE__, __LINE__, true); + CGAL_error_msg("BigFloat error: truncM called with stricter precision than current error."); } else {// B.m == 0 long t = chunkFloor(- a.asLong()) - B.exp; @@ -204,8 +203,7 @@ void BigFloatRep :: truncM(const BigFloatRep& B, const extLong& r, const extLong err = 1; exp = B.exp + t; } else // t < chunkCeil(clLg(B.err)) - core_error(std::string("BigFloat error: truncM called with stricter") - + "precision than current error.", __FILE__, __LINE__, true); + CGAL_error_msg("BigFloat error: truncM called with stricter precision than current error."); } } @@ -216,7 +214,7 @@ CGAL_INLINE_FUNCTION void BigFloatRep::approx(const BigFloatRep& B, const extLong& r, const extLong& a) { if (B.err) { - if (1 + clLg(B.err) <= bitLength(B.m)) + if (static_cast(1 + clLg(B.err)) <= bitLength(B.m)) truncM(B, r + 1, a); else // 1 + clLg(B.err) > lg(B.m) truncM(B, CORE_posInfty, a); @@ -258,7 +256,7 @@ void BigFloatRep::div(const BigInt& N, const BigInt& D, exp = 0; } } else // D == 0 - core_error( "BigFloat error: zero divisor.", __FILE__, __LINE__, true); + CGAL_error_msg( "BigFloat error: zero divisor."); // Call normalization globally -- IP 10/9/98 normal(); @@ -455,6 +453,7 @@ void BigFloatRep::centerize(const BigFloatRep& a, const BigFloatRep& b) { // Zilin & Vikram, 08/24/04 // err = 1 + longValue(chunkShift(r.m, r.exp - exp)); BigInt E = chunkShift(r.m, r.exp - exp); + E = abs(E); bigNormal(E); } @@ -538,8 +537,7 @@ void BigFloatRep :: div(const BigFloatRep& x, const BigFloatRep& y, bigNormal(bigErr); } } else {// y.m <= y.err - core_error("BigFloat error: possible zero divisor.", - __FILE__, __LINE__, true); + CGAL_error_msg("BigFloat error: possible zero divisor."); } // Call normalization globally -- IP 10/9/98 @@ -734,8 +732,7 @@ void BigFloatRep::sqrt(const BigFloatRep& x, const extLong& a, const BigFloat& A } // end of case with error in mantissa }//else } else - core_error("BigFloat error: squareroot called with negative operand.", - __FILE__, __LINE__, true); + CGAL_error_msg("BigFloat error: squareroot called with negative operand."); } //sqrt with initial approximation // compareMExp(x) @@ -822,8 +819,7 @@ BigFloatRep::toDecimal(unsigned int width, bool Scientific) const { if (err > 0 && err >= abs(m)) { // if err is larger than mantissa, sign and significant values // can not be determined. - core_error("BigFloat error: Error is too big!", - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(true, "BigFloat error: Error is too big!"); decOut.rep = "0.0e0"; // error is too big decOut.isScientific = false; decOut.noSignificant = 0; @@ -869,7 +865,7 @@ BigFloatRep::toDecimal(unsigned int width, bool Scientific) const { M <<= e2; // M = x * 2^(e2) } - std::string decRep = M.get_str(); + std::string decRep = M.convert_to(); // Determine the "significant part" of this string, i.e. the part which // is guaranteed to be correct in the presence of error, // except that the last digit which might be subject to +/- 1. @@ -1003,8 +999,7 @@ void BigFloatRep :: fromString(const char *str, extLong prec ) { // NOTE: prec defaults to get_static_defBigFloatInputDigits() (see BigFloat.h) // check that prec is not INFTY if (prec.isInfty()) - core_error("BigFloat error: infinite precision not allowed", - __FILE__, __LINE__, true); + CGAL_error_msg("BigFloat error: infinite precision not allowed"); const char *e = strchr(str, 'e'); int dot = 0; diff --git a/CGAL_Core/include/CGAL/CORE/BigInt.h b/CGAL_Core/include/CGAL/CORE/BigInt.h index 6ab3aeb861b..8904db58b6f 100644 --- a/CGAL_Core/include/CGAL/CORE/BigInt.h +++ b/CGAL_Core/include/CGAL/CORE/BigInt.h @@ -24,448 +24,113 @@ #ifndef _CORE_BIGINT_H_ #define _CORE_BIGINT_H_ -#include +#include #include #include #include + +#if !(defined(CGAL_CORE_USE_BOOST_BACKEND) && BOOST_VERSION > 107900 && defined(CGAL_USE_BOOST_MP)) && !defined(CGAL_DISABLE_GMP) +#define CGAL_CORE_USE_GMP_BACKEND 1 +#endif + namespace CORE { +#ifdef CGAL_CORE_USE_GMP_BACKEND + typedef boost::multiprecision::mpz_int BigInt; +#else + typedef boost::multiprecision::cpp_int BigInt; +#endif -class BigIntRep : public RCRepImpl { -public: - BigIntRep() { - mpz_init(mp); - } - // Note : should the copy-ctor be allowed at all ? [Sylvain Pion] - BigIntRep(const BigIntRep& z) : RCRepImpl() { - mpz_init_set(mp, z.mp); - } - BigIntRep(signed char c) { - mpz_init_set_si(mp, c); - } - BigIntRep(unsigned char c) { - mpz_init_set_ui(mp, c); - } - BigIntRep(signed int i) { - mpz_init_set_si(mp, i); - } - BigIntRep(unsigned int i) { - mpz_init_set_ui(mp, i); - } - BigIntRep(signed short int s) { - mpz_init_set_si(mp, s); - } - BigIntRep(unsigned short int s) { - mpz_init_set_ui(mp, s); - } - BigIntRep(signed long int l) { - mpz_init_set_si(mp, l); - } - BigIntRep(unsigned long int l) { - mpz_init_set_ui(mp, l); - } - BigIntRep(float f) { - mpz_init_set_d(mp, f); - } - BigIntRep(double d) { - mpz_init_set_d(mp, d); - } - BigIntRep(const char* s, int base=0) { - mpz_init_set_str(mp, s, base); - } - BigIntRep(const std::string& s, int base=0) { - mpz_init_set_str(mp, s.c_str(), base); - } - explicit BigIntRep(mpz_srcptr z) { - mpz_init_set(mp, z); - } - ~BigIntRep() { - mpz_clear(mp); - } - - CGAL_CORE_EXPORT CORE_NEW(BigIntRep) - CGAL_CORE_EXPORT CORE_DELETE(BigIntRep) - - mpz_srcptr get_mp() const { - return mp; - } - mpz_ptr get_mp() { - return mp; - } -private: - mpz_t mp; -}; - -typedef RCImpl RCBigInt; -class CGAL_CORE_EXPORT BigInt : public RCBigInt { -public: - /// \name Constructors - //@{ - /// default constructor - BigInt() : RCBigInt(new BigIntRep()) {} - /// constructor for signed char - BigInt(signed char x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for unsigned char - BigInt(unsigned char x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for signed short int - BigInt(signed short int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for unsigned short int - BigInt(unsigned short int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for signed int - BigInt(signed int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for unsigned int - BigInt(unsigned int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for signed long int - BigInt(signed long int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for unsigned long int - BigInt(unsigned long int x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for float - BigInt(float x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for double - BigInt(double x) : RCBigInt(new BigIntRep(x)) {} - /// constructor for const char* with base - BigInt(const char* s, int base=0) : RCBigInt(new BigIntRep(s, base)) {} - /// constructor for std::string with base - BigInt(const std::string& s, int base=0) : RCBigInt(new BigIntRep(s, base)) {} - /// constructor for mpz_srcptr - explicit BigInt(mpz_srcptr z) : RCBigInt(new BigIntRep(z)) {} - //@} - - /// \name Copy-Assignment-Destructor - //@{ - /// copy constructor - BigInt(const BigInt& rhs) : RCBigInt(rhs) { - rep->incRef(); - } - /// assignment operator - BigInt& operator=(const BigInt& rhs) { - if (this != &rhs) { - rep->decRef(); - rep = rhs.rep; - rep->incRef(); - } - return *this; - } - /// destructor - ~BigInt() { - rep->decRef(); - } - //@} - - /// \name Overloaded operators - //@{ - BigInt& operator +=(const BigInt& rhs) { - makeCopy(); - mpz_add(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator -=(const BigInt& rhs) { - makeCopy(); - mpz_sub(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator *=(const BigInt& rhs) { - makeCopy(); - mpz_mul(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator /=(const BigInt& rhs) { - makeCopy(); - mpz_tdiv_q(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator %=(const BigInt& rhs) { - makeCopy(); - mpz_tdiv_r(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator &=(const BigInt& rhs) { - makeCopy(); - mpz_and(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator |=(const BigInt& rhs) { - makeCopy(); - mpz_ior(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator ^=(const BigInt& rhs) { - makeCopy(); - mpz_xor(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigInt& operator <<=(unsigned long ul) { - makeCopy(); - mpz_mul_2exp(get_mp(), get_mp(), ul); - return *this; - } - BigInt& operator >>=(unsigned long ul) { - makeCopy(); - mpz_tdiv_q_2exp(get_mp(), get_mp(), ul); - return *this; - } - //@} - - /// \name unary, increment, decrement operators - //@{ - BigInt operator+() const { - return BigInt(*this); - } - BigInt operator-() const { - BigInt r; - mpz_neg(r.get_mp(), get_mp()); - return r; - } - BigInt& operator++() { - makeCopy(); - mpz_add_ui(get_mp(), get_mp(), 1); - return *this; - } - BigInt& operator--() { - makeCopy(); - mpz_sub_ui(get_mp(), get_mp(), 1); - return *this; - } - BigInt operator++(int) { - BigInt r(*this); - ++(*this); - return r; - } - BigInt operator--(int) { - BigInt r(*this); - --(*this); - return r; - } - //@} - - /// \name Helper Functions - //@{ - /// Has Exact Division - static bool hasExactDivision() { - return false; - } - /// get mpz pointer (const) - mpz_srcptr get_mp() const { - return rep->get_mp(); - } - /// get mpz pointer - mpz_ptr get_mp() { - return rep->get_mp(); - } - //@} - - /// \name String Conversion Functions - //@{ - /// set value from const char* - int set_str(const char* s, int base = 0) { - makeCopy(); - return mpz_set_str(get_mp(), s, base); - } - /// convert to std::string - std::string get_str(int base = 10) const { - int n = mpz_sizeinbase (get_mp(), base) + 2; - char *buffer = new char[n]; - mpz_get_str(buffer, base, get_mp()); - std::string result(buffer); - delete [] buffer; - return result; - } - //@} - - /// \name Conversion Functions - //@{ - /// intValue - int intValue() const { - return static_cast(mpz_get_si(get_mp())); - } - /// longValue - long longValue() const { - return mpz_get_si(get_mp()); - } - /// ulongValue - unsigned long ulongValue() const { - return mpz_get_ui(get_mp()); - } - /// doubleValue - double doubleValue() const { - return mpz_get_d(get_mp()); - } - //@} -}; - -inline BigInt operator+(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_add(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator-(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_sub(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator*(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_mul(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator/(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_tdiv_q(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator%(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_tdiv_r(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator&(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_and(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator|(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_ior(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator^(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_xor(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigInt operator<<(const BigInt& a, unsigned long ul) { - BigInt r; - mpz_mul_2exp(r.get_mp(), a.get_mp(), ul); - return r; -} -inline BigInt operator>>(const BigInt& a, unsigned long ul) { - BigInt r; - mpz_tdiv_q_2exp(r.get_mp(), a.get_mp(), ul); - return r; -} inline int cmp(const BigInt& x, const BigInt& y) { - return mpz_cmp(x.get_mp(), y.get_mp()); -} -inline bool operator==(const BigInt& a, const BigInt& b) { - return cmp(a, b) == 0; -} -inline bool operator!=(const BigInt& a, const BigInt& b) { - return cmp(a, b) != 0; -} -inline bool operator>=(const BigInt& a, const BigInt& b) { - return cmp(a, b) >= 0; -} -inline bool operator>(const BigInt& a, const BigInt& b) { - return cmp(a, b) > 0; -} -inline bool operator<=(const BigInt& a, const BigInt& b) { - return cmp(a, b) <= 0; -} -inline bool operator<(const BigInt& a, const BigInt& b) { - return cmp(a, b) < 0; + return x.compare(y); } -inline std::ostream& operator<<(std::ostream& o, const BigInt& x) { - //return CORE::operator<<(o, x.get_mp()); - return CORE::io_write(o, x.get_mp()); -} -inline std::istream& operator>>(std::istream& i, BigInt& x) { - x.makeCopy(); - //return CORE::operator>>(i, x.get_mp()); - return CORE::io_read(i, x.get_mp()); -} -/// sign -inline int sign(const BigInt& a) { - return mpz_sgn(a.get_mp()); -} -/// abs -inline BigInt abs(const BigInt& a) { - BigInt r; - mpz_abs(r.get_mp(), a.get_mp()); - return r; -} -/// neg -inline BigInt neg(const BigInt& a) { - BigInt r; - mpz_neg(r.get_mp(), a.get_mp()); - return r; -} -/// negate -inline void negate(BigInt& a) { - a.makeCopy(); - mpz_neg(a.get_mp(), a.get_mp()); -} -/// cmpabs -inline int cmpabs(const BigInt& a, const BigInt& b) { - return mpz_cmpabs(a.get_mp(), b.get_mp()); -} +inline int set_str(BigInt& a, const char* s) { + a = BigInt(s); + return 0; // should be -1 if not correct in the base (we ignore) + } -/// \name Conversion Functions -//@{ -/// longValue + /// longValue inline long longValue(const BigInt& a) { - return a.longValue(); + return a.convert_to(); } -/// ulongValue -inline unsigned long ulongValue(const BigInt& a) { - return a.ulongValue(); -} -/// doubleValue + + /// doubleValue inline double doubleValue(const BigInt& a) { - return a.doubleValue(); + return a.convert_to(); } -//@} -/// \name File I/O Functions -//@{ -/// read from file -void readFromFile(BigInt& z, std::istream& in, long maxLength = 0); -/// write to file -void writeToFile(const BigInt& z, std::ostream& in, int base=10, int charsPerLine=80); -//@} - -/// \name Misc Functions -//@{ -/// isEven + /// isEven inline bool isEven(const BigInt& z) { - return mpz_even_p(z.get_mp()); + return bit_test(z,0) == 0; } /// isOdd inline bool isOdd(const BigInt& z) { - return mpz_odd_p(z.get_mp()); + return bit_test(z,0) == 1; } -/// get exponent of power 2 -inline unsigned long getBinExpo(const BigInt& z) { - return mpz_scan1(z.get_mp(), 0); -} -/// get exponent of power k -inline void getKaryExpo(const BigInt& z, BigInt& m, int& e, unsigned long k) { - mpz_t f; - mpz_init_set_ui(f, k); - m.makeCopy(); - e = mpz_remove(m.get_mp(), z.get_mp(), f); - mpz_clear(f); -} - -/// divisible(x,y) = "x | y" inline bool isDivisible(const BigInt& x, const BigInt& y) { - return mpz_divisible_p(x.get_mp(), y.get_mp()) != 0; + BigInt q, r; + divide_qr(x, y, q, r); + return r.is_zero(); } + inline bool isDivisible(int x, int y) { return x % y == 0; } + inline bool isDivisible(long x, long y) { return x % y == 0; + } -/// exact div + /// get exponent of power 2 +inline std::size_t getBinExpo(const BigInt& z) { + if (z.is_zero()) { + return (std::numeric_limits::max)(); + } + return lsb(abs(z)); +} + + // bit length +inline std::size_t bitLength(const BigInt& a){ + if (a.is_zero()) { + return 0; + } + return msb(abs(a))+1; +} + +/// floorLg -- floor of log_2(a) +/** Convention: a=0, floorLg(a) returns -1. + * This makes sense for integer a. + */ +inline long floorLg(const BigInt& a) { + assert(std::size_t((std::numeric_limits::max)()) > bitLength(a)); + return (sign(a) == 0) ? (-1) : static_cast(bitLength(a)-1); +} + + +/// div_rem +inline void div_rem(BigInt& q, BigInt& r, const BigInt& a, const BigInt& b) { + divide_qr(a, b, q, r); +} + + + /// ulongValue +inline unsigned long ulongValue(const BigInt& a) { + assert(a >= BigInt(0)); + return a.convert_to(); +} + + /// exact div inline void divexact(BigInt& z, const BigInt& x, const BigInt& y) { - z.makeCopy(); - mpz_divexact(z.get_mp(), x.get_mp(), y.get_mp()); + BigInt r; + divide_qr(x, y, z, r ); // was void mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) Is this faster? + assert(r.is_zero()); } + // Chee (1/12/2004) The definition of div_exact(x,y) next // ensure that in Polynomials works with both NT=BigInt and NT=int: inline BigInt div_exact(const BigInt& x, const BigInt& y) { @@ -473,50 +138,19 @@ inline BigInt div_exact(const BigInt& x, const BigInt& y) { divexact(z, x, y); // z is set to x/y; return z; } + inline int div_exact(int x, int y) { return x/y; // precondition: isDivisible(x,y) } + inline long div_exact(long x, long y) { return x/y; // precondition: isDivisible(x,y) } - -/// gcd -inline BigInt gcd(const BigInt& a, const BigInt& b) { - BigInt r; - mpz_gcd(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -/// div_rem -inline void div_rem(BigInt& q, BigInt& r, const BigInt& a, const BigInt& b) { - q.makeCopy(); - r.makeCopy(); - mpz_tdiv_qr(q.get_mp(), r.get_mp(), a.get_mp(), b.get_mp()); -} -/// power -inline void power(BigInt& c, const BigInt& a, unsigned long ul) { - c.makeCopy(); - mpz_pow_ui(c.get_mp(), a.get_mp(), ul); +inline BigInt gcd(const BigInt& a, const BigInt& b){ + return boost::multiprecision::gcd(a,b); } -// pow -inline BigInt pow(const BigInt& a, unsigned long ui) { - BigInt r; - power(r, a, ui); - return r; -} - -// bit length -inline int bitLength(const BigInt& a) { - return mpz_sizeinbase(a.get_mp(), 2); -} -/// floorLg -- floor of log_2(a) -/** Convention: a=0, floorLg(a) returns -1. - * This makes sense for integer a. - */ -inline long floorLg(const BigInt& a) { - return (sign(a) == 0) ? (-1) : (bitLength(a)-1); -} /// ceilLg -- ceiling of log_2(a) where a=BigInt, int or long /** Convention: a=0, ceilLg(a) returns -1. * This makes sense for integer a. @@ -524,20 +158,45 @@ inline long floorLg(const BigInt& a) { inline long ceilLg(const BigInt& a) { if (sign(a) == 0) return -1; - unsigned long len = bitLength(a); - return (mpz_scan1(a.get_mp(), 0) == len-1) ? (len-1) : len; + assert(std::size_t((std::numeric_limits::max)()) > bitLength(a)); + std::size_t len = static_cast(bitLength(a)); + + return (lsb(abs(a)) == len - 1) ? (static_cast(len) - 1) : static_cast(len); } + inline long ceilLg(long a) { // need this for Polynomial return ceilLg(BigInt(a)); } + inline long ceilLg(int a) { // need this for Polynomial return ceilLg(BigInt(a)); } -//@} + +/// negate +inline void negate(BigInt& a) { + a= - a; +} + +/// get exponent of power k +inline void getKaryExpo(const BigInt& z, BigInt& m, int& e, unsigned long uk) { + BigInt k(uk), q, r; + e = 0; + m = z; + for(;;) { + divide_qr(m, k, q, r); + if (!r.is_zero()) break; + m = q; + ++e; + } +} + +inline void power(BigInt& c, const BigInt& a, unsigned long ul) { + c = pow(a, ul); +} + +} // namespace CORE -} //namespace CORE - #endif // _CORE_BIGINT_H_ diff --git a/CGAL_Core/include/CGAL/CORE/BigRat.h b/CGAL_Core/include/CGAL/CORE/BigRat.h index 2fbad7d30b1..cc0c8d5ed17 100644 --- a/CGAL_Core/include/CGAL/CORE/BigRat.h +++ b/CGAL_Core/include/CGAL/CORE/BigRat.h @@ -28,354 +28,35 @@ #include namespace CORE { +#ifdef CGAL_CORE_USE_GMP_BACKEND + typedef boost::multiprecision::mpq_rational BigRat; +#else + typedef boost::multiprecision::cpp_rational BigRat; +#endif -class BigRatRep : public RCRepImpl { -public: - BigRatRep() { - mpq_init(mp); - } - // Note : should the copy-ctor be allowed at all ? [Sylvain Pion] - BigRatRep(const BigRatRep& z) : RCRepImpl() { - mpq_init(mp); - mpq_set(mp, z.mp); - } - BigRatRep(signed char c) { - mpq_init(mp); - mpq_set_si(mp, c, 1); - } - BigRatRep(unsigned char c) { - mpq_init(mp); - mpq_set_ui(mp, c, 1); - } - BigRatRep(signed int i) { - mpq_init(mp); - mpq_set_si(mp, i, 1); - } - BigRatRep(unsigned int i) { - mpq_init(mp); - mpq_set_ui(mp, i, 1); - } - BigRatRep(signed short int s) { - mpq_init(mp); - mpq_set_si(mp, s, 1); - } - BigRatRep(unsigned short int s) { - mpq_init(mp); - mpq_set_ui(mp, s, 1); - } - BigRatRep(signed long int l) { - mpq_init(mp); - mpq_set_si(mp, l, 1); - } - BigRatRep(unsigned long int l) { - mpq_init(mp); - mpq_set_ui(mp, l, 1); - } - BigRatRep(float f) { - mpq_init(mp); - mpq_set_d(mp, f); - } - BigRatRep(double d) { - mpq_init(mp); - mpq_set_d(mp, d); - } - BigRatRep(const char* s) { - mpq_init(mp); - mpq_set_str(mp, s, 0); - } - BigRatRep(const std::string& s) { - mpq_init(mp); - mpq_set_str(mp, s.c_str(), 0); - } - explicit BigRatRep(mpq_srcptr q) { - mpq_init(mp); - mpq_set(mp, q); - } - BigRatRep(mpz_srcptr z) { - mpq_init(mp); - mpq_set_z(mp, z); - } - BigRatRep(mpz_srcptr n, mpz_srcptr d) { - mpq_init(mp); - mpz_set(mpq_numref(mp), n); - mpz_set(mpq_denref(mp), d); - mpq_canonicalize(mp); - } - ~BigRatRep() { - mpq_clear(mp); + + inline BigInt numerator(const BigRat& q) + { + return boost::multiprecision::numerator(q); } - CGAL_CORE_EXPORT CORE_NEW(BigRatRep) - CGAL_CORE_EXPORT CORE_DELETE(BigRatRep) - - mpq_srcptr get_mp() const { - return mp; - } - mpq_ptr get_mp() { - return mp; - } -private: - mpq_t mp; -}; //BigRatRep - -class BigFloat; - -typedef RCImpl RCBigRat; -class BigRat : public RCBigRat { -public: - /// \name Constructors - //@{ - /// default constructor - BigRat() : RCBigRat(new BigRatRep()) {} - /// constructor for signed char - BigRat(signed char x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for unsigned char - BigRat(unsigned char x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for signed short int - BigRat(signed short int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for unsigned short int - BigRat(unsigned short int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for signed int - BigRat(signed int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for unsigned int - BigRat(unsigned int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for signed long int - BigRat(signed long int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for unsigned long int - BigRat(unsigned long int x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for float - BigRat(float x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for double - BigRat(double x) : RCBigRat(new BigRatRep(x)) {} - /// constructor for const char* with base - BigRat(const char* s) : RCBigRat(new BigRatRep(s)) {} - /// constructor for std::string with base - BigRat(const std::string& s) : RCBigRat(new BigRatRep(s)) {} - /// constructor for mpq_srcptr - explicit BigRat(mpq_srcptr z) : RCBigRat(new BigRatRep(z)) {} - /// constructor for BigInt - BigRat(const BigInt& z) : RCBigRat(new BigRatRep(z.get_mp())) {} - /// constructor for two BigInts - BigRat(const BigInt& n, const BigInt& d) - : RCBigRat(new BigRatRep(n.get_mp(), d.get_mp())) {} - /// constructor for BigFloat - BigRat(const BigFloat&); - //@} - - /// \name Copy-Assignment-Destructor - //@{ - /// copy constructor - BigRat(const BigRat& rhs) : RCBigRat(rhs) { - rep->incRef(); - } - /// assignment operator - BigRat& operator=(const BigRat& rhs) { - if (this != &rhs) { - rep->decRef(); - rep = rhs.rep; - rep->incRef(); - } - return *this; - } - /// destructor - ~BigRat() { - rep->decRef(); - } - //@} - - /// \name Overloaded operators - //@{ - BigRat& operator +=(const BigRat& rhs) { - makeCopy(); - mpq_add(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigRat& operator -=(const BigRat& rhs) { - makeCopy(); - mpq_sub(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigRat& operator *=(const BigRat& rhs) { - makeCopy(); - mpq_mul(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigRat& operator /=(const BigRat& rhs) { - makeCopy(); - mpq_div(get_mp(), get_mp(), rhs.get_mp()); - return *this; - } - BigRat& operator <<=(unsigned long ul) { - makeCopy(); - mpq_mul_2exp(get_mp(), get_mp(), ul); - return *this; - } - BigRat& operator >>=(unsigned long ul) { - makeCopy(); - mpq_div_2exp(get_mp(), get_mp(), ul); - return *this; - } - //@} - - /// \name div2, unary, increment, decrement operators - //@{ - - /// exact division by 2 (this method is provided for compatibility) - BigRat div2() const { - BigRat r; BigRat t(2); // probably not most efficient way - mpq_div(r.get_mp(), get_mp(), t.get_mp()); - return r; - } - BigRat operator+() const { - return BigRat(*this); - } - BigRat operator-() const { - BigRat r; - mpq_neg(r.get_mp(), get_mp()); - return r; - } - BigRat& operator++() { - makeCopy(); - mpz_add(get_num_mp(),get_num_mp(),get_den_mp()); - return *this; - } - BigRat& operator--() { - makeCopy(); - mpz_sub(get_num_mp(),get_num_mp(),get_den_mp()); - return *this; - } - BigRat operator++(int) { - BigRat r(*this); - ++(*this); - return r; - } - BigRat operator--(int) { - BigRat r(*this); - --(*this); - return r; - } - //@} - - /// \name Helper Functions - //@{ - /// Canonicalize - void canonicalize() { - makeCopy(); - mpq_canonicalize(get_mp()); - } - /// Has Exact Division - static bool hasExactDivision() { - return true; + inline BigInt denominator(const BigRat& q) + { + return boost::multiprecision::denominator(q); } - /// return mpz pointer of numerator (const) - mpz_srcptr get_num_mp() const { - return mpq_numref(get_mp()); - } - /// return mpz pointer of numerator - mpz_ptr get_num_mp() { - return mpq_numref(get_mp()); - } - /// return mpz pointer of denominator - mpz_srcptr get_den_mp() const { - return mpq_denref(get_mp()); - } - /// return mpz pointer of denominator - mpz_ptr get_den_mp() { - return mpq_denref(get_mp()); - } - - /// get mpq pointer (const) - mpq_srcptr get_mp() const { - return rep->get_mp(); - } - /// get mpq pointer - mpq_ptr get_mp() { - return rep->get_mp(); - } - //@} - - /// \name String Conversion Functions - //@{ - /// set value from const char* - int set_str(const char* s, int base = 0) { - makeCopy(); - return mpq_set_str(get_mp(), s, base); - } - /// convert to std::string - std::string get_str(int base = 10) const { - int n = mpz_sizeinbase(mpq_numref(get_mp()), base) + mpz_sizeinbase(mpq_denref(get_mp()), base)+ 3; - char *buffer = new char[n]; - mpq_get_str(buffer, base, get_mp()); - std::string result(buffer); - delete [] buffer; - return result; - } - //@} - - /// \name Conversion Functions - //@{ - /// intValue - int intValue() const { - return static_cast(doubleValue()); - } - /// longValue - long longValue() const { - return static_cast(doubleValue()); - } - /// doubleValue - double doubleValue() const { - return mpq_get_d(get_mp()); - } - /// BigIntValue - BigInt BigIntValue() const { - BigInt r; - mpz_tdiv_q(r.get_mp(), get_num_mp(), get_den_mp()); - return r; - } - //@} -}; //BigRat class - -inline BigRat operator+(const BigRat& a, const BigRat& b) { - BigRat r; - mpq_add(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigRat operator-(const BigRat& a, const BigRat& b) { - BigRat r; - mpq_sub(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigRat operator*(const BigRat& a, const BigRat& b) { - BigRat r; - mpq_mul(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -inline BigRat operator/(const BigRat& a, const BigRat& b) { - BigRat r; - mpq_div(r.get_mp(), a.get_mp(), b.get_mp()); - return r; -} -// Chee (3/19/2004): + // Chee (3/19/2004): // The following definitions of div_exact(x,y) and gcd(x,y) // ensures that in Polynomial /// divisible(x,y) = "x | y" -inline BigRat div_exact(const BigRat& x, const BigRat& y) { - BigRat z; - mpq_div(z.get_mp(), x.get_mp(), y.get_mp()); - return z; -} -/// numerator -inline BigInt numerator(const BigRat& a) { - return BigInt(a.get_num_mp()); -} -/// denominator -inline BigInt denominator(const BigRat& a) { - return BigInt(a.get_den_mp()); -} + inline BigRat div_exact(const BigRat& x, const BigRat& y) { + BigRat z = x / y; + return z; + } -inline BigRat gcd(const BigRat& x, const BigRat& y) { - // return BigRat(1); // Remark: we may want replace this by + inline BigRat gcd(const BigRat& x , const BigRat& y) + { + // return BigRat(1); // Remark: we may want replace this by // the definition of gcd of a quotient field // of a UFD [Yap's book, Chap.3] //Here is one possible definition: gcd of x and y is just the @@ -384,98 +65,17 @@ inline BigRat gcd(const BigRat& x, const BigRat& y) { BigInt n = gcd(numerator(x), numerator(y)); BigInt d = gcd(denominator(x), denominator(y)); return BigRat(n,d); + } -} -// Chee: 8/8/2004: need isDivisible to compile Polynomial -// A trivial implementation is to return true always. But this -// caused tPolyRat to fail. -// So we follow the definition of -// Expr::isDivisible(e1, e2) which checks if e1/e2 is an integer. -inline bool isInteger(const BigRat& x) { - return BigInt(x.get_den_mp()) == 1; -} -inline bool isDivisible(const BigRat& x, const BigRat& y) { - BigRat r; - mpq_div(r.get_mp(), x.get_mp(), y.get_mp()); - return isInteger(r); -} -inline BigRat operator<<(const BigRat& a, unsigned long ul) { - BigRat r; - mpq_mul_2exp(r.get_mp(), a.get_mp(), ul); - return r; -} -inline BigRat operator>>(const BigRat& a, unsigned long ul) { - BigRat r; - mpq_div_2exp(r.get_mp(), a.get_mp(), ul); + /// BigIntValue +inline BigInt BigIntValue(const BigRat& br) +{ + BigInt r, rem; + divide_qr(numerator(br), denominator(br), r, rem); return r; } -inline int cmp(const BigRat& x, const BigRat& y) { - return mpq_cmp(x.get_mp(), y.get_mp()); -} -inline bool operator==(const BigRat& a, const BigRat& b) { - return cmp(a, b) == 0; -} -inline bool operator!=(const BigRat& a, const BigRat& b) { - return cmp(a, b) != 0; -} -inline bool operator>=(const BigRat& a, const BigRat& b) { - return cmp(a, b) >= 0; -} -inline bool operator>(const BigRat& a, const BigRat& b) { - return cmp(a, b) > 0; -} -inline bool operator<=(const BigRat& a, const BigRat& b) { - return cmp(a, b) <= 0; -} -inline bool operator<(const BigRat& a, const BigRat& b) { - return cmp(a, b) < 0; -} - -inline std::ostream& operator<<(std::ostream& o, const BigRat& x) { - //return CORE::operator<<(o, x.get_mp()); - return CORE::io_write(o, x.get_mp()); -} -inline std::istream& operator>>(std::istream& i, BigRat& x) { - x.makeCopy(); - //return CORE::operator>>(i, x.get_mp()); - return CORE::io_read(i, x.get_mp()); -} - -/// sign -inline int sign(const BigRat& a) { - return mpq_sgn(a.get_mp()); -} -/// abs -inline BigRat abs(const BigRat& a) { - BigRat r; - mpq_abs(r.get_mp(), a.get_mp()); - return r; -} -/// neg -inline BigRat neg(const BigRat& a) { - BigRat r; - mpq_neg(r.get_mp(), a.get_mp()); - return r; -} -/// div2 -inline BigRat div2(const BigRat& a) { - BigRat r(a); - return r.div2(); -} -/// longValue -inline long longValue(const BigRat& a) { - return a.longValue(); -} -/// doubleValue -inline double doubleValue(const BigRat& a) { - return a.doubleValue(); -} -/// return BigInt value -inline BigInt BigIntValue(const BigRat& a) { - return a.BigIntValue(); -} +} // namespace CORE -} //namespace CORE #endif // _CORE_BIGRAT_H_ diff --git a/CGAL_Core/include/CGAL/CORE/Config.h b/CGAL_Core/include/CGAL/CORE/Config.h index b85ddbf5538..9f49a7f5986 100644 --- a/CGAL_Core/include/CGAL/CORE/Config.h +++ b/CGAL_Core/include/CGAL/CORE/Config.h @@ -29,4 +29,13 @@ #include +#ifdef CGAL_TEST_SUITE +// disabled for the testsuite to avoid `w` +#define CGAL_CORE_warning_msg(X ,Y) +// if (!(X)) CGAL_error_msg(Y) +#else +#define CGAL_CORE_warning_msg(X ,Y) CGAL_warning_msg(X ,Y) +#endif + + #endif // _CORE_CONFIG_H_ diff --git a/CGAL_Core/include/CGAL/CORE/CoreAux.h b/CGAL_Core/include/CGAL/CORE/CoreAux.h index 8577514dac0..b90d25656bf 100644 --- a/CGAL_Core/include/CGAL/CORE/CoreAux.h +++ b/CGAL_Core/include/CGAL/CORE/CoreAux.h @@ -156,12 +156,6 @@ CGAL_CORE_EXPORT double IntMantissa(double d); // (See CORE_PATH/progs/ieee/frexp.cpp for details) CGAL_CORE_EXPORT int IntExponent(double d); -/// Writes out an error or warning message in the local file CORE_DIAGFILE -/** If last argument (err) is TRUE, then this is considered an error - * (not just warning). In this case, the message is also printed in - * std::cerr, using std::perror(). - * */ -CGAL_CORE_EXPORT void core_error(std::string msg, std::string file, int lineno, bool err); /// This is for debugging messages inline void core_debug(std::string msg){ diff --git a/CGAL_Core/include/CGAL/CORE/CoreAux_impl.h b/CGAL_Core/include/CGAL/CORE/CoreAux_impl.h index 9b335c393b2..434978c8325 100644 --- a/CGAL_Core/include/CGAL/CORE/CoreAux_impl.h +++ b/CGAL_Core/include/CGAL/CORE/CoreAux_impl.h @@ -31,7 +31,6 @@ #include #include -#include namespace CORE { @@ -157,30 +156,5 @@ int IntExponent(double d) { return e-53; } -/// core_error is the method to write Core Library warning or error messages -/** Both warnings and errors are written to a file called CORE_DIAGFILE. - * But errors are also written on std::cerr (similar to std::perror()). - * */ -// Usage: core_error(message, file_with_error, line_number, err_type) -// where err_type=0 means WARNING, error_type=0 means ERROR -CGAL_INLINE_FUNCTION -void core_error(std::string msg, std::string file, int lineno, bool err) { - std::ofstream outFile(CORE_DIAGFILE, std::ios::app); // open to append - if (!outFile) { - // perror("CORE ERROR: cannot open Core Diagnostics file"); - std::cerr << "CORE ERROR: can't open Core Diagnostics file"< - * Chee Yap - * - * WWW URL: https://cs.nyu.edu/exact/ - * Email: exact@cs.nyu.edu - * - * $URL$ - * $Id$ - * SPDX-License-Identifier: LGPL-3.0-or-later - ***************************************************************************/ - -#ifndef _COREIO_IMPL_H_ -#define _COREIO_IMPL_H_ - -#ifdef CGAL_HEADER_ONLY -#define CGAL_INLINE_FUNCTION inline -#else -#define CGAL_INLINE_FUNCTION -#endif - -#include -#include -#include - -namespace CORE { - -CGAL_INLINE_FUNCTION -void core_io_error_handler(const char *f, const char *m) { - std::cout << "\n error_handler"; - std::cout << "::" << f << "::" << m << "\n"; - std::cout.flush(); - std::abort(); -} - -CGAL_INLINE_FUNCTION -void core_io_memory_handler(char *t, const char *f, const char *m) { - if (t == nullptr) { - std::cout << "\n memory_handler"; - std::cout << "::" << f << "::" << m; - std::cout << "memory exhausted\n"; - std::cout.flush(); - std::abort(); - } -} - -// s has size old_size and will be resized to new_size. -CGAL_INLINE_FUNCTION -void allocate (char * &s, int old_size, int new_size) { - if (old_size > new_size) - old_size = new_size; - - if (s == nullptr) - old_size = 0; - - char *t = new char[new_size]; - core_io_memory_handler(t, "CoreIO", "allocate::out of memory error"); - - int i; - for (i = 0; i < old_size; i++) - t[i] = s[i]; - - delete[] s; - s = t; -} - -// appends c to s at position pos. -// sz is the size of s -CGAL_INLINE_FUNCTION -void append_char (char * &s, int & sz, int pos, char c) { - if (pos > sz) - core_io_error_handler("CoreIO", "append_char::invalid argument"); - - if (pos == sz) { - allocate(s, sz, 2*sz); - sz *= 2; - } - - s[pos] = c; -} - -// skip blanks, tabs, line breaks and comment lines -CGAL_INLINE_FUNCTION -int skip_comment_line (std::istream & in) { - char c; - - do { - in.get(c); - while ( c == '#' ) { - do { - in.get(c); - } while ( c != '\n' ); - in.get(c); - } - } while (c == ' ' || c == '\t' || c == '\n'); - - if (in.eof()) - core_io_error_handler("CoreIO::read_from_file()","unexpected end of file."); - - in.putback(c); - return c; -} - -// skips '\\' followed by '\n' -CGAL_INLINE_FUNCTION -char skip_backslash_new_line (std::istream & in) { - char c; - in.get(c); - - while (c == '\\') { - in.get(c); - - if (c == '\n') - in.get(c); - else - core_io_error_handler("CoreIO::operator>>", "\\ must be immediately followed by new line."); - } - - return c; -} - -CGAL_INLINE_FUNCTION -void read_string(std::istream& in, char* &buffer, int sz) { - char c; - int pos=0; - skip_comment_line(in); - - while ( in.get(c) ) { - if ( c == ' ' || c == '\t' || c == '\n' || c == '#') - break; - else - append_char(buffer, sz, pos++, c); - } - append_char(buffer, sz, pos, '\0'); -} - -CGAL_INLINE_FUNCTION -void read_base_number(std::istream& in, BigInt& m, long length, long maxBits) { - char *buffer; - int size, offset; - int base; - bool is_negate; - - char c; - int pos = 0; - skip_comment_line(in); - - // read sign - in.get(c); - if (c == '-') { - is_negate = true; - in.get(c); - } else - is_negate = false; - - // read base and compute digits - if (c == '0') { - in.get(c); - if (c == 'b') { - base = 2; - size = (maxBits == 0 || maxBits > length) ? length : maxBits; - offset = length - size; - } else if (c == 'x') { - base = 16; - size = (maxBits == 0) ? length : (maxBits+3) >> 2; - size = (size > length) ? length : size; - offset = (length - size) << 2; - } else { - base = 8; - size = (maxBits == 0) ? length : (maxBits+2) / 3; - size = (size > length) ? length : size; - offset = (length - size) * 3; - in.putback(c); - } - } else { - base = 10; - size = (maxBits == 0) ? length : (int)std::ceil(maxBits*std::log(2.0)/std::log(10.0)); - size = (size > length) ? length : size; - offset = length - size; - in.putback(c); - } - - buffer = new char[size+2]; - // read digits - for (int i=0; i 0 && base != 10) { - m <<= offset; - } - - if (is_negate) - negate(m); -} - - -CGAL_INLINE_FUNCTION -void write_base_number(std::ostream& out, char* buffer, std::size_t length, int base, int charsPerLine) { - // write big number in a format that gmp's mpz_set_str() can - // automatically recognize with argument base = 0. - if (base == 2) - out << "0b"; - else if (base == 16) - out << "0x"; - else if (base == 8) - out << '0'; - - // write big number in charsPerLine. - char* start, *end, c; - for (std::size_t i=0; i= length) - out << start; - else { - end = start + charsPerLine; - c = *end; - *end = '\0'; - - out << start << "\\\n"; - *end = c; - } - } -} - -CGAL_INLINE_FUNCTION -void readFromFile(BigInt& z, std::istream& in, long maxLength) { - char *buffer; - long length; - - // check type name whether it is Integer or not. - buffer = new char[8]; - read_string(in, buffer, sizeof(buffer)); - if ( std::strcmp(buffer, "Integer") != 0) - core_io_error_handler("BigInt::read_from_file()","type name expected."); - delete[] buffer; - - // read the bit length field. - buffer = new char[100]; - read_string(in, buffer, sizeof(buffer)); - length = std::atol(buffer); - delete[] buffer; - - // read bigint - read_base_number(in, z, length, maxLength); -} - -CGAL_INLINE_FUNCTION -void writeToFile(const BigInt& z, std::ostream& out, int base, int charsPerLine) { - BigInt c = abs(z); - - // get the absolute value string - char* buffer = new char[mpz_sizeinbase(c.get_mp(), base) + 2]; - mpz_get_str(buffer, base, c.get_mp()); - std::size_t length = std::strlen(buffer); - - // write type name of big number and length - //out << "# This is an experimental big number format.\n"; - out << "Integer " << length << "\n"; - - // if bigint is negative, then write an sign '-'. - if ( sign(z) < 0 ) - out << '-'; - - write_base_number(out, buffer, length, base, charsPerLine); - out << "\n"; - delete[] buffer; -} - -CGAL_INLINE_FUNCTION -void readFromFile(BigFloat& bf, std::istream& in, long maxLength) { - char *buffer; - long length; - long exponent; - BigInt mantissa; - - // check type name whether it is Float - buffer = new char[6]; - read_string(in, buffer, sizeof(buffer)); - if (std::strcmp(buffer, "Float") != 0) - core_io_error_handler("BigFloat::read_from_file()", "type name expected"); - delete[] buffer; - - // read base (default is 16384) - buffer = new char[8]; - read_string(in, buffer, sizeof(buffer)); - if (std::strcmp(buffer, "(16384)") != 0) - core_io_error_handler("BigFloat::read_from_file()", "base expected"); - delete[] buffer; - - // read the bit length field. - buffer = new char[100]; - read_string(in, buffer, sizeof(buffer)); - length = std::atol(buffer); - delete[] buffer; - - // read exponent - buffer = new char[100]; - read_string(in, buffer, sizeof(buffer)); - exponent = std::atol(buffer); - delete[] buffer; - - // read mantissa - read_base_number(in, mantissa, length, maxLength); - - // construct BigFloat - bf = BigFloat(mantissa, 0, exponent); -} - -CGAL_INLINE_FUNCTION -void writeToFile(const BigFloat& bf, std::ostream& out, int base, int charsPerLine) { - BigInt c(CORE::abs(bf.m())); - - // get the absolute value string - char* buffer = new char[mpz_sizeinbase(c.get_mp(), base) + 2]; - mpz_get_str(buffer, base, c.get_mp()); - std::size_t length = std::strlen(buffer); - - - // write type name, base, length - //out << "# This is an experimental Big Float format." << std::endl; - out << "Float (16384) " << length << std::endl; - // write exponent - out << bf.exp() << std::endl; - - // write mantissa - if ( CORE::sign(bf.m()) < 0 ) - out << '-'; - - write_base_number(out, buffer, length, base, charsPerLine); - out << '\n'; - delete[] buffer; -} - -/* Underconstruction ---- -void BigFloat::read_from_file2(std::istream& in, long maxLength) { - long length = 1024; - char *buffer; - - // check type name whether it is Float - buffer = new char[7]; - BigInt::read_string(in, buffer, sizeof(buffer)); - if (strcmp(buffer, "NFloat") != 0) - core_io_error_handler("BigFloat::read_from_file2()", "type name expected"); - delete[] buffer; - - // read base (default is 16) - buffer = new char[5]; - BigInt::read_string(in, buffer, sizeof(buffer)); - if (strcmp(buffer, "(16)") != 0) - core_io_error_handler("BigFloat::read_from_file2()", "base expected"); - delete[] buffer; - - // read length field - buffer = new char[100]; - BigInt::read_string(in, buffer, sizeof(buffer)); - - // get the length field if it is not null. - if (buffer[0] != '\0') { - length = atol(buffer); - if (maxLength > 0 && length >= maxLength) - length = maxLength; - } - delete[] buffer; - - // read exponent - buffer = new char[100]; - BigInt::read_string(in, buffer, sizeof(buffer)); - long exp16 = atol(buffer); - delete[] buffer; - - // read mantissa - buffer = new char[length+2]; - //BigInt::read_base_number(in, buffer, length); - - BigInt m16(buffer); - delete[] buffer; - - // convert to base CHUNK_BIT - exp16 = exp16 - length + 1; - if ( m16.is_negative() ) - exp16 ++; - - long tmp_exp = exp16 * 4; - long q = tmp_exp / CHUNK_BIT; - long r = tmp_exp % CHUNK_BIT; - if ( r < 0 ) { - r += CHUNK_BIT; - q --; - } - - BigInt mantissa = m16 << r; - long exponent = q; - - // construct BigFloat - if (--rep->refCount == 0) - delete rep; - - rep = new BigFloatRep(mantissa, 0, exponent); - rep->refCount++; - -} - -// write normal float -// now it assumed to write in hex base, i.e. B=2^4=16 -// (note: our default base B=2^(CHUNK_BIT)=2^14=16384 -void BigFloat::write_to_file2(std::ostream& out, int base, int charsPerLine) { - // convert to base 16. - long new_base = 4; // 2^4 = 16 - long tmp_exp = (rep->exp) * CHUNK_BIT; - long q = tmp_exp / new_base; - long r = tmp_exp % new_base; - std::cout << "CORE_DEBUG: q=" << q << ", r=" << r << std::endl; - if ( r < 0 ) { - r += new_base; - q--; - } - std::cout << "CORE_DEBUG: q=" << q << ", r=" << r << std::endl; - - BigInt m16 = (rep->m) << r; - - int size = mpz_sizeinbase(m16.I, base) + 2; - std::cout << "size=" << size << std::endl; - char* buffer = new char[size]; - - int length = bigint_to_string(m16, buffer, base); - std::cout << "length=" << length << std::endl; - - long exp16 = q + length - 1; - if ( m16.is_negative() ) - exp16 --; - - // write type name, base, length - out << "# This is an experimental Big Float format." << std::endl; - out << "NFloat (16) " << length << std::endl; - - // write exponent - out << exp16 << std::endl; - - // write mantissa - if ( m16.is_negative() ) { - out << '-'; - buffer ++; - } - - BigInt::write_base_number(out, buffer, length, base, charsPerLine); - out << '\n'; - delete[] buffer; -} -*/ - -} //namespace CORE - -#endif // _COREIO_IMPL_H_ diff --git a/CGAL_Core/include/CGAL/CORE/Expr.h b/CGAL_Core/include/CGAL/CORE/Expr.h index 39c1fb84a4c..e6efa1feb7c 100644 --- a/CGAL_Core/include/CGAL/CORE/Expr.h +++ b/CGAL_Core/include/CGAL/CORE/Expr.h @@ -72,9 +72,7 @@ public: Expr(float f) : RCExpr(nullptr) { // check for valid numbers // (i.e., not infinite and not NaN) if (! CGAL_CORE_finite(f)) { - core_error(" ERROR : constructed an invalid float! ", __FILE__, __LINE__, false); - if (get_static_AbortFlag()) - abort(); + CGAL_error_msg("ERROR : constructed an invalid float! "); get_static_InvalidFlag() = -1; } rep = new ConstDoubleRep(f); @@ -83,9 +81,7 @@ public: Expr(double d) : RCExpr(nullptr) { // check for valid numbers // (i.e., not infinite and not NaN) if (! CGAL_CORE_finite(d)) { - core_error(" ERROR : constructed an invalid double! ", __FILE__, __LINE__, false); - if (get_static_AbortFlag()) - abort(); + CGAL_error_msg("ERROR : constructed an invalid double! "); get_static_InvalidFlag() = -2; } rep = new ConstDoubleRep(d); @@ -95,6 +91,14 @@ public: Expr(const BigInt& I) : RCExpr(new ConstRealRep(Real(I))) {} /// constructor for BigRat Expr(const BigRat& R) : RCExpr(new ConstRealRep(Real(R))) {} + /// constructor from expression template + template ::value> > + Expr(const TmplExpr& R) + : RCExpr(new ConstRealRep(Real( + std::conditional_t::value == boost::multiprecision::number_kind_integer, + BigInt, BigRat>(R)))) {} /// constructor for BigFloat Expr(const BigFloat& F) : RCExpr(new ConstRealRep(Real(F))) {} @@ -173,9 +177,7 @@ public: /// /= operator Expr& operator/=(const Expr& e) { if ((e.rep)->getSign() == 0) { - core_error(" ERROR : division by zero ! ",__FILE__, __LINE__, false); - if (get_static_AbortFlag()) - abort(); + CGAL_error_msg("ERROR : division by zero ! "); get_static_InvalidFlag() = -3; } *this = new DivRep(rep, e.rep); @@ -376,9 +378,7 @@ inline Expr operator*(const Expr& e1, const Expr& e2) { /// division inline Expr operator/(const Expr& e1, const Expr& e2) { if (e2.sign() == 0) { - core_error(" ERROR : division by zero ! ", __FILE__, __LINE__, false); - if (get_static_AbortFlag()) - abort(); + CGAL_error_msg("ERROR : division by zero ! "); get_static_InvalidFlag() = -4; } return Expr(new DivRep(e1.Rep(), e2.Rep())); @@ -479,9 +479,7 @@ inline bool isDivisible(const Expr& e1, const Expr& e2) { /// square root inline Expr sqrt(const Expr& e) { if (e.sign() < 0) { - core_error(" ERROR : sqrt of negative value ! ", __FILE__, __LINE__, false); - if (get_static_AbortFlag()) - abort(); + CGAL_error_msg("ERROR : sqrt of negative value ! "); get_static_InvalidFlag() = -5; } return Expr(new SqrtRep(e.Rep())); diff --git a/CGAL_Core/include/CGAL/CORE/ExprRep.h b/CGAL_Core/include/CGAL/CORE/ExprRep.h index 7e4b56557ec..0e721dda16a 100644 --- a/CGAL_Core/include/CGAL/CORE/ExprRep.h +++ b/CGAL_Core/include/CGAL/CORE/ExprRep.h @@ -564,9 +564,7 @@ public: I = ss.isolateRoot(n); // check whether n-th root exists if (I.first == 1 && I.second == 0) { - core_error("CORE ERROR! root index out of bound", - __FILE__, __LINE__, true); - abort(); + CGAL_error_msg("CORE ERROR! root index out of bound"); } // test if the root isolated in I is 0: if ((I.first == 0)&&(I.second == 0)) @@ -583,9 +581,7 @@ public: ss.isolateRoots(I.first, I.second, v); I = v.front(); if (v.size() != 1) { - core_error("CORE ERROR! non-isolating interval", - __FILE__, __LINE__, true); - abort(); + CGAL_error_msg("CORE ERROR! non-isolating interval"); } ffVal = computeFilteredValue(); // Chee: this line seems unnecessary } @@ -1092,8 +1088,7 @@ void AddSubRep::computeExactFlags() { uMSB() = newValue.uMSB(); // chen: to get tighers value. sign() = newValue.sign(); } else if (lowBound.isInfty()) {//check if rootbound is too big - core_error("AddSubRep:root bound has exceeded the maximum size\n \ - but we still cannot decide zero.\n", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "AddSubRep:root bound has exceeded the maximum size but we still cannot decide zero."); } else { // Op(first, second) == 0 lMSB() = CORE_negInfty; sign() = 0; @@ -1186,8 +1181,7 @@ void AddSubRep::computeExactFlags() { //8/9/01, Chee: implement escape precision here: if (i> get_static_EscapePrec()) { get_static_EscapePrecFlag() = -i.asLong();//negative means EscapePrec is used - core_error("Escape precision triggered at", - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "Escape precision triggered"); if (get_static_EscapePrecWarning()) std::cout<< "Escape Precision triggered at " << get_static_EscapePrec() << " bits" << std::endl; @@ -1204,8 +1198,7 @@ void AddSubRep::computeExactFlags() { #endif if (sign() == 0 && ua .isInfty()) { - core_error("AddSubRep: root bound has exceeded the maximum size\n \ - but we still cannot decide zero.\n", __FILE__, __LINE__, true); + CGAL_error_msg("AddSubRep: root bound has exceeded the maximum size but we still cannot decide zero."); } // if (sign == 0 && ua .isInfty()) }// else do progressive } @@ -1236,8 +1229,7 @@ void AddSubRep::computeApproxValue(const extLong& relPrec, { std::ostringstream oss; oss << "CORE WARNING: a huge lMSB in AddSubRep: " << lMSB(); - core_error(oss.str(), - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, oss.str().c_str()); } extLong rf = first->uMSB()-lMSB()+relPrec+EXTLONG_FOUR; // 2 better diff --git a/CGAL_Core/include/CGAL/CORE/Expr_impl.h b/CGAL_Core/include/CGAL/CORE/Expr_impl.h index 2c643aa402a..0a36d169cf5 100644 --- a/CGAL_Core/include/CGAL/CORE/Expr_impl.h +++ b/CGAL_Core/include/CGAL/CORE/Expr_impl.h @@ -702,7 +702,7 @@ void computeExactFlags_temp(ConstRep* t, const Real &value) { } else { t->uMSB() = value.uMSB(); t->lMSB() = value.lMSB(); - core_error("Leaves in DAG is not exact!", __FILE__, __LINE__, true); + CGAL_error_msg("Leafs in DAG is not exact!"); } t->sign() = value.sign(); @@ -809,8 +809,7 @@ void SqrtRep::computeExactFlags() { sign() = child->sign(); if (sign() < 0) - core_error("squareroot is called with negative operand.", - __FILE__, __LINE__, true); + CGAL_error_msg("square root is called with negative operand."); uMSB() = child->uMSB() / EXTLONG_TWO; lMSB() = child->lMSB() / EXTLONG_TWO; @@ -928,7 +927,7 @@ void DivRep::computeExactFlags() { second->computeExactFlags(); if (!second->sign()) - core_error("zero divisor.", __FILE__, __LINE__, true); + CGAL_error_msg("zero divisor."); if (!first->sign()) {// value must be exactly zero. reduceToZero(); @@ -1025,8 +1024,7 @@ void MultRep::computeApproxValue(const extLong& relPrec, { std::ostringstream oss; oss << "CORE WARNING: a huge lMSB in AddSubRep " << lMSB(); - core_error(oss.str(), - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, oss.str().c_str()); } extLong r = relPrec + EXTLONG_FOUR; @@ -1048,8 +1046,7 @@ void DivRep::computeApproxValue(const extLong& relPrec, { std::ostringstream oss; oss << "CORE WARNING: a huge lMSB in AddSubRep " << lMSB(); - core_error(oss.str(), - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, oss.str().c_str()); } extLong rr = relPrec + EXTLONG_SEVEN; // These rules come from @@ -1082,7 +1079,9 @@ void Expr::debug(int mode, int level, int depthLimit) const { else if (mode == Expr::TREE_MODE) rep->debugTree(level, 0, depthLimit); else - core_error("unknown debugging mode", __FILE__, __LINE__, false); + { + CGAL_CORE_warning_msg(false, "unknown debugging mode"); + } std::cout << "---- End Expr debug(): " << std::endl; } @@ -1215,8 +1214,6 @@ void BinOpRep::debugTree(int level, int indent, int depthLimit) const { second->debugTree(level, indent + 2, depthLimit - 1); } -CORE_MEMORY_IMPL(BigIntRep) -CORE_MEMORY_IMPL(BigRatRep) CORE_MEMORY_IMPL(ConstDoubleRep) CORE_MEMORY_IMPL(ConstRealRep) diff --git a/CGAL_Core/include/CGAL/CORE/Filter.h b/CGAL_Core/include/CGAL/CORE/Filter.h index 56649b80c86..e71c8aa8733 100644 --- a/CGAL_Core/include/CGAL/CORE/Filter.h +++ b/CGAL_Core/include/CGAL/CORE/Filter.h @@ -137,8 +137,8 @@ public: } /// division filteredFp operator/ (const filteredFp& x) const { - if (x.fpVal == 0.0) - core_error("possible zero divisor!", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(x.fpVal != 0.0, "possible zero divisor!"); + double xxx = core_abs(x.fpVal) / x.maxAbs - (x.ind+1)*CORE_EPS + DBL_MIN; if (xxx > 0) { double val = fpVal / x.fpVal; @@ -149,8 +149,8 @@ public: } /// square root filteredFp sqrt () const { - if (fpVal < 0.0) - core_error("possible negative sqrt!", __FILE__, __LINE__, false); + + CGAL_CORE_warning_msg( !(fpVal < 0.0), "possible negative sqrt!"); if (fpVal > 0.0) { double val = std::sqrt(fpVal); return filteredFp(val, ( maxAbs / fpVal ) * val, 1 + ind); diff --git a/CGAL_Core/include/CGAL/CORE/Gmp.h b/CGAL_Core/include/CGAL/CORE/Gmp.h deleted file mode 100644 index 4febe840d9a..00000000000 --- a/CGAL_Core/include/CGAL/CORE/Gmp.h +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** - * Core Library Version 1.7, August 2004 - * Copyright (c) 1995-2004 Exact Computation Project - * All rights reserved. - * - * This file is part of CGAL (www.cgal.org). - * - * $URL$ - * $Id$ - * SPDX-License-Identifier: LGPL-3.0-or-later - ***************************************************************************/ - -// CORE LIBRARY FILE -#ifndef _CORE_GMP_H_ -#define _CORE_GMP_H_ - -#include -#include - -namespace CORE { - -CGAL_CORE_EXPORT std::ostream& io_write (std::ostream &, mpz_srcptr); -CGAL_CORE_EXPORT std::ostream& io_write (std::ostream &, mpq_srcptr); -CGAL_CORE_EXPORT std::istream& io_read (std::istream &, mpz_ptr); -CGAL_CORE_EXPORT std::istream& io_read (std::istream &, mpq_ptr); -//std::ostream& operator<< (std::ostream &, mpz_srcptr); -//std::ostream& operator<< (std::ostream &, mpq_srcptr); -//std::istream& operator>> (std::istream &, mpz_ptr); -//std::istream& operator>> (std::istream &, mpq_ptr); - -} //namespace CORE - -#ifdef CGAL_HEADER_ONLY -#include -#endif // CGAL_HEADER_ONLY - -#endif // _CORE_GMP_H_ diff --git a/CGAL_Core/include/CGAL/CORE/Gmp_impl.h b/CGAL_Core/include/CGAL/CORE/Gmp_impl.h deleted file mode 100644 index 479dc0dd0fa..00000000000 --- a/CGAL_Core/include/CGAL/CORE/Gmp_impl.h +++ /dev/null @@ -1,265 +0,0 @@ -/**************************************************************************** - * Core Library Version 1.7, August 2004 - * Copyright (c) 1995-2004 Exact Computation Project - * All rights reserved. - * - * file: GmpIO.cpp - * Adapted from multi-files under /cxx in GMP's source distribution - * - * Zilin Du, 2003 - * - * $URL$ - * $Id$ - * SPDX-License-Identifier: LGPL-3.0-only - ***************************************************************************/ - -/* Auxiliary functions for C++-style input of GMP types. - -Copyright 2001 Free Software Foundation, Inc. - -This file is part of the GNU MP Library. - -*/ - -#ifdef CGAL_HEADER_ONLY -#define CGAL_INLINE_FUNCTION inline -#else -#define CGAL_INLINE_FUNCTION -#endif - -#include -#include -#include -#include -#include - -namespace CORE { - -CGAL_INLINE_FUNCTION -int -__gmp_istream_set_base (std::istream &i, char &c, bool &zero, bool &showbase) -{ - int base; - using std::ios; - - zero = showbase = false; - switch (i.flags() & ios::basefield) - { - case ios::dec: - base = 10; - break; - case ios::hex: - base = 16; - break; - case ios::oct: - base = 8; - break; - default: - showbase = true; // look for initial "0" or "0x" or "0X" - if (c == '0') - { - if (! i.get(c)) - c = 0; // reset or we might loop indefinitely - - if (c == 'x' || c == 'X') - { - base = 16; - i.get(c); - } - else - { - base = 8; - zero = true; // if no other digit is read, the "0" counts - } - } - else - base = 10; - break; - } - - return base; -} - -CGAL_INLINE_FUNCTION -void -__gmp_istream_set_digits (std::string &s, std::istream &i, char &c, bool &ok, int base) -{ - switch (base) - { - case 10: - while (isdigit(c)) - { - ok = true; // at least a valid digit was read - s += c; - if (! i.get(c)) - break; - } - break; - case 8: - while (isdigit(c) && c != '8' && c != '9') - { - ok = true; // at least a valid digit was read - s += c; - if (! i.get(c)) - break; - } - break; - case 16: - while (isxdigit(c)) - { - ok = true; // at least a valid digit was read - s += c; - if (! i.get(c)) - break; - } - break; - } -} - -CGAL_INLINE_FUNCTION -std::istream & -//operator>> (std::istream &i, mpz_ptr z) -io_read (std::istream &i, mpz_ptr z) -{ - using namespace std; - int base; - char c = 0; - std::string s; - bool ok = false, zero, showbase; - - i.get(c); // start reading - - if (i.flags() & ios::skipws) // skip initial whitespace - while (isspace(c) && i.get(c)) - ; - - if (c == '-' || c == '+') // sign - { - if (c == '-') // mpz_set_str doesn't accept '+' - s = "-"; - i.get(c); - } - - while (isspace(c) && i.get(c)) // skip whitespace - ; - - base = __gmp_istream_set_base(i, c, zero, showbase); // select the base - __gmp_istream_set_digits(s, i, c, ok, base); // read the number - - if (i.good()) // last character read was non-numeric - i.putback(c); - else if (i.eof() && (ok || zero)) // stopped just before eof - i.clear(); - - if (ok) - mpz_set_str(z, s.c_str(), base); // extract the number - else if (zero) - mpz_set_ui(z, 0); - else - i.setstate(ios::failbit); // read failed - - return i; -} - -CGAL_INLINE_FUNCTION -std::istream & -//operator>> (std::istream &i, mpq_ptr q) -io_read (std::istream &i, mpq_ptr q) -{ - using namespace std; - int base; - char c = 0; - std::string s; - bool ok = false, zero, showbase; - - i.get(c); // start reading - - if (i.flags() & ios::skipws) // skip initial whitespace - while (isspace(c) && i.get(c)) - ; - - if (c == '-' || c == '+') // sign - { - if (c == '-') - s = "-"; - i.get(c); - } - - while (isspace(c) && i.get(c)) // skip whitespace - ; - - base = __gmp_istream_set_base(i, c, zero, showbase); // select the base - __gmp_istream_set_digits(s, i, c, ok, base); // read the numerator - - if (! ok && zero) // the only digit read was "0" - { - base = 10; - s += '0'; - ok = true; - } - - if (c == '/') // there's a denominator - { - bool zero2 = false; - int base2 = base; - - s += '/'; - ok = false; // denominator is mandatory - i.get(c); - - while (isspace(c) && i.get(c)) // skip whitespace - ; - - if (showbase) // check base of denominator - base2 = __gmp_istream_set_base(i, c, zero2, showbase); - - if (base2 == base || base2 == 10) // read the denominator - __gmp_istream_set_digits(s, i, c, ok, base); - - if (! ok && zero2) // the only digit read was "0" - { // denominator is 0, but that's your business - s += '0'; - ok = true; - } - } - - if (i.good()) // last character read was non-numeric - i.putback(c); - else if (i.eof() && ok) // stopped just before eof - i.clear(); - - if (ok) - mpq_set_str(q, s.c_str(), base); // extract the number - else - i.setstate(ios::failbit); // read failed - - return i; -} - -CGAL_INLINE_FUNCTION -std::ostream& -//operator<< (std::ostream &o, mpz_srcptr z) -io_write (std::ostream &o, mpz_srcptr z) -{ - char *str = new char [mpz_sizeinbase(z,10) + 2]; - str = mpz_get_str(str, 10, z); - o << str ; - delete[] str; - return o; -} - -CGAL_INLINE_FUNCTION -std::ostream& -//operator<< (std::ostream &o, mpq_srcptr q) -io_write (std::ostream &o, mpq_srcptr q) -{ - // size according to GMP documentation - char *str = new char [mpz_sizeinbase(mpq_numref(q), 10) + - mpz_sizeinbase (mpq_denref(q), 10) + 3]; - str = mpq_get_str(str, 10, q); - o << str ; - delete[] str; - return o; -} - -} //namespace CORE diff --git a/CGAL_Core/include/CGAL/CORE/MemoryPool.h b/CGAL_Core/include/CGAL/CORE/MemoryPool.h index 60a95c862e2..2db3de8736e 100644 --- a/CGAL_Core/include/CGAL/CORE/MemoryPool.h +++ b/CGAL_Core/include/CGAL/CORE/MemoryPool.h @@ -57,7 +57,7 @@ public: t = t->next; } //); - //CGAL_warning_msg(count == nObjects * blocks.size(), + //CGAL_CORE_warning_msg(count == nObjects * blocks.size(), // "Cannot delete memory as there are cyclic references"); if(count == nObjects * blocks.size()){ diff --git a/CGAL_Core/include/CGAL/CORE/Promote.h b/CGAL_Core/include/CGAL/CORE/Promote.h index d882b6abcf3..2bc334c959a 100644 --- a/CGAL_Core/include/CGAL/CORE/Promote.h +++ b/CGAL_Core/include/CGAL/CORE/Promote.h @@ -26,10 +26,12 @@ * SPDX-License-Identifier: LGPL-3.0-or-later ***************************************************************************/ -#ifndef __PROMOTE_H__ -#define __PROMOTE_H__ +#ifndef _CORE_PROMOTE_H__ +#define _CORE_PROMOTE_H__ #include +#include +#include namespace CORE { @@ -67,10 +69,10 @@ class Promotion { typedef T ResultT; }; -#define MAX_TYPE(T1, T2) \ +#define CORE_MAX_TYPE(T1, T2) \ typename Promotion::ResultT -#define DEFINE_MAX_TYPE(T1, T2, Tr) \ +#define CORE_DEFINE_MAX_TYPE(T1, T2, Tr) \ template<> class Promotion { \ public: \ typedef Tr ResultT; \ @@ -83,15 +85,15 @@ class Promotion { /* * For example: * - * DEFINE_MAX_TYPE(BigInt, BigRat, BigRat) // define the promotion + * CORE_DEFINE_MAX_TYPE(BigInt, BigRat, BigRat) // define the promotion * * template // define function f with type templates - * MAX_TYPE(T1, T2) f(T1& , T2& ); + * CORE_MAX_TYPE(T1, T2) f(T1& , T2& ); * * or * * template // define function f with type templates - * const MAX_TYPE(T1, T2)& f(T1& , T2& ); + * const CORE_MAX_TYPE(T1, T2)& f(T1& , T2& ); * * BigInt a = 1; * BigRat b = "1/3"; @@ -119,30 +121,30 @@ class Promotion { * */ -class BigInt; +//class BigInt; class BigFloat; -class BigRat; +//class BigRat; class Expr; -DEFINE_MAX_TYPE(long, BigInt, BigInt) -DEFINE_MAX_TYPE(long, BigFloat, BigFloat) -DEFINE_MAX_TYPE(long, BigRat, BigRat) -DEFINE_MAX_TYPE(long, Expr, Expr) +CORE_DEFINE_MAX_TYPE(long, BigInt, BigInt) +CORE_DEFINE_MAX_TYPE(long, BigFloat, BigFloat) +CORE_DEFINE_MAX_TYPE(long, BigRat, BigRat) +CORE_DEFINE_MAX_TYPE(long, Expr, Expr) -DEFINE_MAX_TYPE(int, BigInt, BigInt) -DEFINE_MAX_TYPE(int, BigFloat, BigFloat) -DEFINE_MAX_TYPE(int, BigRat, BigRat) -DEFINE_MAX_TYPE(int, Expr, Expr) +CORE_DEFINE_MAX_TYPE(int, BigInt, BigInt) +CORE_DEFINE_MAX_TYPE(int, BigFloat, BigFloat) +CORE_DEFINE_MAX_TYPE(int, BigRat, BigRat) +CORE_DEFINE_MAX_TYPE(int, Expr, Expr) -DEFINE_MAX_TYPE(BigInt, BigFloat, BigFloat) -DEFINE_MAX_TYPE(BigInt, BigRat, BigRat) -DEFINE_MAX_TYPE(BigInt, Expr, Expr) +CORE_DEFINE_MAX_TYPE(BigInt, BigFloat, BigFloat) +CORE_DEFINE_MAX_TYPE(BigInt, BigRat, BigRat) +CORE_DEFINE_MAX_TYPE(BigInt, Expr, Expr) -DEFINE_MAX_TYPE(BigFloat, BigRat, BigRat) -DEFINE_MAX_TYPE(BigFloat, Expr, Expr) +CORE_DEFINE_MAX_TYPE(BigFloat, BigRat, BigRat) +CORE_DEFINE_MAX_TYPE(BigFloat, Expr, Expr) -DEFINE_MAX_TYPE(BigRat, Expr, Expr) +CORE_DEFINE_MAX_TYPE(BigRat, Expr, Expr) } //namespace CORE -#endif //__PROMOTE_H__ +#endif //_CORE_PROMOTE_H__ diff --git a/CGAL_Core/include/CGAL/CORE/Real.h b/CGAL_Core/include/CGAL/CORE/Real.h index dc4573cda20..6ca51cbf449 100644 --- a/CGAL_Core/include/CGAL/CORE/Real.h +++ b/CGAL_Core/include/CGAL/CORE/Real.h @@ -277,12 +277,12 @@ const long halfLongMin = LONG_MIN /2; struct _real_add { template static Real eval(const T& a, const T& b) { - return a+b; + return T(a+b); } // specialized for two long values static Real eval(long a, long b) { if ((a > halfLongMax && b > halfLongMax) || (a < halfLongMin && b < halfLongMin)) - return BigInt(a)+BigInt(b); + return BigInt(BigInt(a)+ BigInt(b)); else return a+b; } @@ -291,12 +291,12 @@ struct _real_add { struct _real_sub { template static Real eval(const T& a, const T& b) { - return a-b; + return T(a-b); } // specialized for two long values static Real eval(long a, long b) { if ((a > halfLongMax && b < halfLongMin) || (a < halfLongMin && b > halfLongMax)) - return BigInt(a)-BigInt(b); + return BigInt(BigInt(a)-BigInt(b)); else return a-b; } @@ -305,12 +305,12 @@ struct _real_sub { struct _real_mul { template static Real eval(const T& a, const T& b) { - return a*b; + return T(a*b); } // specialized for two long values static Real eval(long a, long b) { if (flrLg(a) + flrLg(b) >= static_cast(LONG_BIT-2)) - return BigInt(a)*BigInt(b); + return BigInt(BigInt(a)*BigInt(b)); else return a*b; } @@ -357,7 +357,7 @@ struct real_div { bf_a.approx(a.BigRatValue(), bf_b.MSB() - bf_b.flrLgErr() + 1, CORE_posInfty); return bf_a.div(bf_b, r); } else // both are BigRat - return a.BigRatValue()/b.BigRatValue(); + return BigRat(a.BigRatValue() / b.BigRatValue()); } else if (a.ID() == REAL_BIGFLOAT || b.ID() == REAL_BIGFLOAT || a.ID() == REAL_DOUBLE || b.ID() == REAL_DOUBLE) { return a.BigFloatValue().div(b.BigFloatValue(), r); @@ -478,11 +478,11 @@ inline Real sqrt(const Real& x) { // unary minus operator template inline Real Realbase_for::operator-() const { - return -ker; + return T(- T(ker)); } template <> inline Real RealLong::operator-() const { - return ker < -LONG_MAX ? -BigInt(ker) : -ker; + return ker < -LONG_MAX ? BigInt(- BigInt(ker)) : -ker; } inline void init_CORE() { diff --git a/CGAL_Core/include/CGAL/CORE/RealRep.h b/CGAL_Core/include/CGAL/CORE/RealRep.h index 94602c2d1cb..1c5d0f13a40 100644 --- a/CGAL_Core/include/CGAL/CORE/RealRep.h +++ b/CGAL_Core/include/CGAL/CORE/RealRep.h @@ -92,10 +92,10 @@ public: int ID() const; long longValue() const { - return ker.longValue(); + return CORE::longValue(ker); } double doubleValue() const { - return ker.doubleValue(); + return CORE::doubleValue(ker); } BigInt BigIntValue() const { return BigInt(ker); @@ -231,7 +231,7 @@ inline BigInt RealBigInt::BigIntValue() const { } template<> inline BigInt RealBigRat::BigIntValue() const { - return ker.BigIntValue(); + return CORE::BigIntValue(ker); } template<> inline BigInt RealBigFloat::BigIntValue() const { @@ -500,11 +500,11 @@ inline unsigned long RealBigRat::height() const { // toString() template<> inline std::string RealBigInt::toString(long, bool) const { - return ker.get_str(); + return ker.convert_to(); } template<> inline std::string RealBigRat::toString(long, bool) const { - return ker.get_str(); + return ker.convert_to(); } template<> inline std::string RealBigFloat::toString(long prec, bool sci) const { diff --git a/CGAL_Core/include/CGAL/CORE/extLong.h b/CGAL_Core/include/CGAL/CORE/extLong.h index 52ba91e321a..c4146ebad84 100644 --- a/CGAL_Core/include/CGAL/CORE/extLong.h +++ b/CGAL_Core/include/CGAL/CORE/extLong.h @@ -31,6 +31,9 @@ #include #include +#include +#include + namespace CORE { #ifndef LONG_MAX @@ -77,6 +80,9 @@ public: extLong(long); /// constructor for \c unsigned long extLong(unsigned long); + /// constructor for \c std::size_t + template && !std::is_same_v>> + extLong(T s); //@} /// \name Arithmetic and assignment operators @@ -149,8 +155,7 @@ const extLong EXTLONG_EIGHT(8); // private comparison function inline int extLong::cmp(const extLong& x) const { if (isNaN() || x.isNaN()) { - core_error("Two extLong NaN's cannot be compared!", - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "Two extLong NaN's cannot be compared!"); } return (val == x.val) ? 0 : ((val > x.val) ? 1 : -1); } @@ -189,6 +194,17 @@ inline extLong::extLong(unsigned long u) { } } +template +inline extLong::extLong(T u) { + if (u >= (std::numeric_limits::max)()) { + val = EXTLONG_MAX; + flag = 1; + } else { + val = static_cast(u); + flag = 0; + } +} + // isNaN defaults to false inline extLong::extLong(bool isNaN) : val(0), flag(0) { if (isNaN) { diff --git a/CGAL_Core/include/CGAL/CORE/extLong_impl.h b/CGAL_Core/include/CGAL/CORE/extLong_impl.h index 24c2bab879d..1d636545eee 100644 --- a/CGAL_Core/include/CGAL/CORE/extLong_impl.h +++ b/CGAL_Core/include/CGAL/CORE/extLong_impl.h @@ -77,7 +77,7 @@ extLong& extLong::operator+= (const extLong& y) { if (flag == 2 || y.flag == 2 || (flag * y.flag < 0)) { #ifdef CORE_DEBUG if (flag * y.flag < 0) //want a message at the first creation of NaN - core_error("extLong NaN Error in addition.", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "extLong NaN Error in addition."); #endif *this = CORE_NaNLong; @@ -96,7 +96,7 @@ extLong& extLong::operator-= (const extLong& y) { if (flag == 2 || y.flag == 2 || (flag * y.flag > 0)) { #ifdef CORE_DEBUG if (flag * y.flag > 0) //want a message at the first creation of NaN - core_error("extLong NaN Error in subtraction.", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "extLong NaN Error in subtraction."); #endif *this = CORE_NaNLong; @@ -131,7 +131,7 @@ extLong& extLong::operator*= (const extLong& y) { *this = CORE_negInfty; } else { #ifdef CORE_DEBUG - core_error("extLong NaN Error in multiplication.",__FILE__,__LINE__,false); + CGAL_CORE_warning_msg(false, "extLong NaN Error in multiplication."); #endif *this = CORE_NaNLong; } @@ -144,9 +144,9 @@ extLong& extLong::operator/= (const extLong& y) { if (flag==2 || y.flag==2 || ((flag != 0) && (y.flag != 0)) || (y.val == 0)) { #ifdef CORE_DEBUG if (y.val == 0) - core_error("extLong NaN Error, Divide by Zero.", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "extLong NaN Error, Divide by Zero."); else if ((flag !=0) && (y.flag !=0)) - core_error("extLong NaN Error, +/-Inf/Inf.", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "extLong NaN Error, +/-Inf/Inf."); #endif *this = CORE_NaNLong; @@ -180,8 +180,7 @@ extLong extLong::operator- () const { // you cannot interpret the returned value! CGAL_INLINE_FUNCTION int extLong::sign() const { - if (flag == 2) - core_error("NaN Sign can not be determined!", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(flag != 2, "NaN Sign can not be determined!"); return ((val == 0) ? 0 : ((val > 0) ? 1 : -1)); } diff --git a/CGAL_Core/include/CGAL/CORE/poly/Curves.tcc b/CGAL_Core/include/CGAL/CORE/poly/Curves.tcc index 4a9c8fb905c..3f53e34da44 100644 --- a/CGAL_Core/include/CGAL/CORE/poly/Curves.tcc +++ b/CGAL_Core/include/CGAL/CORE/poly/Curves.tcc @@ -1336,7 +1336,7 @@ cout <<"Number of roots at " << xCurr << " are " << numRoots<::pseudoRemainder :\n -- divide by zero polynomial", __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "ERROR in Polynomial::pseudoRemainder :\n -- divide by zero polynomial"); return Polynomial(0); // Unit Polynomial (arbitrary!) } if (bTrueDegree > degree) { @@ -771,8 +771,8 @@ BigFloat Polynomial::eval(const BigFloat& f) const { // evaluation template template -MAX_TYPE(NT, T) Polynomial::eval(const T& f) const { // evaluation - typedef MAX_TYPE(NT, T) ResultT; +CORE_MAX_TYPE(NT, T) Polynomial::eval(const T& f) const { // evaluation + typedef CORE_MAX_TYPE(NT, T) ResultT; if (degree == -1) return ResultT(0); if (degree == 0) @@ -892,10 +892,10 @@ BigFloat Polynomial::CauchyUpperBound() const { NT mx = 0; int deg = getTrueDegree(); for (int i = 0; i < deg; ++i) { - mx = core_max(mx, abs(coeff[i])); + mx = core_max(mx, NT(abs(coeff[i]))); } Expr e = mx; - e /= Expr(abs(coeff[deg])); + e /= Expr(NT(abs(coeff[deg]))); e.approx(CORE_INFTY, 2); // get an absolute approximate value with error < 1/4 return (e.BigFloatValue().makeExact() + 2); @@ -922,7 +922,7 @@ BigInt Polynomial::CauchyBound() const { /* compute B^{deg} */ if (rhs <= lhs) { B <<= 1; - rhs *= (BigInt(1)<::UpperBound() const { /* compute B^{deg} */ if (rhs <= (std::max)(lhsPos,lhsNeg)) { B <<= 1; - rhs *= (BigInt(1)<::CauchyLowerBound() const { NT mx = 0; int deg = getTrueDegree(); for (int i = 1; i <= deg; ++i) { - mx = core_max(mx, abs(coeff[i])); + mx = core_max(mx, NT(abs(coeff[i]))); } - Expr e = Expr(abs(coeff[0]))/ Expr(abs(coeff[0]) + mx); + Expr e = Expr(NT(abs(coeff[0])))/ Expr(NT(NT(abs(coeff[0])) + mx)); e.approx(2, CORE_INFTY); // get an relative approximate value with error < 1/4 return (e.BigFloatValue().makeExact().div2()); @@ -1018,8 +1018,8 @@ BigFloat Polynomial::height() const { int deg = getTrueDegree(); NT ht = 0; for (int i = 0; i< deg; i++) - if (ht < abs(coeff[i])) - ht = abs(coeff[i]); + if (ht < NT(abs(coeff[i]))) + ht = NT(abs(coeff[i])); return BigFloat(ht); } diff --git a/CGAL_Core/include/CGAL/CORE/poly/Sturm.h b/CGAL_Core/include/CGAL/CORE/poly/Sturm.h index d044b124c1f..2fe00e3706d 100644 --- a/CGAL_Core/include/CGAL/CORE/poly/Sturm.h +++ b/CGAL_Core/include/CGAL/CORE/poly/Sturm.h @@ -607,8 +607,7 @@ public: if (ff == 0) { NEWTON_DIV_BY_ZERO = true; del = 0; - core_error("Zero divisor in Newton Iteration", - __FILE__, __LINE__, false); + CGAL_CORE_warning_msg(false, "Zero divisor in Newton Iteration"); return 0; } @@ -680,8 +679,7 @@ public: stepsize++; // heuristic } while ((del != 0) && ((del.uMSB() >= -prec) && (count >0))) ; - if (count == 0) core_error("newtonIterE: reached count=0", - __FILE__, __LINE__, true); + CGAL_assertion_msg(count != 0, "newtonIterE: reached count=0"); del = BigFloat(core_abs(del.m()), err, del.exp() ); del.makeCeilExact(); return val; diff --git a/CGAL_Core/package_info/CGAL_Core/license.txt b/CGAL_Core/package_info/CGAL_Core/license.txt index 66d68590d24..5c1c5d8436f 100644 --- a/CGAL_Core/package_info/CGAL_Core/license.txt +++ b/CGAL_Core/package_info/CGAL_Core/license.txt @@ -1,2 +1 @@ -LGPL (v3) LGPL (v3 or later) diff --git a/Circular_kernel_2/package_info/Circular_kernel_2/dependencies b/Circular_kernel_2/package_info/Circular_kernel_2/dependencies index ffbd888bc13..2621ba85ea7 100644 --- a/Circular_kernel_2/package_info/Circular_kernel_2/dependencies +++ b/Circular_kernel_2/package_info/Circular_kernel_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_circles Arithmetic_kernel +CGAL_Core Cartesian_kernel Circular_kernel_2 Distance_2 diff --git a/Circular_kernel_3/package_info/Circular_kernel_3/dependencies b/Circular_kernel_3/package_info/Circular_kernel_3/dependencies index dfceb904b44..476a5513bf0 100644 --- a/Circular_kernel_3/package_info/Circular_kernel_3/dependencies +++ b/Circular_kernel_3/package_info/Circular_kernel_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_spheres Arithmetic_kernel +CGAL_Core Cartesian_kernel Circular_kernel_3 Distance_2 diff --git a/Combinatorial_map/package_info/Combinatorial_map/dependencies b/Combinatorial_map/package_info/Combinatorial_map/dependencies index 279a01a72ae..0902e1798f1 100644 --- a/Combinatorial_map/package_info/Combinatorial_map/dependencies +++ b/Combinatorial_map/package_info/Combinatorial_map/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Combinatorial_map diff --git a/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies b/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies index fa1556add87..09382985c87 100644 --- a/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies +++ b/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Convex_decomposition_3 diff --git a/Convex_hull_3/package_info/Convex_hull_3/dependencies b/Convex_hull_3/package_info/Convex_hull_3/dependencies index 406722ba9af..bdd3aa15916 100644 --- a/Convex_hull_3/package_info/Convex_hull_3/dependencies +++ b/Convex_hull_3/package_info/Convex_hull_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Data/data/meshes/halfcube.off b/Data/data/meshes/halfcube.off new file mode 100644 index 00000000000..4c9a166bc42 --- /dev/null +++ b/Data/data/meshes/halfcube.off @@ -0,0 +1,13 @@ +OFF +5 4 0 + +-1 -1 1 +-1 1 1 +1 -1 -1 +-1 1 -1 +-1 -1 -1 +3 0 4 2 +3 2 4 3 +3 4 0 1 +3 1 3 4 + diff --git a/Documentation/doc/Documentation/Third_party.txt b/Documentation/doc/Documentation/Third_party.txt index b50e062d61c..ceb3eb7e74a 100644 --- a/Documentation/doc/Documentation/Third_party.txt +++ b/Documentation/doc/Documentation/Third_party.txt @@ -56,7 +56,7 @@ or `h The \stl comes with the compiler, and as such no installation is required. \subsection thirdpartyBoost Boost -Version 1.66 or later +Version 1.72 or later The \boost libraries are a set of portable C++ source libraries. Most of \boost libraries are header-only, but a few of them need to be compiled or @@ -72,7 +72,7 @@ from `htt As there is no canonical directory for where to find \boost on Windows, we recommend that you define the environment variable -`BOOST_ROOT` and set it to where you have installed \boost, e.g., `C:\boost\boost_1_69_0`. +`BOOST_ROOT` and set it to where you have installed \boost, e.g., `C:\boost\boost_1_70_0`. \subsection thirdpartyMPFR GNU Multiple Precision Arithmetic (GMP) and GNU Multiple Precision Floating-Point Reliably (MPFR) Libraries GMP Version 4.2 or later, MPFR Version 2.2.1 or later diff --git a/Documentation/doc/Documentation/advanced/Configuration_variables.txt b/Documentation/doc/Documentation/advanced/Configuration_variables.txt index 7c475f54776..d4ac891a30f 100644 --- a/Documentation/doc/Documentation/advanced/Configuration_variables.txt +++ b/Documentation/doc/Documentation/advanced/Configuration_variables.txt @@ -103,8 +103,8 @@ other but never both. \subsection installation_boost Boost Libraries -\subsubsection inst_boost_1_70_plus Version 1.70 and Later -Starting from \boost 1.70, the cmake config mode can be used for configuring the \boost version +\subsubsection inst_boost_1_72_plus Version 1.72 and Later +Starting from \boost 1.72, the cmake config mode can be used for configuring the \boost version to use by setting the environment variable `Boost_DIR` to the path containing the file `BoostConfig.cmake`. For example if you manually installed \boost 1.77 with `--prefix=`, then you should set `Boost_DIR=/lib/cmake/Boost-1.77.0`. diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index aa061c842d8..7002020a1a0 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -31,6 +31,7 @@ \cgalPackageSection{PartPolygons,Polygons} \package_listing{Polygon} +\package_listing{Polygon_repair} \package_listing{Boolean_set_operations_2} \package_listing{Nef_2} \package_listing{Nef_S2} diff --git a/Documentation/doc/biblio/geom.bib b/Documentation/doc/biblio/geom.bib index 28b9dae8862..f57c0ff3f43 100644 --- a/Documentation/doc/biblio/geom.bib +++ b/Documentation/doc/biblio/geom.bib @@ -152054,3 +152054,13 @@ pages = {179--189} year={2009}, organization={ACM} } + +@article{ledoux2014triangulation, + title={A triangulation-based approach to automatically repair GIS polygons}, + author={Ledoux, Hugo and Ohori, Ken Arroyo and Meijers, Martijn}, + journal={Computers \& Geosciences}, + volume={66}, + pages={121--131}, + year={2014}, + publisher={Elsevier} +} diff --git a/Envelope_3/include/CGAL/Env_sphere_traits_3.h b/Envelope_3/include/CGAL/Env_sphere_traits_3.h index 3d272f81b29..bb7f34a25be 100644 --- a/Envelope_3/include/CGAL/Env_sphere_traits_3.h +++ b/Envelope_3/include/CGAL/Env_sphere_traits_3.h @@ -265,7 +265,7 @@ public: if (n_ys == 0) return o; // no intersection // the x coordinate of the solution points - Algebraic xs = m / (2*a_diff); + Algebraic xs = m / (Rational(2)*a_diff); if (n_ys == 1) { // intersection is a point @@ -340,7 +340,7 @@ public: } if (n_xs == 1) { // intersection is a point - Point_2 inter_point(xs[0], (-2*a_diff*xs[0] + m)/(2*b_diff) ); + Point_2 inter_point(xs[0], (-Rational(2)*a_diff*xs[0] + m)/(Rational(2)*b_diff) ); *o++ = inter_point; return o; } @@ -350,8 +350,8 @@ public: // so we construct a COLLINEAR conic (with equation as in (1)) // with 2 endpoints Algebraic ys[2]; - ys[0] = (-2*a_diff*xs[0] + m)/(2*b_diff); - ys[1] = (-2*a_diff*xs[1] + m)/(2*b_diff); + ys[0] = (-Rational(2)*a_diff*xs[0] + m)/(Rational(2)*b_diff); + ys[1] = (-Rational(2)*a_diff*xs[1] + m)/(Rational(2)*b_diff); Alg_point_2 end1(xs[0], ys[0]); Alg_point_2 end2(xs[1], ys[1]); @@ -457,7 +457,7 @@ public: int envelope_coef = 1; if (! m_traits.m_is_lower) envelope_coef = -1; - Sign sign_c_diff = CGAL_NTS sign(c_diff); + Rational sign_c_diff = Rational(sign(c_diff)); Rational la = envelope_coef*2*a_diff*sign_c_diff; Rational lb = envelope_coef*2*b_diff*sign_c_diff; Rational lc = envelope_coef*sign_c_diff*(2*c_diff*z_plane - m); @@ -977,10 +977,10 @@ public: const Rational& u = cv.u(); const Rational& v = cv.v(); // const Rational& w = cv.w(); // unused - Algebraic m = -1 * (2*r*x0 + t*y0 + u); - Algebraic n = 2*s*y0 + t*x0 + v; + Algebraic m = -1 * (Rational(2)*r*x0 + t*y0 + u); + Algebraic n = Rational(2)*s*y0 + t*x0 + v; // line coefficients: A3, B3, C3 - Algebraic A3 = -1*m, B3 = n, C3 = m*x0 - n*y0; + Algebraic A3 = -m, B3 = n, C3 = m*x0 - n*y0; // the tangences of the spheres (in point (x0,y0,z0)): Algebraic z0 = compute_envelope_z_in_point(cv_point, s1); @@ -1077,8 +1077,8 @@ public: Algebraic x_diff = x1 - a, y_diff = y1 - b; // the coefficients are: Algebraic A = 1; - Algebraic B = -2*c; - Algebraic C = x_diff*x_diff + y_diff*y_diff + c*c - sqr_r; + Algebraic B = -Rational(2)*c; + Algebraic C = x_diff*x_diff + y_diff*y_diff + Algebraic(c*c - sqr_r); Algebraic zs[2]; Algebraic* zs_end; diff --git a/Envelope_3/package_info/Envelope_3/dependencies b/Envelope_3/package_info/Envelope_3/dependencies index 5abc02d0186..8b0ee9f9511 100644 --- a/Envelope_3/package_info/Envelope_3/dependencies +++ b/Envelope_3/package_info/Envelope_3/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Apollonius_graph_2 Arithmetic_kernel Arrangement_on_surface_2 +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Filtered_kernel/include/CGAL/Lazy.h b/Filtered_kernel/include/CGAL/Lazy.h index f8c7a62cdbc..2f1201ee239 100644 --- a/Filtered_kernel/include/CGAL/Lazy.h +++ b/Filtered_kernel/include/CGAL/Lazy.h @@ -101,11 +101,26 @@ templateinline std::enable_if_t::value||std::is_e templateinline std::enable_if_t::value||std::is_enum::value, T> exact (T d){return d;} templateinline std::enable_if_t::value||std::is_enum::value, int> depth(T){return -1;} +templateinline std::enable_if_t::value, Quotient> approx(Quotient d){return d;} +templateinline std::enable_if_t::value, Quotient> exact (Quotient d){return d;} +templateinline std::enable_if_t::value, int> depth(Quotient){return -1;} + // For tag classes: Return_base_tag, Homogeneous_tag, Null_vector, Origin templateinline std::enable_if_t::value, T> exact(T){return {};} templateinline std::enable_if_t::value, T> approx(T){return {};} templateinline std::enable_if_t::value, int> depth(T){return -1;} +namespace internal{ +template +struct Evaluate> +{ + void operator()(const Lazy& l) + { + exact(l); + } +}; +} // internal namespace + // For an iterator, exact/approx applies to the objects it points to template ::value>> auto exact(T const& t) {return make_transforming_iterator(t,[](auto const&u)->decltype(auto){return CGAL::exact(u);});} diff --git a/Filtered_kernel/package_info/Filtered_kernel/dependencies b/Filtered_kernel/package_info/Filtered_kernel/dependencies index 4b009ce54e4..5e320e63952 100644 --- a/Filtered_kernel/package_info/Filtered_kernel/dependencies +++ b/Filtered_kernel/package_info/Filtered_kernel/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +CGAL_Core Cartesian_kernel Distance_2 Distance_3 diff --git a/Generalized_map/package_info/Generalized_map/dependencies b/Generalized_map/package_info/Generalized_map/dependencies index bf19ede8a16..97e368892d5 100644 --- a/Generalized_map/package_info/Generalized_map/dependencies +++ b/Generalized_map/package_info/Generalized_map/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Combinatorial_map diff --git a/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h b/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h index 330e5005d53..a4510dd552c 100644 --- a/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h +++ b/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h @@ -25,6 +25,8 @@ #include #include #include +#include + #ifdef CGAL_EIGEN3_ENABLED #include #endif diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 01179cc55c7..906bd670cf8 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -15,7 +15,16 @@ Release date: October 2023 - **Breaking change**: The usage of `boost::optional` has been replaced by `std::optional`. Packages affected are 2D Straight Line Skeleton, 3D Fast Intersection and Distance Computation (AABB Tree), and the Kernel intersection. - **Breaking change**: The usage of `boost::variant` has been replaced by `std::variant`. Packages affected are 2D Arrangements, and the Kernel intersection. - **Breaking change**: The file CMake file `UseCGAL.cmake` has been removed from CGAL. Usages of the CMake variables `${CGAL_USE_FILE}` and `${CGAL_LIBRARIES}` must be replaced by a link to the imported target `CGAL::CGAL`, for example: `target_link_library(the_target PRIVATE CGAL::CGAL)`. +- The minimal supported version of Boost is now 1.72.0 +### Installation + +- The CGAL\_Core library is no longer based on GMP but boost multiprecision now, and can be used with either gmp backend or boost backend. + +### [Polygon Repair](https://doc.cgal.org/6.0/Manual/packages.html#PkgPolygonRepair) (new package) + +- This package provides functions to repair polygons, polygons with holes, and multipolygons with holes + using the odd-even heuristic. #### 2D Arrangements @@ -44,6 +53,12 @@ Release date: October 2023 - Removed the class templates `Gray_image_mesh_domain_3`, `Implicit_mesh_domain_3`, and `Labeled_image_mesh_domain_3` which are deprecated since CGAL-4.13. +### [Quadtrees, Octrees, and Orthtrees](https://doc.cgal.org/6.0/Manual/packages.html#PkgOrthtree) +- **Breaking change**: + - Node splitting behavior and per-node data are now customizable via the Traits class. + - Nodes are now stored as a property map, with properties of each node accessed by index. + - Nearest neighbors functions only work for Orthtrees which provide the necessary functionality. + ### [Polygon Mesh Processing](https://doc.cgal.org/6.0/Manual/packages.html#PkgPolygonMeshProcessing) - Added the function `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures()` which can be used to compute diff --git a/Installation/CMakeLists.txt b/Installation/CMakeLists.txt index fa2102ff7c0..6335ead8411 100644 --- a/Installation/CMakeLists.txt +++ b/Installation/CMakeLists.txt @@ -115,6 +115,9 @@ option(CGAL_ENABLE_TESTING "Build the testing tree." ${BUILD_TESTING}) endif() endif(CGAL_BRANCH_BUILD) +#allow to force disabling boost multiprecision support +option(CGAL_DO_NOT_USE_BOOST_MP "Disable the support of boost multiprecision library" FALSE) + #message(STATUS "Packages found: ${CGAL_CONFIGURED_PACKAGES}") list(SORT CGAL_CONFIGURED_PACKAGES_NAMES) diff --git a/Installation/cmake/modules/CGAL_SetupBoost.cmake b/Installation/cmake/modules/CGAL_SetupBoost.cmake index fccdd488a68..ee561027370 100644 --- a/Installation/cmake/modules/CGAL_SetupBoost.cmake +++ b/Installation/cmake/modules/CGAL_SetupBoost.cmake @@ -17,9 +17,9 @@ set ( CGAL_Boost_Setup TRUE ) include(${CMAKE_CURRENT_LIST_DIR}/CGAL_TweakFindBoost.cmake) -find_package( Boost 1.66 REQUIRED ) +find_package( Boost 1.72 REQUIRED ) -if(Boost_FOUND AND Boost_VERSION VERSION_LESS 1.70) +if(Boost_FOUND AND Boost_VERSION VERSION_LESS 1.72) if(DEFINED Boost_DIR AND NOT Boost_DIR) # Unset that cache variable that is set in the cache by FindBoost # (while it was searching for boost-cmake). diff --git a/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake b/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake index 1d51bbbdac5..347ab99e71c 100644 --- a/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake +++ b/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake @@ -131,12 +131,6 @@ function(CGAL_setup_CGAL_flags target) $<$:/fp:except-> $<$:/bigobj> # Use /bigobj by default ) - elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0.3) - message(STATUS "Apple Clang version ${CMAKE_CXX_COMPILER_VERSION} compiler detected") - message(STATUS "Boost MP is turned off for all Apple Clang versions below 11.0.3!") - target_compile_options(${target} INTERFACE "-DCGAL_DO_NOT_USE_BOOST_MP") - endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") message( STATUS "Using Intel Compiler. Adding -fp-model strict" ) if(WIN32) @@ -166,4 +160,9 @@ function(CGAL_setup_CGAL_flags target) target_compile_options(${target} INTERFACE "-mieee" "-mfp-rounding-mode=d" ) endif() endif() + + if (CGAL_DO_NOT_USE_BOOST_MP) + target_compile_options(${target} INTERFACE "-DCGAL_DO_NOT_USE_BOOST_MP") + endif() + endfunction() diff --git a/Installation/cmake/modules/CGAL_SetupCGAL_CoreDependencies.cmake b/Installation/cmake/modules/CGAL_SetupCGAL_CoreDependencies.cmake index 88b5db13449..816ae656fb4 100644 --- a/Installation/cmake/modules/CGAL_SetupCGAL_CoreDependencies.cmake +++ b/Installation/cmake/modules/CGAL_SetupCGAL_CoreDependencies.cmake @@ -18,9 +18,6 @@ endif() set(CGAL_SetupCGAL_CoreDependencies_included TRUE) #.rst: -# Used Modules -# ^^^^^^^^^^^^ -# - :module:`CGAL_SetupGMP` # # Result Variables # ^^^^^^^^^^^^^^^^ @@ -29,13 +26,10 @@ set(CGAL_SetupCGAL_CoreDependencies_included TRUE) # # Set to `TRUE` if the dependencies of `CGAL_Core` were found. -if(NOT CGAL_DISABLE_GMP) - include(${CMAKE_CURRENT_LIST_DIR}/CGAL_SetupGMP.cmake) - if(GMP_FOUND) - set(CGAL_Core_FOUND TRUE) - set_property(GLOBAL PROPERTY CGAL_Core_FOUND TRUE) - endif() -endif() + +# always found as it requires the minimal version of boost required by CGAL +set(CGAL_Core_FOUND TRUE) +set_property(GLOBAL PROPERTY CGAL_Core_FOUND TRUE) #.rst: # @@ -54,8 +48,10 @@ endif() # function(CGAL_setup_CGAL_Core_dependencies target) - use_CGAL_GMP_support(CGAL_Core INTERFACE) + find_package( Boost 1.72 REQUIRED ) + if (!CGAL_DISABLE_GMP AND GMP_FOUND) + use_CGAL_GMP_support(CGAL_Core INTERFACE) + endif() target_compile_definitions(${target} INTERFACE CGAL_USE_CORE=1) target_link_libraries( CGAL_Core INTERFACE CGAL::CGAL ) - endfunction() diff --git a/Installation/cmake/modules/CGAL_SetupDependencies.cmake b/Installation/cmake/modules/CGAL_SetupDependencies.cmake deleted file mode 100644 index 6051c7d3ce4..00000000000 --- a/Installation/cmake/modules/CGAL_SetupDependencies.cmake +++ /dev/null @@ -1,13 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/CGAL_Macros.cmake) - -if( (GMP_FOUND AND NOT MPFR_FOUND) OR (NOT GMP_FOUND AND MPFR_FOUND) ) - message( FATAL_ERROR "CGAL needs for its full functionality both GMP and MPFR.") -endif() - -if( NOT GMP_FOUND ) - set(CGAL_NO_CORE ON) - message( STATUS "CGAL_Core needs GMP, cannot be configured.") -endif( NOT GMP_FOUND ) - -# finally setup Boost -include(${CMAKE_CURRENT_LIST_DIR}/CGAL_SetupBoost.cmake) diff --git a/Installation/include/CGAL/Installation/internal/enable_third_party_libraries.h b/Installation/include/CGAL/Installation/internal/enable_third_party_libraries.h index dd9ecf8c707..461a41707d1 100644 --- a/Installation/include/CGAL/Installation/internal/enable_third_party_libraries.h +++ b/Installation/include/CGAL/Installation/internal/enable_third_party_libraries.h @@ -35,8 +35,32 @@ # endif // CGAL_USE_MPFR and no #endif // __has_include -#if CGAL_USE_GMP && CGAL_USE_MPFR && ! CGAL_NO_CORE + +// It is easier to disable this number type completely for old versions. +// Before 1.63, I/O is broken. Again, disabling the whole file is just the +// easy solution. +// MSVC had trouble with versions <= 1.69: +// https://github.com/boostorg/multiprecision/issues/98 +// +// Disable also on Windows 32 bits +// because CGAL/cpp_float.h assumes _BitScanForward64 is available +// See https://learn.microsoft.com/en-us/cpp/intrinsics/bitscanforward-bitscanforward64 +// +// Disable also with PowerPC processors, with Boost<1.80 because of that bug: +// https://github.com/boostorg/multiprecision/pull/421 +// +#if !defined CGAL_DO_NOT_USE_BOOST_MP && \ + (!defined _MSC_VER || BOOST_VERSION >= 107000) && \ + (!defined _WIN32 || defined _WIN64) && \ + (BOOST_VERSION >= 108000 || (!defined _ARCH_PPC && !defined _ARCH_PPC64)) +#define CGAL_USE_BOOST_MP 1 +#endif + + +#if CGAL_USE_BOOST_MP +#if ! CGAL_NO_CORE # define CGAL_USE_CORE 1 #endif +#endif #endif // CGAL_INTERNAL_ENABLE_THIRD_PARTY_LIBRARIES_H diff --git a/Installation/include/CGAL/license/Polygon_repair.h b/Installation/include/CGAL/license/Polygon_repair.h new file mode 100644 index 00000000000..b1fc77cedb6 --- /dev/null +++ b/Installation/include/CGAL/license/Polygon_repair.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Andreas Fabri +// +// Warning: this file is generated, see include/CGAL/license/README.md + +#ifndef CGAL_LICENSE_POLYGON_REPAIR_H +#define CGAL_LICENSE_POLYGON_REPAIR_H + +#include +#include + +#ifdef CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE + +# if CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +# if defined(CGAL_LICENSE_WARNING) + + CGAL_pragma_warning("Your commercial license for CGAL does not cover " + "this release of the 2D Polygon Repair package.") +# endif + +# ifdef CGAL_LICENSE_ERROR +# error "Your commercial license for CGAL does not cover this release \ + of the 2D Polygon Repair package. \ + You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +# endif // CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +#else // no CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE + +# if defined(CGAL_LICENSE_WARNING) + CGAL_pragma_warning("\nThe macro CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE is not defined." + "\nYou use the CGAL 2D Polygon Repair package under " + "the terms of the GPLv3+.") +# endif // CGAL_LICENSE_WARNING + +# ifdef CGAL_LICENSE_ERROR +# error "The macro CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE is not defined.\ + You use the CGAL 2D Polygon Repair package under the terms of \ + the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +#endif // no CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE + +#endif // CGAL_LICENSE_POLYGON_REPAIR_H diff --git a/Installation/lib/cmake/CGAL/CGALConfig.cmake b/Installation/lib/cmake/CGAL/CGALConfig.cmake index 93e9e8830f0..0ebd009ac8d 100644 --- a/Installation/lib/cmake/CGAL/CGALConfig.cmake +++ b/Installation/lib/cmake/CGAL/CGALConfig.cmake @@ -127,12 +127,18 @@ if( CGAL_DEV_MODE OR RUNNING_CGAL_AUTO_TEST OR CGAL_TEST_SUITE ) endif() endif() +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0.3) + message(STATUS "Apple Clang version ${CMAKE_CXX_COMPILER_VERSION} compiler detected") + message(STATUS "Boost MP is turned off for all Apple Clang versions below 11.0.3!") + set(CGAL_DO_NOT_USE_BOOST_MP TRUE) +endif() + foreach(comp ${CGAL_FIND_COMPONENTS}) if(NOT comp MATCHES "Core|ImageIO|Qt6") message(FATAL_ERROR "The requested CGAL component ${comp} does not exist!") endif() - if(comp MATCHES "Core" AND CGAL_DISABLE_GMP) - message("CGAL_Core needs GMP and won't be used.") + if(comp MATCHES "Core" AND CGAL_DO_NOT_USE_BOOST_MP) + message(STATUS "CGAL_Core needs Boost multiprecision support and won't be used.") else() list(APPEND CGAL_LIBRARIES CGAL_${comp}) endif() diff --git a/LICENSES/LGPL-3.0-only.txt b/LICENSES/LGPL-3.0-only.txt deleted file mode 100644 index 65c5ca88a67..00000000000 --- a/LICENSES/LGPL-3.0-only.txt +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/Linear_cell_complex/package_info/Linear_cell_complex/dependencies b/Linear_cell_complex/package_info/Linear_cell_complex/dependencies index 53e5ce3f153..a3ecaf39ee2 100644 --- a/Linear_cell_complex/package_info/Linear_cell_complex/dependencies +++ b/Linear_cell_complex/package_info/Linear_cell_complex/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Combinatorial_map diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h index 94220c276e6..a76ee2c5ecf 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h @@ -403,6 +403,13 @@ Mesher_3::Mesher_3(C3T3& c3t3, cells_mesher_.set_stop_pointer(stop_ptr); facets_mesher_.set_stop_pointer(stop_ptr); #endif + + // First surface mesh could modify c3t3 without notifying cells_mesher + // So we have to ensure that no old cell will be left in c3t3 + // Second, the c3t3 object could have been corrupted since the last call + // to `refine_mesh`, for example by inserting new vertices in the + // triangulation. + r_c3t3_.clear_cells_and_facets_from_c3t3(); } @@ -423,13 +430,6 @@ refine_mesh(std::string dump_after_refine_surface_prefix) auto refine_volume_mesh_task_handle = __itt_string_handle_create("Mesher_3 refine volume mesh"); #endif // CGAL_MESH_3_USE_INTEL_ITT - // First surface mesh could modify c3t3 without notifying cells_mesher - // So we have to ensure that no old cell will be left in c3t3 - // Second, the c3t3 object could have been corrupted since the last call - // to `refine_mesh`, for example by inserting new vertices in the - // triangulation. - r_c3t3_.clear_cells_and_facets_from_c3t3(); - const Triangulation& r_tr = r_c3t3_.triangulation(); CGAL_USE(r_tr); diff --git a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h index 5d32379ab0a..22153550cf3 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h @@ -287,7 +287,6 @@ public: return Container_::no_longer_element_to_refine_impl(); } - // Gets the point to insert from the element to refine /// Gets the point to insert from the element to refine Bare_point refinement_point_impl(const Facet& facet) const { diff --git a/Mesh_3/include/CGAL/Mesh_3/Triangulation_helpers.h b/Mesh_3/include/CGAL/Mesh_3/Triangulation_helpers.h index e8517c94945..c0451ceee4b 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Triangulation_helpers.h +++ b/Mesh_3/include/CGAL/Mesh_3/Triangulation_helpers.h @@ -381,11 +381,16 @@ inside_protecting_balls(const Tr& tr, const Vertex_handle v, const Bare_point& p) const { + if(tr.number_of_vertices() == 0) + return false; + typename GT::Compare_weighted_squared_radius_3 cwsr = tr.geom_traits().compare_weighted_squared_radius_3_object(); - Vertex_handle nv = tr.nearest_power_vertex(p, v->cell()); + Cell_handle hint = (v == Vertex_handle()) ? Cell_handle() : v->cell(); + Vertex_handle nv = tr.nearest_power_vertex(p, hint); const Point& nvwp = tr.point(nv); + if(cwsr(nvwp, FT(0)) == CGAL::SMALLER) { typename Tr::Geom_traits::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); diff --git a/Mesh_3/include/CGAL/Polyhedral_mesh_domain_3.h b/Mesh_3/include/CGAL/Polyhedral_mesh_domain_3.h index 60f6d9bc827..1f9551ef1ac 100644 --- a/Mesh_3/include/CGAL/Polyhedral_mesh_domain_3.h +++ b/Mesh_3/include/CGAL/Polyhedral_mesh_domain_3.h @@ -347,7 +347,7 @@ public: } tree_.build(); } - bounding_tree_ = 0; + set_surface_only(); } /// @} diff --git a/Mesh_3/include/CGAL/make_mesh_3.h b/Mesh_3/include/CGAL/make_mesh_3.h index 79277f1643f..68d6198cbbb 100644 --- a/Mesh_3/include/CGAL/make_mesh_3.h +++ b/Mesh_3/include/CGAL/make_mesh_3.h @@ -45,9 +45,10 @@ init_c3t3(C3T3& c3t3, const MeshDomain& domain, const MeshCriteria&, { typedef typename MeshDomain::Point_3 Point_3; typedef typename MeshDomain::Index Index; - typedef std::vector > Initial_points_vector; - typedef typename Initial_points_vector::iterator Ipv_iterator; + typedef typename std::pair PI; + typedef std::vector Initial_points_vector; typedef typename C3T3::Vertex_handle Vertex_handle; + typedef CGAL::Mesh_3::Triangulation_helpers Th; // Mesh initialization : get some points and add them to the mesh Initial_points_vector initial_points; @@ -61,17 +62,18 @@ init_c3t3(C3T3& c3t3, const MeshDomain& domain, const MeshCriteria&, c3t3.triangulation().geom_traits().construct_weighted_point_3_object(); // Insert points and set their index and dimension - for ( Ipv_iterator it = initial_points.begin() ; - it != initial_points.end() ; - ++it ) + for (const PI& pi : initial_points) { - Vertex_handle v = c3t3.triangulation().insert(cwp(it->first)); + if(Th().inside_protecting_balls(c3t3.triangulation(), Vertex_handle(), pi.first)) + continue; + + Vertex_handle v = c3t3.triangulation().insert(cwp(pi.first)); // v could be null if point is hidden if ( v != Vertex_handle() ) { c3t3.set_dimension(v,2); // by construction, points are on surface - c3t3.set_index(v,it->second); + c3t3.set_index(v, pi.second); } } } diff --git a/Mesh_3/package_info/Mesh_3/dependencies b/Mesh_3/package_info/Mesh_3/dependencies index 65eec56b360..0f9c65cd105 100644 --- a/Mesh_3/package_info/Mesh_3/dependencies +++ b/Mesh_3/package_info/Mesh_3/dependencies @@ -2,6 +2,7 @@ AABB_tree Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core CGAL_ImageIO Cartesian_kernel Circulator diff --git a/Mesh_3/test/Mesh_3/CMakeLists.txt b/Mesh_3/test/Mesh_3/CMakeLists.txt index 34e45dacd95..e9a607fd38e 100644 --- a/Mesh_3/test/Mesh_3/CMakeLists.txt +++ b/Mesh_3/test/Mesh_3/CMakeLists.txt @@ -51,6 +51,7 @@ create_single_source_cgal_program( "test_meshing_without_features_determinism.cp create_single_source_cgal_program( "test_mesh_3_issue_1554.cpp" ) create_single_source_cgal_program( "test_mesh_polyhedral_domain_with_features_deprecated.cpp" ) create_single_source_cgal_program( "test_meshing_with_one_step.cpp" ) +create_single_source_cgal_program( "test_meshing_with_one_step_with_features.cpp" ) create_single_source_cgal_program( "test_mesh_cell_base_3.cpp") create_single_source_cgal_program( "test_min_edge_length.cpp") @@ -80,6 +81,7 @@ foreach(target test_mesh_polyhedral_domain_with_features_deprecated test_mesh_cell_base_3 test_meshing_with_one_step + test_meshing_with_one_step_with_features test_min_edge_length test_min_size_criteria test_meshing_polyhedral_complex_with_manifold_and_min_size diff --git a/Mesh_3/test/Mesh_3/test_meshing_with_one_step_with_features.cpp b/Mesh_3/test/Mesh_3/test_meshing_with_one_step_with_features.cpp new file mode 100644 index 00000000000..c3b01f084e7 --- /dev/null +++ b/Mesh_3/test/Mesh_3/test_meshing_with_one_step_with_features.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Domain +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Polyhedron; +typedef CGAL::Polyhedral_mesh_domain_with_features_3 Mesh_domain; + +typedef CGAL::Sequential_tag Concurrency_tag; + +// Triangulation +typedef CGAL::Mesh_triangulation_3::type Tr; + +typedef CGAL::Mesh_complex_3_in_triangulation_3 C3t3; + +// Criteria +typedef CGAL::Mesh_criteria_3 Mesh_criteria; + +typedef CGAL::Mesh_3::Mesher_3 Mesher; + +// To avoid verbose function and named parameters call +using namespace CGAL::parameters; + +int main() +{ + const std::string fname = CGAL::data_file_path("meshes/halfcube.off"); + // Create input polyhedron + Polyhedron polyhedron; + std::ifstream input(fname); + input >> polyhedron; + if(input.fail()){ + std::cerr << "Error: Cannot read file " << fname << std::endl; + return EXIT_FAILURE; + } + input.close(); + + if (!CGAL::is_triangle_mesh(polyhedron)){ + std::cerr << "Input geometry is not triangulated." << std::endl; + return EXIT_FAILURE; + } + + // Create domain + + std::vector polyhedra; + polyhedra.push_back(&polyhedron); + + Mesh_domain domain(polyhedra.begin(), polyhedra.end()); + domain.detect_features(); + + // Mesh criteria (no cell_size set) + Mesh_criteria criteria(facet_angle = 25, + facet_size = 0.17, + facet_distance = 0.017, + edge_size = 0.17); + + // Mesh generation + namespace p = CGAL::parameters; + C3t3 c3t3; + CGAL::Mesh_3::internal::C3t3_initializer()(c3t3, domain, criteria, true); + + Mesher mesher(c3t3, domain, criteria, CGAL::FACET_VERTICES_ON_SURFACE); + mesher.initialize(); + mesher.display_number_of_bad_elements(); + while ( ! mesher.is_algorithm_done() ) mesher.one_step(); + assert(c3t3.triangulation().number_of_vertices() > 200); + + // Output + mesher.display_number_of_bad_elements(); + + return EXIT_SUCCESS; +} diff --git a/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies b/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies index 62f64c4bfd1..a71a4c66f55 100644 --- a/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies +++ b/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies @@ -3,6 +3,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 Boolean_set_operations_2 +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Minkowski_sum_3/package_info/Minkowski_sum_3/dependencies b/Minkowski_sum_3/package_info/Minkowski_sum_3/dependencies index e2123abc9c9..b60f37e8338 100644 --- a/Minkowski_sum_3/package_info/Minkowski_sum_3/dependencies +++ b/Minkowski_sum_3/package_info/Minkowski_sum_3/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Convex_decomposition_3 diff --git a/Nef_3/package_info/Nef_3/dependencies b/Nef_3/package_info/Nef_3/dependencies index 7fec88e2373..92337ba55f1 100644 --- a/Nef_3/package_info/Nef_3/dependencies +++ b/Nef_3/package_info/Nef_3/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/NewKernel_d/package_info/NewKernel_d/dependencies b/NewKernel_d/package_info/NewKernel_d/dependencies index 04e1d32ff87..bfb2e6e6040 100644 --- a/NewKernel_d/package_info/NewKernel_d/dependencies +++ b/NewKernel_d/package_info/NewKernel_d/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +CGAL_Core Filtered_kernel Installation Interval_support diff --git a/Number_types/include/CGAL/CORE_BigFloat.h b/Number_types/include/CGAL/CORE_BigFloat.h index 812036e3aa6..e2def9175fb 100644 --- a/Number_types/include/CGAL/CORE_BigFloat.h +++ b/Number_types/include/CGAL/CORE_BigFloat.h @@ -178,13 +178,16 @@ public: // shift such that err.m()+err.err() fits into long int digits_long = std::numeric_limits::digits; - if(::CORE::bitLength(err.m()+err.err()) >= digits_long){ - long shift = ::CORE::bitLength(err.m()) - digits_long + 1 ; + if(::CORE::bitLength(err.m()+err.err()) >= static_cast(digits_long)){ + assert(std::size_t((std::numeric_limits::max)()) > ::CORE::bitLength(err.m())); + long shift = static_cast(::CORE::bitLength(err.m())) - digits_long + 1; //std::cout << "shift " << shift<< std::endl; - long new_err = ((err.m()+err.err()) >> shift).longValue()+1; + CORE::BigInt bi = (err.m() + err.err()); + bi = bi >> shift; + long new_err = CORE::longValue(bi)+1; err = CORE::BigFloat(0,new_err,0) * CORE::BigFloat::exp2(err.exp()*CORE::CHUNK_BIT+shift); }else{ - err = CORE::BigFloat(0,err.m().longValue()+err.err(),err.exp()); + err = CORE::BigFloat(0, CORE::longValue(err.m())+err.err(),err.exp()); } //print_bf(err,"new_err"); @@ -272,9 +275,10 @@ round(const CORE::BigFloat& x, long rel_prec = CORE::get_static_defRelPrec().toL // long shift = ::CORE::bitLength(m) - rel_prec - 1; long shift ; - if (err == 0) - shift = ::CORE::bitLength(m) - rel_prec - 3; - else + if (err == 0) { + assert(std::size_t((std::numeric_limits::max)()) > ::CORE::bitLength(m)); + shift = static_cast(::CORE::bitLength(m)) - rel_prec - 3; + }else shift = CGAL::relative_precision(x) - rel_prec -1; if( shift > 0 ){ @@ -324,7 +328,7 @@ public: NT w = Width()(x); w /= ::CORE::BigFloat(x.m()-x.err(),0,x.exp()); w = w.abs(); - return -(CORE::ceilLg(w.m()+w.err())+w.exp()*CORE::CHUNK_BIT); + return -(CORE::ceilLg(CORE::BigInt(w.m()+w.err()))+w.exp()*CORE::CHUNK_BIT); } }; diff --git a/Number_types/include/CGAL/CORE_BigInt.h b/Number_types/include/CGAL/CORE_BigInt.h index 0b599543164..d9be3a695d2 100644 --- a/Number_types/include/CGAL/CORE_BigInt.h +++ b/Number_types/include/CGAL/CORE_BigInt.h @@ -10,7 +10,6 @@ // // Author(s) : Michael Hemmer - #ifndef CGAL_CORE_BIGINT_H #define CGAL_CORE_BIGINT_H @@ -24,198 +23,6 @@ #include #include -namespace CGAL { - -// -// Algebraic structure traits -// -template <> class Algebraic_structure_traits< CORE::BigInt > - : public Algebraic_structure_traits_base< CORE::BigInt, - Euclidean_ring_tag > { - public: - typedef Tag_true Is_exact; - typedef Tag_false Is_numerical_sensitive; - - typedef INTERN_AST::Is_square_per_sqrt< Type > - Is_square; - - typedef INTERN_AST::Div_per_operator< Type > Div; - typedef INTERN_AST::Mod_per_operator< Type > Mod; - - class Sqrt - : public CGAL::cpp98::unary_function< Type, Type > { - public: - //! computes the largest NT not larger than the square root of \a a. - Type operator()( const Type& x) const { - Type result; - mpz_sqrt(result.get_mp(), x.get_mp()); - return result; - } - }; - - - class Gcd - : public CGAL::cpp98::binary_function< Type, Type, - Type > { - public: - Type operator()( const Type& x, - const Type& y) const { - if ( x == Type(0) && y == Type(0) ) - return Type(0); - Type result; - mpz_gcd(result.get_mp(), x.get_mp(), y.get_mp()); - return result; - } - }; -}; - -// -// Real embeddable traits -// -template <> class Real_embeddable_traits< CORE::BigInt > - : public INTERN_RET::Real_embeddable_traits_base< CORE::BigInt , CGAL::Tag_true > { - - public: - - class Abs - : public CGAL::cpp98::unary_function< Type, Type > { - public: - Type operator()( const Type& x ) const { - return CORE::abs( x ); - } - }; - - class Sgn - : public CGAL::cpp98::unary_function< Type, ::CGAL::Sign > { - public: - ::CGAL::Sign operator()( const Type& x ) const { - return (::CGAL::Sign) CORE::sign( x ); - } - }; - - class Compare - : public CGAL::cpp98::binary_function< Type, Type, - Comparison_result > { - public: - Comparison_result operator()( const Type& x, - const Type& y ) const { - return CGAL::sign(::CORE::cmp(x,y)); - } - }; - - class To_double - : public CGAL::cpp98::unary_function< Type, double > { - public: - double operator()( const Type& x ) const { - // this call is required to get reasonable values for the double - // approximation - return x.doubleValue(); - } - }; - - class To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - public: - std::pair operator()( const Type& x_ ) const { - CORE::Expr x(x_); - std::pair result; - x.doubleInterval(result.first, result.second); - CGAL_expensive_assertion(result.first <= x); - CGAL_expensive_assertion(result.second >= x); - return result; - } - }; -}; - -/*! \ingroup NiX_Modular_traits_spec - * \brief a model of concept ModularTraits, - * specialization of NiX::Modular_traits. - */ -template<> -class Modular_traits< ::CORE::BigInt > { - typedef Residue RES; - public: - typedef ::CORE::BigInt NT; - typedef CGAL::Tag_true Is_modularizable; - typedef Residue Residue_type; - - struct Modular_image{ - Residue_type operator()(const NT& a){ - NT tmp = a % NT(RES::get_current_prime()); -// TODO: reactivate this assertion -// it fails with core_v1.6x_20040329 -// NiX_assert(tmp.isInt()); - int mi(tmp.longValue()); - if (mi < 0) mi += RES::get_current_prime(); - return Residue_type(mi); - } - }; - struct Modular_image_representative{ - NT operator()(const Residue_type& x){ - return NT(x.get_value()); - } - }; -}; - - -template<> -struct Needs_parens_as_product{ - bool operator()(const CORE::BigInt& x){ - return CGAL_NTS is_negative(x); - } -}; - -// Benchmark_rep specialization -template<> -class Benchmark_rep< CORE::BigInt > { - const CORE::BigInt& t; -public: - //! initialize with a const reference to \a t. - Benchmark_rep( const CORE::BigInt& tt) : t(tt) {} - //! perform the output, calls \c operator\<\< by default. - std::ostream& operator()( std::ostream& out) const { - out << t; - return out; - } - - static std::string get_benchmark_name() { - return "Integer"; - } -}; - - -} //namespace CGAL - -//since types are included by CORE_coercion_traits.h: -#include -#include -#include -#include - -namespace Eigen { - template struct NumTraits; - template<> struct NumTraits - { - typedef CORE::BigInt Real; - typedef CORE::BigRat NonInteger; - typedef CORE::BigInt Nested; - typedef CORE::BigInt Literal; - - static inline Real epsilon() { return 0; } - static inline Real dummy_precision() { return 0; } - - enum { - IsInteger = 1, - IsSigned = 1, - IsComplex = 0, - RequireInitialization = 1, - ReadCost = 6, - AddCost = 30, - MulCost = 50 - }; - }; -} - #include #endif // CGAL_CORE_BIGINT_H diff --git a/Number_types/include/CGAL/CORE_BigRat.h b/Number_types/include/CGAL/CORE_BigRat.h index caf9b73886e..48d8e950db2 100644 --- a/Number_types/include/CGAL/CORE_BigRat.h +++ b/Number_types/include/CGAL/CORE_BigRat.h @@ -10,7 +10,6 @@ // // Author(s) : Michael Hemmer - #ifndef CGAL_CORE_BIGRAT_H #define CGAL_CORE_BIGRAT_H @@ -21,237 +20,6 @@ #include #include // used for To_interval-functor -//#if defined(CGAL_CORE_BIGRAT_NUMER_DENOM_ARE_MEMBERS) -// #define CGAL_CORE_NUMERATOR(X) ((X).numerator()) -// #define CGAL_CORE_DENOMINATOR(X) ((X).denominator()) -//#elif defined(CGAL_CORE_BIGRAT_NUMER_DENOM_ARE_NONMEMBERS) - #define CGAL_CORE_NUMERATOR(X) (numerator((X))) - #define CGAL_CORE_DENOMINATOR(X) (denominator((X))) -//#else - -namespace CGAL { - -// -// Algebraic structure traits -// -template <> class Algebraic_structure_traits< CORE::BigRat > - : public Algebraic_structure_traits_base< CORE::BigRat, - Field_tag > { - public: - typedef Tag_true Is_exact; - typedef Tag_false Is_numerical_sensitive; - - // BigRat are always normalized, so no special simplify-functor is needed - - // Nothing new... -}; - - - - -// -// Real embeddable traits -// -template <> class Real_embeddable_traits< CORE::BigRat > - : public INTERN_RET::Real_embeddable_traits_base< CORE::BigRat , CGAL::Tag_true > { - public: - - class Abs - : public CGAL::cpp98::unary_function< Type, Type > { - public: - Type operator()( const Type& x ) const { - return CORE::abs( x ); - } - }; - - class Sgn - : public CGAL::cpp98::unary_function< Type, ::CGAL::Sign > { - public: - ::CGAL::Sign operator()( const Type& x ) const { - return (::CGAL::Sign) CORE::sign( x ); - } - }; - - class Compare - : public CGAL::cpp98::binary_function< Type, Type, - Comparison_result > { - public: - Comparison_result operator()( const Type& x, - const Type& y ) const { - return CGAL::sign( ::CORE::cmp(x,y)); - } - CGAL_IMPLICIT_INTEROPERABLE_BINARY_OPERATOR_WITH_RT(Type,Comparison_result) - }; - - class To_double - : public CGAL::cpp98::unary_function< Type, double > { - public: - double operator()( const Type& x ) const { - // this call is required to get reasonable values for the double - // approximation - return x.doubleValue(); - } - }; - - class To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - public: - std::pair operator()( const Type& x_ ) const { - CORE::Expr x(x_); - std::pair result; - x.doubleInterval(result.first, result.second); - CGAL_expensive_assertion(result.first <= x); - CGAL_expensive_assertion(result.second >= x); - return result; - } - }; -}; - -/*! \ingroup NiX_Fraction_traits_spec - * \brief Specialization of Fraction_traits for ::leda::rational - */ -template <> -class Fraction_traits< CORE::BigRat > { -public: - typedef CORE::BigRat Type; - typedef ::CGAL::Tag_true Is_fraction; - typedef CORE::BigInt Numerator_type; - typedef Numerator_type Denominator_type; - - typedef Algebraic_structure_traits< Numerator_type >::Gcd Common_factor; - - class Decompose { - public: - typedef Type first_argument_type; - typedef Numerator_type& second_argument_type; - typedef Numerator_type& third_argument_type; - void operator () ( - const Type& rat, - Numerator_type& num, - Numerator_type& den) { - num = CGAL_CORE_NUMERATOR(rat); - den = CGAL_CORE_DENOMINATOR(rat); - } - }; - - class Compose { - public: - typedef Numerator_type first_argument_type; - typedef Numerator_type second_argument_type; - typedef Type result_type; - Type operator ()( - const Numerator_type& num , - const Numerator_type& den ) { - return Type(num, den); - } - }; -}; - -template -class Output_rep< ::CORE::BigRat, F> : public IO_rep_is_specialized { - const ::CORE::BigRat& t; -public: - //! initialize with a const reference to \a t. - Output_rep( const ::CORE::BigRat& tt) : t(tt) {} - //! perform the output, calls \c operator\<\< by default. - std::ostream& operator()( std::ostream& out) const { - switch (IO::get_mode(out)) { - case IO::PRETTY:{ - if(CGAL_CORE_DENOMINATOR(t) == ::CORE::BigRat(1)) - return out < -struct Needs_parens_as_product< ::CORE::BigRat >{ - bool operator()( ::CORE::BigRat t){ - if (CGAL_CORE_DENOMINATOR(t) != 1 ) - return true; - else - return needs_parens_as_product(CGAL_CORE_NUMERATOR(t)) ; - } -}; - -template <> -class Output_rep< ::CORE::BigRat, Parens_as_product_tag > - : public IO_rep_is_specialized -{ - const ::CORE::BigRat& t; -public: - // Constructor - Output_rep( const ::CORE::BigRat& tt) : t(tt) {} - // operator - std::ostream& operator()( std::ostream& out) const { - Needs_parens_as_product< ::CORE::BigRat > needs_parens_as_product; - if (needs_parens_as_product(t)) - return out <<"("<< IO::oformat(t) <<")"; - else - return out << IO::oformat(t); - } -}; - -// Benchmark_rep specialization -template<> -class Benchmark_rep< CORE::BigRat > { - const CORE::BigRat& t; -public: - //! initialize with a const reference to \a t. - Benchmark_rep( const CORE::BigRat& tt) : t(tt) {} - //! perform the output, calls \c operator\<\< by default. - std::ostream& operator()( std::ostream& out) const { - out << "Rational(" << numerator(t) << "," << denominator(t) << ")"; - return out; - } - - static std::string get_benchmark_name() { - return "Rational"; - } -}; - -} //namespace CGAL - -//since types are included by CORE_coercion_traits.h: -#include -#include -#include -#include - -namespace Eigen { - template struct NumTraits; - template<> struct NumTraits - { - typedef CORE::BigRat Real; - typedef CORE::BigRat NonInteger; - typedef CORE::BigRat Nested; - typedef CORE::BigRat Literal; - - static inline Real epsilon() { return 0; } - static inline Real dummy_precision() { return 0; } - - enum { - IsInteger = 0, - IsSigned = 1, - IsComplex = 0, - RequireInitialization = 1, - ReadCost = 6, - AddCost = 150, - MulCost = 100 - }; - }; -} - #include #endif // CGAL_CORE_BIGRAT_H diff --git a/Number_types/include/CGAL/CORE_coercion_traits.h b/Number_types/include/CGAL/CORE_coercion_traits.h index cf1df0bab3e..f5426e10cb3 100644 --- a/Number_types/include/CGAL/CORE_coercion_traits.h +++ b/Number_types/include/CGAL/CORE_coercion_traits.h @@ -24,29 +24,11 @@ #include -//#include namespace CGAL { //CORE internal coercions: - -// The following definitions reflect the interaction of the CORE number types -// with the built in types, -// CORE BigInt: - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(short ,::CORE::BigInt) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(int ,::CORE::BigInt) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(long ,::CORE::BigInt) - - -// CORE BigRat: - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(short ,::CORE::BigRat) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(int ,::CORE::BigRat) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(long ,::CORE::BigRat) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(float ,::CORE::BigRat) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(double ,::CORE::BigRat) - CGAL_DEFINE_COERCION_TRAITS_FROM_TO(::CORE::BigInt,::CORE::BigRat) - // CORE Expr: CGAL_DEFINE_COERCION_TRAITS_FROM_TO(short ,::CORE::Expr) CGAL_DEFINE_COERCION_TRAITS_FROM_TO(int ,::CORE::Expr) @@ -75,12 +57,14 @@ struct Coercion_traits{ typedef Type result_type; Type operator()(const CORE::BigFloat& x) const { return x;} Type operator()(const ::CORE::BigInt x) const { - CORE::BigFloat result; - result.approx(x,CORE::get_static_defRelPrec().toLong(),LONG_MAX); - // Do not use MakeFloorExact as it changes the Bigfloat - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()-result.err(),0,result.exp())) <= x ); - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()+result.err(),0,result.exp())) >= x ); - return result; + CORE::BigFloat result; + result.approx(x,CORE::get_static_defRelPrec().toLong(),LONG_MAX); + // Do not use MakeFloorExact as it changes the Bigfloat + CGAL_postcondition_code(::CORE::BigRat r = ::CORE::BigFloat(result.m()-result.err(),0,result.exp())); + CGAL_postcondition( r <= x ); + CGAL_postcondition_code(::CORE::BigRat r2 = ::CORE::BigFloat(result.m()+result.err(),0,result.exp())); + CGAL_postcondition( r2 >= x ); + return result; } }; }; @@ -95,12 +79,13 @@ struct Coercion_traits{ typedef Type result_type; Type operator()(const CORE::BigFloat& x) const { return x;} Type operator()(const ::CORE::BigRat x) const { - CORE::BigFloat result(x,CORE::get_static_defRelPrec().toLong(),LONG_MAX); - // Do not use MakeFloorExact as it changes the Bigfloat - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()-result.err(),0,result.exp())) <= x ); - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()+result.err(),0,result.exp())) >= x ); - return result; + // Do not use MakeFloorExact as it changes the Bigfloat + CGAL_postcondition_code(::CORE::BigRat r = ::CORE::BigFloat(result.m()-result.err(),0,result.exp())); + CGAL_postcondition( r <= x ); + CGAL_postcondition_code(::CORE::BigRat r2 = ::CORE::BigFloat(result.m()+result.err(),0,result.exp())); + CGAL_postcondition( r2 >= x ); + return result; } }; }; @@ -115,11 +100,13 @@ struct Coercion_traits{ typedef Type result_type; Type operator()(const CORE::BigFloat& x) const { return x;} Type operator()(const ::CORE::Expr x) const { - CORE::BigFloat result(x, CORE::get_static_defRelPrec().toLong(),LONG_MAX); - // Do not use MakeFloorExact as it changes the Bigfloat - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()-result.err(),0,result.exp())) <= x ); - CGAL_postcondition( ::CORE::BigRat(::CORE::BigFloat(result.m()+result.err(),0,result.exp())) >= x ); - return result; + CORE::BigFloat result(x, CORE::get_static_defRelPrec().toLong(),LONG_MAX); + // Do not use MakeFloorExact as it changes the Bigfloat + CGAL_postcondition_code(::CORE::BigRat r = ::CORE::BigFloat(result.m()-result.err(),0,result.exp())); + CGAL_postcondition( r <= x ); + CGAL_postcondition_code(::CORE::BigRat r2 = ::CORE::BigFloat(result.m()+result.err(),0,result.exp())); + CGAL_postcondition( r2 >= x ); + return result; } }; }; @@ -150,6 +137,8 @@ template <> struct Coercion_traits< ::CORE::Expr, CORE::BigFloat > } //namespace CGAL + + #endif // CGAL_USE_CORE #endif //CGAL_CORE_COERCION_TRAITS_H 1 //EOF diff --git a/Number_types/include/CGAL/Counted_number.h b/Number_types/include/CGAL/Counted_number.h index bb3dc060f0e..60b854fdc8f 100644 --- a/Number_types/include/CGAL/Counted_number.h +++ b/Number_types/include/CGAL/Counted_number.h @@ -121,8 +121,12 @@ class Counted_number { Counted_number() {} //explicit Counted_number(int n) :m_rep(n){} explicit Counted_number(NT n) :m_rep(n){} + Counted_number operator-() const - {inc_neg_count();return Counted_number(-m_rep);} + { + inc_neg_count(); NT neg = -m_rep; return Counted_number(neg); + } + Counted_number const & operator+=(Counted_number const &n) { inc_add_count(); @@ -352,7 +356,7 @@ Counted_number operator+(Counted_number const &n1, Counted_number const &n2) { Counted_number::inc_add_count(); - return Counted_number(n1.rep() + n2.rep()); + return Counted_number(NT(n1.rep() + n2.rep())); } template @@ -360,7 +364,7 @@ Counted_number operator-(Counted_number const &n1, Counted_number const &n2) { Counted_number::inc_sub_count(); - return Counted_number(n1.rep() - n2.rep()); + return Counted_number(NT(n1.rep() - n2.rep())); } template @@ -368,7 +372,7 @@ Counted_number operator*(Counted_number const &n1, Counted_number const &n2) { Counted_number::inc_mul_count(); - return Counted_number(n1.rep() * n2.rep()); + return Counted_number(NT(n1.rep() * n2.rep())); } template @@ -376,7 +380,7 @@ Counted_number operator/(Counted_number const &n1, Counted_number const &n2) { Counted_number::inc_div_count(); - return Counted_number(n1.rep() / n2.rep()); + return Counted_number(NT(n1.rep() / n2.rep())); } template< class NT > diff --git a/Number_types/include/CGAL/Number_types/internal/Exact_type_selector.h b/Number_types/include/CGAL/Number_types/internal/Exact_type_selector.h index b0f645eb3a6..d29278219dc 100644 --- a/Number_types/include/CGAL/Number_types/internal/Exact_type_selector.h +++ b/Number_types/include/CGAL/Number_types/internal/Exact_type_selector.h @@ -23,7 +23,9 @@ #include #include -#include +#ifdef CGAL_USE_BOOST_MP +#include +#endif #ifdef CGAL_USE_GMP # include diff --git a/Number_types/include/CGAL/Root_of_traits.h b/Number_types/include/CGAL/Root_of_traits.h index 32d81e481a9..239d580e7bf 100644 --- a/Number_types/include/CGAL/Root_of_traits.h +++ b/Number_types/include/CGAL/Root_of_traits.h @@ -76,7 +76,9 @@ compute_roots_of_2(const NT &a_, const NT &b_, const NT &c_, OutputIterator oit) } } else { - *oit++ = -c/b; return oit; + Root_of_1 cb = -c / b; + *oit++ = Root_of_2(cb); + return oit; } } diff --git a/Number_types/include/CGAL/boost_mp.h b/Number_types/include/CGAL/boost_mp.h index b98980acbc5..42a06de04e7 100644 --- a/Number_types/include/CGAL/boost_mp.h +++ b/Number_types/include/CGAL/boost_mp.h @@ -12,902 +12,8 @@ #ifndef CGAL_BOOST_MP_H #define CGAL_BOOST_MP_H -#include -#include - -// It is easier to disable this number type completely for old versions. -// Before 1.63, I/O is broken. Again, disabling the whole file is just the -// easy solution. -// MSVC had trouble with versions <= 1.69: -// https://github.com/boostorg/multiprecision/issues/98 -// -// Disable also on Windows 32 bits -// because CGAL/cpp_float.h assumes _BitScanForward64 is available -// See https://learn.microsoft.com/en-us/cpp/intrinsics/bitscanforward-bitscanforward64 -// -// Disable also with PowerPC processors, with Boost<1.80 because of that bug: -// https://github.com/boostorg/multiprecision/pull/421 -// -#if !defined CGAL_DO_NOT_USE_BOOST_MP && \ - (!defined _MSC_VER || BOOST_VERSION >= 107000) && \ - (!defined _WIN32 || defined _WIN64) && \ - (BOOST_VERSION >= 108000 || (!defined _ARCH_PPC && !defined _ARCH_PPC64)) -#define CGAL_USE_BOOST_MP 1 - -#include -#include // *ary_function -#include -#include -// We can't just include all Boost.Multiprecision here... -#include -#include -// ... but we kind of have to :-( -#include -#ifdef CGAL_USE_GMP -// Same dance as in CGAL/gmp.h -# include -# if defined(BOOST_MSVC) -# pragma warning(push) -# pragma warning(disable: 4127 4244 4146 4267) // conversion with loss of data - // warning on - applied on unsigned number -# endif - -# include - -# if defined(BOOST_MSVC) -# pragma warning(pop) -# endif - -# include -#endif -#ifdef CGAL_USE_MPFR -# include -#endif - -// TODO: work on the coercions (end of the file) - -namespace CGAL { - -// Algebraic_structure_traits - -template ::value> > -struct AST_boost_mp; - -template -struct AST_boost_mp > - : Algebraic_structure_traits_base< NT, Euclidean_ring_tag > { - typedef NT Type; - typedef Euclidean_ring_tag Algebraic_category; - typedef Boolean_tag::is_exact> Is_exact; - typedef Tag_false Is_numerical_sensitive; - - struct Is_zero: public CGAL::cpp98::unary_function { - bool operator()( const Type& x) const { - return x.is_zero(); - } - }; - - struct Div: - public CGAL::cpp98::binary_function { - template - Type operator()(const T& x, const U& y) const { - return x / y; - } - }; - - struct Mod: - public CGAL::cpp98::binary_function { - template - Type operator()(const T& x, const U& y) const { - return x % y; - } - }; - - struct Gcd : public CGAL::cpp98::binary_function { - template - Type operator()(const T& x, const U& y) const { - return boost::multiprecision::gcd(x, y); - } - }; - - struct Sqrt : public CGAL::cpp98::unary_function { - template - Type operator()(const T& x) const { - return boost::multiprecision::sqrt(x); - } - }; -}; - -template -struct AST_boost_mp > - : public Algebraic_structure_traits_base< NT , Field_tag > { - public: - typedef NT Type; - typedef Field_tag Algebraic_category; - typedef Tag_true Is_exact; - typedef Tag_false Is_numerical_sensitive; - - struct Is_zero: public CGAL::cpp98::unary_function { - bool operator()( const Type& x) const { - return x.is_zero(); - } - }; - - struct Div: - public CGAL::cpp98::binary_function { - template - Type operator()(const T& x, const U& y) const { - return x / y; - } - }; -}; - -template -struct Algebraic_structure_traits > -: AST_boost_mp > {}; -template -struct Algebraic_structure_traits > -: Algebraic_structure_traits::result_type > {}; - -// Real_embeddable_traits - -namespace Boost_MP_internal { - - // here we know that `intv` contains int64 numbers such that their msb is std::numeric_limits::digits-1 - // TODO: possibly return denormals sometimes... - inline - std::pair shift_positive_interval( const std::pair& intv, const int e ) { - CGAL_assertion(intv.first > 0.0); - CGAL_assertion(intv.second > 0.0); - -#ifdef CGAL_LITTLE_ENDIAN - CGAL_assertion_code( - union { - struct { uint64_t man:52; uint64_t exp:11; uint64_t sig:1; } s; - double d; - } conv; - - conv.d = intv.first; - ) -#else - //WARNING: untested! - CGAL_assertion_code( - union { - - struct { uint64_t sig:1; uint64_t exp:11; uint64_t man:52; } s; - double d; - } conv; - - conv.d = intv.first; - ) -#endif - // Check that the exponent of intv.inf is 52, which corresponds to a 53 bit integer - CGAL_assertion(conv.s.exp - ((1 << (11 - 1)) - 1) == std::numeric_limits::digits - 1); - - typedef std::numeric_limits limits; - - // warning: min_exponent and max_exponent are 1 more than what the name suggests - if (e < limits::min_exponent - limits::digits) - return {0, (limits::min)()}; - if (e > limits::max_exponent - limits::digits) - return {(limits::max)(), limits::infinity()}; // intv is positive - - const double scale = std::ldexp(1.0, e); // ldexp call is exact - return { scale * intv.first, scale * intv.second }; // cases that would require a rounding mode have been handled above - } - - // This function checks if the computed interval is correct and if it is tight. - template - bool are_bounds_correct( const double l, const double u, const Type& x ) { - typedef std::numeric_limits limits; - - const double inf = std::numeric_limits::infinity(); - if ( u!=l && (l==-inf || u==inf - || (u==0 && l >= -(limits::min)()) - || (l==0 && u <= (limits::min)())) ) - { - return x > Type((limits::max)()) || - x < Type(-(limits::max)()) || - (x > Type(-(limits::min)()) && x < Type((limits::min)())); - } - - if (!(u == l || u == std::nextafter(l, +inf))) return false; - //TODO: Type(nextafter(l,inf))>x && Type(nextafter(u,-inf)) get_0ulp_interval( const int shift, const uint64_t p ) { - - const double pp_dbl = static_cast(p); - const std::pair intv(pp_dbl, pp_dbl); - - return shift_positive_interval(intv, -shift); - } - - // This one returns 1 unit length interval. - inline - std::pair get_1ulp_interval( const int shift, const uint64_t p ) { - - const double pp_dbl = static_cast(p); - const double qq_dbl = pp_dbl+1; - const std::pair intv(pp_dbl, qq_dbl); - return shift_positive_interval(intv, -shift); - } - - template - std::pair to_interval( ET x, int extra_shift = 0 ); - - // This is a version of to_interval that converts a rational type into a - // double tight interval. - template - std::pair to_interval( ET xnum, ET xden ) { - - CGAL_assertion(!CGAL::is_zero(xden)); - CGAL_assertion_code(const Type input(xnum, xden)); - double l = 0.0, u = 0.0; - if (CGAL::is_zero(xnum)) { // return [0.0, 0.0] - CGAL_assertion(are_bounds_correct(l, u, input)); - return std::make_pair(l, u); - } - CGAL_assertion(!CGAL::is_zero(xnum)); - - // Handle signs. - bool change_sign = false; - const bool is_num_pos = CGAL::is_positive(xnum); - const bool is_den_pos = CGAL::is_positive(xden); - if (!is_num_pos && !is_den_pos) { - xnum = -xnum; - xden = -xden; - } else if (!is_num_pos && is_den_pos) { - change_sign = true; - xnum = -xnum; - } else if (is_num_pos && !is_den_pos) { - change_sign = true; - xden = -xden; - } - CGAL_assertion(CGAL::is_positive(xnum) && CGAL::is_positive(xden)); - - const int64_t num_dbl_digits = std::numeric_limits::digits - 1; - const int64_t msb_num = static_cast(boost::multiprecision::msb(xnum)); - const int64_t msb_den = static_cast(boost::multiprecision::msb(xden)); - -#if 0 // Optimisation for the case of input that are double - // An alternative strategy would be to convert numerator and denominator to - // intervals, then divide. However, this would require setting the rounding - // mode (and dividing intervals is not completely free). An important - // special case is when the rational is exactly equal to a double - // (fit_in_double). Then the denominator is a power of 2, so we can skip - // the division and it becomes unnecessary to set the rounding mode, we - // just need to modify the exponent correction for the denominator. - if(msb_den == static_cast(lsb(xden))) { - std::tie(l,u)=to_interval(xnum, msb_den); - if (change_sign) { - CGAL_assertion(are_bounds_correct(-u, -l, input)); - return {-u, -l}; - } - CGAL_assertion(are_bounds_correct(l, u, input)); - return {u, l}; - } -#endif - - const int64_t msb_diff = msb_num - msb_den; - // Shift so the division result has at least 53 (and at most 54) bits - int shift = static_cast(num_dbl_digits - msb_diff + 1); - CGAL_assertion(shift == num_dbl_digits - msb_diff + 1); - - if (shift > 0) { - xnum <<= +shift; - } else if (shift < 0) { - xden <<= -shift; - } - CGAL_assertion(num_dbl_digits + 1 == - static_cast(boost::multiprecision::msb(xnum)) - - static_cast(boost::multiprecision::msb(xden))); - - ET p, r; - boost::multiprecision::divide_qr(xnum, xden, p, r); - uint64_t uip = static_cast(p); - const int64_t p_bits = static_cast(boost::multiprecision::msb(p)); - bool exact = r.is_zero(); - - if (p_bits > num_dbl_digits) { // case 54 bits - exact &= ((uip & 1) == 0); - uip>>=1; - --shift; - } - std::tie(l, u) = exact ? get_0ulp_interval(shift, uip) : get_1ulp_interval(shift, uip); - - if (change_sign) { - const double t = l; - l = -u; - u = -t; - } - - CGAL_assertion(are_bounds_correct(l, u, input)); - return std::make_pair(l, u); - } - - // This is a version of to_interval that converts an integer type into a - // double tight interval. - template - std::pair to_interval( ET x, int extra_shift) { - - CGAL_assertion_code(const ET input = x); - double l = 0.0, u = 0.0; - if (CGAL::is_zero(x)) { // return [0.0, 0.0] - CGAL_assertion(are_bounds_correct(l, u, input)); - return std::make_pair(l, u); - } - CGAL_assertion(!CGAL::is_zero(x)); - - bool change_sign = false; - const bool is_pos = CGAL::is_positive(x); - if (!is_pos) { - change_sign = true; - x = -x; - } - CGAL_assertion(CGAL::is_positive(x)); - - const int64_t n = static_cast(boost::multiprecision::msb(x)) + 1; - const int64_t num_dbl_digits = std::numeric_limits::digits; - - if (n > num_dbl_digits) { - const int64_t mindig = static_cast(boost::multiprecision::lsb(x)); - int e = static_cast(n - num_dbl_digits); - x >>= e; - if (n - mindig > num_dbl_digits) - std::tie(l, u) = get_1ulp_interval(-e+extra_shift, static_cast(x)); - else - std::tie(l, u) = get_0ulp_interval(-e+extra_shift, static_cast(x)); - } else { - l = u = extra_shift==0 ? static_cast(static_cast(x)) - : std::ldexp(static_cast(static_cast(x)),-extra_shift); - } - - if (change_sign) { - const double t = l; - l = -u; - u = -t; - } - - CGAL_assertion(extra_shift != 0 || are_bounds_correct(l, u, input)); - return std::make_pair(l, u); - } - -} // Boost_MP_internal - -template -struct RET_boost_mp_base - : public INTERN_RET::Real_embeddable_traits_base< NT , CGAL::Tag_true > { - - typedef NT Type; - - struct Is_zero: public CGAL::cpp98::unary_function { - bool operator()( const Type& x) const { - return x.is_zero(); - } - }; - - struct Is_positive: public CGAL::cpp98::unary_function { - bool operator()( const Type& x) const { - return x.sign() > 0; - } - }; - - struct Is_negative: public CGAL::cpp98::unary_function { - bool operator()( const Type& x) const { - return x.sign() < 0; - } - }; - - struct Abs : public CGAL::cpp98::unary_function { - template - Type operator()(const T& x) const { - return boost::multiprecision::abs(x); - } - }; - - struct Sgn : public CGAL::cpp98::unary_function { - ::CGAL::Sign operator()(Type const& x) const { - return CGAL::sign(x.sign()); - } - }; - - struct Compare - : public CGAL::cpp98::binary_function { - Comparison_result operator()(const Type& x, const Type& y) const { - return CGAL::sign(x.compare(y)); - } - }; - - struct To_double - : public CGAL::cpp98::unary_function { - double operator()(const Type& x) const { - return x.template convert_to(); - } - }; - - struct To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - - std::pair - operator()(const Type& x) const { - - // See if https://github.com/boostorg/multiprecision/issues/108 suggests anything better - // assume the conversion is within 1 ulp - // adding IA::smallest() doesn't work because inf-e=inf, even rounded down. - - // We must use to_nearest here. - double i; - const double inf = std::numeric_limits::infinity(); - { - Protect_FPU_rounding P(CGAL_FE_TONEAREST); - i = static_cast(x); - if (i == +inf) { - return std::make_pair((std::numeric_limits::max)(), i); - } else if (i == -inf) { - return std::make_pair(i, std::numeric_limits::lowest()); - } - } - double s = i; - CGAL_assertion(CGAL::abs(i) != inf && CGAL::abs(s) != inf); - - // Throws uncaught exception: Cannot convert a non-finite number to an integer. - // We can catch it earlier by using the CGAL_assertion() one line above. - const int cmp = x.compare(i); - if (cmp > 0) { - s = nextafter(s, +inf); - CGAL_assertion(x.compare(s) < 0); - } - else if (cmp < 0) { - i = nextafter(i, -inf); - CGAL_assertion(x.compare(i) > 0); - } - return std::pair(i, s); - } - }; -}; - -template ::value> > -struct RET_boost_mp; - -template -struct RET_boost_mp > - : RET_boost_mp_base { - typedef NT Type; - struct To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - - std::pair operator()( const Type& x ) const { - return Boost_MP_internal::to_interval(x); - } - }; -}; - -template -struct RET_boost_mp > - : RET_boost_mp_base { - typedef NT Type; - struct To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - - std::pair operator()( const Type& x ) const { - return Boost_MP_internal::to_interval( - boost::multiprecision::numerator(x), boost::multiprecision::denominator(x)); - } - }; -}; - -#ifdef CGAL_USE_MPFR -// Because of these full specializations, things get instantiated more eagerly. Make them artificially partial if necessary. -template <> -struct RET_boost_mp - : RET_boost_mp_base { - typedef boost::multiprecision::mpz_int Type; - struct To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - std::pair - operator()(const Type& x) const { -#if MPFR_VERSION_MAJOR >= 3 - MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ - int r = mpfr_set_z (y, x.backend().data(), MPFR_RNDA); - double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ - if (r == 0 && is_finite (i)) - return std::pair(i, i); - else - { - double s = nextafter (i, 0); - if (i < 0) - return std::pair(i, s); - else - return std::pair(s, i); - } -#else - mpfr_t y; - mpfr_init2 (y, 53); /* Assume IEEE-754 */ - mpfr_set_z (y, x.backend().data(), GMP_RNDD); - double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ - mpfr_set_z (y, x.backend().data(), GMP_RNDU); - double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ - mpfr_clear (y); - return std::pair(i, s); -#endif - } - }; -}; -template <> -struct RET_boost_mp - : RET_boost_mp_base { - typedef boost::multiprecision::mpq_rational Type; - struct To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - std::pair - operator()(const Type& x) const { -# if MPFR_VERSION_MAJOR >= 3 - mpfr_exp_t emin = mpfr_get_emin(); - mpfr_set_emin(-1073); - MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ - int r = mpfr_set_q (y, x.backend().data(), MPFR_RNDA); - r = mpfr_subnormalize (y, r, MPFR_RNDA); /* Round subnormals */ - double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ - mpfr_set_emin(emin); /* Restore old value, users may care */ - // With mpfr_set_emax(1024) we could drop the is_finite test - if (r == 0 && is_finite (i)) - return std::pair(i, i); - else - { - double s = nextafter (i, 0); - if (i < 0) - return std::pair(i, s); - else - return std::pair(s, i); - } -# else - mpfr_t y; - mpfr_init2 (y, 53); /* Assume IEEE-754 */ - mpfr_set_q (y, x.backend().data(), GMP_RNDD); - double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ - mpfr_set_q (y, x.backend().data(), GMP_RNDU); - double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ - mpfr_clear (y); - return std::pair(i, s); -# endif - } - }; -}; -#endif - -template -struct Real_embeddable_traits > -: RET_boost_mp > {}; -template -struct Real_embeddable_traits > -: Real_embeddable_traits::result_type > {}; - -// Modular_traits - -template ::value> > -struct MT_boost_mp { - typedef T NT; - typedef ::CGAL::Tag_false Is_modularizable; - typedef ::CGAL::Null_functor Residue_type; - typedef ::CGAL::Null_functor Modular_image; - typedef ::CGAL::Null_functor Modular_image_representative; -}; - -template -struct MT_boost_mp > { - typedef T NT; - typedef CGAL::Tag_true Is_modularizable; - typedef Residue Residue_type; - - struct Modular_image{ - Residue_type operator()(const NT& a){ - NT tmp(CGAL::mod(a,NT(Residue::get_current_prime()))); - return CGAL::Residue(tmp.template convert_to()); - } - }; - struct Modular_image_representative{ - NT operator()(const Residue_type& x){ - return NT(x.get_value()); - } - }; -}; - -template -struct Modular_traits > -: MT_boost_mp > {}; -template -struct Modular_traits > -: Modular_traits::result_type > {}; - -// Split_double - -template ::value> > -struct SD_boost_mp { - void operator()(double d, NT &num, NT &den) const - { - num = d; - den = 1; - } -}; - -template -struct SD_boost_mp > -{ - void operator()(double d, NT &num, NT &den) const - { - std::pair p = split_numerator_denominator(d); - num = NT(p.first); - den = NT(p.second); - } -}; - -template -struct Split_double > -: SD_boost_mp > {}; -template -struct Split_double > -: Split_double::result_type > {}; - - -// Fraction_traits - -template ::value> > -struct FT_boost_mp { - typedef T Type; - typedef Tag_false Is_fraction; - typedef Null_tag Numerator_type; - typedef Null_tag Denominator_type; - typedef Null_functor Common_factor; - typedef Null_functor Decompose; - typedef Null_functor Compose; -}; - -template -struct FT_boost_mp > { - typedef NT Type; - - typedef ::CGAL::Tag_true Is_fraction; - typedef typename boost::multiprecision::component_type::type Numerator_type; - typedef Numerator_type Denominator_type; - - typedef typename Algebraic_structure_traits< Numerator_type >::Gcd Common_factor; - - class Decompose { - public: - typedef Type first_argument_type; - typedef Numerator_type& second_argument_type; - typedef Denominator_type& third_argument_type; - void operator () ( - const Type& rat, - Numerator_type& num, - Denominator_type& den) { - num = numerator(rat); - den = denominator(rat); - } - }; - - class Compose { - public: - typedef Numerator_type first_argument_type; - typedef Denominator_type second_argument_type; - typedef Type result_type; - Type operator ()( - const Numerator_type& num , - const Denominator_type& den ) { - return Type(num, den); - } - }; -}; - -template -struct Fraction_traits > -: FT_boost_mp > {}; -template -struct Fraction_traits > -: Fraction_traits::result_type > {}; - - -// Coercions - -namespace internal { namespace boost_mp { BOOST_MPL_HAS_XXX_TRAIT_DEF(type) } } - -template -struct Coercion_traits, boost::multiprecision::number > -{ - typedef boost::common_type, boost::multiprecision::number > CT; - typedef Boolean_tag::value> Are_implicit_interoperable; - // FIXME: the implicit/explicit answers shouldn't be the same... - typedef Are_implicit_interoperable Are_explicit_interoperable; - // FIXME: won't compile when they are not interoperable. - typedef typename CT::type Type; - struct Cast{ - typedef Type result_type; - template - Type operator()(const U& x) const { - return Type(x); - } - }; -}; -// Avoid ambiguity with the specialization for ... -template -struct Coercion_traits, boost::multiprecision::number > -{ - typedef boost::multiprecision::number Type; - typedef Tag_true Are_implicit_interoperable; - typedef Tag_true Are_explicit_interoperable; - struct Cast{ - typedef Type result_type; - template - Type operator()(const U& x) const { - return Type(x); - } - }; -}; - -template -struct Coercion_traits < -boost::multiprecision::detail::expression, -boost::multiprecision::detail::expression > -: Coercion_traits < -typename boost::multiprecision::detail::expression::result_type, -typename boost::multiprecision::detail::expression::result_type> -{ }; -// Avoid ambiguity with the specialization for ... -template -struct Coercion_traits < -boost::multiprecision::detail::expression, -boost::multiprecision::detail::expression > -: Coercion_traits < -typename boost::multiprecision::detail::expression::result_type, -typename boost::multiprecision::detail::expression::result_type> -{ }; - -template -struct Coercion_traits, boost::multiprecision::detail::expression > -: Coercion_traits < -boost::multiprecision::number, -typename boost::multiprecision::detail::expression::result_type> -{ }; - -template -struct Coercion_traits, boost::multiprecision::number > -: Coercion_traits < -typename boost::multiprecision::detail::expression::result_type, -boost::multiprecision::number > -{ }; - -// TODO: fix existing coercions -// (double -> rational is implicit only for 1.56+, see ticket #10082) -// The real solution would be to avoid specializing Coercion_traits for all pairs of number types and let it auto-detect what works, so only broken types need an explicit specialization. - -// Ignore types smaller than long -#define CGAL_COERCE_INT(int) \ -template \ -struct Coercion_traits, int> { \ - typedef boost::multiprecision::number Type; \ - typedef Tag_true Are_implicit_interoperable; \ - typedef Tag_true Are_explicit_interoperable; \ - struct Cast{ \ - typedef Type result_type; \ - template Type operator()(const U& x) const { return Type(x); } \ - }; \ -}; \ -template \ -struct Coercion_traits > \ -: Coercion_traits, int> {}; \ -template \ -struct Coercion_traits, int> \ -: Coercion_traits::result_type, int>{}; \ -template \ -struct Coercion_traits > \ -: Coercion_traits::result_type, int>{} - -CGAL_COERCE_INT(short); -CGAL_COERCE_INT(int); -CGAL_COERCE_INT(long); -#undef CGAL_COERCE_INT - -// Ignore bounded-precision rationals -#define CGAL_COERCE_FLOAT(float) \ -template \ -struct Coercion_traits, float> { \ - typedef boost::multiprecision::number Type; \ - typedef Boolean_tag::value != boost::multiprecision::number_kind_integer> Are_implicit_interoperable; \ - typedef Are_implicit_interoperable Are_explicit_interoperable; \ - struct Cast{ \ - typedef Type result_type; \ - template Type operator()(const U& x) const { return Type(x); } \ - }; \ -}; \ -template \ -struct Coercion_traits > \ -: Coercion_traits, float> {}; \ -template \ -struct Coercion_traits, float> \ -: Coercion_traits::result_type, float>{}; \ -template \ -struct Coercion_traits > \ -: Coercion_traits::result_type, float>{} - -CGAL_COERCE_FLOAT(float); -CGAL_COERCE_FLOAT(double); -#undef CGAL_COERCE_FLOAT - -// Because of https://github.com/boostorg/multiprecision/issues/29 , this is not perfect and fails to read some KDS files. - -template <> -class Input_rep : public IO_rep_is_specialized { - boost::multiprecision::cpp_rational& q; -public: - Input_rep(boost::multiprecision::cpp_rational& qq) : q(qq) {} - std::istream& operator()(std::istream& in) const { - internal::read_float_or_quotient(in, q); - return in; - } -}; -#ifdef CGAL_USE_GMP -template <> -class Input_rep : public IO_rep_is_specialized { - boost::multiprecision::mpq_rational& q; -public: - Input_rep(boost::multiprecision::mpq_rational& qq) : q(qq) {} - std::istream& operator()(std::istream& in) const { - internal::read_float_or_quotient(in, q); - return in; - } -}; -#endif - -// Copied from leda_rational.h -namespace internal { - // See: Stream_support/include/CGAL/IO/io.h - template - void read_float_or_quotient(std::istream & is, ET& et); - - template <> - inline void read_float_or_quotient(std::istream & is, boost::multiprecision::cpp_rational& et) - { - internal::read_float_or_quotient(is, et); - } -#ifdef CGAL_USE_GMP - template <> - inline void read_float_or_quotient(std::istream & is, boost::multiprecision::mpq_rational& et) - { - internal::read_float_or_quotient(is, et); - } -#endif -} // namespace internal - -#ifdef CGAL_USE_BOOST_MP - -template< > class Real_embeddable_traits< Quotient > - : public INTERN_QUOTIENT::Real_embeddable_traits_quotient_base< Quotient > { - - public: - typedef Quotient Type; - - class To_interval - : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { - public: - std::pair operator()( const Type& x ) const { - return Boost_MP_internal::to_interval(x.num, x.den); - } - }; -}; - -#endif // CGAL_USE_BOOST_MP - -} //namespace CGAL - +#include #include #include -#endif // BOOST_VERSION -#endif +#endif // CGAL_BOOST_MP_H diff --git a/Number_types/include/CGAL/boost_mp_type.h b/Number_types/include/CGAL/boost_mp_type.h new file mode 100644 index 00000000000..cf18678c011 --- /dev/null +++ b/Number_types/include/CGAL/boost_mp_type.h @@ -0,0 +1,1027 @@ +// Copyright (c) 2017 +// INRIA Saclay-Ile de France (France), +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Marc Glisse + +#ifndef CGAL_BOOST_MP_TYPE_H +#define CGAL_BOOST_MP_TYPE_H + +#include +#include + +// CGAL_USE_BOOST_MP is defined in +// CGAL/Installation/internal/enable_third_party_libraries.h +#if CGAL_USE_BOOST_MP + +#include +#include // *ary_function +#include +#include +// We can't just include all Boost.Multiprecision here... +#include +#include +// ... but we kind of have to :-( +#include +#ifdef CGAL_USE_GMP +// Same dance as in CGAL/gmp.h +# include +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable: 4127 4244 4146 4267) // conversion with loss of data + // warning on - applied on unsigned number +# endif + +# include + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +# include +#endif +#ifdef CGAL_USE_MPFR +# include +#endif + +// TODO: work on the coercions (end of the file) + +namespace CGAL { +template<> +struct Needs_parens_as_product{ + bool operator()(const typename boost::multiprecision::cpp_int& x){ + return x < 0; + } +}; + +template<> +struct Needs_parens_as_product{ + bool operator()(const typename boost::multiprecision::cpp_rational& x){ + if (denominator(x) != 1 ) + return true; + else + return needs_parens_as_product(numerator(x)) ; + } + +}; + +#ifdef CGAL_USE_GMP +template<> +struct Needs_parens_as_product{ + bool operator()(const typename boost::multiprecision::mpz_int& x){ + return x < 0; + } +}; + +template<> +struct Needs_parens_as_product{ + bool operator()(const typename boost::multiprecision::mpq_rational& x){ + if (denominator(x) != 1 ) + return true; + else + return needs_parens_as_product(numerator(x)) ; + } +}; +#endif + + + +// Algebraic_structure_traits + +template ::value> > +struct AST_boost_mp; + +template +struct AST_boost_mp > + : Algebraic_structure_traits_base< NT, Euclidean_ring_tag > { + typedef NT Type; + typedef Euclidean_ring_tag Algebraic_category; + typedef Boolean_tag::is_exact> Is_exact; + typedef Tag_false Is_numerical_sensitive; + + typedef INTERN_AST::Is_square_per_sqrt< Type > Is_square; + + struct Is_zero: public CGAL::cpp98::unary_function { + bool operator()( const Type& x) const { + return x.is_zero(); + } + }; + + struct Div: + public CGAL::cpp98::binary_function { + template + Type operator()(const T& x, const U& y) const { + return x / y; + } + }; + + struct Mod: + public CGAL::cpp98::binary_function { + template + Type operator()(const T& x, const U& y) const { + return x % y; + } + }; + + struct Gcd : public CGAL::cpp98::binary_function { + template + Type operator()(const T& x, const U& y) const { + return boost::multiprecision::gcd(x, y); + } + }; + + struct Sqrt : public CGAL::cpp98::unary_function { + template + Type operator()(const T& x) const { + return boost::multiprecision::sqrt(x); + } + }; +}; + +template +struct AST_boost_mp > + : public Algebraic_structure_traits_base< NT , Field_tag > { + public: + typedef NT Type; + typedef Field_tag Algebraic_category; + typedef Tag_true Is_exact; + typedef Tag_false Is_numerical_sensitive; + + struct Is_zero: public CGAL::cpp98::unary_function { + bool operator()( const Type& x) const { + return x.is_zero(); + } + }; + + struct Div: + public CGAL::cpp98::binary_function { + template + Type operator()(const T& x, const U& y) const { + return x / y; + } + }; +}; + +template +struct Algebraic_structure_traits > +: AST_boost_mp > {}; +template +struct Algebraic_structure_traits > +: Algebraic_structure_traits::result_type > {}; + +// Real_embeddable_traits + +namespace Boost_MP_internal { + + // here we know that `intv` contains int64 numbers such that their msb is std::numeric_limits::digits-1 + // TODO: possibly return denormals sometimes... + inline + std::pair shift_positive_interval( const std::pair& intv, const int e ) { + CGAL_assertion(intv.first > 0.0); + CGAL_assertion(intv.second > 0.0); + +#ifdef CGAL_LITTLE_ENDIAN + CGAL_assertion_code( + union { + struct { uint64_t man:52; uint64_t exp:11; uint64_t sig:1; } s; + double d; + } conv; + + conv.d = intv.first; + ) +#else + //WARNING: untested! + CGAL_assertion_code( + union { + + struct { uint64_t sig:1; uint64_t exp:11; uint64_t man:52; } s; + double d; + } conv; + + conv.d = intv.first; + ) +#endif + // Check that the exponent of intv.inf is 52, which corresponds to a 53 bit integer + CGAL_assertion(conv.s.exp - ((1 << (11 - 1)) - 1) == std::numeric_limits::digits - 1); + + typedef std::numeric_limits limits; + + // warning: min_exponent and max_exponent are 1 more than what the name suggests + if (e < limits::min_exponent - limits::digits) + return {0, (limits::min)()}; + if (e > limits::max_exponent - limits::digits) + return {(limits::max)(), limits::infinity()}; // intv is positive + + const double scale = std::ldexp(1.0, e); // ldexp call is exact + return { scale * intv.first, scale * intv.second }; // cases that would require a rounding mode have been handled above + } + + // This function checks if the computed interval is correct and if it is tight. + template + bool are_bounds_correct( const double l, const double u, const Type& x ) { + typedef std::numeric_limits limits; + + const double inf = std::numeric_limits::infinity(); + if ( u!=l && (l==-inf || u==inf + || (u==0 && l >= -(limits::min)()) + || (l==0 && u <= (limits::min)())) ) + { + return x > Type((limits::max)()) || + x < Type(-(limits::max)()) || + (x > Type(-(limits::min)()) && x < Type((limits::min)())); + } + + if (!(u == l || u == std::nextafter(l, +inf))) return false; + //TODO: Type(nextafter(l,inf))>x && Type(nextafter(u,-inf)) get_0ulp_interval( const int shift, const uint64_t p ) { + + const double pp_dbl = static_cast(p); + const std::pair intv(pp_dbl, pp_dbl); + + return shift_positive_interval(intv, -shift); + } + + // This one returns 1 unit length interval. + inline + std::pair get_1ulp_interval( const int shift, const uint64_t p ) { + + const double pp_dbl = static_cast(p); + const double qq_dbl = pp_dbl+1; + const std::pair intv(pp_dbl, qq_dbl); + return shift_positive_interval(intv, -shift); + } + + template + std::pair to_interval( ET x, int extra_shift = 0 ); + + // This is a version of to_interval that converts a rational type into a + // double tight interval. + template + std::pair to_interval( ET xnum, ET xden ) { + + CGAL_assertion(!CGAL::is_zero(xden)); + CGAL_assertion_code(const Type input(xnum, xden)); + double l = 0.0, u = 0.0; + if (CGAL::is_zero(xnum)) { // return [0.0, 0.0] + CGAL_assertion(are_bounds_correct(l, u, input)); + return std::make_pair(l, u); + } + CGAL_assertion(!CGAL::is_zero(xnum)); + + // Handle signs. + bool change_sign = false; + const bool is_num_pos = CGAL::is_positive(xnum); + const bool is_den_pos = CGAL::is_positive(xden); + if (!is_num_pos && !is_den_pos) { + xnum = -xnum; + xden = -xden; + } else if (!is_num_pos && is_den_pos) { + change_sign = true; + xnum = -xnum; + } else if (is_num_pos && !is_den_pos) { + change_sign = true; + xden = -xden; + } + CGAL_assertion(CGAL::is_positive(xnum) && CGAL::is_positive(xden)); + + const int64_t num_dbl_digits = std::numeric_limits::digits - 1; + const int64_t msb_num = static_cast(boost::multiprecision::msb(xnum)); + const int64_t msb_den = static_cast(boost::multiprecision::msb(xden)); + +#if 0 // Optimisation for the case of input that are double + // An alternative strategy would be to convert numerator and denominator to + // intervals, then divide. However, this would require setting the rounding + // mode (and dividing intervals is not completely free). An important + // special case is when the rational is exactly equal to a double + // (fit_in_double). Then the denominator is a power of 2, so we can skip + // the division and it becomes unnecessary to set the rounding mode, we + // just need to modify the exponent correction for the denominator. + if(msb_den == static_cast(lsb(xden))) { + std::tie(l,u)=to_interval(xnum, msb_den); + if (change_sign) { + CGAL_assertion(are_bounds_correct(-u, -l, input)); + return {-u, -l}; + } + CGAL_assertion(are_bounds_correct(l, u, input)); + return {u, l}; + } +#endif + + const int64_t msb_diff = msb_num - msb_den; + // Shift so the division result has at least 53 (and at most 54) bits + int shift = static_cast(num_dbl_digits - msb_diff + 1); + CGAL_assertion(shift == num_dbl_digits - msb_diff + 1); + + if (shift > 0) { + xnum <<= +shift; + } else if (shift < 0) { + xden <<= -shift; + } + CGAL_assertion(num_dbl_digits + 1 == + static_cast(boost::multiprecision::msb(xnum)) - + static_cast(boost::multiprecision::msb(xden))); + + ET p, r; + boost::multiprecision::divide_qr(xnum, xden, p, r); + uint64_t uip = static_cast(p); + const int64_t p_bits = static_cast(boost::multiprecision::msb(p)); + bool exact = r.is_zero(); + + if (p_bits > num_dbl_digits) { // case 54 bits + exact &= ((uip & 1) == 0); + uip>>=1; + --shift; + } + std::tie(l, u) = exact ? get_0ulp_interval(shift, uip) : get_1ulp_interval(shift, uip); + + if (change_sign) { + const double t = l; + l = -u; + u = -t; + } + + CGAL_assertion(are_bounds_correct(l, u, input)); + return std::make_pair(l, u); + } + + // This is a version of to_interval that converts an integer type into a + // double tight interval. + template + std::pair to_interval( ET x, int extra_shift) { + + CGAL_assertion_code(const ET input = x); + double l = 0.0, u = 0.0; + if (CGAL::is_zero(x)) { // return [0.0, 0.0] + CGAL_assertion(are_bounds_correct(l, u, input)); + return std::make_pair(l, u); + } + CGAL_assertion(!CGAL::is_zero(x)); + + bool change_sign = false; + const bool is_pos = CGAL::is_positive(x); + if (!is_pos) { + change_sign = true; + x = -x; + } + CGAL_assertion(CGAL::is_positive(x)); + + const int64_t n = static_cast(boost::multiprecision::msb(x)) + 1; + const int64_t num_dbl_digits = std::numeric_limits::digits; + + if (n > num_dbl_digits) { + const int64_t mindig = static_cast(boost::multiprecision::lsb(x)); + int e = static_cast(n - num_dbl_digits); + x >>= e; + if (n - mindig > num_dbl_digits) + std::tie(l, u) = get_1ulp_interval(-e+extra_shift, static_cast(x)); + else + std::tie(l, u) = get_0ulp_interval(-e+extra_shift, static_cast(x)); + } else { + l = u = extra_shift==0 ? static_cast(static_cast(x)) + : std::ldexp(static_cast(static_cast(x)),-extra_shift); + } + + if (change_sign) { + const double t = l; + l = -u; + u = -t; + } + + CGAL_assertion(extra_shift != 0 || are_bounds_correct(l, u, input)); + return std::make_pair(l, u); + } + +} // Boost_MP_internal + +template +struct RET_boost_mp_base + : public INTERN_RET::Real_embeddable_traits_base< NT , CGAL::Tag_true > { + + typedef NT Type; + + struct Is_zero: public CGAL::cpp98::unary_function { + bool operator()( const Type& x) const { + return x.is_zero(); + } + }; + + struct Is_positive: public CGAL::cpp98::unary_function { + bool operator()( const Type& x) const { + return x.sign() > 0; + } + }; + + struct Is_negative: public CGAL::cpp98::unary_function { + bool operator()( const Type& x) const { + return x.sign() < 0; + } + }; + + struct Abs : public CGAL::cpp98::unary_function { + template + Type operator()(const T& x) const { + return boost::multiprecision::abs(x); + } + }; + + struct Sgn : public CGAL::cpp98::unary_function { + ::CGAL::Sign operator()(Type const& x) const { + return CGAL::sign(x.sign()); + } + }; + + struct Compare + : public CGAL::cpp98::binary_function { + Comparison_result operator()(const Type& x, const Type& y) const { + return CGAL::sign(x.compare(y)); + } + }; + + struct To_double + : public CGAL::cpp98::unary_function { + double operator()(const Type& x) const { + return x.template convert_to(); + } + }; + + struct To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + + std::pair + operator()(const Type& x) const { + + // See if https://github.com/boostorg/multiprecision/issues/108 suggests anything better + // assume the conversion is within 1 ulp + // adding IA::smallest() doesn't work because inf-e=inf, even rounded down. + + // We must use to_nearest here. + double i; + const double inf = std::numeric_limits::infinity(); + { + Protect_FPU_rounding P(CGAL_FE_TONEAREST); + i = static_cast(x); + if (i == +inf) { + return std::make_pair((std::numeric_limits::max)(), i); + } else if (i == -inf) { + return std::make_pair(i, std::numeric_limits::lowest()); + } + } + double s = i; + CGAL_assertion(CGAL::abs(i) != inf && CGAL::abs(s) != inf); + + // Throws uncaught exception: Cannot convert a non-finite number to an integer. + // We can catch it earlier by using the CGAL_assertion() one line above. + const int cmp = x.compare(i); + if (cmp > 0) { + s = nextafter(s, +inf); + CGAL_assertion(x.compare(s) < 0); + } + else if (cmp < 0) { + i = nextafter(i, -inf); + CGAL_assertion(x.compare(i) > 0); + } + return std::pair(i, s); + } + }; +}; + +template ::value> > +struct RET_boost_mp; + +template +struct RET_boost_mp > + : RET_boost_mp_base { + typedef NT Type; + struct To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + + std::pair operator()( const Type& x ) const { + return Boost_MP_internal::to_interval(x); + } + }; +}; + +template +struct RET_boost_mp > + : RET_boost_mp_base { + typedef NT Type; + struct To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + + std::pair operator()( const Type& x ) const { + return Boost_MP_internal::to_interval( + boost::multiprecision::numerator(x), boost::multiprecision::denominator(x)); + } + }; +}; + +#ifdef CGAL_USE_MPFR +// Because of these full specializations, things get instantiated more eagerly. Make them artificially partial if necessary. +template <> +struct RET_boost_mp + : RET_boost_mp_base { + typedef boost::multiprecision::mpz_int Type; + struct To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + std::pair + operator()(const Type& x) const { +#if MPFR_VERSION_MAJOR >= 3 + MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ + int r = mpfr_set_z (y, x.backend().data(), MPFR_RNDA); + double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ + if (r == 0 && is_finite (i)) + return std::pair(i, i); + else + { + double s = nextafter (i, 0); + if (i < 0) + return std::pair(i, s); + else + return std::pair(s, i); + } +#else + mpfr_t y; + mpfr_init2 (y, 53); /* Assume IEEE-754 */ + mpfr_set_z (y, x.backend().data(), GMP_RNDD); + double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ + mpfr_set_z (y, x.backend().data(), GMP_RNDU); + double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ + mpfr_clear (y); + return std::pair(i, s); +#endif + } + }; +}; +template <> +struct RET_boost_mp + : RET_boost_mp_base { + typedef boost::multiprecision::mpq_rational Type; + struct To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + std::pair + operator()(const Type& x) const { +# if MPFR_VERSION_MAJOR >= 3 + mpfr_exp_t emin = mpfr_get_emin(); + mpfr_set_emin(-1073); + MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ + int r = mpfr_set_q (y, x.backend().data(), MPFR_RNDA); + r = mpfr_subnormalize (y, r, MPFR_RNDA); /* Round subnormals */ + double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ + mpfr_set_emin(emin); /* Restore old value, users may care */ + // With mpfr_set_emax(1024) we could drop the is_finite test + if (r == 0 && is_finite (i)) + return std::pair(i, i); + else + { + double s = nextafter (i, 0); + if (i < 0) + return std::pair(i, s); + else + return std::pair(s, i); + } +# else + mpfr_t y; + mpfr_init2 (y, 53); /* Assume IEEE-754 */ + mpfr_set_q (y, x.backend().data(), GMP_RNDD); + double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ + mpfr_set_q (y, x.backend().data(), GMP_RNDU); + double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ + mpfr_clear (y); + return std::pair(i, s); +# endif + } + }; +}; +#endif + +template +struct Real_embeddable_traits > +: RET_boost_mp > {}; +template +struct Real_embeddable_traits > +: Real_embeddable_traits::result_type > {}; + +// Modular_traits + +template ::value> > +struct MT_boost_mp { + typedef T NT; + typedef ::CGAL::Tag_false Is_modularizable; + typedef ::CGAL::Null_functor Residue_type; + typedef ::CGAL::Null_functor Modular_image; + typedef ::CGAL::Null_functor Modular_image_representative; +}; + +template +struct MT_boost_mp > { + typedef T NT; + typedef CGAL::Tag_true Is_modularizable; + typedef Residue Residue_type; + + struct Modular_image{ + Residue_type operator()(const NT& a){ + NT tmp(CGAL::mod(a,NT(Residue::get_current_prime()))); + return CGAL::Residue(tmp.template convert_to()); + } + }; + struct Modular_image_representative{ + NT operator()(const Residue_type& x){ + return NT(x.get_value()); + } + }; +}; + +template +struct Modular_traits > +: MT_boost_mp > {}; +template +struct Modular_traits > +: Modular_traits::result_type > {}; + +// Split_double + +template ::value> > +struct SD_boost_mp { + void operator()(double d, NT &num, NT &den) const + { + num = d; + den = 1; + } +}; + +template +struct SD_boost_mp > +{ + void operator()(double d, NT &num, NT &den) const + { + std::pair p = split_numerator_denominator(d); + num = NT(p.first); + den = NT(p.second); + } +}; + +template +struct Split_double > +: SD_boost_mp > {}; +template +struct Split_double > +: Split_double::result_type > {}; + + +// Fraction_traits + +template ::value> > +struct FT_boost_mp { + typedef T Type; + typedef Tag_false Is_fraction; + typedef Null_tag Numerator_type; + typedef Null_tag Denominator_type; + typedef Null_functor Common_factor; + typedef Null_functor Decompose; + typedef Null_functor Compose; +}; + +template +struct FT_boost_mp > { + typedef NT Type; + + typedef ::CGAL::Tag_true Is_fraction; + typedef typename boost::multiprecision::component_type::type Numerator_type; + typedef Numerator_type Denominator_type; + + typedef typename Algebraic_structure_traits< Numerator_type >::Gcd Common_factor; + + class Decompose { + public: + typedef Type first_argument_type; + typedef Numerator_type& second_argument_type; + typedef Denominator_type& third_argument_type; + void operator () ( + const Type& rat, + Numerator_type& num, + Denominator_type& den) { + num = numerator(rat); + den = denominator(rat); + } + }; + + class Compose { + public: + typedef Numerator_type first_argument_type; + typedef Denominator_type second_argument_type; + typedef Type result_type; + Type operator ()( + const Numerator_type& num , + const Denominator_type& den ) { + return Type(num, den); + } + }; +}; + +template +struct Fraction_traits > +: FT_boost_mp > {}; +template +struct Fraction_traits > +: Fraction_traits::result_type > {}; + + +// Coercions + +namespace internal { namespace boost_mp { BOOST_MPL_HAS_XXX_TRAIT_DEF(type) } } + +template +struct Coercion_traits, boost::multiprecision::number > +{ + typedef boost::common_type, boost::multiprecision::number > CT; + typedef Boolean_tag::value> Are_implicit_interoperable; + // FIXME: the implicit/explicit answers shouldn't be the same... + typedef Are_implicit_interoperable Are_explicit_interoperable; + // FIXME: won't compile when they are not interoperable. + typedef typename CT::type Type; + struct Cast{ + typedef Type result_type; + template + Type operator()(const U& x) const { + return Type(x); + } + }; +}; +// Avoid ambiguity with the specialization for ... +template +struct Coercion_traits, boost::multiprecision::number > +{ + typedef boost::multiprecision::number Type; + typedef Tag_true Are_implicit_interoperable; + typedef Tag_true Are_explicit_interoperable; + struct Cast{ + typedef Type result_type; + template + Type operator()(const U& x) const { + return Type(x); + } + }; +}; + +template +struct Coercion_traits < +boost::multiprecision::detail::expression, +boost::multiprecision::detail::expression > +: Coercion_traits < +typename boost::multiprecision::detail::expression::result_type, +typename boost::multiprecision::detail::expression::result_type> +{ }; +// Avoid ambiguity with the specialization for ... +template +struct Coercion_traits < +boost::multiprecision::detail::expression, +boost::multiprecision::detail::expression > +: Coercion_traits < +typename boost::multiprecision::detail::expression::result_type, +typename boost::multiprecision::detail::expression::result_type> +{ }; + +template +struct Coercion_traits, boost::multiprecision::detail::expression > +: Coercion_traits < +boost::multiprecision::number, +typename boost::multiprecision::detail::expression::result_type> +{ }; + +template +struct Coercion_traits, boost::multiprecision::number > +: Coercion_traits < +typename boost::multiprecision::detail::expression::result_type, +boost::multiprecision::number > +{ }; + +// TODO: fix existing coercions +// (double -> rational is implicit only for 1.56+, see ticket #10082) +// The real solution would be to avoid specializing Coercion_traits for all pairs of number types and let it auto-detect what works, so only broken types need an explicit specialization. + +// Ignore types smaller than long +#define CGAL_COERCE_INT(int) \ +template \ +struct Coercion_traits, int> { \ + typedef boost::multiprecision::number Type; \ + typedef Tag_true Are_implicit_interoperable; \ + typedef Tag_true Are_explicit_interoperable; \ + struct Cast{ \ + typedef Type result_type; \ + template Type operator()(const U& x) const { return Type(x); } \ + }; \ +}; \ +template \ +struct Coercion_traits > \ +: Coercion_traits, int> {}; \ +template \ +struct Coercion_traits, int> \ +: Coercion_traits::result_type, int>{}; \ +template \ +struct Coercion_traits > \ +: Coercion_traits::result_type, int>{} + +CGAL_COERCE_INT(short); +CGAL_COERCE_INT(int); +CGAL_COERCE_INT(long); +#undef CGAL_COERCE_INT + +// Ignore bounded-precision rationals +#define CGAL_COERCE_FLOAT(float) \ +template \ +struct Coercion_traits, float> { \ + typedef boost::multiprecision::number Type; \ + typedef Boolean_tag::value != boost::multiprecision::number_kind_integer> Are_implicit_interoperable; \ + typedef Are_implicit_interoperable Are_explicit_interoperable; \ + struct Cast{ \ + typedef Type result_type; \ + template Type operator()(const U& x) const { return Type(x); } \ + }; \ +}; \ +template \ +struct Coercion_traits > \ +: Coercion_traits, float> {}; \ +template \ +struct Coercion_traits, float> \ +: Coercion_traits::result_type, float>{}; \ +template \ +struct Coercion_traits > \ +: Coercion_traits::result_type, float>{} + +CGAL_COERCE_FLOAT(float); +CGAL_COERCE_FLOAT(double); +#undef CGAL_COERCE_FLOAT + +// Because of https://github.com/boostorg/multiprecision/issues/29 , this is not perfect and fails to read some KDS files. + +template <> +class Input_rep : public IO_rep_is_specialized { + boost::multiprecision::cpp_rational& q; +public: + Input_rep(boost::multiprecision::cpp_rational& qq) : q(qq) {} + std::istream& operator()(std::istream& in) const { + internal::read_float_or_quotient(in, q); + return in; + } +}; +#ifdef CGAL_USE_GMP +template <> +class Input_rep : public IO_rep_is_specialized { + boost::multiprecision::mpq_rational& q; +public: + Input_rep(boost::multiprecision::mpq_rational& qq) : q(qq) {} + std::istream& operator()(std::istream& in) const { + internal::read_float_or_quotient(in, q); + return in; + } +}; +#endif + +// Copied from leda_rational.h +namespace internal { + // See: Stream_support/include/CGAL/IO/io.h + template + void read_float_or_quotient(std::istream & is, ET& et); + + template <> + inline void read_float_or_quotient(std::istream & is, boost::multiprecision::cpp_rational& et) + { + internal::read_float_or_quotient(is, et); + } +#ifdef CGAL_USE_GMP + template <> + inline void read_float_or_quotient(std::istream & is, boost::multiprecision::mpq_rational& et) + { + internal::read_float_or_quotient(is, et); + } +#endif +} // namespace internal + +#ifdef CGAL_USE_BOOST_MP + +template< > class Real_embeddable_traits< Quotient > + : public INTERN_QUOTIENT::Real_embeddable_traits_quotient_base< Quotient > { + + public: + typedef Quotient Type; + + class To_interval + : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { + public: + std::pair operator()( const Type& x ) const { + return Boost_MP_internal::to_interval(x.num, x.den); + } + }; +}; + +#endif // CGAL_USE_BOOST_MP + +} //namespace CGAL + +namespace Eigen { + template struct NumTraits; + template<> struct NumTraits + { + typedef boost::multiprecision::cpp_int Real; + typedef boost::multiprecision::cpp_rational NonInteger; + typedef boost::multiprecision::cpp_int Nested; + typedef boost::multiprecision::cpp_int Literal; + + static inline Real epsilon() { return 0; } + static inline Real dummy_precision() { return 0; } + + enum { + IsInteger = 1, + IsSigned = 1, + IsComplex = 0, + RequireInitialization = 1, + ReadCost = 6, + AddCost = 30, + MulCost = 50 + }; + }; + + template<> struct NumTraits + { + typedef boost::multiprecision::cpp_rational Real; + typedef boost::multiprecision::cpp_rational NonInteger; + typedef boost::multiprecision::cpp_rational Nested; + typedef boost::multiprecision::cpp_rational Literal; + + static inline Real epsilon() { return 0; } + static inline Real dummy_precision() { return 0; } + + enum { + IsInteger = 0, + IsSigned = 1, + IsComplex = 0, + RequireInitialization = 1, + ReadCost = 6, + AddCost = 150, + MulCost = 100 + }; + }; + +#ifdef CGAL_USE_GMP + + template<> struct NumTraits + { + typedef boost::multiprecision::mpz_int Real; + typedef boost::multiprecision::mpq_rational NonInteger; + typedef boost::multiprecision::mpz_int Nested; + typedef boost::multiprecision::mpz_int Literal; + + static inline Real epsilon() { return 0; } + static inline Real dummy_precision() { return 0; } + + enum { + IsInteger = 1, + IsSigned = 1, + IsComplex = 0, + RequireInitialization = 1, + ReadCost = 6, + AddCost = 30, + MulCost = 50 + }; + }; + + template<> struct NumTraits + { + typedef boost::multiprecision::mpq_rational Real; + typedef boost::multiprecision::mpq_rational NonInteger; + typedef boost::multiprecision::mpq_rational Nested; + typedef boost::multiprecision::mpq_rational Literal; + + static inline Real epsilon() { return 0; } + static inline Real dummy_precision() { return 0; } + + enum { + IsInteger = 0, + IsSigned = 1, + IsComplex = 0, + RequireInitialization = 1, + ReadCost = 6, + AddCost = 150, + MulCost = 100 + }; + }; +#endif // CGAL_USE_GMP + + +} // namespace Eigen + +#endif // BOOST_VERSION +#endif diff --git a/Number_types/include/CGAL/utils_classes.h b/Number_types/include/CGAL/utils_classes.h index 06a569a6d8b..1b4eb95ce89 100644 --- a/Number_types/include/CGAL/utils_classes.h +++ b/Number_types/include/CGAL/utils_classes.h @@ -266,6 +266,17 @@ class Is_valid }; }; +namespace internal +{ +// utility class to be used for calling exact(Lazy) when doing accumulation with EPECK +template +struct Evaluate +{ + void operator()(const NT&) + {} +}; +} // internal namespace + } //namespace CGAL #endif // CGAL_UTILS_CLASSES_H diff --git a/Number_types/test/Number_types/CMakeLists.txt b/Number_types/test/Number_types/CMakeLists.txt index b3dd989c766..73488a8a819 100644 --- a/Number_types/test/Number_types/CMakeLists.txt +++ b/Number_types/test/Number_types/CMakeLists.txt @@ -13,10 +13,6 @@ include_directories(BEFORE include) create_single_source_cgal_program("bench_interval.cpp") create_single_source_cgal_program("cpp_float.cpp") create_single_source_cgal_program("constant.cpp") -create_single_source_cgal_program("CORE_BigFloat.cpp") -create_single_source_cgal_program("CORE_BigInt.cpp") -create_single_source_cgal_program("CORE_BigRat.cpp") -create_single_source_cgal_program("CORE_Expr.cpp") create_single_source_cgal_program("Counted_number.cpp") create_single_source_cgal_program("double.cpp") create_single_source_cgal_program("doubletst.cpp") @@ -66,14 +62,17 @@ create_single_source_cgal_program("utilities.cpp") create_single_source_cgal_program("Exact_rational.cpp") create_single_source_cgal_program("Mpzf_new.cpp") -find_package( GMP ) -if( GMP_FOUND AND NOT CGAL_DISABLE_GMP ) +if( CGAL_Core_FOUND ) create_single_source_cgal_program( "CORE_Expr_ticket_4296.cpp" ) + create_single_source_cgal_program("CORE_BigFloat.cpp") + create_single_source_cgal_program("CORE_BigInt.cpp") + create_single_source_cgal_program("CORE_BigRat.cpp") + create_single_source_cgal_program("CORE_Expr.cpp") find_package(MPFI QUIET) if( MPFI_FOUND ) include( ${MPFI_USE_FILE} ) endif() #MPFI_FOUND -endif() #GMP_FOUND AND NOT CGAL_DISABLE_GMP +endif() #CGAL_Core_FOUND if(NOT CGAL_DISABLE_GMP) create_single_source_cgal_program( "Gmpfi.cpp" ) diff --git a/Number_types/test/Number_types/CORE_BigInt.cpp b/Number_types/test/Number_types/CORE_BigInt.cpp index 8cfe816f043..79b207d2d87 100644 --- a/Number_types/test/Number_types/CORE_BigInt.cpp +++ b/Number_types/test/Number_types/CORE_BigInt.cpp @@ -72,4 +72,3 @@ int main() { return 0; } #endif // CGAL_USE_CORE //EOF - diff --git a/Number_types/test/Number_types/CORE_BigRat.cpp b/Number_types/test/Number_types/CORE_BigRat.cpp index cb3f1f199c7..566bc8c5358 100644 --- a/Number_types/test/Number_types/CORE_BigRat.cpp +++ b/Number_types/test/Number_types/CORE_BigRat.cpp @@ -19,24 +19,25 @@ void test_io(){ std::stringstream ss; CGAL::IO::set_ascii_mode(ss); ss << CGAL::IO::oformat(NT(1)); - //std::cout << ss.str()< #include -#include +#include #include #include @@ -119,7 +119,7 @@ void test_MSB_bug() int main() { precision_bug(); test_istream(); - test_MSB_bug(); + test_MSB_bug(); test_MSB_bug(); typedef CORE::Expr NT; diff --git a/Number_types/test/Number_types/ioformat.cpp b/Number_types/test/Number_types/ioformat.cpp index 54eaa26bf22..457f1be15b1 100644 --- a/Number_types/test/Number_types/ioformat.cpp +++ b/Number_types/test/Number_types/ioformat.cpp @@ -106,7 +106,6 @@ int main() // CORE #ifdef CGAL_USE_CORE - static_assert(CGAL::Output_rep::is_specialized == true); //bug in io for CORE. test_it("CORE::BigInt"); test_it("CORE::BigRat"); diff --git a/Number_types/test/Number_types/test_eigen.cpp b/Number_types/test/Number_types/test_eigen.cpp index 8e0dd9bf194..02151b80ec0 100644 --- a/Number_types/test/Number_types/test_eigen.cpp +++ b/Number_types/test/Number_types/test_eigen.cpp @@ -24,6 +24,8 @@ #ifdef CGAL_EIGEN3_ENABLED #include + + // Just check that it all compiles. template void check_(){ @@ -33,7 +35,7 @@ void check_(){ Eigen::Matrix v(3); v << 1, 2, 3; NT t=v.dot(v); - v+=d*m*(t*v); + v+=d*Eigen::Matrix(m*(t*v)); std::ptrdiff_t si=v.size(); CGAL_USE(si); } diff --git a/Number_types/test/Number_types/utilities.cpp b/Number_types/test/Number_types/utilities.cpp index 48400c97520..e716624ffae 100644 --- a/Number_types/test/Number_types/utilities.cpp +++ b/Number_types/test/Number_types/utilities.cpp @@ -5,7 +5,10 @@ #include #include #include + +#ifdef CGAL_USE_BOOST_MP #include +#endif #ifdef CGAL_USE_GMP #include diff --git a/Optimal_bounding_box/package_info/Optimal_bounding_box/dependencies b/Optimal_bounding_box/package_info/Optimal_bounding_box/dependencies index 249760e2928..37cb6893ab8 100644 --- a/Optimal_bounding_box/package_info/Optimal_bounding_box/dependencies +++ b/Optimal_bounding_box/package_info/Optimal_bounding_box/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Bounding_volumes +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Orthtree/benchmark/Orthtree/construction.cpp b/Orthtree/benchmark/Orthtree/construction.cpp index 36b8800f28c..5d55f39c1ee 100644 --- a/Orthtree/benchmark/Orthtree/construction.cpp +++ b/Orthtree/benchmark/Orthtree/construction.cpp @@ -12,16 +12,14 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kdtree; +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kdtree = Kd_tree_search::Tree; using std::chrono::high_resolution_clock; using std::chrono::duration_cast; diff --git a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp index 69ca01c8958..219d9f1b0f7 100644 --- a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp +++ b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp @@ -12,21 +12,19 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kdtree; - using std::chrono::high_resolution_clock; using std::chrono::duration_cast; using std::chrono::microseconds; +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kdtree = Kd_tree_search::Tree; + int main(int argc, char **argv) { int num_runs = 100; @@ -103,8 +101,8 @@ int main(int argc, char **argv) { // Time how long it takes to find neighbors using the octree auto octreeTime = bench( [&] { - std::vector nearest_neighbors; - octree.nearest_neighbors(search_point, k, std::back_inserter(nearest_neighbors)); + std::vector nearest_neighbors; + octree.nearest_k_neighbors(search_point, k, std::back_inserter(nearest_neighbors)); } ); diff --git a/Orthtree/benchmark/Orthtree/util.h b/Orthtree/benchmark/Orthtree/util.h index de65aa89e81..f619317fa5f 100644 --- a/Orthtree/benchmark/Orthtree/util.h +++ b/Orthtree/benchmark/Orthtree/util.h @@ -10,8 +10,8 @@ template CGAL::Point_set_3 generate(size_t num_points = 1) { - typedef typename Kernel::Point_3 Point; - typedef CGAL::Point_set_3 Point_set; + using Point = typename Kernel::Point_3; + using Point_set = CGAL::Point_set_3; // Create an empty point set Point_set points; diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h new file mode 100644 index 00000000000..97068f40a97 --- /dev/null +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -0,0 +1,101 @@ +/*! + \ingroup PkgOrthtreeConcepts + \cgalConcept + + Refinement of the `OrthtreeTraitsWithData` concept, adding requirements for the + traits class of a `CGAL::Orthtree` in order to supports nearest-neighbor searching. + + Nearest neighbor searches expect a tree where `Node_data` is a model of `ForwardRange`. + The leaf nodes of the tree represent an exclusive partition of the elements contained in the tree. + This means that no element should be contained by more than one node. + + \cgalRefines{OrthtreeTraitsWithData} + + \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModelsEnd +*/ +class CollectionPartitioningOrthtreeTraits { +public: + + + /// \name Types + /// @{ + + /*! + * Sphere Type used for the shrinking-sphere approach for neighbor queries; needs to be copy assignable. + */ + using Sphere_d = unspecified_type; + + /*! + * \brief The data type contained by each node; must be a model of `ForwardRange` in addition to default constructible, copy constructible and copy assignable. + */ + using Node_data = unspecified_type; + + /*! + * \brief An element of the `Node_data` list-like type. + * + * Must be constructible from the value type of a `Node_data::iterator`. + * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. + */ + using Node_data_element = unspecified_type; + + /*! + * \brief Functor with an operator that calculates the squared distance of a `Node_data_element` from a point. + * + * Provides the operator: + * `FT operator()(const Node_data_element&, const Point_d&)` + */ + using Squared_distance_of_element = unspecified_type; + + /*! + * \brief Functor with an operator that constructs a `Sphere_d` from a provided center and squared radius. + * + * Provides the operator: + * `Sphere_d operator()(const Point_d&, const FT&)` + */ + using Construct_sphere_d = unspecified_type; + + /*! + * \brief Functor with an operator that provides the center of a `Sphere_d`. + * + * Provides the operator: + * `Point_d operator()(const Sphere_d&)` + */ + using Construct_center_d = unspecified_type; + + /*! + * \brief Functor with an operator that provides the squared radius of a `Sphere_d`. + * + * Provides the operator: + * `FT operator()(const Sphere_d&)` + */ + using Compute_squared_radius_d = unspecified_type; + + /// @} + + /// \name Operations + /// @{ + + /*! + * constructs an object of type `ConstructSphere_d`. + */ + Construct_sphere_d construct_sphere_d_object() const; + + /*! + * constructs an object of type `ConstructCenter_d`. + */ + Construct_center_d construct_center_d_object() const; + + /*! + * constructs an object of type `ComputeSquaredRadius_d`. + */ + Compute_squared_radius_d compute_squared_radius_d_object() const; + + /*! + * constructs an object of type `Squared_distance_of_element`. + */ + Squared_distance_of_element squared_distance_of_element_object() const; + + /// @} +}; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index fdb0781ba26..9f2e62d5db4 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -6,9 +6,9 @@ template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_2} - \cgalHasModels{CGAL::Orthtree_traits_3} - \cgalHasModels{CGAL::Orthtree_traits_d} + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_base} \cgalHasModelsEnd */ class OrthtreeTraits @@ -17,31 +17,47 @@ public: /// \name Types /// @{ - - typedef unspecified_type Dimension; ///< Dimension type (see `CGAL::Dimension_tag`). - typedef unspecified_type Bbox_d; ///< Bounding box type. - typedef unspecified_type FT; ///< The number type of the %Cartesian coordinates of types `Point_d` - typedef unspecified_type Point_d; ///< Point type. - typedef unspecified_type Sphere_d; ///< The sphere type for neighbor queries. + using Node_index = unspecified_type; ///< An integer type for nodes + constexpr int dimension; ///< Dimension. + using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` + using Point_d = unspecified_type; ///< Point type. + using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` objects. /*! A random access iterator type to enumerate the - %Cartesian coordinates of a point. + %Cartesian coordinates of a point of type `Point_d`. */ - typedef unspecified_type Cartesian_const_iterator_d; - typedef std::array Array; ///< Array used for easy point constructions. - - typedef unspecified_type Adjacency; ///< Specify the adjacency directions + using Cartesian_const_iterator_d = unspecified_type; /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; + * \brief Integral number type which can take on values indicating adjacency directions. + * + * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. + */ + using Adjacency = unspecified_type; ///< Specify the adjacency directions /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; + * \brief Functor with an operator to create the bounding box of the root node. + * + * Provides the operator: + * `Bbox_d operator()()` + * + * The bounding box must enclose all elements contained by the tree. + * It may be tight-fitting. The orthtree constructor produces a bounding box surrounding this region. + * For traits which assign no data to each node, this can be defined to return a fixed region. + */ + using Construct_root_node_bbox = unspecified_type; + + /*! + * \brief Functor with an operator to construct a `Point_d` from an initializer list of type `FT`. + * + * Provides the operator: + * `Point_d operator()(arg1, arg2,...)` + * + * For trees which use a different kernel for the bounding box type, + * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. + */ + using Construct_point_d = unspecified_type; /// @} @@ -49,14 +65,14 @@ public: /// @{ /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const; + * constructs an object of type `Construct_root_node_bbox`. + */ + Construct_root_node_bbox construct_root_node_bbox_object() const; /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const; + * constructs an object of type `Construct_point_d`. + */ + Construct_point_d construct_point_d_object() const; /// @} }; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h new file mode 100644 index 00000000000..f6533340079 --- /dev/null +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -0,0 +1,76 @@ +/*! + \ingroup PkgOrthtreeConcepts + \cgalConcept + + The concept `OrthtreeTraitsWithData` defines the requirements for the + template parameter of the `CGAL::Orthtree` class for a node type that stores data. + + \cgalRefines{OrthtreeTraits} + + \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_base} + \cgalHasModelsEnd +*/ +class OrthtreeTraitsWithData +{ +public: + + /// \name Types + /// @{ + /*! + * \brief The data type contained by each node. Must be default constructible, copy constructible and copy assignable. + */ + using Node_data = unspecified_type; + + /*! + * \brief Functor which initializes elements contained by the root node. + * + * Each node of a tree has an associated `Node_data` value. + * This functor initializes the `Node_data` of the root node. + * It takes no arguments, and returns an instance of `Node_data`. + * + * Provides the operator: + * `Node_data operator()()` + * + * Typically, the `Node_data` of the root node contains all the elements in the tree. + * For a tree in which each node contains a span (such as `std::span()`) this function would return the span containing all items. + * + */ + using Construct_root_node_contents = unspecified_type; + + /*! + * \brief Functor which fills the contents of the nodes children. + * + * Provides the operator: + * `void operator()(Node_index, Orthtree&, const Point_d&)` + * + * The functor is called during refinement of the `Orthtree` on a node after it has been split. The purpose of the functor is to + * distribute the `Node_data`, accessible via `tree.data()`, to the data of the nodes children, accessible via `tree.children()`. + * The first parameter is the `Node_index` of the node. The second parameter provides the instance of the `Orthtree` + * and the last parameter is the barycenter of the node which will be used as shared corner amongst the children of the node. + * + * For a tree in which each node contains a span, this may mean rearranging the contents of the original node + * and producing spans containing a subset of its contents for each of its children. + * For compatibility with locate, the center of the node is considered to be part of the upper half. + */ + using Distribute_node_contents = unspecified_type; + + /// @} + + /// \name Operations + /// @{ + + /*! + * constructs an object of type `Construct_root_node_contents`. + */ + Construct_root_node_contents construct_root_node_contents_object() const; + + /*! + * constructs an object of type `Distribute_node_contents`. + */ + Distribute_node_contents distribute_node_contents_object() const; + + /// @} +}; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 5aeda690079..64e84572cea 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -1,13 +1,8 @@ - /*! \ingroup PkgOrthtreeConcepts \cgalConcept - \brief a traversal provides the functions needed to traverse the - nodes of an orthtree. - - A traversal is used to iterate on a tree with a user-selected order - (e.g. preorder, postorder). + \brief Requirements for defining a traversal strategy of an orthtree. \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtrees::Preorder_traversal} @@ -20,15 +15,15 @@ class OrthtreeTraversal { public: - using Node = unspecified_type; ///< A specialization of [CGAL::Orthtree::Node](@ref CGAL::Orthtree::Node) + using Node_index = unspecified_type; ///< Index type of the orthtree to be traversed /*! - \brief returns the first node to iterate to, given the root of the Orthtree. + \brief returns the first node of the traversal. */ - Node first (Node root) const; + Node_index first_index() const; /*! - \brief returns the next node to iterate to, given the previous node. + \brief returns the next node to be traversed given the previous node `n`. */ - Node next(Node n) const; + Node_index next(Node_index n) const; }; diff --git a/Orthtree/doc/Orthtree/Doxyfile.in b/Orthtree/doc/Orthtree/Doxyfile.in index 980e12e8f6e..2adfd640f36 100644 --- a/Orthtree/doc/Orthtree/Doxyfile.in +++ b/Orthtree/doc/Orthtree/Doxyfile.in @@ -5,3 +5,7 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Quadtrees, Octrees, and Orthtrees" EXTRACT_ALL = false HIDE_UNDOC_MEMBERS = true HIDE_UNDOC_CLASSES = true +WARN_IF_UNDOCUMENTED = false + +HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/orthtree.png \ + ${CGAL_PACKAGE_DOC_DIR}/fig/orthtree_mesh.png diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index bfe72403866..eb7b2559b2a 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -10,9 +10,9 @@ namespace CGAL { \section Section_Orthtree_Introduction Introduction Quadtrees are tree data structures in which each node encloses a -square section of space, and each internal node has exactly 4 +rectangular section of space, and each internal node has exactly 4 children. Octrees are a similar data structure in 3D in which each -node encloses a cubic section of space, and each internal node has +node encloses a rectangular cuboid section of space, and each internal node has exactly 8 children. We call the generalization of such data structure "orthtrees", as @@ -22,20 +22,32 @@ structures in dimensions 4 and higher. This package provides a general data structure `Orthtree` along with aliases for `Quadtree` and `Octree`. These trees can be constructed -with custom point ranges and split predicates, and iterated on with -various traversal methods. - -\cgalFigureBegin{Orthtree_fig, orthtree.png} -Building an %orthtree in 3D (%octree) from a point cloud. -\cgalFigureEnd +with custom contents and split predicates, and iterated on with +various traversal methods. Orthants can be orthotopes and not only hypercubes. +\cgalFigureAnchor{Orthtree_fig } +
+ + + + + + + +
+
+\cgalFigureCaptionBegin{Orthtree_fig} +Top: an %orthtree in 3D (%octree) from a point cloud (top); +Bottom: an %orthtree in 3D (%octree) from the triangle faces of a mesh. +\cgalFigureCaptionEnd \section Section_Orthtree_Building Building -An orthtree is created using a set of points. The points are not -copied: the provided point range is used directly and is rearranged by +A common purpose for an orthtree is to subdivide a collection of points, +and the Orthtree package provides a traits class for this purpose. +The points are not copied: the provided point range is used directly and rearranged by the orthtree. Altering the point range after creating the orthtree -might leave it in an invalid state. The constructor returns a tree +may leave the tree in an invalid state. The constructor returns a tree with a single (root) node that contains all the points. The method [refine()](@ref CGAL::Orthtree::refine) must be called to @@ -43,9 +55,11 @@ subdivide space further. This method uses a split predicate which takes a node as input and returns `true` if this node should be split, `false` otherwise: this enables users to choose on what criterion should the orthtree be refined. Predefined predicates are -provided such as [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) or [Maximum_number_of_inliers](@ref CGAL::Orthtrees::Maximum_number_of_inliers). +provided for the point-set orthtree, +including [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) +and [Maximum_contained_elements](@ref CGAL::Orthtrees::Maximum_contained_elements). -The simplest way to create an orthtree is using a vector of points. +The simplest way to create a point-set orthtree is from a vector of points. The constructor generally expects a separate point range and map, but the point map defaults to `Identity_property_map` if none is provided. @@ -55,29 +69,27 @@ the existing ones do not match users' needs. \subsection Section_Orthtree_Quadtree Building a Quadtree -The `Orthtree` class may be templated with `Orthtree_traits_2` and thus -behave as a %quadtree. For convenience, the alias `Quadtree` is provided. +The `Orthtree` class may be templated with `Orthtree_traits_point<>` +while specifying a 2d ambient space and thus behave as a %quadtree. +For convenience, the alias `CGAL::Quadtree` is provided. -The following example shows how to create a %quadtree object from a -vector of `Point_2` objects and refine it, which means constructing -the tree's space subdivision itself, using a maximum depth of 10 and a -maximum number of inliers per node (bucket size) of 5. The refinement -is stopped as soon as one of the conditions is violated: if a node has -more inliers than `bucket_size` but is already at `max_depth`, it is -not split. Similarly, a node that is at a depth smaller than -`max_depth` but already has fewer inliers than `bucket_size` is not -split. +The following example shows the construction of %quadtree from a vector of `Point_2` objects. +`quadtree.refine(10, 5)` uses the default split predicate, which +enforces a max-depth and a maximum of elements contained per node ("bucket size"). +Nodes are split if their depth is less than 10, and they contain more than 5 points. \cgalExample{Orthtree/quadtree_build_from_point_vector.cpp} \subsection Section_Orthtree_Point_Vector Building an Octree -The `Orthtree` class may be templated with `Orthtree_traits_3` and thus -behave as an %octree. For convenience, the alias `Octree` is provided. +`Orthtree_traits_point<>` can also be templated with dimension 3 and thus +behave as an %octree. For convenience, the alias `CGAL::Octree` is provided. + +The following example shows how to create an %octree from a vector of `Point_3` objects. +As with the %quadtree example, we use the default split predicate. +In this case the maximum depth is 10, and the bucket size is 1. -The following example shows how to create an %octree from a vector of -`Point_3` objects: \cgalExample{Orthtree/octree_build_from_point_vector.cpp} @@ -87,12 +99,12 @@ Some data structures such as `Point_set_3` require a non-default point map type and object. This example illustrates how to create an octree from a `Point_set_3` loaded from a file. It also shows a more explicit way of setting the split predicate when refining the tree. -An octree is constructed from the point set and its map. -The tree is refined with a maximum depth (deepest node allowed) of 10, -and a bucket size (maximum number of points contained by a single node) of 20. -The tree is then written to the standard output. +An %octree is constructed from the point set and its corresponding map, +and then written to the standard output. The split predicate is manually constructed and passed to the refine method. +In this case, we use a maximum number of contained elements with no corresponding maximum depth. +This means that nodes will continue to be split until none contain more than 10 points. \cgalExample{Orthtree/octree_build_from_point_set.cpp} @@ -108,38 +120,47 @@ at depth 7 can hold 14. \subsection Section_Orthtree_Orthtree_Point_Vector Building an Orthtree -The following example shows how to build an generalized orthtree in dimension 4. -A `std::vector` is manually filled with points. -The vector is used as the point set, -an `Identity_property_map` is automatically set as the orthtree's map type, so a map does not need to be provided. +An orthtree can also be used with an arbitrary number of dimensions. +The `Orthtree_traits_point` template can infer the arbitrary dimension count from the d-dimensional kernel. + +The following example shows how to build a generalized orthtree in dimension 4. +As `std::vector` is manually filled with 4-dimensional points. +The vector is used as the point set, and an `Identity_property_map` is automatically +set as the orthtree's map type, so a map does not need to be provided. \cgalExample{Orthtree/orthtree_build.cpp} +\section Section_Orthtree_Properties Properties +The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its value type and stored in a consecutive block of memory. + \section Section_Orthtree_Traversal Traversal -\note For simplicity, the rest of the user manual will only use -octrees, but all the presented features also apply to quadtrees and -higher dimension orthtrees. - %Traversal is the act of navigating among the nodes of the tree. -The `Orthtree` and [Node](@ref CGAL::Orthtree::Node) classes provide a +The `Orthtree` class provides a number of different solutions for traversing the tree. \subsection Section_Orthtree_Manual_Traveral Manual Traversal Because our orthtree is a form of connected acyclic undirected graph, it is possible to navigate between any two nodes. -What that means in practice, is that given a node on the tree, it is possible to +What that means in practice is that given a node on the tree, it is possible to access any other node using the right set of operations. -The `Node` class provides functions that enable the user to access each of its children, as well as its parent (if it exists). +The `Node_index` type provides a handle on a node, and the `orthtree` class provides methods +that enable the user to retrieve the indices of each of its children as well as its parent (if they exist). -The following example demonstrates ways of accessing different nodes of a tree, given a reference to one. +Given any node index `nid`, the `n`th child of that node can be found with `orthtree.child(nid, n)`. +For an octree, values of `n` from 0-7 provide access to the different children. +For non-root nodes, it is possible to access parent nodes using the `orthtree.parent()` accessor. -From the root node, children can be accessed using the subscript operator `CGAL::Orthtree::Node::operator[]()`. -For an octree, values from 0-7 provide access to the different children. +To access grandchildren, it isn't necessary to use nested `orthtree.child()` calls. +Instead, the `orthtree.descendant(node, a, b, ...)` convenience method is provided. +This retrieves the `b`th child of the `a`th child, to any depth. -For non-root nodes, it is possible to access parent nodes using the [parent()](@ref CGAL::Orthtree::Node::parent) accessor. +In most cases, we want to find the descendants of the root node. +For this case, there is another convenience method `orthtree.node(a, b, c, ...)`. +This retrieves the node specified by the descent `a`, `b`, `c`. +It is equivalent to `orthtree.descendant(orthtree.root(), a, b, c, ...)`. -These accessors and operators can be chained to access any node in the tree in a single line of code, as shown in the following example: +The following example demonstrates the use of several of these accessors. \cgalExample{Orthtree/octree_traversal_manual.cpp} @@ -147,9 +168,10 @@ These accessors and operators can be chained to access any node in the tree in a It is often useful to be able to iterate over the nodes of the tree in a particular order. For example, the stream operator `<<` uses a traversal to print out each node. -A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). -To traverse a tree in preorder is to visit each parent immediately followed by its children, -whereas in postorder, traversal the children are visited first. +A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) +and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). +Traversing a tree in preorder means to visit each parent immediately followed by its children, +whereas in postorder traversal the children are visited first. The following example illustrates how to use the provided traversals. @@ -161,13 +183,15 @@ In this case, we print out the nodes of the tree without indentation instead. \subsection Section_Orthtree_Custom_Traversal Custom Traversal -Users can define their own traversal methods by creating models of the -`OrthtreeTraversal` concept. The following example shows how to define a -custom traversal that only traverses the first branch of the octree: +Users can define their own traversal methods by creating models of the `OrthtreeTraversal` concept. +The custom traversal must provide a method which returns the starting point of the traversal (`first_index()`) +and another method which returns the next node in the sequence (`next_index()`). +`next_index()` returns an empty optional when the end of the traversal is reached. +The following example shows how to define a custom traversal that only traverses the first branch an octree: \cgalExample{Orthtree/octree_traversal_custom.cpp} -\subsection Comparison of Traversals +\subsection Section_Orthtree_Cmp_Trav Comparison of Traversals Figure \cgalFigureRef{Orthtree_traversal_fig} shows in which order nodes are visited depending on the traversal method used. @@ -184,12 +208,11 @@ Once an orthtree is built, its structure can be used to accelerate different tas \subsection Section_Orthtree_Nearest_Neighbor Finding the Nearest Neighbor of a Point -The naive way of finding the nearest neighbor of a point requires finding the distance to every other point. +The naive way of finding the nearest neighbor of a point requires finding the distance to all elements. An orthtree can be used to perform the same task in significantly less time. -For large numbers of points, this can be a large enough difference to outweigh the time spent building the tree. +For large numbers of elements, this can be a large enough difference to outweigh the time spent building the tree. -Note that a kd-tree is expected to outperform the orthtree for this task, -it should be preferred unless features specific to the orthtree are needed. +Note that a kd-tree is expected to outperform the orthtree for this task on points, it should be preferred unless features specific to the orthtree are needed. The following example illustrates how to use an octree to accelerate the search for points close to a location. @@ -200,9 +223,14 @@ Results are put in a vector, and then printed. \cgalExample{Orthtree/octree_find_nearest_neighbor.cpp} +Not all octrees are compatible with nearest neighbor functionality, +as the idea of a nearest neighbor may not make sense for some tree contents. +For the nearest neighbor methods to work, the traits class must implement the +`CollectionPartitioningOrthtreeTraits` concept. + \subsection Section_Orthtree_Grade Grading -An orthtree is graded if the difference of depth between two adjacent +An orthtree is considered "graded" if the difference of depth between two adjacent leaves is at most 1 for every pair of leaves. \cgalFigureBegin{Orthtree_quadree_graded_fig, quadtree_graded.png} @@ -256,15 +284,90 @@ purposes. For nontrivial point counts, the naive approach's calculation time dwarfs that of either the %orthtree or kd-tree. +\section Section_Orthtree_Migration Migrating Code Written Before Release 6.0 + +The orthtree package changed to allow for custom data stored per node in the orthtree. To migrate existing code written before \cgal 6.0 `Orthtree_traits_point` can be used for a point-based orthtrees. The aliases `CGAL::Quadtree` and `CGAL::Octree` have been extended by a boolean template parameter to allow for non-cubic cells, which is the default. The data is passed via the traits class and no longer directly to the orthtree. + +Former code to declare and define an Octree with cubic cells was as follows: +\code{.cpp} +typedef CGAL::Point_set_3 Point_set; +typedef CGAL::Octree Octree; +... +Octree octree(points, points.point_map()); +\endcode + +\cgal 6.0 code with identical behavior is now: +\code{.cpp} +typedef CGAL::Point_set_3 Point_set; +typedef CGAL::Octree Octree; +... +Octree octree(points, points.point_map()); +\endcode + +The node class does not exist anymore and has been replaced by the lightweight type `Node_index`. All information formerly contained in the node class is now accessible via the `Orthtree` interface. + +Former node access was as follows: +\code{.cpp} +Orthtree::Node root = orthtree.root(); +Orthtree::Node child = root[0]; +bool is_leaf = child.is_leaf(); + +for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) { + const Orthtree::Point &p = *it; + ... +} +\endcode + +\cgal 6.0 node access is now: +\code{.cpp} +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +Point_set points; +... +Orthtree::Node_index root = orthtree.root(); +Orthtree::Node_index child = orthtree.child(root, 0); +bool is_leaf = orthtree.is_leaf(child); + +for (Octree::Traits::Node_data_element& e : octree.data(child)) { + // Using a pointmap is necessary when using a Point_set_3. + Point& p = points.point(e); + + // If the orthtree is used with a std::vector, Node_data_element is identical to Point. + // Point& p = e; + ... +} +\endcode + +The nearest neighbor search behaves as before, however, the output iterator will be required to store `CollectionPartitioningOrthtreeTraits::Node_data_element`. This may be the actual point or, e.g., in case a `Point_set_3` is used, an index which requires the use of a point map to retrieve the point. + +The provided traversals, i.e., `CGAL::Orthtrees::Leaves_traversal`, `CGAL::Orthtrees::Preorder_traversal`, `CGAL::Orthtrees::Postorder_traversal`, require the orthtree as template parameter now. + +Former traversal use was as follows: +\code{.cpp} +for (Orthtree::Node node : orthtree.traverse()) + ... +\endcode + +\cgal 6.0 traversal use is now: +\code{.cpp} +for (Orthtree::Node_index node : orthtree.traverse>()) + ... +\endcode + \section Section_Orthtree_History History A prototype code was implemented by Pierre Alliez and improved by Tong Zhao and CĂ©dric Portaneri. From this prototype code, the package was -developed by Jackson Campolatarro as part of the Google Summer of Code -2020. Simon Giraudot, supervisor of the GSoC internship, completed and +developed by Jackson Campolattaro as part of the Google Summer of Code 2020. +Simon Giraudot, supervisor of the GSoC internship, completed and finalized the package for integration in CGAL 5.3. Pierre Alliez provided kind help and advice all the way through. +Starting with CGAL 6.0 an API improvement of the Orthtree package was released. +Most notably, the orthtree nodes do not need to store anything. Data to be stored +by the node are managed through a mechanism of dynamic properties. +This improvement was done by Jackson Campolattaro thanks to a funding provided by +INRIA, together with GeometryFactory. */ -} +} \ No newline at end of file diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 22b02350771..98825d8e237 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -1,20 +1,20 @@ -/// \defgroup PkgOrthtreeRef Quadtree\, Octree and Orthtree Reference - -/// \defgroup PkgOrthtreeClasses Classes -/// \ingroup PkgOrthtreeRef - -/// \defgroup PkgOrthtreeTraits Traits -/// \ingroup PkgOrthtreeClasses - -/// \defgroup PkgOrthtreeSplitPredicates Split Predicates -/// \ingroup PkgOrthtreeClasses - -/// \defgroup PkgOrthtreeTraversal Traversal -/// \ingroup PkgOrthtreeClasses +/// \defgroup PkgOrthtreeRef Quadtrees, Octrees and Orthtrees Reference +Quadtree, Octree and Orthtree Reference /// \defgroup PkgOrthtreeConcepts Concepts /// \ingroup PkgOrthtreeRef +/// \defgroup PkgOrthtreeTraits Traits +/// \ingroup PkgOrthtreeRef + +/// \defgroup PkgOrthtreeSplitPredicates Split Predicates +/// \ingroup PkgOrthtreeRef + +/// \defgroup PkgOrthtreeTraversal Traversal +/// \ingroup PkgOrthtreeRef + + + /*! \addtogroup PkgOrthtreeRef @@ -39,22 +39,25 @@ \cgalCRPSection{Concepts} - `OrthtreeTraits` +- `OrthtreeTraitsWithData` - `OrthtreeTraversal` +- `CollectionPartitioningOrthtreeTraits` \cgalCRPSection{Classes} - `CGAL::Quadtree` - `CGAL::Octree` -- `CGAL::Orthtree` +- `CGAL::Orthtree` \cgalCRPSection{Traits} -- `CGAL::Orthtree_traits_2` -- `CGAL::Orthtree_traits_3` -- `CGAL::Orthtree_traits_d` +- `CGAL::Orthtree_traits` +- `CGAL::Orthtree_traits_point` +- `CGAL::Orthtree_traits_base` +- `CGAL::Orthtree_traits_face_graph` \cgalCRPSection{Split Predicates} -- `CGAL::Orthtrees::Maximum_number_of_inliers` +- `CGAL::Orthtrees::Maximum_contained_elements` - `CGAL::Orthtrees::Maximum_depth` -- `CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers` +- `CGAL::Orthtrees::Maximum_depth_and_maximum_contained_elements` \cgalCRPSection{%Traversal} - `CGAL::Orthtrees::Preorder_traversal` diff --git a/Orthtree/doc/Orthtree/dependencies b/Orthtree/doc/Orthtree/dependencies index 1e72914f700..703f46d8926 100644 --- a/Orthtree/doc/Orthtree/dependencies +++ b/Orthtree/doc/Orthtree/dependencies @@ -8,3 +8,4 @@ Property_map STL_Extension Spatial_searching Stream_support +Surface_mesh diff --git a/Orthtree/doc/Orthtree/examples.txt b/Orthtree/doc/Orthtree/examples.txt index ff327b8272f..7d6ccf98a35 100644 --- a/Orthtree/doc/Orthtree/examples.txt +++ b/Orthtree/doc/Orthtree/examples.txt @@ -3,6 +3,7 @@ \example Orthtree/octree_build_from_point_vector.cpp \example Orthtree/octree_build_with_custom_split.cpp \example Orthtree/octree_find_nearest_neighbor.cpp +\example Orthtree/octree_surface_mesh.cpp \example Orthtree/octree_traversal_manual.cpp \example Orthtree/octree_traversal_preorder.cpp \example Orthtree/octree_traversal_custom.cpp diff --git a/Orthtree/doc/Orthtree/fig/orthtree_mesh.png b/Orthtree/doc/Orthtree/fig/orthtree_mesh.png new file mode 100644 index 00000000000..12864286cd0 Binary files /dev/null and b/Orthtree/doc/Orthtree/fig/orthtree_mesh.png differ diff --git a/Orthtree/examples/Orthtree/CMakeLists.txt b/Orthtree/examples/Orthtree/CMakeLists.txt index 432d99b21c4..ee2c7502a8d 100644 --- a/Orthtree/examples/Orthtree/CMakeLists.txt +++ b/Orthtree/examples/Orthtree/CMakeLists.txt @@ -15,6 +15,8 @@ create_single_source_cgal_program("octree_traversal_manual.cpp") create_single_source_cgal_program("octree_traversal_preorder.cpp") create_single_source_cgal_program("octree_grade.cpp") create_single_source_cgal_program("quadtree_build_from_point_vector.cpp") +create_single_source_cgal_program("octree_surface_mesh.cpp") +create_single_source_cgal_program("quadtree_build_manually.cpp") find_package(Eigen3 3.1.91 QUIET) #(requires 3.1.91 or greater) include(CGAL_Eigen_support) diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index 4ef69692131..c4226b0dbe8 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -7,12 +7,11 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; int main(int argc, char **argv) { @@ -33,10 +32,10 @@ int main(int argc, char **argv) { Octree octree(points, points.point_map()); // Build the octree with a small bucket size, using a more verbose method - octree.refine(CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers(5, 10)); + octree.refine(CGAL::Orthtrees::Maximum_contained_elements(10)); // Print out the tree - std::cout << octree; + std::cout << octree << std::endl; return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp index 71f1393df32..1081e155df3 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp @@ -4,11 +4,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::list Point_vector; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; +using Octree = CGAL::Octree; int main() { @@ -27,7 +26,7 @@ int main() { Octree octree(points); // Build the octree - octree.refine(10, 20); + octree.refine(10, 1); // Print out the tree std::cout << octree; diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index 3202c6c9ade..92c7c777a1d 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -7,25 +7,26 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; // Split Predicate -// The predicate is a functor which returns a boolean value, whether a node needs to be split or not +// The predicate is a functor which returns a Boolean value, whether a node needs to be split or not struct Split_by_ratio { std::size_t ratio; - Split_by_ratio(std::size_t ratio) : ratio(ratio) {} + explicit Split_by_ratio(std::size_t ratio) : ratio(ratio) {} - template - bool operator()(const Node &n) const { - return n.size() > (ratio * n.depth()); + template + bool operator()(Node_index i, const Tree &tree) const { + std::size_t num_points = tree.data(i).size(); + std::size_t depth = tree.depth(i); + return num_points > (ratio * depth); } }; diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index e94fca1ee20..2d687aa4215 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -9,14 +9,13 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; -typedef CGAL::Octree Octree; - -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -39,21 +38,42 @@ int main(int argc, char **argv) { // Find the nearest points to a few locations std::vector points_to_find = { - {0, 0, 0}, - {1, 1, 1}, - {-1, -1, -1}, - {-0.46026, -0.25353, 0.32051}, - {-0.460261, -0.253533, 0.320513} + {0, 0, 0}, + {1, 1, 1}, + {-1, -1, -1}, + {-0.46026, -0.25353, 0.32051}, + {-0.460261, -0.253533, 0.320513} }; + for (const Point& p : points_to_find) - octree.nearest_neighbors - (p, 1, // k=1 to find the single closest point - boost::make_function_output_iterator - ([&](const Point& nearest) + octree.nearest_k_neighbors + (p, 1, // k=1 to find the single closest point + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) { std::cout << "the nearest point to (" << p << - ") is (" << nearest << ")" << std::endl; + ") is (" << points.point(nearest) << ")" << std::endl; })); + typename Octree::Sphere s(points_to_find[0], 0.0375); + std::cout << std::endl << "Closest points within the sphere around " << s.center() << " with squared radius of " << s.squared_radius() << ":" << std::endl; + octree.neighbors_within_radius + (s, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << points.point(nearest) << " dist: " << (Point(0, 0, 0) - points.point(nearest)).squared_length() << std::endl; + })); + + std::size_t k = 3; + std::cout << std::endl << "The up to " << k << " closest points to(" << s.center() << ") within a squared radius of " << s.squared_radius() << " are:" << std::endl; + octree.nearest_k_neighbors_within_radius + (s, k, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << "(" << points.point(nearest) << " dist: " << (Point(0, 0, 0) - points.point(nearest)).squared_length() << std::endl; + })); + return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index b00011045c6..5f213ff1233 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -4,10 +4,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::vector Point_vector; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; +using Octree = CGAL::Octree; int main() { diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp new file mode 100644 index 00000000000..676eacae273 --- /dev/null +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include +#include + +#include + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Mesh = CGAL::Surface_mesh; + +using OTraits = CGAL::Orthtree_traits_face_graph>; +using Octree = CGAL::Orthtree; + +void dump_as_polylines(const Octree& ot) +{ + std::ofstream out("octree.polylines.txt"); + for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) + { + if (!ot.is_leaf(node)) + continue; + auto bb = ot.bbox(node); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() << "\n"; + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() << "\n"; +// + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; +// + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; + } +} + +int main(int argc, char** argv) +{ + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh)) + { + std::cerr << "Error: cannot read file" << std::endl; + return EXIT_FAILURE; + } + + Octree tree(mesh, mesh.points()); + OTraits::Split_predicate_node_min_extent sp(0.01); + tree.refine(sp); + + dump_as_polylines(tree); +} diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index 3347b54ff99..a9a3ccba072 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -3,34 +3,42 @@ #include #include +#include #include #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; -typedef CGAL::Octree Octree; -typedef Octree::Node Node; +template +struct First_branch_traversal { -struct First_branch_traversal -{ - Node first (Node root) const - { - return root; + using Node_index = typename Tree::Node_index; + + const Tree& m_orthtree; + + explicit First_branch_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + Node_index first_index() const { + return m_orthtree.root(); } - Node next (Node n) const - { - if (n.is_leaf()) - return Node(); - return n[0]; + std::optional next_index(Node_index n) const { + + // Stop when we reach the base of the tree + if (m_orthtree.is_leaf(n)) + return {}; + + // Always descend the first child + return m_orthtree.child(n, 0); } }; -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -52,8 +60,9 @@ int main(int argc, char **argv) { octree.refine(); // Print out the first branch using custom traversal - for (auto &node : octree.traverse()) - std::cout << node << std::endl; + for (auto node: octree.traverse>()) { + std::cout << octree.to_string(node) << std::endl; + } return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp index dcbe8d0f15c..45c51fa3797 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp @@ -7,14 +7,13 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; -typedef CGAL::Octree Octree; - -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -39,29 +38,30 @@ int main(int argc, char **argv) { std::cout << "Navigation relative to the root node" << std::endl; std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; std::cout << "the root node: " << std::endl; - std::cout << octree.root() << std::endl; + std::cout << octree.to_string(octree.root()) << std::endl; std::cout << "the first child of the root node: " << std::endl; - std::cout << octree.root()[0] << std::endl; + std::cout << octree.to_string(octree.child(octree.root(), 0)) << std::endl; std::cout << "the fifth child: " << std::endl; - std::cout << octree.root()[4] << std::endl; - std::cout << "the fifth child, accessed without the root keyword: " << std::endl; - std::cout << octree[4] << std::endl; + std::cout << octree.to_string(octree.child(octree.root(), 4)) << std::endl; + std::cout << "the fifth child, accessed without going through root: " << std::endl; + std::cout << octree.to_string(octree.node(4)) << std::endl; std::cout << "the second child of the fourth child: " << std::endl; - std::cout << octree.root()[4][1] << std::endl; - std::cout << "the second child of the fourth child, accessed without the root keyword: " << std::endl; - std::cout << octree[4][1] << std::endl; + std::cout << octree.to_string(octree.child(octree.child(octree.root(), 4), 1)) << std::endl; + std::cout << "the second child of the fourth child, accessed without going through root: " << std::endl; + std::cout << octree.to_string(octree.node(4, 1)) << std::endl; std::cout << std::endl; // Retrieve one of the deeper children - Octree::Node cur = octree[3][2]; + Octree::Node_index cur = octree.node(4, 3); std::cout << "Navigation relative to a child node" << std::endl; std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; std::cout << "the third child of the fourth child: " << std::endl; - std::cout << cur << std::endl; + std::cout << octree.to_string(cur) << std::endl; std::cout << "the third child: " << std::endl; - std::cout << cur.parent() << std::endl; + std::cout << octree.to_string(octree.parent(cur)) << std::endl; std::cout << "the next sibling of the third child of the fourth child: " << std::endl; - std::cout << cur.parent()[cur.local_coordinates().to_ulong() + 1] << std::endl; + std::cout << octree.to_string(octree.child(octree.parent(cur), octree.local_coordinates(cur).to_ulong() + 1)) + << std::endl; return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 72587767a60..a359f6217ee 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -7,13 +7,12 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; int main(int argc, char **argv) { @@ -37,9 +36,8 @@ int main(int argc, char **argv) { octree.refine(); // Print out the octree using preorder traversal - for (Octree::Node node : octree.traverse()) { - - std::cout << node << std::endl; + for (auto node : octree.traverse()) { + std::cout << octree.to_string(node) << std::endl; } return EXIT_SUCCESS; diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index f553eccc695..56419faf027 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -1,27 +1,27 @@ #include #include +#include #include -#include +#include #include // Type Declarations -typedef CGAL::Dimension_tag<4> Dimension; -typedef CGAL::Epick_d Kernel; -typedef Kernel::Point_d Point_d; -typedef std::vector Point_vector; - -typedef CGAL::Orthtree_traits_d Traits; -typedef CGAL::Orthtree Orthtree; +const int dimension = 4; +using Kernel = CGAL::Epick_d >; +using Point_d = Kernel::Point_d; +using Point_vector = std::vector; +using Traits = CGAL::Orthtree_traits_point; +using Orthtree = CGAL::Orthtree; int main() { CGAL::Random r; Point_vector points_dd; - for (std::size_t i = 0; i < 5; ++ i) + for (std::size_t i = 0; i < 20; ++ i) { - std::array init; + std::array init{}; for (double& v : init) v = r.get_double(-1., 1.); points_dd.emplace_back (init.begin(), init.end()); @@ -30,5 +30,7 @@ int main() Orthtree orthtree(points_dd); orthtree.refine(10, 5); + std::cout << orthtree << std::endl; + return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp index 34565268857..bd40ace0423 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp @@ -5,11 +5,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_2 Point_2; -typedef std::vector Point_vector; - -typedef CGAL::Quadtree Quadtree; +using Kernel = CGAL::Simple_cartesian; +using Point_2 = Kernel::Point_2; +using Point_vector = std::vector; +using Quadtree = CGAL::Quadtree; int main() { diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp new file mode 100644 index 00000000000..2e38f406c38 --- /dev/null +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +#include +#include + +using Kernel = CGAL::Simple_cartesian; + +namespace CGAL { + +struct empty_type { +}; + +template +struct Orthtree_traits_empty : public Orthtree_traits_base { + + using Self = Orthtree_traits_empty; + using Tree = Orthtree; + + using Node_data = std::array; + + Orthtree_traits_empty(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; + + auto construct_root_node_bbox_object() const { + return [&]() -> typename Self::Bbox_d { return m_bbox; }; + } + + auto construct_root_node_contents_object() const { return [&]() -> Node_data { return {}; }; } + + auto distribute_node_contents_object() { + return [&](typename Tree::Node_index /* n */, Tree& /* tree */, const typename Self::Point_d& /* center */) -> void {}; + } + +private: + + typename Self::Bbox_d m_bbox; + +}; +} + +using EmptyQuadtree = CGAL::Orthtree>; + +int main() { + + // Build an empty quadtree which covers the domain (-1, -1) to (1, 1) + EmptyQuadtree quadtree{{{-1, -1, 1, 1}}}; + + // Split several nodes of the tree + quadtree.split(quadtree.root()); + quadtree.split(quadtree.node(0)); + quadtree.split(quadtree.node(3)); + quadtree.split(quadtree.node(3, 0)); + + std::cout << quadtree.bbox(quadtree.root()) << std::endl; + std::cout << quadtree << std::endl; + return EXIT_SUCCESS; +} diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index bd7e50b3b88..e59a19be46d 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -15,34 +15,27 @@ #include #include -#include +#include namespace CGAL { /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef - \brief Alias that specializes the `Orthtree` class to a 3D octree. + \brief Alias that specializes the `Orthtree` class to a 3D octree storing 3D points. - These two types are exactly equivalent: - - `Octree` - - `Orthtree, PointRange, PointMap>`. - - \warning This is a not a real class but an alias, please refer to - the documentation of `Orthtree`. - - \tparam GeomTraits must be a model of `Kernel` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap` - \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` + \tparam GeomTraits a model of `Kernel` + \tparam PointRange a model of `Range` whose value type is the key type of `PointMap` + \tparam PointMap a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` + \tparam cubic_nodes Boolean to enforce cubic nodes */ -template ::value_type> > -#ifdef DOXYGEN_RUNNING -class Octree; -#else -using Octree = Orthtree, PointRange, PointMap>; -#endif +template < + typename GeomTraits, + typename PointRange, + typename PointMap = Identity_property_map::value_type>, + bool cubic_nodes = false +> +using Octree = Orthtree>; } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index afc958b9754..0cba3e781ba 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -20,10 +20,12 @@ #include #include +#include +#include +#include #include #include #include -#include #include #include @@ -39,123 +41,193 @@ #include #include #include +#include + +#include namespace CGAL { +namespace Orthtree_impl { + +BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) +BOOST_MPL_HAS_XXX_TRAIT_DEF(Squared_distance_of_element) + +template +struct Node_data_wrapper; + +template +struct Node_data_wrapper +{ + using Node_index = typename GT::Node_index; + using Node_data = typename GT::Node_data; + typename CGAL::Properties::Experimental::Property_container::template Array& m_node_contents; + + template + Node_data_wrapper(Property_container& node_properties) + : m_node_contents(node_properties.template get_or_add_property("contents").first) + {} + + const Node_data& operator[](Node_index n) const + { + return m_node_contents[n]; + } + + Node_data& operator[](Node_index n) + { + return m_node_contents[n]; + } +}; + +template +struct Node_data_wrapper +{ + using Node_index = typename GT::Node_index; + using Node_data = void*; + + template + Node_data_wrapper(Property_container&) {} + + void* operator[](Node_index) const + { + return nullptr; + } +}; + +} // end of Orthtree_impl namespace + /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef - \brief A data structure using an axis-aligned hybercubic - decomposition of dD space for efficient point access and - computations. + \brief A data structure using an axis-aligned hyperrectangle + decomposition of dD space for efficient access and + computation. - \details It builds a hierarchy of nodes which subdivide the space - based on a collection of points. Each node represents an - axis-aligned hypercubic region of space. A node contains the range - of points that are present in the region it defines, and it may + \details It builds a hierarchy of nodes which subdivides the space. + Each node represents an axis-aligned hyperrectangle region of space. + The contents of nodes depend on the traits class, non-leaf nodes also contain \f$2^{dim}\f$ other nodes which further subdivide the region. \sa `CGAL::Quadtree` \sa `CGAL::Octree` - \tparam Traits_ must be a model of `OrthtreeTraits` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap_` - \tparam PointMap_ must be a model of `ReadablePropertyMap` whose value type is `Traits_::Point_d` + \tparam GeomTraits must be a model of `OrthtreeTraits` or `OrthtreeTraitsWithData`. */ -template > -class Orthtree -{ - +template +class Orthtree { public: - /// \name Template Types /// @{ - typedef Traits_ Traits; ///< Geometry traits - typedef PointRange_ PointRange; ///< Point range - typedef PointMap_ PointMap; ///< Point map + using Traits = GeomTraits; ///< Geometry traits /// @} /// \name Traits Types /// @{ - typedef typename Traits::Dimension Dimension; ///< Dimension of the tree - typedef typename Traits::FT FT; ///< Number type. - typedef typename Traits::Point_d Point; ///< Point type. - typedef typename Traits::Bbox_d Bbox; ///< Bounding box type. - typedef typename Traits::Sphere_d Sphere; ///< Sphere type. +#ifndef DOXYGEN_RUNNING + static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; + static inline constexpr bool supports_neighbor_search = true;// Orthtree_impl::has_Squared_distance_of_element::value; +#else + static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitsWithData` and `false` otherwise. + static inline constexpr bool supports_neighbor_search = bool_value; ///< `true` if `GeomTraits` is a model of `CollectionPartitioningOrthtreeTraits` and `false` otherwise. +#endif + static constexpr int dimension = Traits::dimension; ///< Dimension of the tree + using Kernel = typename Traits::Kernel; ///< Kernel type. + using FT = typename Traits::FT; ///< Number type. + using Point = typename Traits::Point_d; ///< Point type. + using Bbox = typename Traits::Bbox_d; ///< Bounding box type. + using Sphere = typename Traits::Sphere_d; ///< Sphere type. + using Adjacency = typename Traits::Adjacency; ///< Adjacency type. + + using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. +#ifndef DOXYGEN_RUNNING + using Node_data = typename Orthtree_impl::Node_data_wrapper::Node_data; +#else + using Node_data = std::conditional_t; +#endif - /// \cond SKIP_IN_MANUAL - typedef typename Traits::Array Array; ///< Array type. - typedef typename Traits::Construct_point_d_from_array - Construct_point_d_from_array; - typedef typename Traits::Construct_bbox_d - Construct_bbox_d; - /// \endcond /// @} /// \name Public Types /// @{ /*! - * \brief Self typedef for convenience. + * \brief Self alias for convenience. */ - typedef Orthtree Self; + using Self = Orthtree; /*! * \brief Degree of the tree (number of children of non-leaf nodes). */ - typedef Dimension_tag<(2 << (Dimension::value-1))> Degree; + static constexpr int degree = (2 << (dimension - 1)); /*! - * \brief The Sub-tree / Orthant type. + \brief Set of bits representing this node's relationship to its parent. + + Equivalent to an array of Booleans, where index[0] is whether `x` + is greater, index[1] is whether `y` is greater, index[2] is whether + `z` is greater, and so on for higher dimensions if needed. + Used to represent a node's relationship to the center of its parent. */ - class Node; + using Local_coordinates = std::bitset; + + /*! + \brief Coordinates representing this node's relationship + with the rest of the tree. + + Each value `(x, y, z, ...)` of global coordinates is calculated by doubling + the parent's global coordinates and adding the local coordinates. + */ + using Global_coordinates = std::array; /*! * \brief A predicate that determines whether a node must be split when refining a tree. */ - typedef std::function Split_predicate; + using Split_predicate = std::function; /*! - * \brief A model of `ConstRange` whose value type is `Node`. + * \brief A model of `ForwardRange` whose value type is `Node_index`. */ #ifdef DOXYGEN_RUNNING - typedef unspecified_type Node_range; + using Node_index_range = unspecified_type; #else - typedef boost::iterator_range > Node_range; + using Node_index_range = boost::iterator_range>; #endif - /// \cond SKIP_IN_MANUAL - /*! - * \brief A function that determines the next node in a traversal given the current one. + * \brief A model of `LvaluePropertyMap` with `Node_index` as key type and `T` as value type. */ - typedef std::function Node_traversal_method_const; - - /// \endcond - - /// \cond SKIP_IN_MANUAL - typedef typename PointRange::iterator Range_iterator; - typedef typename std::iterator_traits::value_type Range_type; - typedef Orthtrees::internal::Cartesian_ranges Cartesian_ranges; - /// \endcond +#ifdef DOXYGEN_RUNNING + template + using Property_map = unspecified_type; +#else + template + using Property_map = Properties::Experimental::Property_array_handle; +#endif /// @} private: // data members : + using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; + using Node_property_container = Properties::Experimental::Property_container; + + template + using Property_array = typename Properties::Experimental::Property_container::template Array; + Traits m_traits; /* the tree traits */ - PointRange& m_range; /* input point range */ - PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ - Node m_root; /* root node of the orthtree */ + Node_property_container m_node_properties; + Orthtree_impl::Node_data_wrapper m_node_contents; + Property_array& m_node_depths; + Property_array& m_node_coordinates; + Property_array>& m_node_parents; + Property_array>& m_node_children; - Point m_bbox_min; /* input bounding box min value */ + using Bbox_dimensions = std::array; + Bbox m_bbox; + std::vector m_side_per_depth; /* precomputed (potentially approximated) side length per node's depth */ - std::vector m_side_per_depth; /* side length per node's depth */ - - Cartesian_ranges cartesian_range; /* a helper to easily iterator on coordinates of points */ + Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ public: @@ -163,141 +235,88 @@ public: /// @{ /*! - \brief creates an orthtree from a collection of points. + \brief constructs an orthtree for a traits instance. - The constructed orthtree has a root node, with no children, that - contains the points passed. That root node has a bounding box that - encloses all of the points passed, with padding according to the - `enlarge_ratio`. - - That root node is built as a `n`-cube (square in 2D, cube in 3D, - etc.) whose edge size is the longest bounding box edge multiplied - by `enlarge_ratio`. Using an `enlarge_ratio>1.0` prevents some - points from being exactly on the border of some cells, which can - lead to over-refinement. + The constructed orthtree has a root node with no children, + containing the contents determined by `Construct_root_node_contents` from the traits class. + That root node has a bounding box determined by `Construct_root_node_bbox` from the traits class, + which typically encloses its contents. This single-node orthtree is valid and compatible - with all Orthtree functionality, but any performance benefits are + with all orthtree functionality, but any performance benefits are unlikely to be realized until `refine()` is called. - \warning The input point range is not copied. It is used directly - and is rearranged by the `Orthtree`. Altering the point range - after creating the orthtree might leave it in an invalid state. - - \param point_range input point range. - \param point_map property map to access the input points. - \param enlarge_ratio ratio to which the bounding box should be enlarged. \param traits the traits object. */ - Orthtree(PointRange& point_range, - PointMap point_map = PointMap(), - const FT enlarge_ratio = 1.2, - Traits traits = Traits()) - : m_traits (traits) - , m_range (point_range) - , m_point_map (point_map) - , m_root(Node(), 0) - { - Array bbox_min; - Array bbox_max; + explicit Orthtree(Traits traits) : + m_traits(traits), + m_node_contents(m_node_properties), + m_node_depths(m_node_properties.template get_or_add_property("depths", 0).first), + m_node_coordinates(m_node_properties.template get_or_add_property("coordinates").first), + m_node_parents(m_node_properties.template get_or_add_property>("parents").first), + m_node_children(m_node_properties.template get_or_add_property>("children").first) { + + m_node_properties.emplace(); // init bbox with first values found + m_bbox = m_traits.construct_root_node_bbox_object()(); + + // Determine dimensions of the root bbox + + Bbox_dimensions size; + for (int i = 0; i < dimension; ++i) { - const Point& p = get (m_point_map, *(point_range.begin())); - std::size_t i = 0; - for (const FT& x : cartesian_range(p)) - { - bbox_min[i] = x; - bbox_max[i] = x; - ++ i; - } + size[i] = (m_bbox.max)()[i] - (m_bbox.min)()[i]; } - - for (const Range_type& r : point_range) - { - const Point& p = get (m_point_map, r); - std::size_t i = 0; - for (const FT& x : cartesian_range(p)) - { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++ i; - } - } - - Array bbox_centroid; - FT max_length = FT(0); - for (std::size_t i = 0 ; i < Dimension::value; ++ i) - { - bbox_centroid[i] = (bbox_min[i] + bbox_max[i]) / FT(2); - max_length = (std::max)(max_length, bbox_max[i] - bbox_min[i]); - } - max_length *= enlarge_ratio / FT(2); - - for (std::size_t i = 0 ; i < Dimension::value; ++ i) - { - bbox_min[i] = bbox_centroid[i] - max_length; - bbox_max[i] = bbox_centroid[i] + max_length; - } - - Construct_point_d_from_array construct_point_d_from_array - = m_traits.construct_point_d_from_array_object(); - // save orthtree attributes - m_bbox_min = construct_point_d_from_array(bbox_min); - m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - m_root.points() = {point_range.begin(), point_range.end()}; + m_side_per_depth.push_back(size); + + if constexpr (has_data) + data(root()) = m_traits.construct_root_node_contents_object()(); + } + + /*! + constructs an orthtree from a set of arguments provided to the traits constructor + */ + template = 2>> + explicit Orthtree(Args&& ... args) + : Orthtree(Traits(std::forward(args)...)) + {} + + /// copy constructor + explicit Orthtree(const Orthtree& other) : + m_traits(other.m_traits), + m_node_properties(other.m_node_properties), + m_node_contents(m_node_properties), + m_node_depths(m_node_properties.template get_property("depths")), + m_node_coordinates(m_node_properties.template get_property("coordinates")), + m_node_parents(m_node_properties.template get_property>("parents")), + m_node_children(m_node_properties.template get_property>("children")), + m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} + + /// move constructor + explicit Orthtree(Orthtree&& other) : + m_traits(other.m_traits), + m_node_properties(std::move(other.m_node_properties)), + m_node_contents(m_node_properties), + m_node_depths(m_node_properties.template get_property("depths")), + m_node_coordinates(m_node_properties.template get_property("coordinates")), + m_node_parents(m_node_properties.template get_property>("parents")), + m_node_children(m_node_properties.template get_property>("children")), + m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) + { + other.m_node_properties.emplace(); } /// @} - /// \cond SKIP_IN_MANUAL - - // copy constructor - Orthtree (const Orthtree& other) - : m_traits (other.m_traits) - , m_range (other.m_range) - , m_point_map (other.m_point_map) - , m_root (other.m_root.deep_copy()) - , m_bbox_min (other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) - { } - - // move constructor - Orthtree (Orthtree&& other) - : m_traits (other.m_traits) - , m_range (other.m_range) - , m_point_map (other.m_point_map) - , m_root (other.m_root) - , m_bbox_min (other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) - { - other.m_root = Node(Node(), 0); - } // Non-necessary but just to be clear on the rule of 5: - // assignment operators deleted (PointRange is a ref) - Orthtree& operator= (const Orthtree& other) = delete; - Orthtree& operator= (Orthtree&& other) = delete; - // Destructor - ~Orthtree() - { - std::queue nodes; - nodes.push(m_root); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()) - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (node[i]); - node.free(); - } - } + // assignment operators deleted + Orthtree& operator=(const Orthtree& other) = delete; - // move constructor - /// \endcond + Orthtree& operator=(Orthtree&& other) = delete; /// \name Tree Building /// @{ @@ -305,105 +324,82 @@ public: /*! \brief recursively subdivides the orthtree until it meets the given criteria. - The split predicate is a `std::function` that takes a `Node` and - returns a Boolean value (where `true` implies that a `Node` needs to - be split, `false` that the `Node` should be a leaf). + The split predicate should return `true` if a leaf node should be split and `false` otherwise. This function may be called several times with different predicates: in that case, nodes already split are left unaltered, while nodes that were not split and for which `split_predicate` returns `true` are split. - \param split_predicate determines whether or not a node needs to - be subdivided. + \param split_predicate determines whether or not a leaf node needs to be subdivided. */ void refine(const Split_predicate& split_predicate) { - // If the tree has already been refined, reset it - if (!m_root.is_leaf()){ - std::queue nodes; - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (m_root[i]); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()) - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (node[i]); - node.free(); - } - m_root.unsplit(); - } - - // Reset the side length map, too - m_side_per_depth.resize(1); - // Initialize a queue of nodes that need to be refined - std::queue todo; - todo.push(m_root); + std::queue todo; + todo.push(0); // Process items in the queue until it's consumed fully while (!todo.empty()) { // Get the next element - Node current = todo.front(); + auto current = todo.front(); todo.pop(); // Check if this node needs to be processed - if (split_predicate(current)) { + if (split_predicate(current, *this)) { - // Check if we've reached a new max depth - if (current.depth() == depth()) { - - // Update the side length map - m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); - } - - // Split the node, redistributing its points to its children + // Split the node, redistributing its contents to its children split(current); - // Process each of its children - for (int i = 0; i < Degree::value; ++i) - todo.push(current[i]); + } + // Check if the node has children which need to be processed + if (!is_leaf(current)) { + + // Process each of its children + for (int i = 0; i < degree; ++i) + todo.push(child(current, i)); } } } /*! - - \brief Convenience overload that refines an orthtree using a - maximum depth and maximum number of points in a node as split + \brief convenience overload that refines an orthtree using a + maximum depth and maximum number of contained elements in a node as split predicate. This is equivalent to calling - `refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, + `refine(Orthtrees::Maximum_depth_and_maximum_contained_elements(max_depth, bucket_size))`. The refinement is stopped as soon as one of the conditions is - violated: if a node has more inliers than `bucket_size` but is + violated: if a node contains more elements than `bucket_size` but is already at `max_depth`, it is not split. Similarly, a node that is - at a depth smaller than `max_depth` but already has fewer inliers + at a depth smaller than `max_depth` but already contains fewer elements than `bucket_size`, it is not split. + \warning This convenience method is only appropriate for trees with traits classes where + `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. + \param max_depth deepest a tree is allowed to be (nodes at this depth will not be split). - \param bucket_size maximum points a node is allowed to contain. + \param bucket_size maximum number of items a node is allowed to contain. */ void refine(size_t max_depth = 10, size_t bucket_size = 20) { - refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, bucket_size)); + refine(Orthtrees::Maximum_depth_and_maximum_contained_elements(max_depth, bucket_size)); } /*! \brief refines the orthtree such that the difference of depth between two immediate neighbor leaves is never more than 1. + + This is done only by adding nodes, nodes are never removed. */ void grade() { // Collect all the leaf nodes - std::queue leaf_nodes; - for (Node leaf : traverse(Orthtrees::Leaves_traversal())) { - // TODO: I'd like to find a better (safer) way of doing this + std::queue leaf_nodes; + for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { leaf_nodes.push(leaf); } @@ -411,42 +407,42 @@ public: while (!leaf_nodes.empty()) { // Get the next node - Node node = leaf_nodes.front(); + Node_index node = leaf_nodes.front(); leaf_nodes.pop(); // Skip this node if it isn't a leaf anymore - if (!node.is_leaf()) + if (!is_leaf(node)) continue; // Iterate over each of the neighbors for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - Node neighbor = node.adjacent_node(direction); + auto neighbor = adjacent_node(node, direction); // If it doesn't exist, skip it - if (neighbor.is_null()) + if (!neighbor) continue; // Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // TODO: This check might be redundant, if it doesn't affect performance maybe I could remove it - if (neighbor.parent() == node.parent()) + if (parent(*neighbor) == parent(node)) continue; // If it's already been split, skip it - if (!neighbor.is_leaf()) + if (!is_leaf(*neighbor)) continue; // Check if the neighbor breaks our grading rule // TODO: could the rule be parametrized? - if ((node.depth() - neighbor.depth()) > 1) { + if ((depth(node) - depth(*neighbor)) > 1) { // Split the neighbor - split(neighbor); + split(*neighbor); // Add newly created children to the queue - for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(neighbor[i]); + for (int i = 0; i < degree; ++i) { + leaf_nodes.push(child(*neighbor, i)); } } } @@ -459,22 +455,15 @@ public: /// @{ /*! - \brief returns the root node. + * \brief provides direct read-only access to the tree traits. */ - Node root() const { return m_root; } + const Traits& traits() const { return m_traits; } /*! - \brief Convenience function to access the child nodes of the root - node by their indices. - - `my_tree[5]` is equivalent to `my_tree.root()[5]`. - - \sa `Node::operator[]()` - - \param index the index of the child node. - \return the accessed node. + \brief provides access to the root node, and by + extension the rest of the tree. */ - Node operator[](std::size_t index) const { return m_root[index]; } + Node_index root() const { return 0; } /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). @@ -482,54 +471,156 @@ public: std::size_t depth() const { return m_side_per_depth.size() - 1; } /*! - \brief constructs a node range using a tree-traversal function. + \brief constructs a node index range using a tree-traversal function. - This method allows to iterate on the nodes of the tree with a + This method allows iteration over the nodes of the tree with a user-selected order (preorder, postorder, leaves-only, etc.). - \tparam Traversal model of `OrthtreeTraversal` that provides functions - compatible with the type of the orthree + \tparam Traversal a model of `OrthtreeTraversal` - \param traversal the instance of `Traversal` used + \param traversal class defining the traversal strategy - \return a forward input iterator over the nodes of the tree + \return a `ForwardRange` over the node indices of the tree */ - template - Node_range traverse(const Traversal &traversal = Traversal()) const { + template + Node_index_range traverse(Traversal traversal) const { - Node first = traversal.first(m_root); + Node_index first = traversal.first_index(); - Node_traversal_method_const next - = [&](const Node& n) -> Node { return traversal.next(n); }; + auto next = [=](const Self&, Node_index index) -> std::optional { + return traversal.next_index(index); + }; - return boost::make_iterator_range(Traversal_iterator(first, next), - Traversal_iterator()); + return boost::make_iterator_range(Index_traversal_iterator(*this, first, next), + Index_traversal_iterator()); + } + + + /*! + \brief convenience method for using a traversal without constructing it yourself + + \tparam Traversal a model of `OrthtreeTraversal` + + \param args Arguments to to pass to the traversal's constructor, excluding the first (always an orthtree reference) + + \return a `ForwardRange` over the node indices of the tree + */ + template + Node_index_range traverse(Args&& ...args) const { + return traverse(Traversal{*this, std::forward(args)...}); + } + + // TODO shall we document it? + FT + compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const + { + CGAL_assertion(depth <= m_side_per_depth.size()); + // an odd coordinate will be first compute at the current depth, + // while an even coordinate has already been computed at a previous depth. + // So while the coordinate is even, we decrease the depth to end up of the first + // non-even coordinate to compute it (with particular case for bbox limits). + // Note that is depth becomes too large, we might end up with incorrect coordinates + // due to rounding errors. + if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth + if (gc == 0) return (m_bbox.min)()[ci]; + if (gc % 2 !=0) + { + FT size = depth < m_side_per_depth.size() + ? m_side_per_depth[depth][ci] + : m_side_per_depth[depth-1][ci]/FT(2); + return (m_bbox.min)()[ci] + int(gc) * size; + } + std::size_t nd = depth; + do{ + --nd; + gc = gc >> 1; + } + while((gc&1)==0); // while even, shift + return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; } /*! \brief constructs the bounding box of a node. - \note The object constructed is not the bounding box of the point - subset inside the node, but the bounding box of the node itself - (cubic). + \note The object constructed is not the bounding box of the node's contents, + but the bounding box of the node itself. + + \param n node to generate a bounding box for + + \return the bounding box of the node n */ - Bbox bbox(const Node &node) const { - // Determine the side length of this node - FT size = m_side_per_depth[node.depth()]; + Bbox bbox(Node_index n) const { + using Cartesian_coordinate = std::array; + Cartesian_coordinate min_corner, max_corner; + std::size_t node_depth = depth(n); - // Determine the location this node should be split - Array min_corner; - Array max_corner; - for (int i = 0; i < Dimension::value; i++) { - - min_corner[i] = m_bbox_min[i] + (node.global_coordinates()[i] * size); - max_corner[i] = min_corner[i] + size; + for (int i = 0; i < dimension; i++) + { + min_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i], node_depth, i); + max_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i]+1, node_depth, i); } + return {std::apply(m_traits.construct_point_d_object(), min_corner), + std::apply(m_traits.construct_point_d_object(), max_corner)}; + } - // Create the bbox - Construct_bbox_d construct_bbox - = m_traits.construct_bbox_d_object(); - return construct_bbox(min_corner, max_corner); + /// @} + + /// \name Custom Properties + /// @{ + + /*! + \brief gets a property for nodes, adding it if it does not already exist. + + \tparam T the type of the property to add + + \param name the name of the new property + \param default_value the default value assigned to nodes for this property + + \return pair of the property map and a Boolean which is `true` if the property needed to be created + */ + template + std::pair, bool> add_property(const std::string& name, const T default_value = T()) { + auto p = m_node_properties.get_or_add_property(name, default_value); + return std::pair, bool>(Property_map(p.first), p.second); + } + + /*! + \brief gets a property of the nodes if it exists. + + \tparam T the type of the property to retrieve + + \param name the name of the property + + \return an optional containing the property map if it exists + */ + template + std::optional> property(const std::string& name) { + auto p = m_node_properties.template get_property_if_exists(name); + if (p) + return std::optional >(Property_map(*p)); + else + return std::nullopt; + } + + /*! + \brief returns a vector of all property names. + */ + std::vector properties() const { + return m_node_properties.properties(); + } + + /*! + \brief removes the node property from the tree. + + \tparam T the type of the property to remove + + \param property the property to be removed from the tree. + + \return true if property was a valid property of the tree. + */ + template + bool remove_property(Property_map property) { + return m_node_properties.remove_property(property.array()); } /// @} @@ -538,37 +629,39 @@ public: /// @{ /*! - \brief finds the leaf node which contains the point `p`. + \brief finds the leaf node which contains a particular point in space. - Traverses the orthtree and finds the deepest cell that has a + Traverses the orthtree and finds the leaf cell that has a domain enclosing the point passed. The point passed must be within - the region enclosed by the orthtree (bbox of the root node). + the region enclosed by the orthtree (bbox of the root node). The point is contained in the + lower cell of each direction if its coordinate is lower than the center. \param point query point. - \return the node which contains the point. + + \return the index of the node which contains the point. */ - Node locate(const Point &point) const { + Node_index locate(const Point& point) const { // Make sure the point is enclosed by the orthtree - CGAL_precondition (CGAL::do_intersect(point, bbox(m_root))); + CGAL_precondition (CGAL::do_intersect(point, bbox(root()))); // Start at the root node - auto node_for_point = m_root; + Node_index node_for_point = root(); // Descend the tree until reaching a leaf node - while (!node_for_point.is_leaf()) { + while (!is_leaf(node_for_point)) { // Find the point to split around Point center = barycenter(node_for_point); // Find the index of the correct sub-node - typename Node::Local_coordinates index; - std::size_t dimension = 0; + Local_coordinates local_coords; + std::size_t dim = 0; for (const auto& r : cartesian_range(center, point)) - index[dimension ++] = (get<0>(r) < get<1>(r)); + local_coords[dim++] = (get<0>(r) <= get<1>(r)); // Find the correct sub-node of the current node - node_for_point = node_for_point[index.to_ulong()]; + node_for_point = child(node_for_point, local_coords.to_ulong()); } // Return the result @@ -576,39 +669,91 @@ public: } /*! - \brief finds the `k` nearest neighbors of `query`. + \brief finds the `k` nearest neighbors of the point `query`. - Nearest neighbors are outputted in order of increasing distance to - `query`. + Nearest neighbors are outputted in order of increasing distance to `query`. - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. - \param query a query point. - \param k the number of neighbors. - \param output the output iterator. - */ + \tparam OutputIterator a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` objects. + + \param query query point + \param k number of neighbors to find + \param output output iterator + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ template - OutputIterator nearest_neighbors (const Point& query, - std::size_t k, - OutputIterator output) const { - Sphere query_sphere (query, (std::numeric_limits::max)()); - return nearest_k_neighbors_in_radius(query_sphere, k, output); + auto nearest_k_neighbors(const Point& query, + std::size_t k, + OutputIterator output) const -> std::enable_if_t { + Sphere query_sphere(query, (std::numeric_limits::max)()); + CGAL_precondition(k > 0); + + return nearest_k_neighbors_within_radius(query_sphere, k, output); } /*! - \brief finds the points in `sphere`. + \brief finds the elements in the sphere `query`. - Nearest neighbors are outputted in order of increasing distance to - the center of `sphere`. + Elements are outputted in order of increasing distance to + the center of the sphere. - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. - \param query query sphere. - \param output output iterator. - */ + \tparam OutputIterator a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` objects. + + \param query query sphere + \param output output iterator + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ template - OutputIterator nearest_neighbors (const Sphere& query, OutputIterator output) const { + auto neighbors_within_radius(const Sphere& query, OutputIterator output) const -> std::enable_if_t { + return nearest_k_neighbors_within_radius(query, (std::numeric_limits::max)(), output); + } + + /*! + \brief finds at most `k` elements within a specific radius that are + nearest to the center of the sphere `query`: if `query` does not contain + at least `k` elements, only contained elements will be returned. + + This function is useful when the user already knows how sparse the elements are, + or if they do not care about elements that are too far away. + Setting a small radius may have performance benefits. + + \tparam OutputIterator must be a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` + + \param query the region to search within + \param k the number of elements to find + \param output the output iterator to add the found elements to (in order of increasing distance) + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ + template + auto nearest_k_neighbors_within_radius( + const Sphere& query, + std::size_t k, + OutputIterator output + ) const -> std::enable_if_t { + CGAL_precondition(k > 0); Sphere query_sphere = query; - return nearest_k_neighbors_in_radius(query_sphere, - (std::numeric_limits::max)(), output); + + // todo: this type is over-constrained, this must be made more generic + struct Node_element_with_distance { + typename Traits::Node_data_element element; + FT distance; + }; + + // Create an empty list of elements + std::vector element_list; + if (k != (std::numeric_limits::max)()) + element_list.reserve(k); + + // Invoking the recursive function adds those elements to the vector (passed by reference) + nearest_k_neighbors_recursive(query_sphere, root(), element_list, k); + + // Add all the points found to the output + for (auto& item : element_list) + *output++ = item.element; + + return output; } /*! @@ -617,15 +762,18 @@ public: \note this function requires the function `bool CGAL::do_intersect(QueryType, Traits::Bbox_d)` to be defined. - This function finds all the intersecting nodes and returns them as const pointers. + This function finds all the intersecting leaf nodes and writes their indices to the output iterator. + + \tparam Query the primitive class (e.g., sphere, ray) + \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types - \tparam Query the primitive class (e.g. sphere, ray) - \tparam OutputIterator a model of `OutputIterator` that accepts `Node` objects \param query the intersecting primitive. \param output output iterator. + + \return the output iterator after writing */ - template - OutputIterator intersected_nodes (const Query& query, OutputIterator output) const { + template + OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { return intersected_nodes_recursive(query, root(), output); } @@ -637,14 +785,17 @@ public: /*! \brief compares the topology of the orthtree with that of `rhs`. - Trees may be considered equivalent even if they contain different points. - Equivalent trees must have the same bounding box and the same node structure. - Node structure is evaluated by comparing the root nodes using the node equality operator. + Trees may be considered equivalent even if they have different contents. + Equivalent trees must have the same root bounding box and the same node structure. + + \param rhs the other orthtree + + \return `true` if the trees have the same topology, and `false` otherwise */ - bool operator==(const Self &rhs) const { + bool operator==(const Self& rhs) const { // Identical trees should have the same bounding box - if (rhs.m_bbox_min != m_bbox_min || rhs.m_side_per_depth[0] != m_side_per_depth[0]) + if (rhs.m_bbox != m_bbox || rhs.m_side_per_depth[0] != m_side_per_depth[0]) return false; // Identical trees should have the same depth @@ -652,273 +803,613 @@ public: return false; // If all else is equal, recursively compare the trees themselves - return Node::is_topology_equal(rhs.m_root, m_root); + return is_topology_equal(*this, rhs); } /*! \brief compares the topology of the orthtree with that of `rhs`. + + \param rhs the other orthtree + + \return `false` if the trees have the same topology, and `true` otherwise */ - bool operator!=(const Self &rhs) const { + bool operator!=(const Self& rhs) const { return !operator==(rhs); } /// @} - // TODO: Document this - // TODO: Could this method name be reduced to just "center" ? - Point barycenter(const Node& node) const { + /// \name Node Access + /// @{ - // Determine the side length of this node - FT size = m_side_per_depth[node.depth()]; + /*! + \brief determines whether the node specified by index `n` is a leaf node. + */ + bool is_leaf(Node_index n) const { + return !m_node_children[n].has_value(); + } - // Determine the location this node should be split - Array bary; - std::size_t i = 0; - for (const FT& f : cartesian_range(m_bbox_min)) - { - bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f; - ++ i; + /*! + \brief determines whether the node specified by index `n` is the root node. + */ + bool is_root(Node_index n) const { + return n == 0; + } + + /*! + \brief determines the depth of the node specified. + + The root node has depth 0, its children have depth 1, and so on. + + \param n index of the node to check. + + \return the depth of the node n within its tree. + */ + std::size_t depth(Node_index n) const { + return m_node_depths[n]; + } + + /*! + \brief retrieves a reference to the `Node_data` associated with the node specified by `n` if + `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. + */ + std::conditional_t& data(Node_index n){ + return m_node_contents[n]; + } + + /*! + \brief retrieves a const reference to the `Node_data` associated with the node specified by `n` if + `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. + */ + std::conditional_t data(Node_index n) const{ + return m_node_contents[n]; + } + + /*! + \brief retrieves the global coordinates of the node. + */ + Global_coordinates global_coordinates(Node_index n) const { + return m_node_coordinates[n]; + } + + /*! + \brief retrieves the local coordinates of the node. + */ + Local_coordinates local_coordinates(Node_index n) const { + Local_coordinates result; + for (std::size_t i = 0; i < dimension; ++i) + result[i] = global_coordinates(n)[i] & 1; + return result; + } + + /*! + \brief returns this n's parent. + + \pre `!is_root()` + + \param n index of the node to retrieve the parent of + + \return the index of the parent of node n + */ + Node_index parent(Node_index n) const { + CGAL_precondition (!is_root(n)); + return *m_node_parents[n]; + } + + /*! + \brief returns a node's `i`th child. + + \pre `!is_leaf()` + + \param n index of the node to retrieve the child of + \param i in [0, 2^D) specifying the child to retrieve + + \return the index of the `i`th child of node n + */ + Node_index child(Node_index n, std::size_t i) const { + CGAL_precondition (!is_leaf(n)); + return *m_node_children[n] + i; + } + + /*! + \brief retrieves an arbitrary descendant of the node specified by `node`. + + Convenience function to avoid the need to call `orthtree.child(orthtree.child(node, 0), 1)`. + + Each index in `indices` specifies which child to enter as descending the tree from `node` down. + Indices are evaluated in the order they appear as parameters, so + `descendant(root, 0, 1)` returns the second child of the first child of the root. + + \param node the node to descend + \param indices the integer indices specifying the descent to perform + + \return the index of the specified descendant node + */ + template + Node_index descendant(Node_index node, Indices... indices) { + return recursive_descendant(node, indices...); + } + + /*! + \brief convenience function for retrieving arbitrary nodes. + + Equivalent to `tree.descendant(tree.root(), indices...)`. + + \param indices the integer indices specifying the descent to perform, starting from the root + + \return the index of the specified node + */ + template + Node_index node(Indices... indices) { + return descendant(root(), indices...); + } + + /*! + \brief finds the next sibling in the parent of the node specified by the index `n`. + + Traverses the tree in increasing order of local index (e.g., 000, 001, 010, etc.) + + \param n the index of the node to find the sibling of + + \return the index of the next sibling of n + if n is not the last node in its parent, otherwise `std::nullopt`. + */ + const std::optional next_sibling(Node_index n) const { + + // Root node has no siblings + if (is_root(n)) return {}; + + // Find out which child this is + std::size_t local_coords = local_coordinates(n).to_ulong(); + + // The last child has no more siblings + if (int(local_coords) == degree - 1) + return {}; + + // The next sibling is the child of the parent with the following local coordinates + return child(parent(n), local_coords + 1); + } + + /*! + \brief finds the next sibling of the parent of the node specified by `n` if it exists. + + \param n the index node to find the sibling up of. + + \return The index of the next sibling of the parent of n + if n is not the root and its parent has a sibling, otherwise nothing. + */ + const std::optional next_sibling_up(Node_index n) const { + + // the root node has no next sibling up + if (n == 0) return {}; + + auto up = std::optional{parent(n)}; + while (up) { + + if (next_sibling(*up)) return {next_sibling(*up)}; + + up = is_root(*up) ? std::optional{} : std::optional{parent(*up)}; } - // Convert that location into a point - Construct_point_d_from_array construct_point_d_from_array - = m_traits.construct_point_d_from_array_object(); - return construct_point_d_from_array(bary); + return {}; } + /*! + \brief finds the leaf node reached when descending the tree and always choosing child 0. + + This is the starting point of a depth-first traversal. + + \param n the index of the node to find the deepest first child of. + + \return the index of the deepest first child of node n. + */ + Node_index deepest_first_child(Node_index n) const { + + auto first = n; + while (!is_leaf(first)) + first = child(first, 0); + + return first; + } + + /*! + \brief finds node reached when descending the tree to a depth `d` and always choosing child 0. + + Similar to `deepest_first_child()`, but does go to a fixed depth. + + \param n the index of the node to find the `d`th first child of. + \param d the depth to descend to. + + \return the index of the `d`th first child, nothing if the tree is not deep enough. + */ + std::optional first_child_at_depth(Node_index n, std::size_t d) const { + + std::queue todo; + todo.push(n); + + while (!todo.empty()) { + Node_index node = todo.front(); + todo.pop(); + + if (depth(node) == d) + return node; + + if (!is_leaf(node)) + for (int i = 0; i < degree; ++i) + todo.push(child(node, i)); + } + + return {}; + } + + /*! + \brief splits a node into subnodes. + + Only leaf nodes should be split. + When a node is split it is no longer a leaf node. + The full set of `degree` children are constructed automatically, and their values are set. + Contents of this node are _not_ propagated automatically, this is responsibility of the + `distribute_node_contents_object` in the traits class. + + \param n index of the node to split + */ + void split(Node_index n) { + + // Make sure the node hasn't already been split + CGAL_precondition (is_leaf(n)); + + // Split the node to create children + using Local_coordinates = Local_coordinates; + m_node_children[n] = m_node_properties.emplace_group(degree); + for (std::size_t i = 0; i < degree; i++) { + + Node_index c = *m_node_children[n] + i; + + // Make sure the node isn't one of its own children + CGAL_assertion(n != *m_node_children[n] + i); + + Local_coordinates local_coordinates{i}; + for (int i = 0; i < dimension; i++) + m_node_coordinates[c][i] = (2 * m_node_coordinates[n][i]) + local_coordinates[i]; + m_node_depths[c] = m_node_depths[n] + 1; + m_node_parents[c] = n; + } + + // Check if we've reached a new max depth + if (depth(n) + 1 == m_side_per_depth.size()) { + // Update the side length map with the dimensions of the children + Bbox_dimensions size = m_side_per_depth.back(); + Bbox_dimensions child_size; + for (int i = 0; i < dimension; ++i) + child_size[i] = size[i] / FT(2); + m_side_per_depth.push_back(child_size); + } + + // Find the point around which the node is split + Point center = barycenter(n); + + // Add the node's contents to its children + if constexpr (has_data) + m_traits.distribute_node_contents_object()(n, *this, center); + } + + /*! + * \brief returns the center point of a node. + * + * @param n index of the node to find the center point for + * + * @return the center point of node n + */ + Point barycenter(Node_index n) const { + std::size_t node_depth = depth(n); + // the barycenter is computed as the lower corner of the lexicographically top child node + std::array bary; + for (int i = 0; i < dimension; i++) + bary[i] = compute_cartesian_coordinate(2 * global_coordinates(n)[i]+1, node_depth+1, i); + + return std::apply(m_traits.construct_point_d_object(), bary); + } + + /*! + \brief determines whether a pair of subtrees have the same topology. + + \param lhsNode index of a node in lhsTree + \param lhsTree an Orthtree + \param rhsNode index of a node in rhsTree + \param rhsTree another Orthtree + + @return true if lhsNode and rhsNode have the same topology, false otherwise + */ + static bool is_topology_equal(Node_index lhsNode, const Self& lhsTree, Node_index rhsNode, const Self& rhsTree) { + + // If one node is a leaf, and the other isn't, they're not the same + if (lhsTree.is_leaf(lhsNode) != rhsTree.is_leaf(rhsNode)) + return false; + + // If both nodes are non-leaf + if (!lhsTree.is_leaf(lhsNode)) { + + // Check all the children + for (int i = 0; i < degree; ++i) { + // If any child cell is different, they're not the same + if (!is_topology_equal(lhsTree.child(lhsNode, i), lhsTree, + rhsTree.child(rhsNode, i), rhsTree)) + return false; + } + } + + return (lhsTree.global_coordinates(lhsNode) == rhsTree.global_coordinates(rhsNode)); + } + + /*! + \brief helper function for calling `is_topology_equal()` on the root nodes of two trees. + + \param lhs an Orthtree + \param rhs another Orthtree + + \return `true` if `lhs` and `rhs` have the same topology, and `false` otherwise + */ + static bool is_topology_equal(const Self& lhs, const Self& rhs) { + return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); + } + + /*! + \brief finds the directly adjacent node in a specific direction + + \pre `direction.to_ulong < 2 * dimension` + + Adjacent nodes are found according to several properties: + - adjacent nodes may be larger than the seek node, but never smaller + - a node has at most `2 * dimension` different adjacent nodes (in 3D: left, right, up, down, front, back) + - adjacent nodes are not required to be leaf nodes + + Here's a diagram demonstrating the concept for a quadtree: + + ``` + +---------------+---------------+ + | | | + | | | + | | | + | A | | + | | | + | | | + | | | + +-------+-------+---+---+-------+ + | | | | | | + | A | (S) +---A---+ | + | | | | | | + +---+---+-------+---+---+-------+ + | | | | | | + +---+---+ A | | | + | | | | | | + +---+---+-------+-------+-------+ + ``` + + - (S) : Seek node + - A : Adjacent node + + Note how the top adjacent node is larger than the seek node. The + right adjacent node is the same size, even though it contains + further subdivisions. + + This implementation returns the adjacent node if it's found. If + there is no adjacent node in that direction, it returns a null + node. + + \param n index of the node to find a neighbor of + \param direction which way to find the adjacent node relative to + this one. Each successive bit selects the direction for the + corresponding dimension: for an octree in 3D, 010 means: negative + direction in X, position direction in Y, negative direction in Z. + + \return the index of the adjacent node if it exists, nothing otherwise. + */ + std::optional adjacent_node(Node_index n, const Local_coordinates& direction) const { + + // Direction: LEFT RIGHT DOWN UP BACK FRONT + // direction: 000 001 010 011 100 101 + + // Nodes only have up to 2*dim different adjacent nodes (since boxes have 6 sides) + CGAL_precondition(direction.to_ulong() < dimension * 2); + + // The root node has no adjacent nodes! + if (is_root(n)) return {}; + + // The least significant bit indicates the sign (which side of the node) + bool sign = direction[0]; + + // The first two bits indicate the dimension/axis (x, y, z) + uint8_t dim = uint8_t((direction >> 1).to_ulong()); + + // Create an offset so that the bit-significance lines up with the dimension (e.g., 1, 2, 4 --> 001, 010, 100) + int8_t offset = (uint8_t) 1 << dim; + + // Finally, apply the sign to the offset + offset = (sign ? offset : -offset); + + // Check if this child has the opposite sign along the direction's axis + if (local_coordinates(n)[dim] != sign) { + // This means the adjacent node is a direct sibling, the offset can be applied easily! + return {child(parent(n), local_coordinates(n).to_ulong() + offset)}; + } + + // Find the parent's neighbor in that direction, if it exists + auto adjacent_node_of_parent = adjacent_node(parent(n), direction); + + // If the parent has no neighbor, then this node doesn't have one + if (!adjacent_node_of_parent) return {}; + + // If the parent's adjacent node has no children, then it's this node's adjacent node + if (is_leaf(*adjacent_node_of_parent)) + return adjacent_node_of_parent; + + // Return the nearest node of the parent by subtracting the offset instead of adding + return {child(*adjacent_node_of_parent, local_coordinates(n).to_ulong() - offset)}; + } + + /*! + \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. + + \param n index of the node to find a neighbor of + \param adjacency which way to find the adjacent node relative to this one + */ + std::optional adjacent_node(Node_index n, Adjacency adjacency) const { + return adjacent_node(n, std::bitset(static_cast(adjacency))); + } + + /// @} + private: // functions : - void reassign_points(Node &node, Range_iterator begin, Range_iterator end, const Point ¢er, - std::bitset coord = {}, - std::size_t dimension = 0) { - - // Root case: reached the last dimension - if (dimension == Dimension::value) { - - node[coord.to_ulong()].points() = {begin, end}; - - return; - } - - // Split the point collection around the center point on this dimension - Range_iterator split_point = std::partition - (begin, end, - [&](const Range_type &a) -> bool { - // This should be done with cartesian iterator but it seems - // complicated to do efficiently - return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); - }); - - // Further subdivide the first side of the split - std::bitset coord_left = coord; - coord_left[dimension] = false; - reassign_points(node, begin, split_point, center, coord_left, dimension + 1); - - // Further subdivide the second side of the split - std::bitset coord_right = coord; - coord_right[dimension] = true; - reassign_points(node, split_point, end, center, coord_right, dimension + 1); + Node_index recursive_descendant(Node_index node, std::size_t i) { return child(node, i); } + template + Node_index recursive_descendant(Node_index node, std::size_t i, Indices... remaining_indices) { + return recursive_descendant(child(node, i), remaining_indices...); } - void split(Node& node) { + bool do_intersect(Node_index n, const Sphere& sphere) const { - // Make sure the node hasn't already been split - CGAL_precondition (node.is_leaf()); - - // Split the node to create children - node.split(); - - // Find the point to around which the node is split - Point center = barycenter(node); - - // Add the node's points to its children - reassign_points(node, node.points().begin(), node.points().end(), center); - } - - bool do_intersect(const Node &node, const Sphere &sphere) const { - - // Create a cubic bounding box from the node - Bbox node_cube = bbox(node); - - // Check for overlap between the node's box and the sphere as a box, to quickly catch some cases - // FIXME: Activating this causes slower times -// if (!do_overlap(node_cube, sphere.bbox())) -// return false; + // Create a bounding box from the node + Bbox node_box = bbox(n); // Check for intersection between the node and the sphere - return CGAL::do_intersect(node_cube, sphere); + return CGAL::do_intersect(node_box, sphere); } - // TODO: There has to be a better way than using structs like these! - struct Point_with_distance { - Point point; - FT distance; - }; - struct Node_index_with_distance { - typename Node::Local_coordinates index; - FT distance; - - Node_index_with_distance (const typename Node::Local_coordinates& index, - const FT& distance) - : index(index), distance(distance) - { } - }; - - void nearest_k_neighbors_recursive(Sphere& search_bounds, const Node &node, - std::vector &results, FT epsilon = 0) const { - - // Check whether the node has children - if (node.is_leaf()) { - - // Base case: the node has no children - - // Loop through each of the points contained by the node - // Note: there might be none, and that should be fine! - for (auto point_index : node.points()) { - - // Retrieve each point from the orthtree's point map - auto point = get(m_point_map, point_index); - - // Pair that point with its distance from the search point - Point_with_distance current_point_with_distance = - {point, squared_distance(point, search_bounds.center())}; - - // Check if the new point is within the bounds - if (current_point_with_distance.distance < search_bounds.squared_radius()) { - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Delete a point if we need to make room - results.pop_back(); - } - - // Add the new point - results.push_back(current_point_with_distance); - - // Sort the list - std::sort(results.begin(), results.end(), [=](auto &left, auto &right) { - return left.distance < right.distance; - }); - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Set the search radius - search_bounds = Sphere(search_bounds.center(), results.back().distance + epsilon); - } - } - } - } else { - - // Recursive case: the node has children - - // Create a list to map children to their distances - std::vector children_with_distances; - children_with_distances.reserve(Degree::value); - - // Fill the list with child nodes - for (int index = 0; index < Degree::value; ++index) { - Node child_node = node[index]; - - // Add a child to the list, with its distance - children_with_distances.emplace_back(typename Node::Local_coordinates(index), - CGAL::squared_distance(search_bounds.center(), barycenter(child_node))); - } - - // Sort the children by their distance from the search point - std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto &left, auto &right) { - return left.distance < right.distance; - }); - - // Loop over the children - for (auto child_with_distance : children_with_distances) { - Node child_node = node[child_with_distance.index.to_ulong()]; - - // Check whether the bounding box of the child intersects with the search bounds - if (do_intersect(child_node, search_bounds)) { - - // Recursively invoke this function - nearest_k_neighbors_recursive(search_bounds, child_node, results); - } - } - } - } - - template - Node_output_iterator intersected_nodes_recursive(const Query &query, const Node &node, + template + Node_output_iterator intersected_nodes_recursive(const Query& query, Node_index node, Node_output_iterator output) const { // Check if the current node intersects with the query if (CGAL::do_intersect(query, bbox(node))) { - // if this node is a leaf, than it's considered an intersecting node - if (node.is_leaf()) { + // if this node is a leaf, then it's considered an intersecting node + if (is_leaf(node)) { *output++ = node; return output; } // Otherwise, each of the children need to be checked - for (int i = 0; i < Degree::value; ++i) { - intersected_nodes_recursive(query, node[i], output); + for (int i = 0; i < degree; ++i) { + intersected_nodes_recursive(query, child(node, i), output); } } return output; } - /*! - \brief finds the `k` points within a specific radius that are - nearest to the center of `query_sphere`. + template + auto nearest_k_neighbors_recursive( + Sphere& search_bounds, + Node_index node, + std::vector& results, + std::size_t k, + FT epsilon = 0) const -> std::enable_if_t { - This function guarantees that there are no closer points than the ones returned, - but it does not guarantee that it will return at least `k` points. - For a query where the search radius encloses `k` or fewer points, all enclosed points will be returned. - If the search radius is too small, no points may be returned. - This function is useful when the user already knows how sparse the points are, - or if they do not care about points that are too far away. - Setting a small radius may have performance benefits. + // Check whether the node has children + if (is_leaf(node)) { - \tparam OutputIterator must be a model of `OutputIterator` that accepts points - \param query_sphere the region to search within - \param k the number of points to find - \param output the output iterator to add the found points to (in order of increasing distance) - */ - template - OutputIterator nearest_k_neighbors_in_radius - (Sphere& query_sphere, - std::size_t k, OutputIterator output) const { + // Base case: the node has no children - // Create an empty list of points - std::vector points_list; - if (k != (std::numeric_limits::max)()) - points_list.reserve(k); + // Loop through each of the elements contained by the node + // Note: there might be none, and that should be fine! + for (auto& e : data(node)) { - // Invoking the recursive function adds those points to the vector (passed by reference) - nearest_k_neighbors_recursive(query_sphere, m_root, points_list); + // Pair that element with its distance from the search point + Result current_element_with_distance = + { e, m_traits.squared_distance_of_element_object()(e, m_traits.construct_center_d_object()(search_bounds)) }; - // Add all the points found to the output - for (auto &item : points_list) - *output++ = item.point; + // Check if the new element is within the bounds + if (current_element_with_distance.distance < m_traits.compute_squared_radius_d_object()(search_bounds)) { - return output; + // Check if the results list is full + if (results.size() == k) { + // Delete a element if we need to make room + results.pop_back(); + } + + // Add the new element + results.push_back(current_element_with_distance); + + // Sort the list + std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Check if the results list is full + if (results.size() == k) { + + // Set the search radius + search_bounds = m_traits.construct_sphere_d_object()(m_traits.construct_center_d_object()(search_bounds), results.back().distance + epsilon); + } + } + } + } + else { + + struct Node_index_with_distance { + Node_index index; + FT distance; + + Node_index_with_distance(const Node_index& index, FT distance) : + index(index), distance(distance) {} + }; + + // Recursive case: the node has children + + // Create a list to map children to their distances + std::vector children_with_distances; + children_with_distances.reserve(Self::degree); + + // Fill the list with child nodes + for (int i = 0; i < Self::degree; ++i) { + auto child_node = child(node, i); + + FT squared_distance = 0; + Point c = m_traits.construct_center_d_object()(search_bounds); + Point b = barycenter(child_node); + for (const auto r : cartesian_range(c, b)) { + FT d = (get<0>(r) - get<1>(r)); + squared_distance += d * d; + } + + // Add a child to the list, with its distance + children_with_distances.emplace_back( + child_node, + squared_distance + ); + } + + // Sort the children by their distance from the search point + std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Loop over the children + for (auto child_with_distance : children_with_distances) { + + // Check whether the bounding box of the child intersects with the search bounds + if (CGAL::do_intersect(bbox(child_with_distance.index), search_bounds)) { + + // Recursively invoke this function + nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results, k); + } + } + } } public: /// \cond SKIP_IN_MANUAL - void dump_to_polylines (std::ostream& os) const - { - for (const Node& n : traverse()) - if (n.is_leaf()) - { + void dump_to_polylines(std::ostream& os) const { + for (const auto n: traverse()) + if (is_leaf(n)) { Bbox box = bbox(n); - dump_box_to_polylines (box, os); + dump_box_to_polylines(box, os); } } - void dump_box_to_polylines (const Bbox_2& box, std::ostream& os) const - { + void dump_box_to_polylines(const Bbox_2& box, std::ostream& os) const { // dump in 3D for visualisation os << "5 " << box.xmin() << " " << box.ymin() << " 0 " @@ -927,8 +1418,8 @@ public: << box.xmax() << " " << box.ymin() << " 0 " << box.xmin() << " " << box.ymin() << " 0" << std::endl; } - void dump_box_to_polylines (const Bbox_3& box, std::ostream& os) const - { + + void dump_box_to_polylines(const Bbox_3& box, std::ostream& os) const { // Back face os << "5 " << box.xmin() << " " << box.ymin() << " " << box.zmin() << " " @@ -960,17 +1451,21 @@ public: << box.xmax() << " " << box.ymax() << " " << box.zmax() << std::endl; } - friend std::ostream& operator<< (std::ostream& os, const Self& orthtree) - { - // Create a range of nodes - auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal()); - // Iterate over the range - for (auto &n : nodes) { + std::string to_string(Node_index node) { + std::stringstream stream; + internal::print_orthtree_node(stream, node, *this); + return stream.str(); + } + + friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { + // Iterate over all nodes + for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth - for (int i = 0; i < n.depth(); ++i) + for (std::size_t i = 0; i < orthtree.depth(n); ++i) os << ". "; // Print the node - os << n << std::endl; + internal::print_orthtree_node(os, n, orthtree); + os << std::endl; } return os; } @@ -981,6 +1476,4 @@ public: } // namespace CGAL -#include - #endif // CGAL_ORTHTREE_H diff --git a/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h b/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h index 38b4ef3f5aa..80cdc3cd3e5 100644 --- a/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h +++ b/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h @@ -29,8 +29,8 @@ namespace internal template struct Cartesian_ranges { - typedef typename Traits::Point_d Point; - typedef typename Traits::Cartesian_const_iterator_d Cartesian_const_iterator; + using Point = typename Traits::Point_d; + using Cartesian_const_iterator = typename Traits::Cartesian_const_iterator_d; using Range_single = CGAL::Iterator_range; diff --git a/Orthtree/include/CGAL/Orthtree/IO.h b/Orthtree/include/CGAL/Orthtree/IO.h index 3c5748397c9..1bf6bdd7143 100644 --- a/Orthtree/include/CGAL/Orthtree/IO.h +++ b/Orthtree/include/CGAL/Orthtree/IO.h @@ -22,46 +22,39 @@ namespace CGAL namespace internal { -template -std::ostream& print_orthtree_node(std::ostream& os, const Node& node) +template +std::ostream& print_orthtree_node(std::ostream& os, const Node_index& node, const Tree &tree) { - // Show the depth of the node -// for (int i = 0; i < node.depth(); ++i) -// os << ". "; // Wrap information in brackets os << "{ "; // Index identifies which child this is os << "("; - for (std::size_t i = 0; i < node.local_coordinates().size(); ++ i) - os << node.local_coordinates()[i]; + for (std::size_t i = 0; i < tree.local_coordinates(node).size(); ++ i) + os << tree.local_coordinates(node)[i]; os << ") "; // Location os << "( "; - for (const auto& b : node.global_coordinates()) + for (const auto& b : tree.global_coordinates(node)) os << b << " "; os << ") "; // Depth os << "(" - << +node.depth() // The + forces printing as an int instead of a char + << +tree.depth(node) // The + forces printing as an int instead of a char << ") "; os << "(" - << node.size() + << tree.data(node).size() << ") "; -// // If a node has points, indicate how many -// if (!node.is_empty()) -// os << "[" << node.num_points() << " points] "; - // If a node is a leaf, mark it - os << (node.is_leaf() ? "[leaf] " : ""); + os << (tree.is_leaf(node) ? "[leaf] " : ""); // If a node is root, mark it - os << (node.is_root() ? "[root] " : ""); + os << (tree.is_root(node) ? "[root] " : ""); // Wrap information in brackets os << "}"; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h deleted file mode 100644 index 777066a295d..00000000000 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright (c) 2007-2020 INRIA (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) : Jackson Campolattaro, CĂ©dric Portaneri, Tong Zhao - -#ifndef CGAL_ORTHTREE_NODE_H -#define CGAL_ORTHTREE_NODE_H - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace CGAL { - -/// \cond SKIP_IN_MANUAL -namespace Orthtrees -{ - -// Non-documented, or testing purpose only -struct Node_access -{ - template - static Node create_node (Node parent, LC local_coordinates) - { - return Node(parent, local_coordinates); - } - - template - static typename Node::Point_range& points(Node node) { return node.points(); } - - template - static void split(Node node) { return node.split(); } - - template - static void free(Node node) - { - typedef Dimension_tag<(2 << (Node::Dimension::value - 1))> Degree; - std::queue nodes; - nodes.push(node); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()){ - for (std::size_t i = 0; i < Degree::value; ++ i){ - nodes.push (node[i]); - } - } - node.free(); - } - } - -}; - -} // namespace Orthtrees -/// \endcond - -/*! - - \brief represents a single node of the tree. Alternatively referred - to as a cell, orthant, or sub-tree. - - A `Node` is a lightweight object and thus generally passed by - copy. It is also a model of `ConstRange` with value type `Traits::Point_d`. - - \cgalModels{ConstRange} - */ -template -class Orthtree::Node -{ - -public: - - /// \name Types - /// @{ - - typedef Orthtree Enclosing; ///< Orthtree type (enclosing class). - typedef typename Enclosing::Dimension Dimension; ///< Dimension type. - typedef typename Enclosing::Degree Degree; ///< Degree type. - - /*! - \brief Self typedef for convenience. - */ - typedef typename Orthtree::Node Self; - - - /// \cond SKIP_IN_MANUAL - /*! - * \brief Array for containing the child nodes of this node. - */ - typedef std::array Children; - /// \endcond - - /*! - \brief Set of bits representing this node's relationship to its parent. - - Equivalent to an array of Booleans, where index[0] is whether `x` - is greater, index[1] is whether `y` is greater, index[2] is whether - `z` is greater, and so on for higher dimensions if needed. - Used to represent a node's relationship to the center of its parent. - */ - typedef std::bitset Local_coordinates; - - /*! - \brief Coordinates representing this node's relationship - with the rest of the tree. - - Each value `(x, y, z, ...)` of global coordinates is calculated by doubling - the parent's global coordinates and adding the local coordinates. - */ - typedef std::array Global_coordinates; - - - typedef typename PointRange::const_iterator const_iterator; ///< constant iterator type. - - /*! - \brief Adjacency directions. - */ - typedef typename Traits::Adjacency Adjacency; - - /// @} - -private: - - /// \cond SKIP_IN_MANUAL - /*! - \brief point range type. - */ - typedef typename PointRange::iterator iterator; ///< constant iterator type. - typedef boost::iterator_range Point_range; - /// \endcond - - // make Node trivially copiabled - struct Data - { - Point_range points; - Self parent; - std::uint8_t depth; - Global_coordinates global_coordinates; - std::unique_ptr children; - - Data (Self parent) - : parent (parent), depth (0) { } - }; - - Data* m_data; - - - /// \cond SKIP_IN_MANUAL - - // Only the Orthtree class has access to the non-default - // constructor, mutators, etc. - friend Enclosing; - - // Hidden class to access methods for testing purposes - friend Orthtrees::Node_access; - - /*! - * \brief Access to the content held by this node - * \return a reference to the collection of point indices - */ - Point_range &points() { return m_data->points; } - const Point_range &points() const { return m_data->points; } - - /// \name Construction - /// @{ - - /*! - \brief creates a new node, optionally as the child of a parent - - If no parent is provided, the node created is assumed to be the - root of a tree. This means that `parent.is_null()` returns - `true`, and the depth is zero. If a parent is provided, the node - becomes the child of that parent. In that case, an index should - be passed, telling this node its relationship to its parent. - Depth and global coordinates are automatically determined in the - constructor, and should generally be considered immutable after - construction. - - \param parent the node containing this one - \param index this node's relationship to its parent - */ - explicit Node(Self parent, Local_coordinates local_coordinates) - : m_data (new Data(parent)) { - - if (!parent.is_null()) { - - m_data->depth = parent.m_data->depth + 1; - - for (int i = 0; i < Dimension::value; i++) - m_data->global_coordinates[i] = (2 * parent.m_data->global_coordinates[i]) + local_coordinates[i]; - - } - else - for (int i = 0; i < Dimension::value; i++) - m_data->global_coordinates[i] = 0; - } - - void free() { delete m_data; } - - Node deep_copy(Self parent = Node()) const - { - if (is_null()) - return Node(); - - Node out; - out.m_data = new Data(parent); - - out.m_data->points = m_data->points; - out.m_data->depth = m_data->depth; - out.m_data->global_coordinates = m_data->global_coordinates; - std::unique_ptr children; - if (!is_leaf()) - { - out.m_data->children = std::make_unique(); - for (int index = 0; index < Degree::value; index++) - (*out.m_data->children)[index] = (*this)[index].deep_copy(out); - } - return out; - } - - /// @} - - /// \name Mutators - /// @{ - - /*! - \brief splits a node into subnodes. - - Only leaf nodes should be split. - When a node is split it is no longer a leaf node. - A number of `Degree::value` children are constructed automatically, and their values are set. - Contents of this node are _not_ propagated automatically. - It is the responsibility of the caller to redistribute the points contained by a node after splitting - */ - void split() { - - CGAL_precondition (is_leaf()); - - m_data->children = std::make_unique(); - for (int index = 0; index < Degree::value; index++) { - - (*m_data->children)[index] = std::move(Self(*this, {Local_coordinates(index)})); - - } - } - - /*! - * \brief eliminates this node's children, making it a leaf node. - * - * When a node is un-split, its children are automatically deleted. - * After un-splitting a node it will be considered a leaf node. - */ - void unsplit() { - - m_data->children.reset(); - } - - /// @} - - /// \endcond - - -public: - - /// \cond SKIP_IN_MANUAL - // Default creates null node - Node() : m_data(nullptr) { } - - // Comparison operator - bool operator< (const Node& other) const { return m_data < other.m_data; } - /// \endcond - - /// \name Type & Location - /// @{ - - /*! - \brief returns `true` if the node is null, `false` otherwise. - */ - bool is_null() const { return (m_data == nullptr); } - - /*! - \brief returns `true` if the node has no parent, `false` otherwise. - \pre `!is_null()` - */ - bool is_root() const - { - CGAL_precondition(!is_null()); - return m_data->parent.is_null(); - } - - /*! - \brief returns `true` if the node has no children, `false` otherwise. - \pre `!is_null()` - */ - bool is_leaf() const - { - CGAL_precondition(!is_null()); - return (!m_data->children); - } - - /*! - \brief returns this node's depth. - \pre `!is_null()` - */ - std::uint8_t depth() const - { - CGAL_precondition (!is_null()); - return m_data->depth; - } - - /*! - \brief returns this node's local coordinates (in relation to its parent). - \pre `!is_null()` - */ - Local_coordinates local_coordinates() const { - - CGAL_precondition (!is_null()); - // TODO: There must be a better way of doing this! - - Local_coordinates result; - - for (std::size_t i = 0; i < Dimension::value; ++ i) - result[i] = global_coordinates()[i] & 1; - - return result; - } - - /*! - \brief returns this node's global coordinates. - \pre `!is_null()` - */ - Global_coordinates global_coordinates() const - { - CGAL_precondition (!is_null()); - return m_data->global_coordinates; - } - - - /// \name Adjacency - /// @{ - - /*! - \brief returns this node's parent. - \pre `!is_null()` - */ - Self parent() const - { - CGAL_precondition (!is_null()); - return m_data->parent; - } - - /*! - \brief returns the nth child of this node. - - \pre `!is_null()` - \pre `!is_leaf()` - \pre `0 <= index && index < Degree::value` - - The orthtree subdivides the space in 2 on each dimension - available, so a child node can be accessed by selecting a Boolean - value for each dimension. The `index` parameter is thus - interpreted as a bitmap, where each bit matches a dimension - (starting by the least significant bit for coordinate X). - - For example, in the case of an octree (dimension 3): - - - index 0 (000 in binary) is the children on the "minimum corner" (xmin, ymin, zmin) - - index 1 (001 in binary) is on (xmax, ymin, zmin) - - index 2 (010 in binary) is on (xmin, ymax, zmin) - - index 6 (101 in binary) is on (xmax, ymin, zmax) - - Diagram of a quadtree: - - ``` - Children of the current node: - - Y axis - A - | +-------------------+-------------------+ - | | Coord: Ymax Xmin | Coord: Ymax Xmax | - | | Bitmap: 1 0 | Bitmap: 1 1 | - | | | | - | | -> index = 2 | -> index = 3 | - | | | | - | | | | - | | | | - | | | | - | +-------------------+-------------------+ - | | Coord: Ymin Xmin | Coord: Ymin Xmax | - | | Bitmap: 0 0 | Bitmap: 0 1 | - | | | | - | | -> index = 0 | -> index = 1 | - | | | | - | | | | - | | | | - | | | | - | +-------------------+-------------------+ - | - +--------------------------------------------> X axis - 0 - ``` - - The operator can be chained. For example, `n[5][2][3]` returns the - third child of the second child of the fifth child of a node `n`. - */ - Self operator[](std::size_t index) const { - - CGAL_precondition (!is_null()); - CGAL_precondition (!is_leaf()); - CGAL_precondition (index < Degree::value); - - return (*m_data->children)[index]; - } - - /*! - \brief finds the directly adjacent node in a specific direction - - \pre `!is_null()` - \pre `direction.to_ulong < 2 * Dimension::value` - - Adjacent nodes are found according to several properties: - - adjacent nodes may be larger than the seek node, but never smaller - - a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back) - - adjacent nodes are not required to be leaf nodes - - Here's a diagram demonstrating the concept for a Quadtree: - - ``` - +---------------+---------------+ - | | | - | | | - | | | - | A | | - | | | - | | | - | | | - +-------+-------+---+---+-------+ - | | | | | | - | A | (S) +---A---+ | - | | | | | | - +---+---+-------+---+---+-------+ - | | | | | | - +---+---+ A | | | - | | | | | | - +---+---+-------+-------+-------+ - ``` - - - (S) : Seek node - - A : Adjacent node - - Note how the top adjacent node is larger than the seek node. The - right adjacent node is the same size, even though it contains - further subdivisions. - - This implementation returns the adjacent node if it's found. If - there is no adjacent node in that direction, it returns a null - node. - - \param direction which way to find the adjacent node relative to - this one. Each successive bit selects the direction for the - corresponding dimension: for an Octree in 3D, 010 means: negative - direction in X, position direction in Y, negative direction in Z. - - \return the adjacent node if it exists, a null node otherwise. - */ - Self adjacent_node (Local_coordinates direction) const - { - CGAL_precondition(!is_null()); - - // Direction: LEFT RIGHT DOWN UP BACK FRONT - // direction: 000 001 010 011 100 101 - - // Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides) - CGAL_precondition(direction.to_ulong() < Dimension::value * 2); - - // The root node has no adjacent nodes! - if (is_root()) - return Self(); - - // The least significant bit indicates the sign (which side of the node) - bool sign = direction[0]; - - // The first two bits indicate the dimension/axis (x, y, z) - uint8_t dimension = uint8_t((direction >> 1).to_ulong()); - - // Create an offset so that the bit-significance lines up with the dimension (e.g. 1, 2, 4 --> 001, 010, 100) - int8_t offset = (uint8_t) 1 << dimension; - - // Finally, apply the sign to the offset - offset = (sign ? offset : -offset); - - // Check if this child has the opposite sign along the direction's axis - if (local_coordinates()[dimension] != sign) { - - // This means the adjacent node is a direct sibling, the offset can be applied easily! - return parent()[local_coordinates().to_ulong() + offset]; - } - - // Find the parent's neighbor in that direction if it exists - Self adjacent_node_of_parent = parent().adjacent_node(direction); - - // If the parent has no neighbor, then this node doesn't have one - if (adjacent_node_of_parent.is_null()) - return Node(); - - // If the parent's adjacent node has no children, then it's this node's adjacent node - if (adjacent_node_of_parent.is_leaf()) - return adjacent_node_of_parent; - - // Return the nearest node of the parent by subtracting the offset instead of adding - return adjacent_node_of_parent[local_coordinates().to_ulong() - offset]; - - } - - /*! - \brief equivalent to `adjacent_node()`, with an adjacency direction - rather than a bitset. - */ - Self adjacent_node(Adjacency adjacency) const { - return adjacent_node(std::bitset(static_cast(adjacency))); - } - - /// @} - - /// \name Point Range - /// @{ - - /*! - \brief checks whether the node is empty of points or not. - */ - bool empty() const { - return m_data->points.empty(); - } - - /*! - \brief returns the number of points of this node. - */ - std::size_t size() const { - return std::size_t(std::distance(m_data->points.begin(), m_data->points.end())); - } - - /*! - \brief returns the iterator at the start of the collection of - points held by this node. - */ - const_iterator begin() const { return m_data->points.begin(); } - - /*! - \brief returns the iterator at the end of the collection of - points held by this node. - */ - const_iterator end() const { return m_data->points.end(); } - - /// @} - - /// \cond SKIP_IN_MANUAL - /// \name Operators - /// @{ - - /*! - * \brief compares the topology of this node to another node. - * - * \todo - * - * \param rhs node to compare with - * \return whether the nodes have different topology. - */ - bool operator==(const Self &rhs) const { - return m_data == rhs.m_data; - } - - static bool is_topology_equal (const Self& a, const Self& b) - { - CGAL_assertion (!a.is_null() && !b.is_null()); - - // If one node is a leaf, and the other isn't, they're not the same - if (a.is_leaf() != b.is_leaf()) - return false; - - // If both nodes are non-leaf nodes - if (!a.is_leaf()) { - - // Check all the children - for (int i = 0; i < Degree::value; ++i) { - // If any child cell is different, they're not the same - if (!is_topology_equal(a[i], b[i])) - return false; - } - } - - // If both nodes are leaf nodes, they must be in the same location - return (a.global_coordinates() == b.global_coordinates()); - } - - friend std::ostream& operator<< (std::ostream& os, const Self& node) - { - return internal::print_orthtree_node(os, node); - } - /// \endcond -}; - -} - -#endif //CGAL_ORTHTREE_NODE_H diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index e8d93415d5e..c3b0d929b62 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -18,33 +18,40 @@ namespace CGAL { +template +class Orthtree; + namespace Orthtrees { /*! \ingroup PkgOrthtreeSplitPredicates - \brief A class used to choose when a node should be split depending on the number of inliers. + \brief A class used to choose when a node should be split depending on the number of contained elements. This is a bucket size predicate that considers a node should be split if it contains more than a certain number of items. + + \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` + and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ -class Maximum_number_of_inliers { +class Maximum_contained_elements { std::size_t m_bucket_size; public: /*! - \brief creates a predicate based on the number of inliers (bucket size). + \brief creates a predicate based on the number of contained elements. */ - Maximum_number_of_inliers(std::size_t bucket_size) : + Maximum_contained_elements(std::size_t bucket_size) : m_bucket_size(bucket_size) {} /*! - \brief returns `true` if `n` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(const Node &n) const { - return (n.size() > m_bucket_size); + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { + return (tree.data(i).size() > m_bucket_size); } + }; /*! @@ -65,30 +72,33 @@ public: Maximum_depth(std::size_t max_depth) : m_max_depth(max_depth) {} /*! - \brief returns `true` if `n` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(const Node &n) const { - return n.depth() < m_max_depth; + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { + return (tree.depth(i) < m_max_depth); } + }; /*! \ingroup PkgOrthtreeSplitPredicates - \brief A class used to choose when a node should be split depending on the depth and the number of inliers. + \brief A class used to choose when a node should be split depending on the depth and the number of contained elements. This predicate makes a note split if it contains more than a certain number of items and if its depth is lower than a certain limit. The refinement is stopped as soon as one of the conditions is - violated: if a node has more inliers than `bucket_size` but is + violated: if a node has more elements than `bucket_size` but is already at `max_depth`, it is not split. Similarly, a node that is - at a depth smaller than `max_depth` but already has fewer inliers + at a depth smaller than `max_depth` but already has fewer elements than `bucket_size`, it is not split. + \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` + and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ -class Maximum_depth_and_maximum_number_of_inliers { +class Maximum_depth_and_maximum_contained_elements { std::size_t m_max_depth, m_bucket_size; @@ -96,18 +106,19 @@ public: /*! \brief creates a predicate using maximum depth or bucket size. */ - Maximum_depth_and_maximum_number_of_inliers(std::size_t max_depth, std::size_t bucket_size) : + Maximum_depth_and_maximum_contained_elements(std::size_t max_depth, std::size_t bucket_size) : m_max_depth(max_depth), m_bucket_size(bucket_size) {} /*! - \brief returns `true` if `n` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(const Node &n) const { - std::size_t num_points = n.size(); - std::size_t depth = n.depth(); + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { + std::size_t num_points = tree.data(i).size(); + std::size_t depth = tree.depth(i); return (num_points > m_bucket_size && depth < m_max_depth); } + }; } // Orthtrees diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index d8a594e4ef0..10152287538 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -14,7 +14,10 @@ #include +#include + #include +#include #include /// \cond SKIP_IN_MANUAL @@ -22,18 +25,21 @@ namespace CGAL { /*! - * \ingroup PkgOrthtreeClasses + * \ingroup PkgOrthtreeTraversal * - * \brief + * \brief Wraps a traversal definition to produce an iterator which traverses the tree when incremented. * * \todo * - * \tparam Value + * \tparam Tree The orthtree type to iterate over */ -template -class Traversal_iterator : - public boost::iterator_facade, Value, boost::forward_traversal_tag> { - +template +class Index_traversal_iterator : public boost::iterator_facade< + Index_traversal_iterator, + const typename Tree::Node_index, + boost::forward_traversal_tag, + const typename Tree::Node_index +> { public: /// \name Types @@ -44,7 +50,9 @@ public: * * \todo */ - typedef std::function Traversal_function; + using Traversal_function = std::function(const Tree&, std::size_t)>; + + using Node_index = typename Tree::Node_index; /// @} @@ -54,44 +62,51 @@ public: /// @{ /*! - * \brief + * \brief Default constructor, creates an end sentinel * * \todo */ - Traversal_iterator() : m_value(), m_next() {} + Index_traversal_iterator() : m_next() {} /*! * \brief * * \todo * + * \param tree * \param first * \param next */ - Traversal_iterator(Value first, const Traversal_function &next) : m_value(first), m_next(next) {} + Index_traversal_iterator(const Tree& tree, Node_index first, const Traversal_function& next) : + m_tree(&tree), m_index(first), m_next(next) {} /// @} private: + friend class boost::iterator_core_access; - bool equal(Traversal_iterator const &other) const { - return m_value == other.m_value; + bool equal(Index_traversal_iterator const& other) const { + return m_index == other.m_index; } void increment() { - m_value = m_next(m_value); + // invoking increment on the sentinel is undefined behavior + m_index = m_next(*m_tree, *m_index); } - Value &dereference() const { - return const_cast(m_value); + Node_index dereference() const { + return *m_index; } private: - Value m_value; + const Tree* m_tree = nullptr; + std::optional m_index; Traversal_function m_next; + }; + } /// \endcond diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 1ab9b77ccea..acd1db941f6 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -22,159 +22,80 @@ namespace CGAL { -/// \cond SKIP_IN_MANUAL -// Forward declaration -template -class Orthtree; -/// \endcond - namespace Orthtrees { -/// \cond SKIP_IN_MANUAL - -template -Node next_sibling(Node n) { - - // Passing null returns the first node - if (n.is_null()) - return Node(); - - // If this node has no parent, it has no siblings - if (n.parent().is_null()) - return Node(); - - // Find out which child this is - std::size_t index = n.local_coordinates().to_ulong(); - - constexpr static int degree = Node::Degree::value; - // Return null if this is the last child - if (int(index) == degree - 1) - return Node(); - - // Otherwise, return the next child - return n.parent()[index + 1]; -} - -template -Node next_sibling_up(Node n) { - - if (n.is_null()) - return Node(); - - Node up = n.parent(); - - while (!up.is_null()) { - - if (!next_sibling(up).is_null()) - return next_sibling(up); - - up = up.parent(); - } - - return Node(); -} - -template -Node deepest_first_child(Node n) { - - if (n.is_null()) - return Node(); - - // Find the deepest child on the left - Node first = n; - while (!first.is_leaf()) - first = first[0]; - return first; -} - -template -Node first_child_at_depth(Node n, std::size_t depth) { - - if (n.is_null()) - return Node(); - - std::stack todo; - todo.push(n); - - if (n.depth() == depth) - return n; - - while (!todo.empty()) - { - Node node = todo.top(); - todo.pop(); - - if (node.depth() == depth) - return node; - - if (!node.is_leaf()) - for (int i = 0; i < Node::Degree::value; ++ i) - todo.push(node[std::size_t(Node::Degree::value - 1 - i)]); - } - - return Node(); -} - -/// \endcond - /*! \ingroup PkgOrthtreeTraversal \brief A class used for performing a preorder traversal. + \tparam Tree an instance of `Orthtree` + A preorder traversal starts from the root towards the leaves. \cgalModels{OrthtreeTraversal} */ +template struct Preorder_traversal { +private: - template - Node first(Node root) const { - return root; + const Tree& m_orthtree; + +public: + + using Node_index = typename Tree::Node_index; + + Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + Node_index first_index() const { + return m_orthtree.root(); } - template - Node next(Node n) const { + std::optional next_index(Node_index n) const { - if (n.is_leaf()) { + if (m_orthtree.is_leaf(n)) { - Node next = next_sibling(n); + auto next = m_orthtree.next_sibling(n); - if (next.is_null()) - return next_sibling_up(n); + if (!next) + next = m_orthtree.next_sibling_up(n); return next; + } else { + return m_orthtree.child(n, 0); } - else // Return the first child of this node - return n[0]; } + }; /*! \ingroup PkgOrthtreeTraversal \brief A class used for performing a postorder traversal. + \tparam Tree an instance of `Orthtree` + A postorder traversal starts from the leaves towards the root. \cgalModels{OrthtreeTraversal} */ +template struct Postorder_traversal { +private: - template - Node first(Node root) const { + const Tree& m_orthtree; - return deepest_first_child(root); +public: + + using Node_index = typename Tree::Node_index; + + Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + Node_index first_index() const { + return m_orthtree.deepest_first_child(m_orthtree.root()); } - template - Node next(Node n) const { - - Node next = deepest_first_child(next_sibling(n)); - - if (next.is_null()) - next = n.parent(); - - return next; + std::optional next_index(Node_index n) const { + return m_orthtree.index(next(&m_orthtree[n])); } }; @@ -182,27 +103,37 @@ struct Postorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal on leaves only. - All non-leave nodes are ignored. + \tparam Tree an instance of `Orthtree` + + All non-leaf nodes are ignored. \cgalModels{OrthtreeTraversal} */ +template struct Leaves_traversal { +private: - template - Node first(Node root) const { + const Tree& m_orthtree; - return deepest_first_child(root); +public: + + using Node_index = typename Tree::Node_index; + + Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + Node_index first_index() const { + return m_orthtree.deepest_first_child(m_orthtree.root()); } - template - Node next(Node n) const { + std::optional next_index(Node_index n) const { - Node next = deepest_first_child(next_sibling(n)); + if (m_orthtree.next_sibling(n)) + return m_orthtree.deepest_first_child(*m_orthtree.next_sibling(n)); - if (next.is_null()) - next = deepest_first_child(next_sibling_up(n)); + if (m_orthtree.next_sibling_up(n)) + return m_orthtree.deepest_first_child(*m_orthtree.next_sibling_up(n)); - return next; + return {}; } }; @@ -210,52 +141,58 @@ struct Leaves_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal of a specific depth level. - All trees at another depth are ignored. If the selected depth is + \tparam Tree an instance of `Orthtree` + + All tree nodes at another depth are ignored. If the selected depth is + All tree nodes at another depth are ignored. If the selected depth is higher than the maximum depth of the orthtree, no node will be traversed. \cgalModels{OrthtreeTraversal} */ +template struct Level_traversal { - private: - const std::size_t depth; + const Tree& m_orthtree; + const std::size_t m_depth; public: + using Node_index = typename Tree::Node_index; + /*! constructs a `depth`-level traversal. */ - Level_traversal (std::size_t depth) : depth(depth) { } + Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} - template - Node first(Node root) const { - return first_child_at_depth(root, depth); + Node_index first_index() const { + // assumes the tree has at least one child at m_depth + return *m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } - template - Node next(Node n) const { - std::cerr << depth << " "; - Node next = next_sibling(n); + std::optional next_index(Node_index n) const { - if (next.is_null()) - { - Node up = n; - do - { - up = next_sibling_up(up); - if (up.is_null()) - return Node(); - next = first_child_at_depth(up, depth); - } - while (next.is_null()); + auto next = m_orthtree.next_sibling(n); + + if (!next) { + + auto up = n; + do { + + if (!m_orthtree.next_sibling_up(up)) + return {}; + + up = *m_orthtree.next_sibling_up(up); + next = m_orthtree.first_child_at_depth(up, m_depth); + + } while (!next); } return next; } }; -} // Orthtree +} // Orthtrees } // CGAL #endif //CGAL_ORTHTREE_TRAVERSALS_H diff --git a/Orthtree/include/CGAL/Orthtree_traits.h b/Orthtree/include/CGAL/Orthtree_traits.h new file mode 100644 index 00000000000..cc1665622e7 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits.h @@ -0,0 +1,61 @@ +// Copyright (c) 2023 INRIA (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) : Sven Oesau + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + Traits class for defining an orthtree using the class `CGAL::Orthtree` without storing data in the nodes. + + \tparam GeomTraits model of `Kernel`. + \tparam dimension the dimension of the ambient Euclidean space. + + \cgalModels{OrthtreeTraits} + \sa `CGAL::Octree` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_base` +*/ +template +struct Orthtree_traits : public Orthtree_traits_base { +public: + using Base = Orthtree_traits_base; + using Self = Orthtree_traits; + using Tree = Orthtree; + + using Node_index = typename Base::Node_index; + + Orthtree_traits() {} + + auto construct_root_node_bbox_object() const { + return [&]() -> typename Self::Bbox_d { + return {std::apply(Self::construct_point_d_object(), std::array{-1.0, -1.0, -1.0}), + std::apply(Self::construct_point_d_object(), std::array{1.0, 1.0, 1.0})}; + }; + } +}; + +} + + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_2.h b/Orthtree/include/CGAL/Orthtree_traits_2.h deleted file mode 100644 index b849ae450f5..00000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_2.h +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2020 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) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_2_H -#define CGAL_ORTHTREE_TRAITS_2_H - -#include - -#include -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_2` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_3` - \sa `CGAL::Orthtree_traits_d` -*/ -template -struct Orthtree_traits_2 -{ -public: - - /// \name Types - /// @{ - - typedef Dimension_tag<2> Dimension; ///< Dimension type. - typedef Bbox_2 Bbox_d; ///< Bounding box type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_2 Point_d; ///< Point type. - typedef typename GeomTraits::Circle_2 Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_2 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 2-bit integers. - * - * The first bit indicates the axis (0 = x, 1 = y), - * the second bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | - * | y+ - * | * - * 0 *------+------* 1 | - * | | - * | +-----* x+ - * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 00 | 0 | LEFT | - * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | DOWN | - * | `+y` | 11 | 3 | UP | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP - }; - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array[0], array[1]); - } - }; -#endif - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (min[0], min[1], max[0], max[1]); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_2_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_3.h b/Orthtree/include/CGAL/Orthtree_traits_3.h deleted file mode 100644 index c50028fb8f2..00000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_3.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2020 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) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_3_H -#define CGAL_ORTHTREE_TRAITS_3_H - -#include - -#include -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_3` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Octree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_d` -*/ -template -struct Orthtree_traits_3 -{ -public: - - /// \name Types - /// @{ - - typedef Dimension_tag<3> Dimension; ///< Dimension type. - typedef Bbox_3 Bbox_d; ///< Bounding box type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_3 Point_d; ///< Point type. - typedef typename GeomTraits::Sphere_3 Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 3-bit integers, - * though the numbers 6 and 7 are not used because there are only 6 different directions. - * - * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - * the third bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | * 5 - * | / y+ - * |/ * z+ - * 0 *------+------* 1 | * - * /| |/ - * / | +-----* x+ - * 4 * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 000 | 0 | LEFT | - * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | DOWN | - * | `+y` | 011 | 3 | UP | - * | `-z` | 100 | 4 | BACK | - * | `+z` | 101 | 5 | FRONT | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP, - BACK, - FRONT - }; - - /// \cond SKIP_IN_MANUAL - enum Child { - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_BACK, - RIGHT_TOP_BACK, - LEFT_BOTTOM_FRONT, - RIGHT_BOTTOM_FRONT, - LEFT_TOP_FRONT, - RIGHT_TOP_FRONT - }; - /// \endcond - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array[0], array[1], array[2]); - } - }; -#endif - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (min[0], min[1], min[2], max[0], max[1], max[2]); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_3_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h new file mode 100644 index 00000000000..9750b2684ac --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -0,0 +1,167 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_ORTHTREE_TRAITS_BASE_H +#define ORTHTREE_ORTHTREE_TRAITS_BASE_H + +#include + +#include +#include +#include +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_base` is a base class providing common choices for types and functors. + The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. + + \tparam GeomTraits a model of `Kernel`. + \tparam dim dimension of the ambient Euclidean space. + + \sa `CGAL::Orthtree_traits_point` + \sa `CGAL::Orthtree_traits_face_graph` +*/ + +template +struct Orthtree_traits_base { + /// \name Types + /// @{ + using Node_index = std::size_t; + using Kernel = GeomTraits; + static constexpr int dimension = dim; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_d; + using Bbox_d = typename GeomTraits::Iso_box_d; + using Sphere_d = typename GeomTraits::Sphere_d; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_d; + /*! + * Adjacency type. + * + * \note This type is used to identify adjacency directions with + * easily understandable keywords (left, right, up, down, ...) and is thus + * mainly useful in 2D and 3D. In + * higher dimensions, such keywords do not exist and this type is + * simply an integer. Conversions from this integer to bitsets still + * work but do not provide any user-friendly API for adjacency selection. + * + * Two directions along each axis in %Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 3-bit integers in the 3D case or as 2-bit integers in the 2D case. + * In the 3d case the numbers 6 and 7 are not used because there are only 6 different directions. + * + * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + * the third bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram and table showing the 3D case may be a useful reference (2D case is identical with one dimension less): + * + * 3 * + * | * 4 + * | / y+ + * |/ * + * 0 *------+------* 1 | + * /| | + * / | +-----* x+ + * 5 * | / + * * 2 / + * * z+ + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 000 | 0 | LEFT | + * | `+x` | 001 | 1 | RIGHT | + * | `-y` | 010 | 2 | DOWN | + * | `+y` | 011 | 3 | UP | + * | `-z` | 100 | 4 | BACK | + * | `+z` | 101 | 5 | FRONT | + */ + using Adjacency = int; + /// @} + + auto construct_point_d_object() const { + return [](auto... Args) -> Point_d { + std::initializer_list args_list{Args...}; + return Point_d{static_cast(args_list.size()), args_list.begin(), args_list.end()}; + }; + } +}; + +template +struct Orthtree_traits_base { + using Node_index = std::size_t; + using Kernel = GeomTraits; + static constexpr int dimension = 2; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_2; + using Bbox_d = typename GeomTraits::Iso_rectangle_2; + using Sphere_d = typename GeomTraits::Circle_2; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; + + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP + }; + + auto construct_point_d_object() const { + return [](const FT& x, const FT& y) -> Point_d { + return {x, y}; + }; + } +}; + +template +struct Orthtree_traits_base { + using Node_index = std::size_t; + using Kernel = GeomTraits; + static constexpr int dimension = 3; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_3; + using Bbox_d = typename GeomTraits::Iso_cuboid_3; + using Sphere_d = typename GeomTraits::Sphere_3; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; + + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + + enum Child { + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, + LEFT_TOP_BACK, + RIGHT_TOP_BACK, + LEFT_BOTTOM_FRONT, + RIGHT_BOTTOM_FRONT, + LEFT_TOP_FRONT, + RIGHT_TOP_FRONT + }; + + auto construct_point_d_object() const { + return [](const FT& x, const FT& y, const FT& z) -> Point_d { + return {x, y, z}; + }; + } +}; + +} + +#endif //ORTHTREE_ORTHTREE_TRAITS_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_d.h b/Orthtree/include/CGAL/Orthtree_traits_d.h deleted file mode 100644 index 4f48caba07f..00000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_d.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2020 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) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_D_H -#define CGAL_ORTHTREE_TRAITS_D_H - -#include - -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_d` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - \tparam DimensionTag specialization of `CGAL::Dimension_tag`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Orthtree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_3` -*/ - -template -struct Orthtree_traits_d -{ -public: - - /// \name Types - /// @{ - - typedef DimensionTag Dimension; ///< Dimension type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_d Point_d; ///< Point type. - typedef typename GeomTraits::Sphere_d Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_d Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - -#ifdef DOXYGEN_RUNNING - typedef unspecified_type Bbox_d; ///< Bounding box type. -#else - class Bbox_d - { - Point_d m_min, m_max; - public: - - Bbox_d (const Point_d& pmin, const Point_d& pmax) - : m_min (pmin), m_max (pmax) - { } - - const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_min; } - const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_max; } - }; -#endif - - /*! - Adjacency type. - - \note This type is used to identify adjacency directions with - easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In - higher dimensions, such keywords do not exist and this type is - simply an integer. Conversions from this integer to bitsets still - works but do not provide any easier API for adjacency selection. - */ - typedef int Adjacency; - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array.begin(), array.end()); - } - }; -#endif - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (Point_d (min.begin(), min.end()), - Point_d (max.begin(), max.end())); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_D_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h new file mode 100644 index 00000000000..ffa353cd613 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -0,0 +1,168 @@ +// Copyright (c) 2023 INRIA (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 + + +#ifndef CGAL_ORTHREE_TRAITS_FACE_GRAPH_H +#define CGAL_ORTHREE_TRAITS_FACE_GRAPH_H + +#include + +#include + +#include +#include +#include + +namespace CGAL { + +/*! +\ingroup PkgOrthtreeTraits + +Traits class for the `Orthtree` class to be used to construct a 3D octree around +a triangulated surface mesh. Each node of the octree will store all the faces of the +mesh intersected by its bounding box. The subdivision of the octree is controlled +by the nested class `Orthtree_traits_face_graph::Split_predicate_node_min_extent` +to which the minimal extent of a node should be provided. + +\tparam TriangleMesh a model of `FaceListGraph` with all faces being triangles +\tparam VertexPointMap a property map associating points to the vertices of `TriangleMesh` + +\cgalModels{OrthtreeTraitsWithData} +*/ +template +struct Orthtree_traits_face_graph : public Orthtree_traits_base< + typename Kernel_traits::value_type>::type, 3 > { + + Orthtree_traits_face_graph(const TriangleMesh& pm, VertexPointMap vpm) + : m_pm(pm), m_vpm(vpm) {} + + /// \name Types + /// @{ + + using Base = Orthtree_traits_base< + typename Kernel_traits::value_type>::type, 3 >; + using Self = Orthtree_traits_face_graph; + using Tree = Orthtree; + + using Point_d = typename Self::Point_d; + using Bbox_d = typename Self::Bbox_d; + using FT = typename Self::FT; + using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; + + using Node_index = typename Base::Node_index; + using Node_data = std::vector::face_descriptor>; + + using Geom_traits = typename Kernel_traits::type; + + using Construct_root_node_bbox = std::function; + using Construct_root_node_contents = std::function; + using Distribute_node_contents = std::function; + + /// @} + + /// \name Operations + /// @{ + + auto construct_root_node_bbox_object() const { + return [&]() -> Bbox_d { + + std::array min = {0.0, 0}, max = {0.0, 0}; + if (faces(m_pm).begin() != faces(m_pm).end()) { + bool first = true; + for (auto v: vertices(m_pm)) { + const Point_d& p_v = get(m_vpm, v); + for (int i = 0; i < 3; ++i) { + if (first || p_v[i] < min[i]) min[i] = p_v[i]; + if (first || p_v[i] > max[i]) max[i] = p_v[i]; + } + first=false; + } + } + + return {std::apply(Self::construct_point_d_object(), min), + std::apply(Self::construct_point_d_object(), max)}; + }; + } + + auto construct_root_node_contents_object() const { + return [&]() -> Node_data { + return {faces(m_pm).begin(), faces(m_pm).end()}; + }; + } + + auto distribute_node_contents_object() const { + return [&](Node_index n, Tree& tree, const Point_d& /* center */) -> void { + Node_data& ndata = tree.data(n); + for (int i = 0; i < 8; ++i) { + Node_index child = tree.child(n, i); + Node_data& child_data = tree.data(child); + Bbox_d bbox = tree.bbox(child); + for (auto f : ndata) { + typename boost::graph_traits::halfedge_descriptor + h = halfedge(f, m_pm); + typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), + get(m_vpm, target(h, m_pm)), + get(m_vpm, target(next(h, m_pm), m_pm))); + if (do_intersect(t, bbox)) + child_data.push_back(f); + } + } + }; + } + + /// @} + + /// Recommended split predicate to pass to `Orthtree::refine()` function so + /// that the octree is refined until a node is either empty or has an extent + /// that would be smaller after split than the corresponding value provided to the constructor. + class Split_predicate_node_min_extent { + + std::array m_min_extent; + + public: + + /// constructor with `me` being the minimal value a node extent could be + /// (same value for all dimension). + Split_predicate_node_min_extent(const FT& me) + : m_min_extent({me, me, me}) {} + + /// constructor with `me` being the minimal value a node extent could be + /// (one value per dimension). + Split_predicate_node_min_extent(const std::array& me) + : m_min_extent(me) {} + + /*! + \brief returns `true` if `ni` should be split, `false` otherwise. + */ + template + bool operator()(NodeIndex ni, const Tree& tree) const { + if (tree.data(ni).empty()) return false; + + Bbox_d bb = tree.bbox(ni); + + for (int i = 0; i < 3; ++i) + if (((bb.max)()[i] - (bb.min)()[i]) < 2 * m_min_extent[i]) + return false; + return true; + } + }; + + +private: + + const TriangleMesh& m_pm; + VertexPointMap m_vpm; +}; + +} // end of CGAL namespace + + +#endif // CGAL_ORTHREE_TRAITS_FACE_GRAPH_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h new file mode 100644 index 00000000000..84e95bde81c --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -0,0 +1,201 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL { + +template +void reassign_points( + Tree& tree, PointMap& point_map, + typename Tree::Node_index n, const typename Tree::Point& center, typename Tree::Node_data points, + std::bitset coord = {}, std::size_t dimension = 0 +) { + + // Root case: reached the last dimension + if (dimension == Tree::dimension) { + tree.data(tree.child(n, coord.to_ulong())) = points; + return; + } + + // Split the point collection around the center point on this dimension + auto split_point = std::partition( + points.begin(), points.end(), + [&](const auto& p) -> bool { + return (get(point_map, p)[int(dimension)] < center[int(dimension)]); + } + ); + + // Further subdivide the first side of the split + std::bitset coord_left = coord; + coord_left[dimension] = false; + reassign_points(tree, point_map, n, center, {points.begin(), split_point}, coord_left, dimension + 1); + + // Further subdivide the second side of the split + std::bitset coord_right = coord; + coord_right[dimension] = true; + reassign_points(tree, point_map, n, center, {split_point, points.end()}, coord_right, dimension + 1); +} + +/*! + \ingroup PkgOrthtreeTraits + + Traits class for defining an orthtree of points using the class `CGAL::Orthtree`. + + \tparam GeomTraits model of `Kernel`. + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` and whose iterator type is model of `RandomAccessIterator` + \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is a point type from `GeomTraits` matching the current dimension + \tparam dimension the dimension of the ambient Euclidean space. + + \warning The input point set is not copied. It is used directly + and is rearranged by the `Orthtree`. Altering the point range + after creating the orthtree will leave it in an invalid state. + + \cgalModels{CollectionPartitioningOrthtreeTraits} + \sa `CGAL::Octree` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_base` +*/ +template < + typename GeomTraits, + typename PointRange, + typename PointMap = Identity_property_map::value_type>, + bool hypercubic_nodes = false, + int dimension = Ambient_dimension< + typename std::iterator_traits::value_type, + GeomTraits + >::value +> +struct Orthtree_traits_point : public Orthtree_traits_base { +public: + /// \name Types + /// @{ + using Node_data = boost::iterator_range; + /// @} + + using Base = Orthtree_traits_base; + using Self = Orthtree_traits_point; + using Tree = Orthtree; + + using Node_index = typename Base::Node_index; + using Node_data_element = typename std::iterator_traits::value_type; + + Orthtree_traits_point( + PointRange& points, + PointMap point_map = PointMap() + ) : m_points(points), m_point_map(point_map) {} + + auto construct_root_node_bbox_object() const { + return [&]() -> typename Self::Bbox_d { + + std::array bbox_min, bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const typename Self::Point_d& point = get(m_point_map, *(m_points.begin())); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } + } + // Expand bbox to contain all points + for (const auto& p: m_points) { + const typename Self::Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } + } + +#if !defined(_MSC_VER) || _MSC_VER > 1920 + if constexpr (hypercubic_nodes) { +#else + if (hypercubic_nodes) { +#endif + std::array center; + typename Self::FT max_side = 0; + for (int i = 0; i < Self::dimension; i++) { + typename Self::FT side = bbox_max[i] - bbox_min[i]; + max_side = (std::max)(max_side, side); + center[i] = (bbox_min[i] + bbox_max[i]) * 0.5f; + } + max_side *= 0.5f; + for (int i = 0; i < Self::dimension; i++) { + bbox_min[i] = center[i] - max_side; + bbox_max[i] = center[i] + max_side; + } + } + + return {std::apply(Self::construct_point_d_object(), bbox_min), + std::apply(Self::construct_point_d_object(), bbox_max)}; + }; + } + + auto construct_root_node_contents_object() const { + return [&]() -> typename Self::Node_data { + return {m_points.begin(), m_points.end()}; + }; + } + + auto distribute_node_contents_object() const { + return [&](Node_index n, Tree& tree, const typename Self::Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); + }; + } + + auto construct_sphere_d_object() const { + return [](const typename Self::Point_d& center, const typename Self::FT& squared_radius) -> typename Self::Sphere_d { + return typename Self::Sphere_d(center, squared_radius); + }; + } + + auto construct_center_d_object() const { + return [](const typename Self::Sphere_d& sphere) -> typename Self::Point_d { + return sphere.center(); + }; + } + + auto compute_squared_radius_d_object() const { + return [](const typename Self::Sphere_d& sphere) -> typename Self::FT { + return sphere.squared_radius(); + }; + } + + auto squared_distance_of_element_object() const { + return [&](const Node_data_element& index, const typename Self::Point_d& point) -> typename Self::FT { + return CGAL::squared_distance(get(m_point_map, index), point); + }; + } + + PointRange& m_points; + PointMap m_point_map; +}; + +} + + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 0434df2d13a..5b6086bcf73 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -15,34 +15,28 @@ #include #include -#include +#include namespace CGAL { /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef - \brief Alias that specializes the `Orthtree` class to a 2D quadtree. - - These two types are exactly equivalent: - - `Quadtree` - - `Orthtree, PointRange, PointMap>`. - - \warning This is a not a real class but an alias, please refer to - the documentation of `Orthtree`. + \brief Alias that specializes the `Orthtree` class to a 2D quadtree storing 2D points. \tparam GeomTraits must be a model of `Kernel` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap` + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` + \tparam square_nodes Boolean to enforce square nodes */ template ::value_type> > -#ifdef DOXYGEN_RUNNING -class Quadtree; -#else -using Quadtree = Orthtree, PointRange, PointMap>; -#endif + ::value_type>, + bool squared_nodes = false +> + +using Quadtree = Orthtree>; + } // namespace CGAL diff --git a/Orthtree/package_info/Orthtree/dependencies b/Orthtree/package_info/Orthtree/dependencies index bcb222ae196..d8fbb010a7a 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,6 +1,10 @@ Algebraic_foundations +Cartesian_kernel +Circulator Distance_2 Distance_3 +Filtered_kernel +Hash_map Installation Intersections_2 Intersections_3 @@ -9,7 +13,11 @@ Kernel_23 Modular_arithmetic Number_types Orthtree +Point_set_2 Profiling_tools Property_map STL_Extension +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 diff --git a/Orthtree/test/Orthtree/CMakeLists.txt b/Orthtree/test/Orthtree/CMakeLists.txt index 6776af02157..c483d861c73 100644 --- a/Orthtree/test/Orthtree/CMakeLists.txt +++ b/Orthtree/test/Orthtree/CMakeLists.txt @@ -16,6 +16,7 @@ create_single_source_cgal_program("test_octree_traverse.cpp") create_single_source_cgal_program("test_octree_intersecting.cpp") create_single_source_cgal_program("test_octree_copy_move_constructors.cpp") create_single_source_cgal_program("test_octree_kernels.cpp") +create_single_source_cgal_program("test_octree_custom_properties.cpp") create_single_source_cgal_program("test_node_index.cpp") create_single_source_cgal_program("test_node_adjacent.cpp") diff --git a/Orthtree/test/Orthtree/test_node.cpp b/Orthtree/test/Orthtree/test_node.cpp deleted file mode 100644 index 13e3a3f0728..00000000000 --- a/Orthtree/test/Orthtree/test_node.cpp +++ /dev/null @@ -1,55 +0,0 @@ - -#include -#include -#include -#include - -typedef CGAL::Orthtree::Node::iterator> Node; - -int main(void) { - - // Build a new node - Node n = Node(); - - // Check that its values are correct - assert(n.is_root()); - assert(n.is_leaf()); - assert(!n.parent()); - assert(n.depth() == 0); - assert(n.location()[0] == 0 && n.location()[1] == 0 && n.location()[2] == 0); - - // Split the node - n.split(); - - // Check that it's children's values are also correct - for (std::size_t i = 0; i < 8; ++i) { - - assert(!n[i].is_root()); - assert(n[i].is_leaf()); - assert(*n[i].parent() == n); - assert(n[i].depth() == 1); - } - - // Check that the parent has updated - assert(n.is_root()); - assert(!n.is_leaf()); - - // Split one of the children - n[1].split(); - - // Check each of that child's children - for (std::size_t i = 0; i < 8; ++i) { - - assert(!n[1][i].is_root()); - assert(n[1][i].is_leaf()); - assert(*n[1][i].parent() == n[1]); - assert(*n[1][i].parent()->parent() == n); - assert(n[1][i].depth() == 2); - } - - // Check that the child's values have updated - assert(!n[1].is_root()); - assert(!n[1].is_leaf()); - - return 0; -} diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 084d72dc517..db9581b7225 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -3,16 +3,14 @@ #include #include -#include #include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; -typedef Octree::Node Node; -typedef Octree::Traits Traits; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Traits = Octree::Traits; int main(void) { @@ -42,33 +40,43 @@ int main(void) { std::cout << octree << std::endl; // Root node should have no siblings - assert(octree.root().adjacent_node(0).is_null()); - assert(octree.root().adjacent_node(1).is_null()); - assert(octree.root().adjacent_node(2).is_null()); - assert(octree.root().adjacent_node(3).is_null()); - assert(octree.root().adjacent_node(4).is_null()); - assert(octree.root().adjacent_node(5).is_null()); + assert(!octree.adjacent_node(octree.root(), 0)); + assert(!octree.adjacent_node(octree.root(), 1)); + assert(!octree.adjacent_node(octree.root(), 2)); + assert(!octree.adjacent_node(octree.root(), 3)); + assert(!octree.adjacent_node(octree.root(), 4)); + assert(!octree.adjacent_node(octree.root(), 5)); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.root()[Traits::LEFT_TOP_BACK]; + auto left_top_back = octree.node(Traits::LEFT_TOP_BACK); - assert(octree.root()[Traits::RIGHT_TOP_BACK] == left_top_back.adjacent_node(Traits::RIGHT)); - assert(octree.root()[Traits::LEFT_BOTTOM_BACK] == left_top_back.adjacent_node(Traits::DOWN)); - assert(octree.root()[Traits::LEFT_TOP_FRONT] == left_top_back.adjacent_node(Traits::FRONT)); - assert(left_top_back.adjacent_node(Traits::LEFT).is_null()); - assert(left_top_back.adjacent_node(Traits::UP).is_null()); - assert(left_top_back.adjacent_node(Traits::BACK).is_null()); + assert(octree.node(Traits::RIGHT_TOP_BACK) == + *octree.adjacent_node(left_top_back, Traits::RIGHT)); + assert(octree.node(Traits::LEFT_BOTTOM_BACK) == + *octree.adjacent_node(left_top_back, Traits::DOWN)); + assert(octree.node(Traits::LEFT_TOP_FRONT) == + *octree.adjacent_node(left_top_back, Traits::FRONT)); + assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); + assert(!octree.adjacent_node(left_top_back, Traits::UP)); + assert(!octree.adjacent_node(left_top_back, Traits::BACK)); - std::cout << octree.root()[Traits::LEFT_BOTTOM_BACK] << std::endl; + auto right_top_back_of_left_bottom_back = octree.node(Traits::LEFT_BOTTOM_BACK, Traits::RIGHT_TOP_BACK); - auto right_top_back_of_left_bottom_back = octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::RIGHT_TOP_BACK]; - assert(octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::LEFT_TOP_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::LEFT)); - assert(octree.root()[Traits::RIGHT_BOTTOM_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT)); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::UP).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::DOWN).is_null()); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::BACK).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::FRONT).is_null()); + assert( + octree.node(Traits::LEFT_BOTTOM_BACK, Traits::LEFT_TOP_BACK) == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT) + ); + assert( + octree.node(Traits::RIGHT_BOTTOM_BACK) == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) + ); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::UP).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::DOWN).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::FRONT).has_value()); + + // A node at the back of the tree should have no neighbor to its back + assert(!octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::BACK)); return 0; } diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 9ee08bd1083..d8216f6e996 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -3,14 +3,13 @@ #include #include -#include #include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; int main(void) { @@ -37,12 +36,11 @@ int main(void) { Octree octree(points, points.point_map()); octree.refine(10, 1); - std::cout << "root: " << octree.root().local_coordinates() << std::endl; - std::cout << "first child: " << octree.root()[0].local_coordinates() << std::endl; - std::cout << "fifth child: " << octree.root()[4].local_coordinates() << std::endl; - std::cout << "fifth child of first child: " << octree.root()[0][4].local_coordinates() << std::endl; - - // TODO + std::cout << "root: " << octree.local_coordinates(octree.root()) << std::endl; + std::cout << "first child: " << octree.local_coordinates(octree.child(octree.root(), 0)) << std::endl; + std::cout << "fifth child: " << octree.local_coordinates(octree.child(octree.root(), 4)) << std::endl; + std::cout << "fifth child of first child: " + << octree.local_coordinates(octree.child(octree.child(octree.root(), 0), 4)) << std::endl; return 0; } diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 375894cc54c..f36ba6aeb94 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -1,17 +1,14 @@ - -#include #include -#include #include - +#include +#include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_1_node() { @@ -23,8 +20,10 @@ void test_1_node() { Octree octree(points, points.point_map()); octree.refine(10, 1); + Octree::Bbox expected_bbox{-1, -1, -1, -1, -1, -1}; + // Compare the top (only) node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1, -1, -1, -1, -1, -1)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1, -1, -1, -1, -1, -1)); } void test_9_nodes() { @@ -35,21 +34,21 @@ void test_9_nodes() { points.insert({1, 1, 1}); // Create the octree - Octree octree(points, points.point_map(), 1.1); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Compare the top node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1, -1, -1, 1, 1, 1)); // Compare the child nodes - assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.node(0)) == Octree::Bbox(-1, -1, -1, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == Octree::Bbox(0, -1, -1, 1, 0, 0)); + assert(octree.bbox(octree.node(2)) == Octree::Bbox(-1, 0, -1, 0, 1, 0)); + assert(octree.bbox(octree.node(3)) == Octree::Bbox(0, 0, -1, 1, 1, 0)); + assert(octree.bbox(octree.node(4)) == Octree::Bbox(-1, -1, 0, 0, 0, 1)); + assert(octree.bbox(octree.node(5)) == Octree::Bbox(0, -1, 0, 1, 0, 1)); + assert(octree.bbox(octree.node(6)) == Octree::Bbox(-1, 0, 0, 0, 1, 1)); + assert(octree.bbox(octree.node(7)) == Octree::Bbox(0, 0, 0, 1, 1, 1)); } void test_25_nodes() { @@ -62,41 +61,70 @@ void test_25_nodes() { points.insert({1, 0.5, 1}); // Create the octree - Octree octree(points, points.point_map(), 1.5); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Compare the top node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1, -1, -1, 1, 1, 1)); // Compare the child nodes - assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.node(0)) == Octree::Bbox(-1, -1, -1, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == Octree::Bbox(0, -1, -1, 1, 0, 0)); + assert(octree.bbox(octree.node(2)) == Octree::Bbox(-1, 0, -1, 0, 1, 0)); + assert(octree.bbox(octree.node(3)) == Octree::Bbox(0, 0, -1, 1, 1, 0)); + assert(octree.bbox(octree.node(4)) == Octree::Bbox(-1, -1, 0, 0, 0, 1)); + assert(octree.bbox(octree.node(5)) == Octree::Bbox(0, -1, 0, 1, 0, 1)); + assert(octree.bbox(octree.node(6)) == Octree::Bbox(-1, 0, 0, 0, 1, 1)); + assert(octree.bbox(octree.node(7)) == Octree::Bbox(0, 0, 0, 1, 1, 1)); // Compare children of the first child - assert(octree.bbox(octree.root()[0][0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); - assert(octree.bbox(octree.root()[0][1]) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); - assert(octree.bbox(octree.root()[0][2]) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); - assert(octree.bbox(octree.root()[0][3]) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); - assert(octree.bbox(octree.root()[0][4]) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); - assert(octree.bbox(octree.root()[0][5]) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); - assert(octree.bbox(octree.root()[0][6]) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); - assert(octree.bbox(octree.root()[0][7]) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); + assert(octree.bbox(octree.node(0, 0)) == + Octree::Bbox(-1, -1, -1, -0.5, -0.5, -0.5)); + assert(octree.bbox(octree.node(0, 1)) == + Octree::Bbox(-0.5, -1, -1, 0, -0.5, -0.5)); + assert(octree.bbox(octree.node(0, 2)) == + Octree::Bbox(-1, -0.5, -1, -0.5, 0, -0.5)); + assert(octree.bbox(octree.node(0, 3)) == + Octree::Bbox(-0.5, -0.5, -1, 0, 0, -0.5)); + assert(octree.bbox(octree.node(0, 4)) == + Octree::Bbox(-1, -1, -0.5, -0.5, -0.5, 0)); + assert(octree.bbox(octree.node(0, 5)) == + Octree::Bbox(-0.5, -1, -0.5, 0, -0.5, 0)); + assert(octree.bbox(octree.node(0, 6)) == + Octree::Bbox(-1, -0.5, -0.5, -0.5, 0, 0)); + assert(octree.bbox(octree.node(0, 7)) == + Octree::Bbox(-0.5, -0.5, -0.5, 0, 0, 0)); // Compare children of the last child - assert(octree.bbox(octree.root()[7][0]) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); - assert(octree.bbox(octree.root()[7][1]) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); - assert(octree.bbox(octree.root()[7][2]) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); - assert(octree.bbox(octree.root()[7][3]) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); - assert(octree.bbox(octree.root()[7][4]) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); - assert(octree.bbox(octree.root()[7][5]) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); - assert(octree.bbox(octree.root()[7][6]) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); - assert(octree.bbox(octree.root()[7][7]) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.node(7, 0)) == + Octree::Bbox(0, 0, 0, 0.5, 0.5, 0.5)); + assert(octree.bbox(octree.node(7, 1)) == + Octree::Bbox(0.5, 0, 0, 1, 0.5, 0.5)); + assert(octree.bbox(octree.node(7, 2)) == + Octree::Bbox(0, 0.5, 0, 0.5, 1, 0.5)); + assert(octree.bbox(octree.node(7, 3)) == + Octree::Bbox(0.5, 0.5, 0, 1, 1, 0.5)); + assert(octree.bbox(octree.node(7, 4)) == + Octree::Bbox(0, 0, 0.5, 0.5, 0.5, 1)); + assert(octree.bbox(octree.node(7, 5)) == + Octree::Bbox(0.5, 0, 0.5, 1, 0.5, 1)); + assert(octree.bbox(octree.node(7, 6)) == + Octree::Bbox(0, 0.5, 0.5, 0.5, 1, 1)); + assert(octree.bbox(octree.node(7, 7)) == + Octree::Bbox(0.5, 0.5, 0.5, 1, 1, 1)); + + // All child nodes should share a vertex + auto center_of_last_child = octree.bbox(octree.node(7, 7)).vertex(0); + assert(octree.bbox(octree.node(7, 0)).vertex(7) == center_of_last_child); + assert(octree.bbox(octree.node(7, 1)).vertex(4) == center_of_last_child); + assert(octree.bbox(octree.node(7, 2)).vertex(6) == center_of_last_child); + assert(octree.bbox(octree.node(7, 3)).vertex(5) == center_of_last_child); + assert(octree.bbox(octree.node(7, 4)).vertex(2) == center_of_last_child); + assert(octree.bbox(octree.node(7, 5)).vertex(3) == center_of_last_child); + assert(octree.bbox(octree.node(7, 6)).vertex(1) == center_of_last_child); + + // Nodes of different sizes should also share vertices + assert(octree.bbox(octree.node(7, 0)).vertex(0) == octree.bbox(octree.node(0, 7)).vertex(7)); } int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index ae6272fe149..e802572ccff 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -1,21 +1,54 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include -#include +#include +#include + #include - -#include #include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; +#include +#include -int main(void) +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Octree_without_data = CGAL::Orthtree>; + +template +int test(Tree &tree) +{ + assert (tree.is_leaf(tree.root())); // tree is not refined yet + + Tree copy1 (tree); + assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either + assert (tree == copy1); // tree should be equal to copy1 + + tree.refine(CGAL::Orthtrees::Maximum_depth(5)); + assert (!tree.is_leaf(tree.root())); // tree is now refined + assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined + assert (tree != copy1); // tree should be different from copy1 + + Tree copy2 (tree); + assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined + assert (tree == copy2); // tree should be equal to copy2 + + Tree move (std::move(tree)); + assert (!move.is_leaf(move.root())); // move should be refined + + assert (tree.is_leaf(tree.root())); // tree should be back to init state (unrefined) + assert (copy1.is_leaf(copy1.root())); // copy1 still unaffected and still unrefined + assert (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined + assert (move == copy2); // move should be equal to copy2 + + return EXIT_SUCCESS; +} + +int main() { std::size_t nb_pts = 100; Point_set points; @@ -24,28 +57,9 @@ int main(void) for (std::size_t i = 0; i < nb_pts; ++i) points.insert(*(generator++)); - Octree base (points, points.point_map()); - assert (base.root().is_leaf()); // base is not refined yet + Octree base(points, points.point_map()); + test(base); - Octree copy1 (base); - assert (copy1.root().is_leaf()); // copy1 is thus not refined either - assert (base == copy1); // base should be equal to copy1 - - base.refine(); - assert (!base.root().is_leaf()); // base is now refined - assert (copy1.root().is_leaf()); // copy1 should be unaffected and still unrefined - assert (base != copy1); // base should be different from copy1 - - Octree copy2 (base); - assert (!copy2.root().is_leaf()); // copy2 should be refined - assert (base == copy2); // base should be equal to copy2 - - Octree move (std::move(base)); - assert (!move.root().is_leaf()); // move should be refined - assert (base.root().is_leaf()); // base should be back to init state (unrefined) - assert (copy1.root().is_leaf()); // copy1 still unaffected and still unrefined - assert (!copy2.root().is_leaf()); // copy2 unaffected by move and still refined - assert (move == copy2); // move should be equal to copy2 - - return EXIT_SUCCESS; + Octree_without_data base2({}); + test(base2); } diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp new file mode 100644 index 00000000000..2c4ab94918b --- /dev/null +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -0,0 +1,72 @@ +#define CGAL_TRACE_STREAM std::cerr + +#include + +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; + +int main(void) { + + std::size_t nb_pts = 100; + Point_set points; + CGAL::Random_points_in_cube_3 generator; + points.reserve(nb_pts); + for (std::size_t i = 0; i < nb_pts; ++i) + points.insert(*(generator++)); + + Octree tree(points, points.point_map()); + + // Testing built in node properties + typename Octree::Property_map data_prop = *tree.property("contents"); + CGAL_USE(data_prop); + + // list of properties + assert(tree.properties().size() == 5); + auto prop1 = tree.add_property("test", int(5)); + assert(prop1.second); + // One property added + assert(tree.properties().size() == 6); + // Default value should be respected + assert(prop1.first[tree.root()] == 5); + // Changes to individual nodes should be respected + prop1.first[tree.root()] = 0; + assert(prop1.first[tree.root()] == 0); + + auto prop2 = tree.add_property("test", int(0)); + assert(!prop2.second); + assert(tree.properties().size() == 6); + + auto prop3 = tree.add_property("test2", std::string()); + assert(prop3.second); + assert(tree.properties().size() == 7); + + auto prop4 = tree.property("test"); + assert(prop4.has_value()); + + auto prop5 = tree.property("test"); + assert(!prop5.has_value()); + + // Expanding the tree; new nodes should be assigned the default value + tree.refine(10, 1); + for (auto n : tree.traverse>()) { + // Everything but the root will have the default value + if (!tree.is_root(n)) assert(prop1.first[n] == 5); + } + // The root should have preserved its custom value + assert(prop1.first[tree.root()] == 0); + + tree.remove_property(prop1.first); + assert(tree.properties().size() == 6); + + return EXIT_SUCCESS; +} diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index 1eb88263774..ce6d3c95dcb 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -1,16 +1,15 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include -#include #include +#include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_identical_trees() { diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index a0a574bd9ef..19acd787d9a 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -1,36 +1,36 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include #include -#include + #include - -#include #include +#include +#include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; -std::size_t count_jumps(Octree &octree) { +std::size_t count_jumps(Octree& octree) { std::size_t jumps = 0; - for (auto &node : octree.traverse(Leaves_traversal())) { + for (auto node: octree.traverse()) { for (int direction = 0; direction < 6; ++direction) { - auto adjacent_node = node.adjacent_node(direction); + auto adjacent_node = octree.adjacent_node(node, direction); - if (adjacent_node.is_null()) + if (!adjacent_node) continue; - if ((node.depth() - adjacent_node.depth()) > 1) + if ((octree.depth(node) - octree.depth(*adjacent_node)) > 1) jumps++; } } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 59ce353fd28..7576ad168ec 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -1,17 +1,17 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include #include -#include -#include #include +#include +#include +#include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::vector Point_vector; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; +using Octree = CGAL::Octree; int main(void) { @@ -34,7 +34,7 @@ int main(void) { points.emplace_back(-0.9, -1, -1); // Create an octree from the vector - Octree octree(points); + Octree octree(Octree::Traits{points}); // Build the octree octree.refine(10, 2); @@ -46,7 +46,7 @@ int main(void) { auto query = Point{1, 1, 1}; // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // A point should only intersect one node @@ -63,15 +63,15 @@ int main(void) { auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(4 == nodes.size()); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[3]); + assert(octree.node(Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); + assert(octree.node(Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); + assert(octree.node(Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); + assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); } // Intersection with a ray @@ -81,19 +81,22 @@ int main(void) { auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0}); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(8 == nodes.size()); - assert(octree[Octree::Traits::LEFT_BOTTOM_BACK] == nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_BACK] == nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[3]); - assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == nodes[4]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[5]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[6]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[7]); + assert(octree.node(Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); + assert( + octree.node(Octree::Traits::RIGHT_BOTTOM_BACK, Octree::Traits::LEFT_TOP_FRONT) + == nodes[1] + ); + assert(octree.node(Octree::Traits::LEFT_TOP_BACK) == nodes[2]); + assert(octree.node(Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); + assert(octree.node(Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); + assert(octree.node(Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); + assert(octree.node(Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); + assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_kernels.cpp b/Orthtree/test/Orthtree/test_octree_kernels.cpp index 58d49eb4fcc..5e477d4a9eb 100644 --- a/Orthtree/test/Orthtree/test_octree_kernels.cpp +++ b/Orthtree/test/Orthtree/test_octree_kernels.cpp @@ -1,16 +1,16 @@ #include +#include +#include #include #include #include -#include -#include template void test() { - typedef typename Kernel::Point_3 Point; - typedef CGAL::Point_set_3 Point_set; - typedef CGAL::Octree Octree; + using Point = typename Kernel::Point_3; + using Point_set = CGAL::Point_set_3; + using Octree = CGAL::Octree; Point_set points; CGAL::Random_points_in_cube_3 generator; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index d601e361466..8bbc8932d9e 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -1,19 +1,18 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include -#include #include +#include +#include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_1_point() { @@ -52,24 +51,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.root()[0] == octree.locate({-1, -1, -1})); - assert(octree.root()[1] == octree.locate({1, -1, -1})); - assert(octree.root()[2] == octree.locate({-1, 1, -1})); - assert(octree.root()[3] == octree.locate({1, 1, -1})); - assert(octree.root()[4] == octree.locate({-1, -1, 1})); - assert(octree.root()[5] == octree.locate({1, -1, 1})); - assert(octree.root()[6] == octree.locate({-1, 1, 1})); - assert(octree.root()[7] == octree.locate({1, 1, 1})); + assert(octree.node(0) == octree.locate({-1, -1, -1})); + assert(octree.node(1) == octree.locate({1, -1, -1})); + assert(octree.node(2) == octree.locate({-1, 1, -1})); + assert(octree.node(3) == octree.locate({1, 1, -1})); + assert(octree.node(4) == octree.locate({-1, -1, 1})); + assert(octree.node(5) == octree.locate({1, -1, 1})); + assert(octree.node(6) == octree.locate({-1, 1, 1})); + assert(octree.node(7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.root()[3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.root()[4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.node(0) == octree.locate({-0.99, -0.99, -0.99})); + assert(octree.node(1) == octree.locate({0.99, -0.99, -0.99})); + assert(octree.node(2) == octree.locate({-0.99, 0.99, -0.99})); + assert(octree.node(3) == octree.locate({0.99, 0.99, -0.99})); + assert(octree.node(4) == octree.locate({-0.99, -0.99, 0.99})); + assert(octree.node(5) == octree.locate({0.99, -0.99, 0.99})); + assert(octree.node(6) == octree.locate({-0.99, 0.99, 0.99})); + assert(octree.node(7) == octree.locate({0.99, 0.99, 0.99})); } @@ -93,24 +92,24 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.root()[0] == octree.locate({-1, -1, -1})); - assert(octree.root()[1] == octree.locate({1, -1, -1})); - assert(octree.root()[2] == octree.locate({-1, 1, -1})); - assert(octree.root()[3][3][3] == octree.locate({1, 1, -1})); - assert(octree.root()[4][4][4] == octree.locate({-1, -1, 1})); - assert(octree.root()[5] == octree.locate({1, -1, 1})); - assert(octree.root()[6] == octree.locate({-1, 1, 1})); - assert(octree.root()[7] == octree.locate({1, 1, 1})); + assert(octree.node(0) == octree.locate({-1, -1, -1})); + assert(octree.node(1) == octree.locate({1, -1, -1})); + assert(octree.node(2) == octree.locate({-1, 1, -1})); + assert(octree.node(3, 3, 3, 3, 3) == octree.locate({1, 1, -1})); + assert(octree.node(4, 4, 4) == octree.locate({-1, -1, 1})); + assert(octree.node(5) == octree.locate({1, -1, 1})); + assert(octree.node(6) == octree.locate({-1, 1, 1})); + assert(octree.node(7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.root()[3][3][3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.root()[4][4][4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.node(0) == octree.locate({-0.99, -0.99, -0.99})); + assert(octree.node(1) == octree.locate({0.99, -0.99, -0.99})); + assert(octree.node(2) == octree.locate({-0.99, 0.99, -0.99})); + assert(octree.node(3, 3, 3, 3, 3) == octree.locate({0.99, 0.99, -0.99})); + assert(octree.node(4, 4, 4) == octree.locate({-0.99, -0.99, 0.99})); + assert(octree.node(5) == octree.locate({0.99, -0.99, 0.99})); + assert(octree.node(6) == octree.locate({-0.99, 0.99, 0.99})); + assert(octree.node(7) == octree.locate({0.99, 0.99, 0.99})); } diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index 8e87eaab152..2f37a75236c 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -1,30 +1,28 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include -#include #include #include #include #include #include +#include +#include #include #include using namespace std::chrono; -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kd_tree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kd_tree = Kd_tree_search::Tree; void naive_vs_octree(std::size_t dataset_size) { @@ -47,7 +45,7 @@ void naive_vs_octree(std::size_t dataset_size) { { FT distance_nearest = (std::numeric_limits::max)(); - for (auto &p : points.points()) { + for (auto& p: points.points()) { FT distance_current = CGAL::squared_distance(p, random_point); if (distance_current < distance_nearest) { @@ -72,10 +70,9 @@ void naive_vs_octree(std::size_t dataset_size) { octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); { - // TODO: Write a nearest-neighbor implementation and use it here - std::vector k_neighbors; - octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); - octree_nearest = *k_neighbors.begin(); + std::vector k_neighbors; + octree.nearest_k_neighbors(random_point, 1, std::back_inserter(k_neighbors)); + octree_nearest = get(points.point_map(), *k_neighbors.begin()); } duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; @@ -109,9 +106,9 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { Kd_tree kd_tree(points.points().begin(), points.points().end()); kd_tree.build(); auto kd_tree_start_time = high_resolution_clock::now(); - Kd_tree_search search(kd_tree, random_point, (unsigned int)(K)); + Kd_tree_search search(kd_tree, random_point, (unsigned int) (K)); duration kd_tree_elapsed_time = high_resolution_clock::now() - kd_tree_start_time; - for (auto p : search) + for (auto p: search) kd_tree_nearest_neighbors.push_back(p.first); std::cout << "Kd_tree --> " @@ -120,11 +117,11 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { << std::endl; // Do the same using the octree - std::vector octree_nearest_neighbors; + std::vector octree_nearest_neighbors; Octree octree(points, points.point_map()); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); - octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); + octree.nearest_k_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; std::cout << "Octree --> " @@ -137,12 +134,13 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { // Check that they produce the same answer for (std::size_t j = 0; j < K; ++j) - assert(octree_nearest_neighbors[j] == kd_tree_nearest_neighbors[j]); + assert(get(points.point_map(), octree_nearest_neighbors[j]) == kd_tree_nearest_neighbors[j]); } int main(void) { + naive_vs_octree(21); naive_vs_octree(500); naive_vs_octree(1000); naive_vs_octree(10000); diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index ff517782179..ff75dd18edc 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -1,18 +1,34 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include -#include #include +#include +#include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; -typedef Octree::Node Node; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; + +class Split_nth_child_of_root { + std::size_t m_n; +public: + + Split_nth_child_of_root(std::size_t n) : m_n(n) {} + + template + bool operator()(const Node& node) const { + return (node.depth() == 1 && node.local_coordinates().to_ulong() == m_n); + } + + template + bool operator()(Node_index i, const Tree &tree) const { + return (tree.depth(i) == 1 && tree.local_coordinates(i).to_ulong() == m_n); + } +}; void test_1_point() { @@ -24,13 +40,9 @@ void test_1_point() { Octree octree(points, points.point_map()); octree.refine(10, 1); - // Check that the topology matches - Node single_node = CGAL::Orthtrees::Node_access::create_node(Node(), 0); - CGAL::Orthtrees::Node_access::points(single_node) - = CGAL::Orthtrees::Node_access::points(octree.root()); - assert(Node::is_topology_equal(single_node, octree.root())); + // Check that the root node was never split + assert(octree.is_leaf(octree.root())); assert(0 == octree.depth()); - CGAL::Orthtrees::Node_access::free(single_node); } void test_2_points() { @@ -45,12 +57,10 @@ void test_2_points() { octree.refine(10, 1); // The octree should have been split once - Node other = CGAL::Orthtrees::Node_access::create_node(Node(), 0); - CGAL::Orthtrees::Node_access::split(other); - assert(Node::is_topology_equal(other, octree.root())); + Octree other(points, points.point_map()); + other.split(other.root()); + assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); - CGAL::Orthtrees::Node_access::free(other); - } void test_4_points() { @@ -65,14 +75,18 @@ void test_4_points() { Octree octree(points, points.point_map()); octree.refine(10, 1); - // The octree should have been split once on the first level, and twice on the second - Node other = CGAL::Orthtrees::Node_access::create_node(Node(), 0); - CGAL::Orthtrees::Node_access::split(other); - CGAL::Orthtrees::Node_access::split(other[3]); - CGAL::Orthtrees::Node_access::split(other[7]); - assert(Node::is_topology_equal(other, octree.root())); + Octree other(points, points.point_map()); + other.split(other.root()); + other.split(other.node(3)); + other.split(other.node(7)); + assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); - CGAL::Orthtrees::Node_access::free(other); + + // Applying another splitting criterion shouldn't reset the tree. + octree.refine(Split_nth_child_of_root(2)); + other.split(other.node(2)); + assert(Octree::is_topology_equal(other, octree)); + } int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 57853df8dfa..e7ec64fd357 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -2,17 +2,18 @@ #include #include - -#include #include +#include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; + +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Level_traversal = CGAL::Orthtrees::Level_traversal; bool test_preorder_1_node() { @@ -53,7 +54,31 @@ bool test_preorder_9_nodes() { assert(*iter == octree.root()); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[i])); + assert(*iter == octree.node(i)); + } + + return true; +} + +bool test_level_9_nodes() { + + // Define the dataset + Point_set points; + points.insert({-1, -1, -1}); + points.insert({1, -1, -1}); + + // Create the octree + Octree octree(points, points.point_map()); + octree.refine(10, 1); + + // Create the range + auto nodes = octree.traverse(static_cast(1)); + + // Check each item in the range + auto iter = nodes.begin(); + for (int i = 0; i < 8; ++i) { + assert(*iter == octree.node(i)); + iter++; } return true; @@ -71,6 +96,7 @@ bool test_preorder_25_nodes() { // Create the octree Octree octree(points, points.point_map()); octree.refine(10, 1); + std::cout << octree << std::endl; // Create the range auto nodes = octree.traverse(); @@ -79,28 +105,28 @@ bool test_preorder_25_nodes() { auto iter = nodes.begin(); assert(*iter == octree.root()); iter++; - assert((*iter == octree.root()[0])); + assert(*iter == octree.node(0)); iter++; - assert((*iter == octree.root()[1])); + assert(*iter == octree.node(1)); iter++; - assert((*iter == octree.root()[2])); + assert((*iter == octree.node(2))); iter++; - assert((*iter == octree.root()[3])); + assert(*iter == octree.node(3)); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[3][i])); + assert(*iter == octree.node(3, i)); } iter++; - assert((*iter == octree.root()[4])); + assert((*iter == octree.node(4))); iter++; - assert((*iter == octree.root()[5])); + assert((*iter == octree.node(5))); iter++; - assert((*iter == octree.root()[6])); + assert((*iter == octree.node(6))); iter++; - assert((*iter == octree.root()[7])); + assert((*iter == octree.node(7))); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[7][i])); + assert(*iter == octree.node(7, i)); } return true; @@ -110,6 +136,7 @@ int main(void) { test_preorder_1_node(); test_preorder_9_nodes(); + test_level_9_nodes(); test_preorder_25_nodes(); return 0; diff --git a/Partition_2/package_info/Partition_2/dependencies b/Partition_2/package_info/Partition_2/dependencies index 0d4ee19ad2b..2d383a64c88 100644 --- a/Partition_2/package_info/Partition_2/dependencies +++ b/Partition_2/package_info/Partition_2/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Periodic_3_mesh_3/package_info/Periodic_3_mesh_3/dependencies b/Periodic_3_mesh_3/package_info/Periodic_3_mesh_3/dependencies index eb2b8e24cc3..bd1fc7bd057 100644 --- a/Periodic_3_mesh_3/package_info/Periodic_3_mesh_3/dependencies +++ b/Periodic_3_mesh_3/package_info/Periodic_3_mesh_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Periodic_3_triangulation_3/package_info/Periodic_3_triangulation_3/dependencies b/Periodic_3_triangulation_3/package_info/Periodic_3_triangulation_3/dependencies index b31b936ff04..619cafd0606 100644 --- a/Periodic_3_triangulation_3/package_info/Periodic_3_triangulation_3/dependencies +++ b/Periodic_3_triangulation_3/package_info/Periodic_3_triangulation_3/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Point_set_processing_3/package_info/Point_set_processing_3/dependencies b/Point_set_processing_3/package_info/Point_set_processing_3/dependencies index dcdadf3e8dc..3d33ad32a3e 100644 --- a/Point_set_processing_3/package_info/Point_set_processing_3/dependencies +++ b/Point_set_processing_3/package_info/Point_set_processing_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 diff --git a/Poisson_surface_reconstruction_3/package_info/Poisson_surface_reconstruction_3/dependencies b/Poisson_surface_reconstruction_3/package_info/Poisson_surface_reconstruction_3/dependencies index 5c532a3d624..e8a4a934cd6 100644 --- a/Poisson_surface_reconstruction_3/package_info/Poisson_surface_reconstruction_3/dependencies +++ b/Poisson_surface_reconstruction_3/package_info/Poisson_surface_reconstruction_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h new file mode 100644 index 00000000000..0992dda022c --- /dev/null +++ b/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h @@ -0,0 +1,101 @@ +/*! \ingroup PkgPolygon2Concepts + * \cgalConcept + * + * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} + * + * A model of this concept represents a multipolygon with holes. + * + * \cgalHasModelsBegin + * \cgalHasModels{CGAL::Multipolygon_with_holes_2} + * \cgalHasModelsEnd + */ + +class MultipolygonWithHoles_2 { +public: + +/// \name Types +/// @{ + +//! the polygon type used to represent each polygon with holes of the multipolygon. +typedef unspecified_type Polygon_with_holes_2; + +/*! a bidirectional iterator over the polygons with holes. + * Its value type is `Polygon_with_holes_2`. + */ +typedef unspecified_type Polygon_with_holes_iterator; + +/*! a bidirectional const iterator over the polygons with holes. + * Its value type is `Polygon_with_holes_2`. + */ +typedef unspecified_type Polygon_with_holes_const_iterator; + +//! range type for iterating over polygons with holes. +typedef unspecified_type Polygon_with_holes_container; + +//! size type +typedef unsigned int Size; + +/// @} + +/// \name Creation +/// @{ + +/*! constructs a multipolygon using a range of polygons with holes. + */ +template +MultipolygonWithHoles_2(InputIterator begin, InputIterator end); + +/// @} + +/// \name Predicates +/// @{ + +/*! returns the number of polygons with holes. + */ +Size number_of_polygons_wih_holes(); + +/// @} + +/// \name Access Functions +/// @{ + +/*! returns the begin iterator of the polygons with holes. + */ +Polygon_with_holes_iterator polygons_with_holes_begin(); + +/*! returns the past-the-end iterator of the polygons with holes. + */ + Polygon_with_holes_iterator polygons_with_holes_end(); + +/*! returns the begin iterator of the polygons with holes. + */ +Polygon_with_holes_const_iterator polygons_with_holes_begin() const; + +/*! returns the past-the-end iterator of the polygons with holes. + */ +Polygon_with_holes_const_iterator polygons_with_holes_end() const; + +/*! returns the range of polygons with holes. + */ +const Polygon_with_holes_container& polygons_with_holes() const; + +/// @} + +/// \name Modifiers +/// @{ + +/*! adds a given polygon with holes to the multipolygon. + */ +void add_polygon_with_holes(const Polygon_with_holes_2& polygon); + +/*! erases the specified polygon. + */ +void erase_polygon_with_holes(Polygon_with_holes_const_iterator pit); + +/*! removes all the polygons with holes. + */ +void clear(); + +/// @} + +}; /* end MultipolygonWithHoles_2 */ diff --git a/Polygon/doc/Polygon/PackageDescription.txt b/Polygon/doc/Polygon/PackageDescription.txt index 49c33a81228..8083eb8e15b 100644 --- a/Polygon/doc/Polygon/PackageDescription.txt +++ b/Polygon/doc/Polygon/PackageDescription.txt @@ -16,7 +16,13 @@ \cgalInclude{CGAL/draw_polygon_with_holes_2.h} */ /// \defgroup PkgDrawPolygonWithHoles2 Draw a 2D Polygon with Holes -/// \ingroup PkgPolygon2Ref + +/*! + \code + #include + \endcode +*/ +/// \defgroup PkgDrawMultipolygonWithHoles2 Draw a 2D Multipolygon with Holes /*! \addtogroup PkgPolygon2Ref @@ -42,11 +48,13 @@ \cgalCRPSection{Concepts} - `PolygonTraits_2` - `GeneralPolygonWithHoles_2` +- `MultipolygonWithHoles_2` \cgalCRPSection{Classes} - `CGAL::Polygon_2` - `CGAL::Polygon_with_holes_2` - `CGAL::General_polygon_with_holes_2` +- `CGAL::Multipolygon_with_holes_2` \cgalCRPSection{Global Functions} - `CGAL::area_2()` @@ -67,4 +75,7 @@ \cgalCRPSection{Draw a Polygon_with_holes_2} - \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink +\cgalCRPSection{Draw a Multipolygon_with_holes_2} +- \link PkgDrawMultipolygonWithHoles2 CGAL::draw() \endlink + */ diff --git a/Polygon/doc/Polygon/Polygon.txt b/Polygon/doc/Polygon/Polygon.txt index f84a10c8a8c..093cb03d5f6 100644 --- a/Polygon/doc/Polygon/Polygon.txt +++ b/Polygon/doc/Polygon/Polygon.txt @@ -37,6 +37,19 @@ points, but little more. Especially, computed values are not cached. That is, when the `Polygon_2::is_simple()` member function is called twice or more, the result is computed each time anew. +\section secPolygonWithHole Polygons with Holes and Multipolygons with Holes + +This package also provides classes to represent polygons with holes and multipolygons with holes. + +For polygons with holes, these are `Polygon_with_holes_2` and `General_polygon_with_holes_2`. They can store a polygon that represents +the outer boundary and a sequence of polygons that represent the holes. + +For multipolygons with holes, there is `Multipolygon_with_holes_2`. +It stores a sequence of polygons with holes. + +These classes do not add any semantic requirements on the simplicity +or orientation of their boundary polygons. + \section secPolygonExamples Examples \subsection subsecPolygon The Polygon Class @@ -46,6 +59,13 @@ some member functions. \cgalExample{Polygon/Polygon.cpp} +\subsection SubsectionPolygonRepair_Multipolygon The Multipolygon with Holes Class + +The following example shows the creation of a multipolygon with holes and the traversal +of the polygons in it. + +\cgalExample{Polygon/multipolygon.cpp} + \cgalFigureBegin{polygon2_algo,pgn_algos.png} A polygon and some points \cgalFigureEnd @@ -82,7 +102,8 @@ vertices. It additionally provides a member function `Polygon_2::vertices()` tha \subsection subsecPolygonDraw Draw a Polygon -A polygon can be visualized by calling the \link PkgDrawPolygon2 CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window (a version exists for polygon with holes, cf. \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink). +A polygon can be visualized by calling the \link PkgDrawPolygon2 CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window. Versions for polygons with holes and multipolygons with holes also exist, cf. \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink and \link PkgDrawMultipolygonWithHoles2 +CGAL::draw() \endlink. \cgalExample{Polygon/draw_polygon.cpp} @@ -94,15 +115,4 @@ Result of the run of the draw_polygon program. A window shows the polygon and al \cgalFigureEnd */ - -\section secPolygonWithHole Polygons with Holes - -This package also provides two classes to represent polygons with holes, -namely `Polygon_with_holes_2` and `General_polygon_with_holes_2`. They -can store a polygon that represents the outer boundary and a sequence -of polygons that represent the holes. - -These classes do not add any semantic requirements on the simplicity -or orientation of their boundary polygons. - } diff --git a/Polygon/doc/Polygon/examples.txt b/Polygon/doc/Polygon/examples.txt index 3a608da09f4..2341aa5721d 100644 --- a/Polygon/doc/Polygon/examples.txt +++ b/Polygon/doc/Polygon/examples.txt @@ -7,4 +7,6 @@ \example Stream_support/Polygon_WKT.cpp \example Polygon/draw_polygon.cpp \example Polygon/draw_polygon_with_holes.cpp +\example Polygon/multipolygon.cpp +\example Polygon/draw_multipolygon_with_holes.cpp */ diff --git a/Polygon/examples/Polygon/CMakeLists.txt b/Polygon/examples/Polygon/CMakeLists.txt index ef957f4c572..1d559bbe210 100644 --- a/Polygon/examples/Polygon/CMakeLists.txt +++ b/Polygon/examples/Polygon/CMakeLists.txt @@ -18,4 +18,5 @@ endforeach() if(CGAL_Qt6_FOUND) target_link_libraries(draw_polygon PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(draw_polygon_with_holes PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_multipolygon_with_holes PUBLIC CGAL::CGAL_Basic_viewer) endif() diff --git a/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp new file mode 100644 index 00000000000..bda77eb6323 --- /dev/null +++ b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main() { + std::string wkt = "MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3)))"; + std::istringstream iss(wkt); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + CGAL::draw(mp); + + return 0; +} diff --git a/Polygon/examples/Polygon/multipolygon.cpp b/Polygon/examples/Polygon/multipolygon.cpp new file mode 100644 index 00000000000..31ce7d37401 --- /dev/null +++ b/Polygon/examples/Polygon/multipolygon.cpp @@ -0,0 +1,31 @@ +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main() { + + Point_2 p1_outer[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; + Point_2 p1_inner[] = {Point_2(0.2,0.2), Point_2(0.8,0.2), Point_2(0.8,0.8), Point_2(0.2,0.8)}; + + Polygon_with_holes_2 p1(Polygon_2(p1_outer, p1_outer+4)); + Polygon_2 h(p1_inner, p1_inner+4); + p1.add_hole(h); + + Point_2 p2_outer[] = {Point_2(0.4,0.4), Point_2(0.6,0.4), Point_2(0.6,0.6), Point_2(0.4,0.6)}; + Polygon_with_holes_2 p2(Polygon_2(p2_outer, p2_outer+4)); + + Multipolygon_with_holes_2 mp; + mp.add_polygon_with_holes(p1); + mp.add_polygon_with_holes(p2); + + for (auto const& p: mp.polygons_with_holes()) { + std::cout << p << std::endl; + } + + return 0; +} diff --git a/Polygon/include/CGAL/General_polygon_with_holes_2.h b/Polygon/include/CGAL/General_polygon_with_holes_2.h index 42173fc2ad6..bb19fcf9943 100644 --- a/Polygon/include/CGAL/General_polygon_with_holes_2.h +++ b/Polygon/include/CGAL/General_polygon_with_holes_2.h @@ -28,8 +28,8 @@ namespace CGAL { * * The class `General_polygon_with_holes_2` models the concept * `GeneralPolygonWithHoles_2`. It represents a general polygon with holes. - * It is parameterized with a type `Polygon` used to define the exposed - * type `Polygon_2`. This type represents the outer boundary of the general + * It is parameterized with a type `Polygon_` used to define the exposed + * type `%Polygon_2`. This type represents the outer boundary of the general * polygon and each hole. * * \tparam Polygon_ must have input and output operators. diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h new file mode 100644 index 00000000000..50edf6d592d --- /dev/null +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -0,0 +1,186 @@ +// Copyright (c) 2005 +// Utrecht University (The Netherlands), +// ETH Zurich (Switzerland), +// INRIA Sophia-Antipolis (France), +// Max-Planck-Institute Saarbruecken (Germany), +// and Tel-Aviv University (Israel). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s): Ken Arroyo Ohori + +#ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H +#define CGAL_MULTIPOLYGON_WITH_HOLES_2_H + +#include + +namespace CGAL { + +/*! \ingroup PkgPolygon2Ref + * + * The class `Multipolygon_with_holes_2` models the concept `MultipolygonWithHoles_2`. + * It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate + * the types `Polygon_2` and `Polygon_with_holes_2`. + * The latter is used to represent each polygon with holes. The former is converted to the latter. + * + * \cgalModels{MultipolygonWithHoles_2} + */ +template > +class Multipolygon_with_holes_2 { +public: + /// \name Definition + + /// @{ + + /// polygon type + using Polygon_2 = CGAL::Polygon_2; + + /// polygon with holes type + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; + + /// @} + + using Polygon_with_holes_container = std::deque; + + using Polygon_with_holes_iterator = typename Polygon_with_holes_container::iterator; + using Polygon_with_holes_const_iterator = typename Polygon_with_holes_container::const_iterator; + + /// the size type + using Size = unsigned int; + + /*! %Default constructor. */ + Multipolygon_with_holes_2() {} + + /*! Constructor from polygons. */ + template + Multipolygon_with_holes_2(PolygonsInputIterator p_begin, + PolygonsInputIterator p_end) : + m_polygons(p_begin, p_end) + {} + + Polygon_with_holes_container& polygons_with_holes() { return m_polygons; } + + const Polygon_with_holes_container& polygons_with_holes() const { return m_polygons; } + + Polygon_with_holes_iterator polygons_with_holes_begin() { return m_polygons.begin(); } + + Polygon_with_holes_iterator polygons_with_holes_end() { return m_polygons.end(); } + + Polygon_with_holes_const_iterator polygons_with_holes_begin() const { return m_polygons.begin(); } + + Polygon_with_holes_const_iterator polygons_with_holes_end() const { return m_polygons.end(); } + + void add_polygon(const Polygon_2& pgn) { m_polygons.push_back(Polygon_with_holes_2(pgn)); } + + void add_polygon_with_holes(const Polygon_with_holes_2& pgn) { m_polygons.push_back(pgn); } + + void add_polygon_with_holes(Polygon_with_holes_2&& pgn) { m_polygons.emplace_back(std::move(pgn)); } + + void erase_polygon_with_holes(Polygon_with_holes_iterator pit) { m_polygons.erase(pit); } + + void clear() { m_polygons.clear(); } + + Size number_of_polygons_with_holes() const { return static_cast(m_polygons.size()); } + +protected: + Polygon_with_holes_container m_polygons; +}; + + +template +bool operator==(const Multipolygon_with_holes_2& p1, + const Multipolygon_with_holes_2& p2) +{ + typedef typename + Multipolygon_with_holes_2::Polygon_with_holes_const_iterator HCI; + typedef CGAL::Polygon_with_holes_2 Polygon_2; + if(&p1 == &p2) + return (true); + + if(p1.number_of_polygons_with_holes() != p2.number_of_polygons_with_holes()) + return (false); + + std::list tmp_list(p2.polygons_with_holes_begin(), p2.polygons_with_holes_end()); + + HCI i = p1.polygons_with_holes_begin(); + for(; i!= p1.polygons_with_holes_end(); ++i) + { + typename std::list::iterator j = + (std::find(tmp_list.begin(), tmp_list.end(), *i)); + + if(j == tmp_list.end()) + return (false); + + tmp_list.erase(j); + } + + + CGAL_assertion(tmp_list.empty()); + return (true); +} + +template +inline bool operator!=(const Multipolygon_with_holes_2& p1, + const Multipolygon_with_holes_2& p2) +{ + return (!(p1==p2)); +} +/*! +inserts a multipolygon with holes to the output stream `os`. + +An \ascii and a binary format exist. The format can be selected with +the \cgal modifiers for streams, `set_ascii_mode()` and `set_binary_mode()`, +respectively. The modifier `set_pretty_mode()` can be used to allow for (a +few) structuring comments in the output. Otherwise, the output would +be free of comments. The default for writing is \ascii without comments. + +The number of polygons is written followed by the polygons. For each polygon, +the number of points of the outer boundary is written followed by the +points themselves in counterclockwise order. Then, the number of holes +is written, and for each hole, the number of points on its outer +boundary is written followed by the points themselves in clockwise +order. + +\relates Multipolygon_with_holes_2 +*/ +template +std::ostream& operator<<(std::ostream& os, + const Multipolygon_with_holes_2& mp) { + typename Multipolygon_with_holes_2::Polygon_with_holes_const_iterator i; + + switch(IO::get_mode(os)) { + case IO::ASCII : + os << mp.number_of_polygons_with_holes() << ' '; + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { + os << *i << ' '; + } + return os; + + case IO::BINARY : + os << mp.number_of_polygons_with_holes(); + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { + os << *i ; + } + return os; + + default: + os << "Multipolygon_with_holes_2(" << std::endl; + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { + os << " " << *i << std::endl; + } + + os << ")" << std::endl; + return os; + } +} + + +} //namespace CGAL + +#endif // CGAL_MULTIPOLYGON_WITH_HOLES_2_H diff --git a/Polygon/include/CGAL/Polygon_2_algorithms.h b/Polygon/include/CGAL/Polygon_2_algorithms.h index a66bff191bf..2a5997f821d 100644 --- a/Polygon/include/CGAL/Polygon_2_algorithms.h +++ b/Polygon/include/CGAL/Polygon_2_algorithms.h @@ -25,6 +25,7 @@ #include #include #include +#include /// namespace CGAL { @@ -142,6 +143,7 @@ area_2( ForwardIterator first, ForwardIterator last, const PolygonTraits& traits) { typedef typename PolygonTraits::FT FT; + internal::Evaluate evaluate; result = FT(0); // check if the polygon is empty if (first == last) return; @@ -153,6 +155,7 @@ area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); + evaluate(result); second = third; } } @@ -179,6 +182,7 @@ polygon_area_2( ForwardIterator first, ForwardIterator last, const PolygonTraits& traits) { typedef typename PolygonTraits::FT FT; + internal::Evaluate evaluate; FT result = FT(0); // check if the polygon is empty if (first == last) return result; @@ -190,6 +194,7 @@ polygon_area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); + evaluate(result); second = third; } return result; diff --git a/Polygon/include/CGAL/Polygon_with_holes_2.h b/Polygon/include/CGAL/Polygon_with_holes_2.h index e41104c943a..23eb826e9b2 100644 --- a/Polygon/include/CGAL/Polygon_with_holes_2.h +++ b/Polygon/include/CGAL/Polygon_with_holes_2.h @@ -30,8 +30,8 @@ namespace CGAL { The class `Polygon_with_holes_2` models the concept `GeneralPolygonWithHoles_2`. It represents a linear polygon with holes. It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate -the type `Polygon_2`. The latter is used to -represents the outer boundary and the boundary of the holes (if any exist). +the type `Polygon_2`. This poygon type is used to +represent the outer boundary and the boundary of the holes (if any exist). \cgalModels{GeneralPolygonWithHoles_2} diff --git a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h new file mode 100644 index 00000000000..909b4b29da8 --- /dev/null +++ b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h @@ -0,0 +1,211 @@ +// Copyright (c) 1997 +// Utrecht University (The Netherlands), +// ETH Zurich (Switzerland), +// INRIA Sophia-Antipolis (France), +// Max-Planck-Institute Saarbruecken (Germany), +// and Tel-Aviv University (Israel). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Ken Arroyo Ohori +// Guillaume Damiand + +#ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H +#define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H + +#include + +#ifdef DOXYGEN_RUNNING +namespace CGAL { + +/*! + * \ingroup PkgDrawMultipolygonWithHoles2 + * + * opens a new window and draws `aph`, an instance of the + * `CGAL::Multipolygon_with_holes_2` class. A call to this function is blocking, that + * is the program continues as soon as the user closes the window. This function + * requires `CGAL_Qt6`, and is only available if the macro + * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target + * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the definition + * `CGAL_USE_BASIC_VIEWER`. + * \tparam PH an instance of the `CGAL::Multipolygon_with_holes_2` class. + * \param aph the multipolygon with holes to draw. + */ + +template +void draw(const MPH& aph); + +} /* namespace CGAL */ + +#endif + +#ifdef CGAL_USE_BASIC_VIEWER + +#include +#include + +namespace CGAL { + +// Viewer class for Multipolygon_with_holes_2 +template +class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { + using Base = Basic_viewer_qt; + using Mpwh = Multipolygon; + using Pwh = typename Mpwh::Polygon_with_holes_2; + using Pgn = typename Mpwh::Polygon_2; + using Pnt = typename Pgn::Point_2; + +public: + /// Construct the viewer. + /// @param parent the active window to draw + /// @param mpwh the multipolygon to view + /// @param title the title of the window + Mpwh_2_basic_viewer_qt(QWidget* parent, const Mpwh& mpwh, + const char* title = "Basic Multipolygon_with_holes_2 Viewer") : + Base(parent, title, true, true, true, false, false), + m_mpwh(mpwh) { + if (mpwh.number_of_polygons_with_holes() == 0) return; + + // mimic the computation of Camera::pixelGLRatio() + auto bbox = bounding_box(); + CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); + CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); + auto diameter = (maxv - minv).norm(); + m_pixel_ratio = diameter / m_height; + } + + /*! Intercept the resizing of the window. + */ + virtual void resizeGL(int width, int height) { + CGAL::QGLViewer::resizeGL(width, height); + m_width = width; + m_height = height; + CGAL::qglviewer::Vec p; + auto ratio = camera()->pixelGLRatio(p); + m_pixel_ratio = ratio; + add_elements(); + } + + /*! Obtain the pixel ratio. + */ + double pixel_ratio() const { return m_pixel_ratio; } + + /*! Compute the bounding box. + */ + CGAL::Bbox_2 bounding_box() { + Bbox_2 bbox; + if (m_mpwh.number_of_polygons_with_holes() > 0) bbox = m_mpwh.polygons_with_holes().front().outer_boundary().bbox(); + for (auto const& pwh: m_mpwh.polygons_with_holes()) { + bbox += pwh.outer_boundary().bbox(); + } + return bbox; + } + + /*! Compute the elements of a multipolygon with holes. + */ + void add_elements() { + clear(); + + for (auto const& p: m_mpwh.polygons_with_holes()) { + CGAL::IO::Color c(rand()%255,rand()%255,rand()%255); + face_begin(c); + + const Pnt* point_in_face; + const auto& outer_boundary = p.outer_boundary(); + compute_loop(outer_boundary, false); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + + for (auto it = p.holes_begin(); it != p.holes_end(); ++it) { + compute_loop(*it, true); + add_point_in_face(*point_in_face); + } + + face_end(); + } + } + +protected: + /*! Compute the face + */ + void compute_loop(const Pgn& p, bool hole) { + if (hole) add_point_in_face(p.vertex(p.size()-1)); + + auto prev = p.vertices_begin(); + auto it = prev; + add_point(*it); + add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + add_segment(*prev, *it); // add segment with previous point + add_point(*it); + add_point_in_face(*it); // add point in face + prev = it; + } + + // Add the last segment between the last point and the first one + add_segment(*prev, *(p.vertices_begin())); + } + + virtual void keyPressEvent(QKeyEvent* e) { + // Test key pressed: + // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } + + // Call: * add_elements() if the model changed, followed by + // * redraw() if some viewing parameters changed that implies some + // modifications of the buffers + // (eg. type of normal, color/mono) + // * update() just to update the drawing + + // Call the base method to process others/classicals key + Base::keyPressEvent(e); + } + +private: + //! The window width in pixels. + int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; + + //! The window height in pixels. + int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; + + //! The ratio between pixel and opengl units (in world coordinate system). + double m_pixel_ratio = 1; + + //! The polygon with holes to draw. + const Mpwh& m_mpwh; +}; + +// Specialization of draw function. +template +void draw(const CGAL::Multipolygon_with_holes_2& mpwh, + const char* title = "Multipolygon_with_holes_2 Basic Viewer") +{ +#if defined(CGAL_TEST_SUITE) + bool cgal_test_suite = true; +#else + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); +#endif + + if (! cgal_test_suite) { + using Mpwh = CGAL::Multipolygon_with_holes_2; + using Viewer = Mpwh_2_basic_viewer_qt; + CGAL::Qt::init_ogl_context(4,3); + int argc = 1; + const char* argv[2] = {"t2_viewer", nullptr}; + QApplication app(argc, const_cast(argv)); + Viewer mainwindow(app.activeWindow(), mpwh, title); + mainwindow.add_elements(); + mainwindow.show(); + app.exec(); + } +} + +} // End namespace CGAL + +#endif // CGAL_USE_BASIC_VIEWER + +#endif // CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h index 520ebbec9be..fd1ad888f5a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h @@ -25,6 +25,7 @@ #include #include +#include #include // needed for CGAL::exact(FT)/CGAL::exact(Lazy_exact_nt) @@ -286,12 +287,14 @@ face_border_length(typename boost::graph_traits::halfedge_descripto const PolygonMesh& pmesh, const NamedParameters& np = parameters::default_values()) { - typename GetGeomTraits::type::FT result = 0; + using FT = typename GetGeomTraits::type::FT; + ::CGAL::internal::Evaluate evaluate; + FT result = 0; for(typename boost::graph_traits::halfedge_descriptor haf : halfedges_around_face(h, pmesh)) { result += edge_length(haf, pmesh, np); - exact(result); + evaluate(result); } return result; @@ -576,11 +579,14 @@ area(FaceRange face_range, { typedef typename boost::graph_traits::face_descriptor face_descriptor; - typename GetGeomTraits::type::FT result = 0; + using FT = typename GetGeomTraits::type::FT; + FT result = 0; + ::CGAL::internal::Evaluate evaluate; + for(face_descriptor f : face_range) { result += face_area(f, tmesh, np); - exact(result); + evaluate(result); } return result; @@ -693,7 +699,10 @@ volume(const TriangleMesh& tmesh, typedef typename boost::graph_traits::face_descriptor face_descriptor; - typename GetGeomTraits::type::FT volume = 0; + using FT = typename GetGeomTraits::type::FT; + ::CGAL::internal::Evaluate evaluate; + + FT volume = 0; typename CGAL::Kernel_traits::type>::Kernel::Compute_volume_3 cv3; @@ -703,7 +712,7 @@ volume(const TriangleMesh& tmesh, get(vpm, target(halfedge(f, tmesh), tmesh)), get(vpm, target(next(halfedge(f, tmesh), tmesh), tmesh)), get(vpm, target(prev(halfedge(f, tmesh), tmesh), tmesh))); - exact(volume); + evaluate(volume); } return volume; diff --git a/Polygon_mesh_processing/package_info/Polygon_mesh_processing/dependencies b/Polygon_mesh_processing/package_info/Polygon_mesh_processing/dependencies index 6d471658093..f44718e3c9b 100644 --- a/Polygon_mesh_processing/package_info/Polygon_mesh_processing/dependencies +++ b/Polygon_mesh_processing/package_info/Polygon_mesh_processing/dependencies @@ -3,6 +3,7 @@ Algebraic_foundations Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Polygon_repair/benchmark/Polygon_repair/clipart.cpp b/Polygon_repair/benchmark/Polygon_repair/clipart.cpp new file mode 100644 index 00000000000..013265e835b --- /dev/null +++ b/Polygon_repair/benchmark/Polygon_repair/clipart.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +void print_timer(clock_t start_time) { + clock_t stop_time = clock(); + double seconds = (stop_time-start_time)/(double)CLOCKS_PER_SEC; + std::cout << seconds << " seconds"; +} + +int main(int argc, char* argv[]) { + + std::string folder_in = "/Volumes/T7 Shield/out_fix"; + std::string folder_out = "/Volumes/T7 Shield/repaired"; + double desired_width = 500.0; + clock_t start_time; + + for (const auto& file: std::filesystem::directory_iterator(folder_in)) { + + if (file.path().filename().extension() != ".obj") continue; + std::cout << "Reading " << file.path().filename() << "..."; + if (std::filesystem::exists(folder_out + "/" + file.path().stem().string() + ".svg")) { + std::cout << " skipped: already processed" << std::endl; + continue; + } + + Polygon_repair pr; + std::vector vertices; + std::vector> edges; + + std::ifstream ifs(file.path()); + std::string line; + + while (std::getline(ifs, line)) { + std::istringstream iss(line); + char c; + iss >> c; + if (c == 'v') { + double x, y; + iss >> x >> y; + vertices.emplace_back(x, y); + } else if (c == 'l') { + unsigned int a, b; + iss >> a >> b; + edges.push_back(std::make_pair(vertices[a-1], vertices[b-1])); + } + } ifs.close(); + + std::unordered_set, + boost::hash>> edges_to_insert; + + for (auto const& edge: edges) { + if (edge.first == edge.second) continue; + std::pair pair = (edge.first < edge.second)? + std::make_pair(edge.first, edge.second) : std::make_pair(edge.second, edge.first); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + + Polygon_repair::Triangulation::Face_handle search_start; + for (auto const& edge: edges_to_insert) { + Polygon_repair::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); + Polygon_repair::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va + pr.triangulation().even_odd_insert_constraint(va, vb); + search_start = vb->face(); + } + + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + } Multipolygon_with_holes_2 mp = pr.multipolygon(); + + if (mp.number_of_polygons_with_holes() > 0) { + CGAL::Bbox_2 bbox = mp.polygons_with_holes().front().bbox(); + for (auto const& polygon: mp.polygons_with_holes()) { + bbox += polygon.outer_boundary().bbox(); + } Kernel::Vector_2 translate(-bbox.xmin(), -bbox.ymin()); + double scale = desired_width/(bbox.xmax()-bbox.xmin()); + + + std::ofstream ofs(folder_out + "/" + file.path().stem().string() + ".svg"); + ofs << "" << std::endl; + + for (auto const& polygon: mp.polygons_with_holes()) { + // std::cout << polygon << std::endl; + ofs << "\t" << std::endl; + for (auto const& hole: polygon.holes()) { + // std::cout << hole << std::endl; + ofs << "\t" << std::endl; + } + } + + ofs << ""; + ofs.close(); + + if (CGAL::Polygon_repair::is_valid(mp)) { + std::cout << " ok" << std::endl; + } + } + + } + + return 0; +} diff --git a/Polygon_repair/benchmark/Polygon_repair/validate_wkt.cpp b/Polygon_repair/benchmark/Polygon_repair/validate_wkt.cpp new file mode 100644 index 00000000000..af560d37629 --- /dev/null +++ b/Polygon_repair/benchmark/Polygon_repair/validate_wkt.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + +// std::string folder = "/Users/ken/Downloads/big polygons/"; + std::string folder = "data/in"; + + for (const auto& file: std::filesystem::directory_iterator(folder)) { + if (file.path().filename().extension() != ".wkt") continue; + std::cout << "Testing " << file.path().filename() << "... "; + + // Read test file + std::string in; + std::getline(std::ifstream(file.path()), in); + + // Load test file + std::istringstream iss(in); + bool valid = true; + if (in.find("POLYGON") == 0) { + Polygon_with_holes_2 p; + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + valid = CGAL::Polygon_repair::is_valid(p); + } + } else if (in.find("MULTIPOLYGON") == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + valid = CGAL::Polygon_repair::is_valid(mp); + } if (valid) std::cout << "Valid" << std::endl; + } + + return 0; +} diff --git a/Polygon_repair/benchmark/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/benchmark/Polygon_repair/write_labeled_triangulation.cpp new file mode 100644 index 00000000000..5b21e141505 --- /dev/null +++ b/Polygon_repair/benchmark/Polygon_repair/write_labeled_triangulation.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + + std::ifstream ifs("data/in/nesting-spike.wkt"); + + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(ifs, mp); + + Polygon_repair pr; + pr.add_to_triangulation_even_odd(mp); + pr.label_triangulation_even_odd(); + + std::cout << "{" << std::endl; + std::cout << "\t\"type\": \"FeatureCollection\"," << std::endl; + std::cout << "\t\"features\": [" << std::endl; + + for (Polygon_repair::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); + face != pr.triangulation().finite_faces_end(); ++face) { + std::cout << "\t\t{" << std::endl; + std::cout << "\t\t\t\"type\": \"Feature\"," << std::endl; + std::cout << "\t\t\t\"properties\": {" << std::endl; + std::cout << "\t\t\t\t\"label\": " << face->label() << std::endl; + std::cout << "\t\t\t}," << std::endl; + std::cout << "\t\t\t\"geometry\": {" << std::endl; + std::cout << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; + std::cout << "\t\t\t\t\"coordinates\": [" << std::endl; + std::cout << "\t\t\t\t\t[" << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; + std::cout << "\t\t\t\t\t]" << std::endl; + std::cout << "\t\t\t\t]" << std::endl; + std::cout << "\t\t\t}" << std::endl; + std::cout << "\t\t}"; + Polygon_repair::Triangulation::Finite_faces_iterator next_face = face; + ++next_face; + if (next_face != pr.triangulation().finite_faces_end()) std::cout << ","; + std::cout << std::endl; + } + + std::cout << "\t]" << std::endl; + std::cout << "}" << std::endl; + + pr.reconstruct_multipolygon(); + Multipolygon_with_holes_2 repaired = pr.multipolygon(); + + return 0; +} diff --git a/Polygon_repair/benchmark/Polygon_repair/write_repaired_polygons.cpp b/Polygon_repair/benchmark/Polygon_repair/write_repaired_polygons.cpp new file mode 100644 index 00000000000..e31996ae33e --- /dev/null +++ b/Polygon_repair/benchmark/Polygon_repair/write_repaired_polygons.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + + // std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); + // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); + + std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); + std::ofstream ofs("/Users/ken/Downloads/2.geojson"); + + std::string in; + std::getline(ifs, in); + std::istringstream iss(in); + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + Polygon_repair pr; + pr.add_to_triangulation_even_odd(p); + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + Multipolygon_with_holes_2 rmp = pr.multipolygon(); + std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + + ofs << std::fixed; + ofs << std::setprecision(15); + + ofs << "{" << std::endl; + ofs << "\t\"type\": \"MultiPolygon\"," << std::endl; + ofs << "\t\"coordinates\": [" << std::endl; + for (int i = 0; i < rmp.polygons_with_holes().size(); ++i) { + ofs << "\t\t[" << std::endl; + + ofs << "\t\t\t[" << std::endl; + for (int j = 0; j < rmp.polygons_with_holes()[i].outer_boundary().size(); ++j) { + ofs << "\t\t\t\t[" << rmp.polygons_with_holes()[i].outer_boundary()[j].x() << + ", " << rmp.polygons_with_holes()[i].outer_boundary()[j].y() << "]"; + if (j < rmp.polygons_with_holes()[i].outer_boundary().size()-1) ofs << ","; + ofs << std::endl; + } ofs << "\t\t\t]"; + if (rmp.polygons_with_holes()[i].number_of_holes() > 0) ofs << ","; + ofs << std::endl; + + for (int j = 0; j < rmp.polygons_with_holes()[i].holes().size(); ++j) { + ofs << "\t\t\t[" << std::endl; + for (int k = 0; k < rmp.polygons_with_holes()[i].holes()[j].size(); ++k) { + ofs << "\t\t\t\t[" << rmp.polygons_with_holes()[i].holes()[j][k].x() << + ", " << rmp.polygons_with_holes()[i].holes()[j][k].y() << "]"; + if (k < rmp.polygons_with_holes()[i].holes()[j].size()-1) ofs << ","; + ofs << std::endl; + } + ofs << "\t\t\t]"; + if (j < rmp.polygons_with_holes()[i].holes().size()-1) ofs << ","; + ofs << std::endl; + } + + ofs << "\t\t]"; + if (i < rmp.polygons_with_holes().size()-1) ofs << ","; + ofs << std::endl; + } ofs << "\t]" << std::endl; + ofs << "}" << std::endl; + + return 0; +} diff --git a/Polygon_repair/doc/Polygon_repair/Doxyfile.in b/Polygon_repair/doc/Polygon_repair/Doxyfile.in new file mode 100644 index 00000000000..2ef1e12b3c0 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/Doxyfile.in @@ -0,0 +1,3 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 2D Polygon Repair" + diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt new file mode 100644 index 00000000000..0bd41658ad4 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -0,0 +1,38 @@ +// PRETTY PACKAGE NAME should equal the project title in Doxyfile.in + +/// \defgroup PkgPolygonRepairRef 2D Polygon Repair Reference + +/// \defgroup PkgPolygonRepairFunctions Functions +/// \ingroup PkgPolygonRepairRef + +/*! +\addtogroup PkgPolygonRepairRef + +\cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair} +\cgalPkgPicture{Polygon_repair-small.png} + +\cgalPkgSummaryBegin +\cgalPkgAuthors{Ken Arroyo Ohori} +\cgalPkgDesc{This package provides algorithms to repair 2D polygons, polygons with holes, +and multipolygons with holes, by selecting faces of the arrangement of the input based on a selection rule. +Currently, only the even-odd rule is provided. } +\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepairRef} +\cgalPkgSummaryEnd + +\cgalPkgShortInfoBegin +\cgalPkgSince{6.0} +\cgalPkgDependsOn{\ref PkgPolygon2, \ref PkgTriangulation2} +\cgalPkgBib{cgal:a-pr} +\cgalPkgLicense{\ref licensesGPL "GPL"} +\cgalPkgShortInfoEnd + +\cgalPkgDescriptionEnd + +\cgalClassifedRefPages + +\cgalCRPSection{Functions} +- `CGAL::Polygon_repair::repair()` + +\cgalCRPSection{Simplification Rules} +- `CGAL::Polygon_repair::Even_odd_rule` +*/ diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt new file mode 100644 index 00000000000..26b6f6d62c7 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -0,0 +1,188 @@ +namespace CGAL { +/*! + +\mainpage User Manual +\anchor Chapter_2D_Polygon_repair + +\cgalAutoToc +\author Ken Arroyo Ohori + +\section SectionPolygonRepair_Introduction Introduction + +This package implements a polygon repair method. Starting from possibly +invalid input in the form of a polygon, polygon with holes or multipolygon +with holes, the method computes an arrangement of the input edges, labels +each face according to what it represents (exterior, polygon interior +or hole), and reconstructs the polygon(s) represented by the arrangement. +The method returns valid output stored in a multipolygon with holes. + +Different arrangement and labelling heuristics are possible, but +currently only the even-odd rule is implemented in this package. +This rule results in areas that are alternately assigned as polygon +interiors and exterior/holes each time that an input edge is passed. +It does not distinguish between edges that are part of outer boundaries +from those of inner boundaries. In a next version we will add the +winding number rule. + +\section SectionPolygonRepair_Definitions Definitions + +- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ +that is bounded by a cycle of linear edges, which is known as its +outer boundary. This outer boundary should be simple, +meaning that the interiors of its edges are pairwise disjoint and all of +its vertices have a degree of two. It is thus topologically equivalent to a +disk and is represented internally as the sequence of points at the common +end points of the edges around its outer boundary. + +- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ +that is bounded by one outer boundary and zero or more inner boundaries, +where each inner boundary represents a hole in the polygon. Considered +independently, each boundary should be simple. The different boundaries of a polygon +are allowed to intersect tangentially at their common vertices (with no common +edges), forming vertices with degrees of a multiple of two the tangential points. +The interior of a polygon with holes should form a connected point set. +Note that a valid polygon can also be represented as a valid polygon with +holes (where the number of holes is zero). + +- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ +that is represented by a set of zero or more valid polygons with holes. +The interiors of the polygons with holes should be pairwise disjoint, but they +are allowed to intersect tangentially at their common vertices. Note that +a valid polygon with holes can also be represented as a valid multipolygon +with holes (with only one polygon). + +\cgalFigureBegin{valid, valid.svg} +Valid: (a) polygon, (b-c) polygons with holes, and (d-e) multipolygons with holes. +(c) and (e) show cases where boundaries intersect tangentially at a single vertex. +\cgalFigureEnd + +\cgalFigureBegin{invalid, invalid.svg} +Invalid: (a) self-intersecting polygon self-intersection, (b) self-touching polygon, +(c-d) polygons with badly nested holes, (e) polygon with hole touching at edge, +(f) polygon with hole that separates interior into two parts, (g) multipolygon +with overlapping polygons, and (h) multipolygon with polygons that touch at an edge. +\cgalFigureEnd + +\subsection SubsectionPolygonRepair_Output Stricter Conditions for Output + +The conditions listed above are sufficient to define valid polygons, polygons +with holes and multipolygons with holes for most applications. However, in +order to ensure unique deterministic output from the repair algorithm, +the valid multipolygons with holes returned by the package conform to more +strict criteria: + +- Adjacent collinear edges touching at vertices of degree two are merged +- The sequence of vertices representing a boundary starts from its +lexicographically smallest vertex +- Outer boundaries are oriented counter-clockwise and inner boundaries are +oriented clockwise +- The inner boundaries of a polygon with holes are stored in lexicographic +order +- The polygons with holes of a multipolygon with holes are also stored in +lexicographic order + +\section SectionPolygonRepair_Algorithm Algorithm + +Broadly, the algorithm consists of three steps: + +-# Arrangement: the edges in the polygon, polygon with +holes or multipolygon with holes are added as edges in the arrangement. +-# Labeling of the faces: the resulting faces are labeled with ids +according to what they represent (exterior, polygon interior or hole). +-# Reconstruction of the multipolygon: each boundary is reconstructed, +then these are assembled into individual polygons with holes and put into a +single multipolygon with holes. + +\cgalFigureBegin{inout, inout.svg} +Examples of polygons with holes (a-d) and multipolygons with holes +(e-h) before (left) and after (right) being repaired. +\cgalFigureEnd + +\subsection SubsectionPolygonRepair_Arrangement Arrangement + +For the purposes of the repair operation, the input polygon, polygon with holes +or multipolygon is merely used as a container of input line segments. These line +segments are added to the arrangement as edges. Internally, this is done using +a constrained triangulation where they are added as constraints. + +With the even-odd rule, only the edges that are present an odd number of +times in the input will be edges in the final arrangement. +When these edges are only partially overlapping, only the parts that overlap +an odd number of times will be edges in the final arrangement. + +This procedure is done in two steps: 1. preprocessing to eliminate identical +edges that are present an even number of times, and 2. adding edges incrementally +while applying an even-odd counting mechanism, which erases existing (parts of) +edges when new overlapping ones are added. + +\subsection SubsectionPolygonRepair_Labeling Labeling + +First, the polygon exterior is labeled. For this, all of the faces that can be +accessed from the exterior without passing through an edge are labeled as exterior +faces. + +Then, all other faces are labeled. For the even-odd rule, the label applied +alternates between polygon interior and hole every time that an edge is passed. + +\subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the Multipolygon + +The algorithm reconstructs the multipolygon boundary by boundary, obtaining +counter-clockwise cycles for outer boundaries and clockwise cycles for inner +boundaries. Once all boundaries have been reconstructed, the boundaries are assembled +into multipolygons using the face labels to know which polygon with holes inner/outer +boundaries belong to, and using the orientation to distinguish between the outer and +inner boundaries of each polygon with holes. + +\subsection SubsectionPolygonRepair_Notes Notes on the Output + +If the input is already valid, the method will return a valid output representing +the same area. However, the output might be different in order to conform to the +stricter conditions to generate deterministic output (see +\ref SubsectionPolygonRepair_Output). + +Also, it is worth noting that even the repair of a single polygon without holes +but with self-intersections can result in a multipolygon with holes. This is why +the repair function will always return a multipolygon with holes. The user can +then check whether it consists of a single polygon with holes, and if a polygon +with holes has zero holes and extract these if needed. + +\section SectionPolygonRepair_Examples Examples + +\subsection SubsectionPolygonRepair_Repair Repairing a (Multi)polygon + +It is possible to repair a polygon, polygon with holes or multipolygon with holes +using the even-odd rule by calling the `Polygon_repair::repair()` function +as shown in the following example. This function returns a repaired multipolygon with holes. + +\cgalExample{Polygon_repair/repair_polygon_2.cpp} + +\section SectionPolygonRepair_Performance Performance + +The method can repair large invalid polygons of millions of vertices in a few +seconds as long as the number of intersections between line segments is limited. +This is a realistic assumption with many invalid data sets, which only have +relatively minor issues involving a small number of their vertices/edges. +However, it is worth noting that there can be a potentially quadratic number of +intersection between edges in the worst case, leading to much worse performance +since all of these intersections need to be calculated in the overlay. + +| Polygon | Vertices | Holes | Time | +| :----: | :----: | :----: | | +| ![ ](Corine180927.jpg) | 101973 | 298 | 0.652 sec | +| ![ ](Corine2018418.jpg) | 43925 | 125 | 0.190 sec | + +\section SectionPolygonRepair_History History + +The polygon repair method as originally developed is described by Ledoux et al. +\cgalCite{ledoux2014triangulation} and implemented in the +prepair software. +This package is a reimplementation of the method with a new approach to label +and reconstruct the multipolygons. It also incorporates improvements later +added to prepair, such as the application of the even-odd counting heuristics +to edges, which enables correct counting even on partially overlapping edges. + +Ken Arroyo Ohori developed this package during the Google Summer of +Code 2023 mentored by SĂ©bastien Loriot and Andreas Fabri. + +*/ +} /* namespace CGAL */ diff --git a/Polygon_repair/doc/Polygon_repair/dependencies b/Polygon_repair/doc/Polygon_repair/dependencies new file mode 100644 index 00000000000..6df3ace8d07 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/dependencies @@ -0,0 +1,7 @@ +Manual +Kernel_23 +STL_Extension +Algebraic_foundations +Circulator +Stream_support +Polygon \ No newline at end of file diff --git a/Polygon_repair/doc/Polygon_repair/examples.txt b/Polygon_repair/doc/Polygon_repair/examples.txt new file mode 100644 index 00000000000..278e5049cf2 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/examples.txt @@ -0,0 +1,3 @@ +/*! +\example Polygon_repair/repair_polygon_2.cpp +*/ diff --git a/Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg b/Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg new file mode 100644 index 00000000000..3748a819d2e Binary files /dev/null and b/Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg differ diff --git a/Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg b/Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg new file mode 100644 index 00000000000..aba0e3879a2 Binary files /dev/null and b/Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg differ diff --git a/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png b/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png new file mode 100644 index 00000000000..634b116b03e Binary files /dev/null and b/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png differ diff --git a/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.svg b/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.svg new file mode 100644 index 00000000000..4fb58ada724 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/inout.svg b/Polygon_repair/doc/Polygon_repair/fig/inout.svg new file mode 100644 index 00000000000..d04cd3d601c --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/inout.svg @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (a) + + + + + + + (b) + + + + + + + (c) + + + + + + + (d) + + + + + + + (e) + + + + + + + (f) + + + + + + + (g) + + + + + + + (h) + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/invalid.svg b/Polygon_repair/doc/Polygon_repair/fig/invalid.svg new file mode 100644 index 00000000000..28a48816f75 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/invalid.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + (b) + + + + + + + (a) + + + + + + + (c) + + + + + + + (f) + + + + + + + (g) + + + + + + + + + + + + + + + + + + (d) + + + + + + + + + + + + + + + (e) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (h) + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/valid.svg b/Polygon_repair/doc/Polygon_repair/fig/valid.svg new file mode 100644 index 00000000000..524d308b323 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/valid.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (b) + + + + + + + (a) + + + + + + + (c) + + + + + + + (d) + + + + + + + (e) + + + + + + diff --git a/Polygon_repair/examples/Polygon_repair/CMakeLists.txt b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt new file mode 100644 index 00000000000..4350cb89227 --- /dev/null +++ b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt @@ -0,0 +1,16 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.23) +project(Polygon_repair_Examples) + +find_package(CGAL REQUIRED) + +# create a target per cppfile +file( + GLOB cppfiles + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +foreach(cppfile ${cppfiles}) + create_single_source_cgal_program("${cppfile}") +endforeach() \ No newline at end of file diff --git a/Polygon_repair/examples/Polygon_repair/data/bridge-edge.wkt b/Polygon_repair/examples/Polygon_repair/data/bridge-edge.wkt new file mode 100644 index 00000000000..1e86cf79506 --- /dev/null +++ b/Polygon_repair/examples/Polygon_repair/data/bridge-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/examples/Polygon_repair/data/nesting-spike.wkt b/Polygon_repair/examples/Polygon_repair/data/nesting-spike.wkt new file mode 100644 index 00000000000..e0df5a0dc7d --- /dev/null +++ b/Polygon_repair/examples/Polygon_repair/data/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,2 1,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp new file mode 100644 index 00000000000..be6a011b4a0 --- /dev/null +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main() { + std::ifstream in("data/bridge-edge.wkt"); + Polygon_with_holes_2 pin; + CGAL::IO::read_polygon_WKT(in, pin); + + Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair(pin); + if (mp.number_of_polygons_with_holes() > 1) { + CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + } else { + CGAL::IO::write_polygon_WKT(std::cout, mp.polygons_with_holes()[0]); + } + + return 0; +} diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h new file mode 100644 index 00000000000..b2dc51bfe36 --- /dev/null +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -0,0 +1,35 @@ +// Copyright (c) 2023 GeometryFactory. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H +#define CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H + +#include + +namespace CGAL { + +namespace Polygon_repair { + +/// \addtogroup PkgPolygonRepairRef +/// @{ + +/*! + Tag class to select the even odd rule when calling `CGAL::Polygon_repair::repair()`. + */ + struct Even_odd_rule {}; + +///@} + +} // namespace Polygon_repair + +} // namespace CGAL + +#endif // CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H diff --git a/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h new file mode 100644 index 00000000000..0f148279548 --- /dev/null +++ b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h @@ -0,0 +1,56 @@ +// Copyright (c) 2023 GeometryFactory. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H +#define CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H + +#include + +#include + +namespace CGAL { +namespace Polygon_repair { +namespace internal { + +template > +class Triangulation_face_base_with_repair_info_2 : public FaceBase { + int _label; + bool _processed; +public: + using Vertex_handle = typename FaceBase::Vertex_handle; + using Face_handle = typename FaceBase::Face_handle; + + template + struct Rebind_TDS { + using FaceBase2 = typename FaceBase::template Rebind_TDS::Other; + using Other = Triangulation_face_base_with_repair_info_2; + }; + + Triangulation_face_base_with_repair_info_2() : FaceBase() {} + + Triangulation_face_base_with_repair_info_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2) + : FaceBase(v0, v1, v2) {} + + Triangulation_face_base_with_repair_info_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2, + Face_handle n0, Face_handle n1, Face_handle n2) + : FaceBase(v0, v1, v2, n0, n1, n2) {} + + const bool& processed() const { return _processed; } + bool& processed() { return _processed; } + const int& label() const { return _label; } + int& label() { return _label; } +}; + +} // namespace internal +} // namespace Polygon_repair +} //namespace CGAL + +#endif // CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H diff --git a/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h new file mode 100644 index 00000000000..e75be739e61 --- /dev/null +++ b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h @@ -0,0 +1,127 @@ +// Copyright (c) 2023 GeometryFactory. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H +#define CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H + +#include +#include + +namespace CGAL { +namespace Polygon_repair { +namespace internal { + +template +class Triangulation_with_even_odd_constraints_2 : public Triangulation_ { +public: + using Base_triangulation = Triangulation_; + using Point = typename Triangulation_::Point; + using Edge = typename Triangulation_::Edge; + using Vertex_handle = typename Triangulation_::Vertex_handle; + using Face_handle = typename Triangulation_::Face_handle; + using List_edges = typename Triangulation_::List_edges; + using List_faces = typename Triangulation_::List_faces; + using All_faces_iterator = typename Triangulation_::All_faces_iterator; + + class Interior_tester { + const Triangulation_with_even_odd_constraints_2 *t; + public: + Interior_tester() {} + Interior_tester(const Triangulation_with_even_odd_constraints_2 *tr) : t(tr) {} + + bool operator()(const All_faces_iterator & fit) const { + return fit->label() < 1; + } + }; + + // iterator over interior faces. + class Interior_faces_iterator : public Filter_iterator { + using Base = Filter_iterator; + using Self = Interior_faces_iterator; + public: + Interior_faces_iterator() : Base() {} + Interior_faces_iterator(const Base &b) : Base(b) {} + Self& operator++() { Base::operator++(); return *this; } + Self& operator--() { Base::operator--(); return *this; } + Self operator++(int) { Self tmp(*this); ++(*this); return tmp; } + Self operator--(int) { Self tmp(*this); --(*this); return tmp; } + operator Face_handle() const { return Base::base(); } + }; + + // Inserts point p in the triangulation and returns the corresponding vertex. + Vertex_handle insert(const Point &p, Face_handle f = Face_handle()) { + return Base_triangulation::insert(p, f); + } + + // Add constraint from va to vb using the odd-even rule + void even_odd_insert_constraint(Vertex_handle va, Vertex_handle vb) { + + // Degenerate edge + if (va == vb) return; + + // [va, vb] is either an existing edge OR + // there's an existing shorter edge from va in the direction of vb + Vertex_handle vc; // [va, vc] is the first edge along [va, vb] + Face_handle incident_face; // incident to [va, vc] + int opposite_vertex; // opposite to [va, vc] + if (Base_triangulation::includes_edge(va, vb, vc, incident_face, opposite_vertex)) { + if (Base_triangulation::is_constrained(Edge(incident_face, opposite_vertex))) { + Base_triangulation::remove_constrained_edge(incident_face, opposite_vertex); + } else Base_triangulation::mark_constraint(incident_face, opposite_vertex); + if (vc != vb) even_odd_insert_constraint(vc, vb); // process edges along [vc, vb] + return; + } + + // [va, vb] intersects a constrained edge or an existing vertex + List_faces intersected_faces; + List_edges conflict_boundary_ab, conflict_boundary_ba; + Vertex_handle intersection; + if (Base_triangulation::find_intersected_faces(va, vb, intersected_faces, conflict_boundary_ab, conflict_boundary_ba, intersection)) { + if (intersection != va && intersection != vb) { + even_odd_insert_constraint(va, intersection); + even_odd_insert_constraint(intersection, vb); + } else even_odd_insert_constraint(va, vb); + return; + } + + // Otherwise + Base_triangulation::triangulate_hole(intersected_faces, conflict_boundary_ab, conflict_boundary_ba); + if (intersection != vb) { + even_odd_insert_constraint(intersection, vb); + } + } + + // Add constraint from pa to pb using the odd-even rule + void even_odd_insert_constraint(Point pa, Point pb) { + Vertex_handle va = insert(pa); + Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va + even_odd_insert_constraint(va, vb); + } + + // Starts at an arbitrary interior face + Interior_faces_iterator interior_faces_begin() { + return CGAL::filter_iterator(Base_triangulation::all_faces_end(), + Interior_tester(this), + Base_triangulation::all_faces_begin()); + } + + // Past-the-end iterator + Interior_faces_iterator interior_faces_end() { + return CGAL::filter_iterator(Base_triangulation::all_faces_end(), + Interior_tester(this)); + } +}; + +} // namespace internal +} // namespace Polygon_repair +} // namespace CGAL + +#endif // CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h new file mode 100644 index 00000000000..0188f1630fb --- /dev/null +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -0,0 +1,708 @@ +// Copyright (c) 2023 GeometryFactory. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_POLYGON_REPAIR_H +#define CGAL_POLYGON_REPAIR_H + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace CGAL { + +namespace Polygon_repair { + +#ifndef DOXYGEN_RUNNING +template +class Polygon_repair; +#endif + +/// \ingroup PkgPolygonRepairFunctions +/// repairs polygon `p` using the given rule +/// \tparam Kernel parameter of the input and output polygons +/// \tparam Container parameter of the input and output polygons +/// \tparam Rule must be `Even_odd_rule` +template +Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule = Rule()) +{ + static_assert(std::is_same_v); + CGAL::Polygon_repair::Polygon_repair pr; + pr.add_to_triangulation_even_odd(p); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); +} + +/// \ingroup PkgPolygonRepairFunctions +/// repairs polygon with holes `p` using the given rule +/// \tparam Kernel parameter of the input and output polygons +/// \tparam Container parameter of the input and output polygons +/// \tparam Rule must be `Even_odd_rule` +template +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule = Rule()) +{ + static_assert(std::is_same_v); + CGAL::Polygon_repair::Polygon_repair pr; + pr.add_to_triangulation_even_odd(p); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); +} + +/// \ingroup PkgPolygonRepairFunctions +/// repairs multipolygon with holes `p` using the given rule +/// \tparam Kernel parameter of the input and output polygons +/// \tparam Container parameter of the input and output polygons +/// \tparam Rule must be `Even_odd_rule` +template +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule = Rule()) +{ + static_assert(std::is_same_v); + CGAL::Polygon_repair::Polygon_repair pr; + pr.add_to_triangulation_even_odd(p); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); +} + +template +bool is_valid(const Polygon_2& polygon) { + if (polygon.vertices().size() < 3) { + std::cout << "Invalid: less than 3 vertices" << std::endl; + return false; + } for (auto const& edge: polygon.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices" << std::endl; + return false; + } + } if (!polygon.is_simple()) { + std::cout << "Invalid: not simple" << std::endl; + return false; + } return true; +} + +template +bool is_valid(const Polygon_with_holes_2& polygon) { + + // Validate outer boundary + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices in outer boundary" << std::endl; + return false; + } + } if (!polygon.outer_boundary().is_simple()) { + std::cout << "Invalid: outer boundary not simple" << std::endl; + return false; + } + + // Validate holes + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices in hole" << std::endl; + return false; + } + } if (!hole.is_simple()) { + std::cout << "Invalid: hole not simple" << std::endl; + return false; + } + } + + // Create triangulation of outer boundary + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + for (auto const& edge: polygon.outer_boundary().edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersection in outer boundary" << std::endl; + return false; + } + } if (vt.number_of_faces() == 0) { + std::cout << "Invalid: no outer boundary" << std::endl; + return false; + } for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list::Validation_triangulation::Face_handle> to_check; + std::list to_check_added_by; + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet + if (to_check_added_by.front() < 0) { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } CGAL_assertion(regions == 1 && holes == 0); + + // Hole nesting + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + int li; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != 1) { + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; + return false; + } + } + for (auto const& edge: hole.edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; + return false; + } + } + } + + // Connected interior + for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } to_check.clear(); + to_check_added_by.clear(); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + regions = 0; + holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet + if (to_check_added_by.front() < 0) { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions != 1) { + std::cout << "Invalid: disconnected interior" << std::endl; + return false; + } CGAL_assertion(holes == polygon.number_of_holes()); + + return true; +} + +template +bool is_valid(const Multipolygon_with_holes_2& multipolygon) { + + // Validate polygons + for (auto const& polygon: multipolygon.polygons_with_holes()) { + if (!is_valid(polygon)) return false; + } + + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + int li; + for (auto const& polygon: multipolygon.polygons_with_holes()) { + + + if (vt.number_of_faces() > 0) { + + // Relabel + for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list::Validation_triangulation::Face_handle> to_check; + std::list to_check_added_by; + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet + if (to_check_added_by.front() < 0) { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } + + // Test vertices in labeled triangulation + for (auto const& vertex: polygon.outer_boundary().vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + } + + } + + // Insert constraints while checking for intersections + for (auto const& edge: polygon.outer_boundary().edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + } + } + + return true; +} + +#ifndef DOXYGEN_RUNNING + +template > +class Polygon_repair { +public: + using Vertex_base = CGAL::Triangulation_vertex_base_2; + using Face_base = CGAL::Constrained_triangulation_face_base_2; + using Face_base_with_repair_info = internal::Triangulation_face_base_with_repair_info_2; + using Triangulation_data_structure = CGAL::Triangulation_data_structure_2; + using Tag = typename std::conditional::value, + CGAL::Exact_predicates_tag, + CGAL::Exact_intersections_tag>::type; + using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; + using Triangulation = internal::Triangulation_with_even_odd_constraints_2; + // TODO: Edge_map and Vertex_map use std::set and set::map with exact kernels since Point_2 can't be hashed otherwise + using Edge_map = typename std::conditional::value, + std::unordered_set, + boost::hash>>, + std::set>>::type; + using Vertex_map = typename std::conditional::value, + std::unordered_map, + std::map>::type; + + using Validation_tag = CGAL::No_constraint_intersection_tag; + using Validation_triangulation = CGAL::Constrained_triangulation_2; + + struct Polygon_less { + using Polygon_2 = CGAL::Polygon_2; + bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { + typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); + typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); + while (va != pa.vertices_end() && vb != pb.vertices_end()) { + if (*va != *vb) return *va < *vb; + ++va; + ++vb; + } + if (vb == pb.vertices_end()) return false; + return true; + } + }; + + struct Polygon_with_holes_less { + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; + Polygon_less pl; + bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { + if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; + if (pl(pb.outer_boundary(), pa.outer_boundary())) return false; + typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); + typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); + while (ha != pa.holes_end() && hb != pb.holes_end()) { + if (pl(*ha, *hb)) return true; + if (pl(*hb, *ha)) return false; + } + if (hb == pb.holes_end()) return false; + return true; + } + }; + + /// \name Creation + Polygon_repair() : number_of_polygons(0), number_of_holes(0) {} + + /// \name Modifiers + /// @{ + + // Add edges of the polygon to the triangulation + void add_to_triangulation_even_odd(const Polygon_2& polygon) { + + // Get unique edges + for (auto const& edge: polygon.edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); + } + + // Insert vertices + Vertex_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename Vertex_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges + for (auto const& edge: edges_to_insert) { + t.even_odd_insert_constraint(edge.first, edge.second); + } + } + + // Add edges of the polygon to the triangulation + void add_to_triangulation_even_odd(const Polygon_with_holes_2& polygon) { + + // Get unique edges + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); + } + } + + // Insert vertices + Vertex_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename Vertex_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges + for (auto const& edge: edges_to_insert) { + t.even_odd_insert_constraint(edge.first, edge.second); + } + } + + // Add edges of the polygon to the triangulation + void add_to_triangulation_even_odd(const Multipolygon_with_holes_2& multipolygon) { + + // Get unique edges + for (auto const& polygon: multipolygon.polygons_with_holes()) { + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); + } + } + } + + // Insert vertices + Vertex_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename Vertex_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges + for (auto const& edge: edges_to_insert) { + t.even_odd_insert_constraint(edge.first, edge.second); + } + } + + // Label a region of adjacent triangles without passing through constraints + // adjacent triangles that involve passing through constraints are added to to_check + template + static void label_region(T& tt, typename T::Face_handle face, int label, + std::list& to_check, + std::list& to_check_added_by) { + // std::cout << "Labelling region with " << label << std::endl; + std::list to_check_in_region; + face->label() = label; + to_check_in_region.push_back(face); + face->processed() = true; // processed means added to a list (to ensure elements are only added once) + + while (!to_check_in_region.empty()) { + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (!tt.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { + if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { // unlabeled + to_check_in_region.front()->neighbor(neighbour)->label() = label; + to_check_in_region.push_back(to_check_in_region.front()->neighbor(neighbour)); + to_check_in_region.front()->neighbor(neighbour)->processed() = true; + } + } else { // constrained + if (!to_check_in_region.front()->neighbor(neighbour)->processed()) { // not added to to_check + to_check.push_back(to_check_in_region.front()->neighbor(neighbour)); + to_check_added_by.push_back(label); + to_check_in_region.front()->neighbor(neighbour)->processed() = true; + } + } + } to_check_in_region.pop_front(); + } + } + + // Label triangles in triangulation + void label_triangulation_even_odd() { + + // Simplify collinear edges (gets rid of order dependency) + for (auto vertex: t.all_vertex_handles()) { + typename Triangulation::Edge_circulator first_edge = t.incident_edges(vertex); + typename Triangulation::Edge_circulator current_edge = first_edge; + std::vector incident_constrained_edges; + do { + if (t.is_constrained(*current_edge)) { + incident_constrained_edges.push_back(*current_edge); + } ++current_edge; + } while (current_edge != first_edge); + if (incident_constrained_edges.size() == 2) { + typename Kernel::Point_2 v1 = incident_constrained_edges.front().first->vertex(incident_constrained_edges.front().first->ccw(incident_constrained_edges.front().second))->point(); + typename Kernel::Point_2 v2 = incident_constrained_edges.back().first->vertex(incident_constrained_edges.back().first->ccw(incident_constrained_edges.back().second))->point(); + if (CGAL::collinear(v1, vertex->point(), v2)) { + // std::cout << "Collinear points" << std::endl; + // std::cout << "v1: " << v1 << std::endl; + // std::cout << "in: " << vertex->point() << std::endl; + // std::cout << "v2: " << v2 << std::endl; + t.remove_incident_constraints(vertex); + t.remove(vertex); + t.insert_constraint(v1, v2); + } + } + } + + // Init labels + for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } + + // Label exterior with label -1, marking it as processed and + // putting interior triangles adjacent to it in to_check + std::list to_check; + std::list to_check_added_by; + label_region(t, t.infinite_face(), -1, to_check, to_check_added_by); + + // Label region of front element to_check list + while (!to_check.empty()) { + + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet + if (to_check_added_by.front() < 0) { + label_region(t, to_check.front(), number_of_polygons+1, to_check, to_check_added_by); + ++number_of_polygons; + } else { + label_region(t, to_check.front(), -(number_of_holes+2), to_check, to_check_added_by); + ++number_of_holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + + } // std::cout << number_of_polygons << " polygons with " << number_of_holes << " holes in triangulation" << std::endl; + } + + // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it + void reconstruct_ring(std::list& ring, + typename Triangulation::Face_handle face_adjacent_to_boundary, + int opposite_vertex) { + // std::cout << "Reconstructing ring for face " << face_adjacent_to_boundary->label() << "..." << std::endl; + + // Create ring + typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; + int current_opposite_vertex = opposite_vertex; + do { + CGAL_assertion(current_face->label() == face_adjacent_to_boundary->label()); + current_face->processed() = true; + typename Triangulation::Vertex_handle pivot_vertex = current_face->vertex(current_face->cw(current_opposite_vertex)); + // std::cout << "\tAdding point " << pivot_vertex->point() << std::endl; + ring.push_back(pivot_vertex->point()); + typename Triangulation::Face_circulator fc = t.incident_faces(pivot_vertex, current_face); + do { + ++fc; + } while (fc->label() != current_face->label()); + current_face = fc; + current_opposite_vertex = fc->cw(fc->index(pivot_vertex)); + } while (current_face != face_adjacent_to_boundary || + current_opposite_vertex != opposite_vertex); + + // Start at lexicographically smallest vertex + typename std::list::iterator smallest_vertex = ring.begin(); + for (typename std::list::iterator current_vertex = ring.begin(); + current_vertex != ring.end(); ++current_vertex) { + if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; + } + if (ring.front() != *smallest_vertex) { + ring.splice(ring.begin(), ring, smallest_vertex, ring.end()); + } + } + + // Reconstruct multipolygon based on the triangles labeled as inside the polygon + void reconstruct_multipolygon() { + mp.clear(); + std::vector> polygons; // outer boundaries + std::vector, Polygon_less>> holes; // holes are ordered (per polygon) + polygons.resize(number_of_polygons); + holes.resize(number_of_polygons); + + for (auto const face: t.all_face_handles()) { + face->processed() = false; + } + for (auto const &face: t.finite_face_handles()) { + if (face->label() < 1) continue; // exterior triangle + if (face->processed()) continue; // already reconstructed + for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { + if (face->label() == face->neighbor(opposite_vertex)->label()) continue; // not adjacent to boundary + + // Reconstruct ring + std::list ring; + reconstruct_ring(ring, face, opposite_vertex); + + // Put ring in polygons + Polygon_2 polygon(ring.begin(), ring.end()); + // std::cout << "Reconstructed ring for polygon " << face->label() << " with ccw? " << (polygon.orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; + if (polygon.orientation() == CGAL::COUNTERCLOCKWISE) { + polygons[face->label()-1] = polygon; + } else { + holes[face->label()-1].insert(polygon); + } break; + } + } + + // Create polygons with holes and put in multipolygon + std::set, Polygon_with_holes_less> ordered_polygons; + for (std::size_t i = 0; i < polygons.size(); ++i) { + ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); + } + for (auto const& polygon: ordered_polygons) { + // std::cout << "Adding polygon " << polygon << std::endl; + mp.add_polygon_with_holes(polygon); + } + } + + // Erases the triangulation. + void clear() { + t.clear(); + unique_edges.clear(); + mp.clear(); + number_of_polygons = 0; + number_of_holes = 0; + search_start = Triangulation::Face_handle(); + } + + /// @} + + /// \name Access Functions + /// @{ + + Triangulation& triangulation() { + return t; + } + + Multipolygon_with_holes_2 multipolygon() { + return mp; + } + + /// @} + + +protected: + Triangulation t; + Edge_map unique_edges; + Multipolygon_with_holes_2 mp; + int number_of_polygons, number_of_holes; + typename Triangulation::Face_handle search_start; +}; + +#endif // DOXYGEN_RUNNING + +} // namespace Polygon_repair +} // namespace CGAL + +#endif // CGAL_POLYGON_REPAIR_H diff --git a/Polygon_repair/package_info/Polygon_repair/copyright b/Polygon_repair/package_info/Polygon_repair/copyright new file mode 100644 index 00000000000..b9a65603a2e --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/copyright @@ -0,0 +1 @@ +GeometryFactory (France) diff --git a/Polygon_repair/package_info/Polygon_repair/dependencies b/Polygon_repair/package_info/Polygon_repair/dependencies new file mode 100644 index 00000000000..7e57c5ca79c --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/dependencies @@ -0,0 +1,25 @@ +Algebraic_foundations +Arithmetic_kernel +Cartesian_kernel +CGAL_Core +Circulator +Distance_2 +Distance_3 +Filtered_kernel +Hash_map +Installation +Intersections_2 +Intersections_3 +Interval_support +Kernel_23 +Modular_arithmetic +Number_types +Polygon +Polygon_repair +Profiling_tools +Property_map +STL_Extension +Spatial_sorting +Stream_support +TDS_2 +Triangulation_2 diff --git a/Polygon_repair/package_info/Polygon_repair/description.txt b/Polygon_repair/package_info/Polygon_repair/description.txt new file mode 100644 index 00000000000..faa7c2ab3f9 --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/description.txt @@ -0,0 +1 @@ +Package Polygon_repair provides functions to repair polygons. diff --git a/Polygon_repair/package_info/Polygon_repair/license.txt b/Polygon_repair/package_info/Polygon_repair/license.txt new file mode 100644 index 00000000000..8bb8efcb72b --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/license.txt @@ -0,0 +1 @@ +GPL (v3 or later) diff --git a/Polygon_repair/package_info/Polygon_repair/long_description.txt b/Polygon_repair/package_info/Polygon_repair/long_description.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Polygon_repair/package_info/Polygon_repair/maintainer b/Polygon_repair/package_info/Polygon_repair/maintainer new file mode 100644 index 00000000000..2427333ef57 --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/maintainer @@ -0,0 +1 @@ +GeometryFactory diff --git a/Polygon_repair/test/Polygon_repair/CMakeLists.txt b/Polygon_repair/test/Polygon_repair/CMakeLists.txt new file mode 100644 index 00000000000..faced1f8805 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/CMakeLists.txt @@ -0,0 +1,25 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.23) +project(Polygon_repair_Tests) + +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6) + +# create a target per cppfile +#file( +# GLOB cppfiles +# RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} +# ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +#foreach(cppfile ${cppfiles}) +# create_single_source_cgal_program("${cppfile}") +#endforeach() + +create_single_source_cgal_program( "draw_test_polygons.cpp" ) +create_single_source_cgal_program( "exact_test.cpp") +create_single_source_cgal_program( "repair_polygon_2_test.cpp" ) + +if(CGAL_Qt6_FOUND) + target_link_libraries(draw_test_polygons PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(exact_test PUBLIC CGAL::CGAL_Basic_viewer) +endif() diff --git a/Polygon_repair/test/Polygon_repair/data/in/back-and-forth.wkt b/Polygon_repair/test/Polygon_repair/data/in/back-and-forth.wkt new file mode 100644 index 00000000000..3911b8f5203 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/back-and-forth.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0,1 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/bowtie.wkt b/Polygon_repair/test/Polygon_repair/data/in/bowtie.wkt new file mode 100644 index 00000000000..3005eb3e3f6 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/bowtie.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,1 0,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/bridge-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/bridge-edge.wkt new file mode 100644 index 00000000000..1e86cf79506 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/bridge-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons-nesting.wkt b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons-nesting.wkt new file mode 100644 index 00000000000..6b945e81ff7 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons-nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,3 1,3 2,0 2,0 1)),((1 0,2 0,2 3,1 3,1 0)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons.wkt b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons.wkt new file mode 100644 index 00000000000..662e1017f9c --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,3 1,3 2,0 2,0 1)),((1 0,2 0,2 3,1 3,1 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/edge.wkt new file mode 100644 index 00000000000..66839d34531 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/empty-multipolygon.wkt b/Polygon_repair/test/Polygon_repair/data/in/empty-multipolygon.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/empty-multipolygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/empty-polygon.wkt b/Polygon_repair/test/Polygon_repair/data/in/empty-polygon.wkt new file mode 100644 index 00000000000..aec93ace4b9 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/empty-polygon.wkt @@ -0,0 +1 @@ +POLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-all.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-all.wkt new file mode 100644 index 00000000000..34482d54ef0 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-all.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-as-loop.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-as-loop.wkt new file mode 100644 index 00000000000..8c5a4a1c74c --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-as-loop.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.25,0.75 0.25,0.75 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-carved.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-carved.wkt new file mode 100644 index 00000000000..7b20d27fcfb --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-carved.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1 0.5,1 1,0.5 1,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-filled.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-filled.wkt new file mode 100644 index 00000000000..ca716734779 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-filled.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25)),((0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-outside.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-outside.wkt new file mode 100644 index 00000000000..9d16d9c87ca --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-outside.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-partly-outside.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-partly-outside.wkt new file mode 100644 index 00000000000..da99d6a31e5 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-partly-outside.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.75 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 0.75)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-touching-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-edge.wkt new file mode 100644 index 00000000000..4953794c84a --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.5 1,0.25 0.75,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-touching-once.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-once.wkt new file mode 100644 index 00000000000..e9e070b9224 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-once.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole-touching-twice.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-twice.wkt new file mode 100644 index 00000000000..54f1083af91 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-twice.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,1 0,0.75 0.75,0 1,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/hole.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole.wkt new file mode 100644 index 00000000000..6c0e1bfe42b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/hole.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/nesting-spike.wkt b/Polygon_repair/test/Polygon_repair/data/in/nesting-spike.wkt new file mode 100644 index 00000000000..e0df5a0dc7d --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,2 1,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/nesting.wkt b/Polygon_repair/test/Polygon_repair/data/in/nesting.wkt new file mode 100644 index 00000000000..142ea6dc746 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/not-closed.wkt b/Polygon_repair/test/Polygon_repair/data/in/not-closed.wkt new file mode 100644 index 00000000000..ce5c6b3dbf0 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/not-closed.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-inside.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-inside.wkt new file mode 100644 index 00000000000..646ca0d5b5a --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-inside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0.25,2 0.25,2 0.75,1 0.75,1 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-partial.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-partial.wkt new file mode 100644 index 00000000000..2dc63c23822 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-partial.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 0.5,1 0.5,1 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge.wkt new file mode 100644 index 00000000000..6e59f2e83e5 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 1,1 1,1 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/point-double.wkt b/Polygon_repair/test/Polygon_repair/data/in/point-double.wkt new file mode 100644 index 00000000000..ddecaff4c21 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/point-double.wkt @@ -0,0 +1 @@ +POLYGON((0 0,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/point.wkt b/Polygon_repair/test/Polygon_repair/data/in/point.wkt new file mode 100644 index 00000000000..1c9d31654c2 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/point.wkt @@ -0,0 +1 @@ +POLYGON((0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spike-boundary.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-boundary.wkt new file mode 100644 index 00000000000..477315fcf87 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spike-boundary.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spike-in.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-in.wkt new file mode 100644 index 00000000000..49f02f9d1e4 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spike-in.wkt @@ -0,0 +1 @@ +POLYGON((0 0,0.5 0.5,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spike-long.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-long.wkt new file mode 100644 index 00000000000..1668214ed24 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spike-long.wkt @@ -0,0 +1 @@ +POLYGON((0 0,2 1,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spike-out.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-out.wkt new file mode 100644 index 00000000000..7a95b726a2f --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spike-out.wkt @@ -0,0 +1 @@ +POLYGON((0 0,-0.5 -0.5,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spikes-fp.wkt b/Polygon_repair/test/Polygon_repair/data/in/spikes-fp.wkt new file mode 100644 index 00000000000..2bb7ded142f --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spikes-fp.wkt @@ -0,0 +1 @@ +POLYGON((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spikes.wkt b/Polygon_repair/test/Polygon_repair/data/in/spikes.wkt new file mode 100644 index 00000000000..c831dd8e250 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spikes.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/spiral.wkt b/Polygon_repair/test/Polygon_repair/data/in/spiral.wkt new file mode 100644 index 00000000000..d79a0892188 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/spiral.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.4 0.4,0.6 0.4,0.6 0.6,0.4 0.6,0.5 0.5,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/square-hole-rhombus.wkt b/Polygon_repair/test/Polygon_repair/data/in/square-hole-rhombus.wkt new file mode 100644 index 00000000000..1cfa76db480 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/square-hole-rhombus.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0,1 0.5,0.5 1,0 0.5,0.5 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/square.wkt b/Polygon_repair/test/Polygon_repair/data/in/square.wkt new file mode 100644 index 00000000000..d97ff8d423b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/square.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/in/star.wkt b/Polygon_repair/test/Polygon_repair/data/in/star.wkt new file mode 100644 index 00000000000..eb43ea7debb --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/in/star.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1.5 3,3 0,0 1.5,3 3,1.5 0,0 3,3 1.5,0 0)) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/back-and-forth.wkt b/Polygon_repair/test/Polygon_repair/data/ref/back-and-forth.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/back-and-forth.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/bowtie.wkt b/Polygon_repair/test/Polygon_repair/data/ref/bowtie.wkt new file mode 100644 index 00000000000..0ca7c0da217 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/bowtie.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,0.5 0.5,0 1,0 0)),((0.5 0.5,1 0,1 1,0.5 0.5))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/bridge-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/bridge-edge.wkt new file mode 100644 index 00000000000..99b3f4c1cbb --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/bridge-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons-nesting.wkt b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons-nesting.wkt new file mode 100644 index 00000000000..64190bb04e9 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons-nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,1 1,1 2,0 2,0 1)),((1 0,2 0,2 1,1 1,1 0)),((1 2,2 2,2 3,1 3,1 2)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25)),((2 1,3 1,3 2,2 2,2 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons.wkt b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons.wkt new file mode 100644 index 00000000000..815bc8c5625 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,1 1,1 2,0 2,0 1)),((1 0,2 0,2 1,1 1,1 0)),((1 2,2 2,2 3,1 3,1 2)),((2 1,3 1,3 2,2 2,2 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/edge.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/empty-multipolygon.wkt b/Polygon_repair/test/Polygon_repair/data/ref/empty-multipolygon.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/empty-multipolygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/empty-polygon.wkt b/Polygon_repair/test/Polygon_repair/data/ref/empty-polygon.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/empty-polygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-all.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-all.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-all.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-as-loop.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-as-loop.wkt new file mode 100644 index 00000000000..b5ee1d02287 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-as-loop.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-carved.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-carved.wkt new file mode 100644 index 00000000000..d6e9a18d49e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-carved.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.5,0.5 0.5,0.5 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-filled.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-filled.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-filled.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-outside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-outside.wkt new file mode 100644 index 00000000000..1d003ce0e92 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-outside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-partly-outside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-partly-outside.wkt new file mode 100644 index 00000000000..503189d5d25 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-partly-outside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.75,0.75 0.75,0.75 1,0 1,0 0)),((0.75 1,1 1,1 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-edge.wkt new file mode 100644 index 00000000000..4d3bb234397 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0.5 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.5 1,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-once.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-once.wkt new file mode 100644 index 00000000000..b5ee1d02287 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-once.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-twice.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-twice.wkt new file mode 100644 index 00000000000..bc530227b8f --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-twice.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,0.25 0.25,0 1,0 0)),((0 1,0.75 0.75,1 0,1 1,0 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/hole.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole.wkt new file mode 100644 index 00000000000..99b3f4c1cbb --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/hole.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/nesting-spike.wkt b/Polygon_repair/test/Polygon_repair/data/ref/nesting-spike.wkt new file mode 100644 index 00000000000..15587830fca --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/nesting.wkt b/Polygon_repair/test/Polygon_repair/data/ref/nesting.wkt new file mode 100644 index 00000000000..15587830fca --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/not-closed.wkt b/Polygon_repair/test/Polygon_repair/data/ref/not-closed.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/not-closed.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-inside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-inside.wkt new file mode 100644 index 00000000000..f801b7ff6ec --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-inside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.25,2 0.25,2 0.75,1 0.75,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-partial.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-partial.wkt new file mode 100644 index 00000000000..5588dadf3e6 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-partial.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,2 0,2 0.5,1 0.5,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge.wkt new file mode 100644 index 00000000000..ed4275a4e2a --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,2 0,2 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/point-double.wkt b/Polygon_repair/test/Polygon_repair/data/ref/point-double.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/point-double.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/point.wkt b/Polygon_repair/test/Polygon_repair/data/ref/point.wkt new file mode 100644 index 00000000000..21e6672095b --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/point.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spike-boundary.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-boundary.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spike-boundary.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spike-in.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-in.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spike-in.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spike-long.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-long.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spike-long.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spike-out.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-out.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spike-out.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spikes-fp.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spikes-fp.wkt new file mode 100644 index 00000000000..861654da607 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spikes-fp.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spikes.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spikes.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spikes.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/spiral.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spiral.wkt new file mode 100644 index 00000000000..9ba30e469e2 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/spiral.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0.1 0.1,0 0),(0.1 0.1,0.2 0.2,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.3 0.3,0.2 0.2),(0.3 0.3,0.4 0.4,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3)),((0.4 0.4,0.6 0.4,0.6 0.6,0.4 0.6,0.5 0.5,0.4 0.4))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/square-hole-rhombus.wkt b/Polygon_repair/test/Polygon_repair/data/ref/square-hole-rhombus.wkt new file mode 100644 index 00000000000..d55952100ce --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/square-hole-rhombus.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,0.5 0,0 0.5,0 0)),((0 0.5,0.5 1,0 1,0 0.5)),((0.5 0,1 0,1 0.5,0.5 0)),((0.5 1,1 0.5,1 1,0.5 1))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/square.wkt b/Polygon_repair/test/Polygon_repair/data/ref/square.wkt new file mode 100644 index 00000000000..511245f448e --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/square.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/data/ref/star.wkt b/Polygon_repair/test/Polygon_repair/data/ref/star.wkt new file mode 100644 index 00000000000..b782e4bbd40 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/data/ref/star.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1.2 0.6,1 1,0.6 1.2,0 0)),((0 1.5,0.6 1.2,0.75 1.5,0.6 1.8,0 1.5)),((0 3,0.6 1.8,1 2,1.2 2.4,0 3)),((0.75 1.5,1 1,1.5 0.75,2 1,2.25 1.5,2 2,1.5 2.25,1 2,0.75 1.5)),((1.2 0.6,1.5 0,1.8 0.6,1.5 0.75,1.2 0.6)),((1.2 2.4,1.5 2.25,1.8 2.4,1.5 3,1.2 2.4)),((1.8 0.6,3 0,2.4 1.2,2 1,1.8 0.6)),((1.8 2.4,2 2,2.4 1.8,3 3,1.8 2.4)),((2.25 1.5,2.4 1.2,3 1.5,2.4 1.8,2.25 1.5))) \ No newline at end of file diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp new file mode 100644 index 00000000000..ead5bda9e75 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if defined(__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + +#include + +#include +#include +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + + for (const auto& file: std::filesystem::directory_iterator("data/in")) { + if (file.path().filename().extension() != ".wkt") continue; + std::cout << "Reading " << file.path().filename() << "..." << std::endl; + + if (file.path().filename() != "nesting-spike.wkt") continue; + + std::string in; + std::getline(std::ifstream(file.path()), in); + std::istringstream iss(in); + Multipolygon_with_holes_2 rmp; + + if (in.find("POLYGON") == 0) { + Polygon_with_holes_2 p; + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + } CGAL::draw(p); + rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); + } else if (in.find("MULTIPOLYGON") == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + CGAL::draw(mp); + rmp = CGAL::Polygon_repair::repair(mp, CGAL::Polygon_repair::Even_odd_rule()); + } std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + CGAL::draw(rmp); + + } + + return 0; +} + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp new file mode 100644 index 00000000000..d3d7fceb673 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -0,0 +1,75 @@ +#define CGAL_NO_CDT_2_WARNING + +#include +#include +#include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if defined(__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + +#include + +#include +#include +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + + std::string in = "POLYGON((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5))"; + std::istringstream iss(in); + Multipolygon_with_holes_2 rmp; + + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + CGAL::draw(p); + Polygon_repair pr; + for (auto const edge: p.outer_boundary().edges()) { + pr.triangulation().even_odd_insert_constraint(edge.source(), edge.target()); + } int spikes = 20; + for (auto const& hole: p.holes()) { + for (auto const edge: hole.edges()) { + if (spikes-- <= 0) break; + pr.triangulation().even_odd_insert_constraint(edge.source(), edge.target()); + } + } + pr.label_triangulation_even_odd(); + pr.reconstruct_multipolygon(); + rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); + std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + CGAL::draw(rmp); + + return 0; +} + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp new file mode 100644 index 00000000000..1f7e022becf --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -0,0 +1,105 @@ +#include +#include +#include + +#include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if defined(__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main() { + + for (const auto& file: std::filesystem::directory_iterator("data/in")) { + if (file.path().filename().extension() != ".wkt") continue; + std::cout << "Testing " << file.path().filename() << "... "; + + // Read test file + std::string in; + std::getline(std::ifstream(file.path()), in); + + // Load test file and repair to create output + std::istringstream iss(in); + Multipolygon_with_holes_2 rmp, refmp; + if (in.find("POLYGON") == 0) { + Polygon_with_holes_2 p; + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + } rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); + } else if (in.find("MULTIPOLYGON") == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + rmp = CGAL::Polygon_repair::repair(mp, CGAL::Polygon_repair::Even_odd_rule()); + } std::stringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + rmp.clear(); + CGAL::IO::read_multi_polygon_WKT(oss, rmp); + + // Read reference file + std::string ref_path = "data/ref/"; + ref_path += file.path().filename().string(); + std::ifstream ref_ifs(ref_path); + if (ref_ifs.fail()) { + std::cout << std::endl << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + std::cout << "\tno reference output -> skipped" << std::endl; + continue; + } std::string ref; + std::getline(ref_ifs, ref); + ref += "\n"; + std::stringstream refss(ref); + CGAL::IO::read_multi_polygon_WKT(refss, refmp); + + // Compare output with reference file + if (rmp == refmp) { + std::cout << "ok" << std::endl; + } else { + std::cout << "fail" << std::endl; + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out << std::flush; + std::cout << "\tref: " << ref << std::flush; + } + assert(rmp == refmp); + + // Test orientations + for (auto const& polygon: rmp.polygons_with_holes()) { + assert(polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); + for (auto const &hole: polygon.holes()) { + assert(hole.orientation() == CGAL::CLOCKWISE); + } + } + } + + return 0; +} + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif diff --git a/Polygonal_surface_reconstruction/package_info/Polygonal_surface_reconstruction/dependencies b/Polygonal_surface_reconstruction/package_info/Polygonal_surface_reconstruction/dependencies index 650e4a65343..dfaa76af3a5 100644 --- a/Polygonal_surface_reconstruction/package_info/Polygonal_surface_reconstruction/dependencies +++ b/Polygonal_surface_reconstruction/package_info/Polygonal_surface_reconstruction/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Alpha_shapes_2 Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Convex_hull_2 @@ -21,8 +22,8 @@ Number_types Point_set_3 Point_set_processing_3 Polygon -Polygonal_surface_reconstruction Polygon_mesh_processing +Polygonal_surface_reconstruction Principal_component_analysis Principal_component_analysis_LGPL Profiling_tools diff --git a/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies b/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies index 46b60927a44..b1e47ef3222 100644 --- a/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies +++ b/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Polynomial/test/Polynomial/test_polynomial.h b/Polynomial/test/Polynomial/test_polynomial.h index c1943d96ae9..1b5bb0868ac 100644 --- a/Polynomial/test/Polynomial/test_polynomial.h +++ b/Polynomial/test/Polynomial/test_polynomial.h @@ -307,7 +307,6 @@ void io() { std::ostringstream os; CGAL::IO::set_pretty_mode(os); os << CGAL::IO::oformat(POLY(NT(3))); - //std::cout < + +#include +#include +#include + +#include + +#ifndef DOXYGEN_RUNNING + +namespace CGAL::Properties::Experimental { + +template +class Property_array_base { +public: + + Property_array_base() = default; + + Property_array_base(const Property_array_base& rhs) = delete; + + virtual ~Property_array_base() = default; + + // Declare virtual functions here, for things which need to be done within the Property container + // todo: these should mostly be private, and made available using friend + + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) = 0; + + virtual std::shared_ptr> clone(const std::vector& active_indices) = 0; + + virtual void copy(const Property_array_base& other) = 0; + + // desactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 + virtual void move(Property_array_base&& other) = 0; +#endif + + virtual void append(const Property_array_base& other) = 0; + + virtual void reserve(std::size_t n) = 0; + + virtual void shrink_to_fit() = 0; + + virtual void swap(Index a, Index b) = 0; + + virtual void reset(Index i) = 0; + + virtual const std::type_info& type() const = 0; + + virtual void transfer_from(const Property_array_base& other_base, Index other_index, Index this_index) = 0; + +}; + +/*! + * \brief Indexed storage for arbitrary types + * + * todo: make this effectively private, prioritize the use of Property_array_handle + * + * @tparam T + */ +template +class Property_array : public Property_array_base { + + std::vector m_data; + const std::vector& m_active_indices; + T m_default_value; + +public: + + using value_type = T; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + + Property_array(const std::vector& active_indices, const T& default_value) : + m_data(), m_active_indices(active_indices), m_default_value(default_value) { + + m_data.reserve(active_indices.capacity()); + m_data.resize(active_indices.size(), m_default_value); + } + + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) override { + return std::make_shared>(active_indices, m_default_value); + } + + virtual std::shared_ptr> clone(const std::vector& active_indices) override { + auto new_array = std::make_shared>(active_indices, m_default_value); + new_array->m_data = m_data; + return new_array; + } + + virtual void copy(const Property_array_base& other_base) override { + auto& other = dynamic_cast&>(other_base); + m_data = other.m_data; + CGAL_precondition(m_active_indices.size() == m_data.size()); + } + +// deactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 + virtual void move(Property_array_base&& other_base) override { + auto&& other = static_cast&&>(other_base); + m_data = std::move(other.m_data); + CGAL_precondition(m_active_indices.size() == m_data.size()); + } +#endif + + virtual void append(const Property_array_base& other_base) override { + auto& other = dynamic_cast&>(other_base); + CGAL_precondition(m_data.size() + other.m_data.size() == m_active_indices.size()); + m_data.insert(m_data.end(), other.m_data.begin(), other.m_data.end()); + } + + virtual void reserve(std::size_t n) override { + CGAL_precondition(m_active_indices.size() == n); + m_data.resize(n, m_default_value); + }; + + virtual void shrink_to_fit() override { + m_data.shrink_to_fit(); + } + + virtual void swap(Index a, Index b) override { + // todo: maybe cast to index, instead of casting index to size? + CGAL_precondition(std::size_t(a) < m_data.size() && std::size_t(b) < m_data.size()); + std::iter_swap(m_data.begin() + a, m_data.begin() + b); + }; + + virtual void reset(Index i) override { + CGAL_precondition(std::size_t(i) < m_data.size()); + m_data[std::size_t(i)] = m_default_value; + }; + + virtual const std::type_info& type() const override { return typeid(T); }; + + virtual void transfer_from(const Property_array_base& other_base, + Index other_index, Index this_index) override { + + CGAL_precondition(other_base.type() == type()); + auto& other = dynamic_cast&>(other_base); + CGAL_precondition(std::size_t(other_index) < other.capacity() && std::size_t(this_index) < capacity()); + m_data[this_index] = other.m_data[other_index]; + } + +public: + + // todo: there's not really a good reason to use these, maybe they should be removed + + [[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + + [[nodiscard]] std::size_t capacity() const { return m_data.size(); } + + const_reference operator[](Index i) const { + CGAL_precondition(std::size_t(i) < m_data.size()); + return m_data[std::size_t(i)]; + } + + reference operator[](Index i) { + CGAL_precondition(std::size_t(i) < m_data.size()); + return m_data[std::size_t(i)]; + } + + iterator begin() { return m_data.begin(); } + + iterator end() { return m_data.end(); } + + const_iterator begin() const { return m_data.begin(); } + + const_iterator end() const { return m_data.end(); } + +public: + + bool operator==(const Property_array& other) const { + return &other == this; + } + + bool operator!=(const Property_array& other) const { return !operator==(other); } + +}; + +// todo: property maps/array handles should go in their own file + +// todo: add const/read-only handle +template +class Property_array_handle { + + std::reference_wrapper> m_array; + +public: + + // Necessary for use as a boost::property_type + using key_type = Index; + using value_type = T; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using category = boost::lvalue_property_map_tag; + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + + Property_array_handle(Property_array& array) : m_array(array) {} + + [[nodiscard]] std::size_t size() const { return m_array.get().size(); } + + [[nodiscard]] std::size_t capacity() const { return m_array.get().capacity(); } + + Property_array& array() const { return m_array.get(); } + + // todo: This might not be needed, if the other operator[] is made const + const_reference operator[](Index i) const { return m_array.get()[i]; } + + reference operator[](Index i) { return m_array.get()[i]; } + + // todo: maybe these can be const, in an lvalue property map? + iterator begin() { return m_array.get().begin(); } + + iterator end() { return m_array.get().end(); } + + const_iterator begin() const { return m_array.get().begin(); } + + const_iterator end() const { return m_array.get().end(); } + + bool operator==(const Property_array_handle& other) const { return other.m_array.get() == m_array.get(); } + + bool operator!=(const Property_array_handle& other) const { return !operator==(other); } + + inline friend reference get(Property_array_handle p, const Index& i) { return p[i]; } + + inline friend void put(Property_array_handle p, const Index& i, const T& v) { p[i] = v; } + +}; + +template +class Property_container { + + std::multimap>> m_properties; + std::vector m_active_indices{}; + +public: + + template + using Array = Property_array; + + Property_container() = default; + + Property_container(const Property_container& other) { + m_active_indices = other.m_active_indices; + + for (auto [name, array] : other.m_properties) { + // todo: this could probably be made faster using emplace_hint + m_properties.emplace( + name, + array->clone(m_active_indices) + ); + } + } + + Property_container(Property_container&& other) { *this = std::move(other); } + + // This is not exactly an assignment as existing unique properties are kept. + Property_container& operator=(const Property_container& other) { + m_active_indices = other.m_active_indices; + + for (auto [name, array] : other.m_properties) { + // search if property already exists + auto range = m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(*array) == typeid((*it->second))) + break; + } + + if (it != range.second) + it->second->copy(*array); + else + m_properties.emplace(name, array->clone(m_active_indices)); + } + + return *this; + } + + // This is not exactly an assignment as existing unique properties are kept. + Property_container& operator=(Property_container&& other) { + m_active_indices = std::move(other.m_active_indices); + + for (auto [name, array] : other.m_properties) { + // search if property already exists + auto range = m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(*array) == typeid((*it->second))) + break; + } + + if (it != range.second) + it->second->copy(std::move(*array)); + else + m_properties.emplace(name, array->clone(m_active_indices)); + } + + // The moved-from property map should retain all of its properties, but contain 0 elements + other.reserve(0); + return *this; + } + + template + std::pair>, bool> + get_or_add_property(const std::string& name, const T default_value = T()) { + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( + name, + std::make_shared>( + m_active_indices, + default_value + ) + ); + + return {{*dynamic_cast*>(it->second.get())}, true}; + } + + template + Property_array& add_property(const std::string& name, const T default_value = T()) { + // todo: I'm not settled on the naming, but it's really convenient to have a function like this + auto [array, created] = get_or_add_property(name, default_value); + CGAL_precondition(created); + return array.get(); + } + +/* + // todo: misleading name, maybe it could be add_same_properties? + void copy_properties(const Property_container& other) { + for (auto [name, other_array]: other.m_properties) { + // If this container doesn't have any property by this name, add it (with the same type as in other) + if (!property_exists(name)) + m_property_arrays.emplace(name, other_array->empty_clone(m_active_indices)); + } + }*/ + + template + const Property_array& get_property(const std::string& name) const { + return *(get_property_if_exists(name)); + } + + template + Property_array& get_property(const std::string& name) { + return *(get_property_if_exists(name)); + } + + template + std::optional>> get_property_if_exists(const std::string& name) const { + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return *typed_array_ptr; + } + + return {}; + } + + template + bool property_exists(const std::string& name) const { + auto range = m_properties.equal_range(name); + + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return true; + } + + return false; + } + + /*! + * Removes all properties with the name from the container. + * + * @param name + * @return number of removed properties. + */ + std::size_t remove_properties(const std::string& name) { return m_properties.erase(name); } + + template + bool remove_property(const Property_array& arrayToRemove) { + const Property_array_base* ref = dynamic_cast*>(&arrayToRemove); + for (auto it = m_properties.begin(); it != m_properties.end(); it++) { + auto const& [name, array] = *it; + if (array.get() == ref) { + m_properties.erase(it); + return true; + } + } + return false; + } + + void remove_all_properties_except(const std::vector& preserved_names) { + // todo: if this is used often, it should take a parameter pack instead of a vector + // A fold expression could then be used in place of std::find for better performance + for (auto it = m_properties.begin(); it != m_properties.end();) { + auto const& [name, array] = *it; + if (std::find(preserved_names.begin(), preserved_names.end(), name) == preserved_names.end()) + it = m_properties.erase(it); + else + it++; + } + } + + std::vector properties() const { + std::vector property_names{}; + for (auto const& [name, _]: m_properties) + property_names.emplace_back(name); + return property_names; + } + + std::size_t num_properties() const { return m_properties.size(); } + +/* Deactivated as there may be several Property_maps with different types but the same name. + const std::type_info& property_type(const std::string& name) const { + if (auto it = m_property_arrays.find(name); it != m_property_arrays.end()) + return it->second->type(); + else + return typeid(void); + }*/ + +public: + + void reserve(std::size_t n) { + m_active_indices.resize(n); + for (auto [name, array]: m_properties) + array->reserve(n); + } + + void resize(std::size_t n) { + reserve(n); + std::fill(m_active_indices.begin(), m_active_indices.end(), true); + } + + [[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + + [[nodiscard]] std::size_t capacity() const { return m_active_indices.size(); } + + Index emplace_back() { + + // Expand the storage and return the last element + reserve(capacity() + 1); + m_active_indices.back() = true; + auto first_new_index = Index(capacity() - 1); + reset(first_new_index); + return first_new_index; + } + + Index emplace() { + + // If there are empty slots, return the index of one of them and mark it as full + auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; }); + if (first_unused != m_active_indices.end()) { + *first_unused = true; + auto index = Index(std::distance(m_active_indices.begin(), first_unused)); + reset(index); + return index; + } + + return emplace_back(); + } + + Index emplace_group_back(std::size_t n) { + + // Expand the storage and return the start of the new region + reserve(capacity() + n); + for (auto it = m_active_indices.end() - n; it < m_active_indices.end(); ++it) + *it = true; + return Index(capacity() - n); + } + + Index emplace_group(std::size_t n) { + + auto search_start = m_active_indices.begin(); + while (search_start != m_active_indices.end()) { + + // Find the first unused cell + auto unused_begin = std::find_if( + search_start, m_active_indices.end(), + [](bool used) { return !used; } + ); + + auto unused_end = unused_begin; + + // Determine if the group fits + if (std::distance(unused_begin, m_active_indices.end()) >= static_cast::iterator>::difference_type>(n)) + unused_end = std::find_if( + unused_begin, (std::min)(unused_begin + n, m_active_indices.end()), + [](bool used) { return used; } + ); + + // If the discovered range was large enough + if (std::distance(unused_begin, unused_end) >= static_cast::iterator>::difference_type>(n)) { + + // Mark the indices as used, and reset the properties of each of them + // todo: it would be better to provide a function to set a range + for (auto it = unused_begin; it < unused_end; ++it) { + *it = true; + reset(Index(std::distance(m_active_indices.begin(), it))); + } + + // Return the first index of the range + return Index(std::distance(m_active_indices.begin(), unused_begin)); + } + + // If we didn't find a large enough region, continue our search after the end + search_start = unused_end; + } + + // If no empty regions were found, expand the storage + return emplace_group_back(n); + } + + void swap(Index a, Index b) { + for (auto [name, array]: m_properties) + array->swap(a, b); + } + + void reset(Index i) { + for (auto [name, array]: m_properties) + array->reset(i); + } + + void erase(Index i) { + m_active_indices[i] = false; + for (auto [name, array]: m_properties) + array->reset(i); + } + + bool is_erased(Index i) const { + return !m_active_indices[i]; + } + + // todo: I'd prefer to eliminate this, if possible + void mark_active(Index i) { + return m_active_indices[i] = true; + } + + void mark_inactive(Index i) { + return m_active_indices[i] = false; + } + + std::vector active_list() const { + std::vector indices; + for (std::size_t i = 0; i < m_active_indices.size(); ++i) + if (m_active_indices[i]) indices.emplace_back(i); + return indices; + } + + std::vector inactive_list() const { + std::vector indices; + for (std::size_t i = 0; i < m_active_indices.size(); ++i) + if (!m_active_indices[i]) indices.emplace_back(i); + return indices; + } + + void shrink_to_fit() { + for (auto [name, array]: m_properties) + array->shrink_to_fit(); + } + + /*! + * Adds the elements of the other container to this container for each property which is present in this container. + * + * Gaps in both containers are preserved, and all elements of the other container are guaranteed + * to appear after the elements of this container. + * Properties in this container which don't appear in the other container are extended with default values. + * Properties in the other container which don't appear in this one are not included. + * todo: merge() would be useful as well, but could break contiguous regions in the other container + * + * @param other + */ + void append(const Property_container& other) { + + m_active_indices.insert(m_active_indices.end(), other.m_active_indices.begin(), other.m_active_indices.end()); + for (auto [name, array]: m_properties) { + + auto range = other.m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(array.get()) == typeid((it->second.get()))) + break; + } + + if (it != range.second) + array->append(*it->second.get()); + else + array->reserve(m_active_indices.size()); + } + } + +/* + // todo: maybe should be renamed to transfer_from, but I'd rather remove this functionality entirely + void transfer(const Property_container& other, Index other_index, Index this_index) { + CGAL_precondition(other.m_property_arrays.size() == m_property_arrays.size()); + for (auto [name, array]: m_property_arrays) { + auto other_array = other.m_property_arrays.at(name); + array->transfer_from(*other_array, other_index, this_index); + } + }*/ + + // todo: maybe a compress() method? +}; + +} + +#endif // DOXYGEN_RUNNING + +#endif //CGAL_PROPERTY_CONTAINTER_H diff --git a/Property_map/test/Property_map/CMakeLists.txt b/Property_map/test/Property_map/CMakeLists.txt index b0480bfa209..9f920ea9611 100644 --- a/Property_map/test/Property_map/CMakeLists.txt +++ b/Property_map/test/Property_map/CMakeLists.txt @@ -8,6 +8,7 @@ create_single_source_cgal_program("test_property_map.cpp") create_single_source_cgal_program("dynamic_property_map.cpp") create_single_source_cgal_program("dynamic_properties_test.cpp") create_single_source_cgal_program("kernel_converter_properties_test.cpp") +create_single_source_cgal_program("test_Property_container.cpp") find_package(OpenMesh QUIET) if(OpenMesh_FOUND) diff --git a/Property_map/test/Property_map/test_Property_container.cpp b/Property_map/test/Property_map/test_Property_container.cpp new file mode 100644 index 00000000000..1efe25f4001 --- /dev/null +++ b/Property_map/test/Property_map/test_Property_container.cpp @@ -0,0 +1,261 @@ + +#include +#include + +using namespace CGAL::Properties::Experimental; + +void test_property_creation() { + + Property_container properties; + + // Should return an integer array which didn't previously exist + auto [integers, created] = properties.get_or_add_property("integer", 5); + static_assert(std::is_same_v>>); + assert(created); + assert(properties.num_properties() == 1); + + auto [floats, _] = properties.get_or_add_property("float"); + static_assert(std::is_same_v>>); + assert(properties.num_properties() == 2); + + // get() should retreive the same arrays + assert(integers.get() == properties.get_property("integer")); + assert(floats.get() == properties.get_property("float")); + + // remove() should delete a property array & return if it existed + assert(!properties.remove_properties("not-a-real-property")); + auto removed = properties.remove_property(integers); + assert(removed); + assert(properties.num_properties() == 1); + + // Add a new property + auto [bools, bools_created] = properties.get_or_add_property("bools", false); + static_assert(std::is_same_v>>); + Property_array& b = bools.get(); + CGAL_USE(b); +} + +void test_element_access() { + + Property_container properties; + + auto& integers = properties.add_property("integers", 5); + + // Reserve space for 100 elements + properties.reserve(100); + assert(properties.capacity() == 100); + assert(properties.size() == 0); + + // Newly emplaced elements should go at the front + assert(properties.emplace() == 0); + assert(properties.emplace() == 1); + assert(properties.emplace() == 2); + assert(properties.size() == 3); + + // Make sure that the new elements are equal to the default value + assert(integers[0] == 5); + assert(integers[1] == 5); + assert(integers[2] == 5); + + // Add a new property + auto& floats = properties.add_property("floats", 6.0f); + + // The new property array should already be of the right size + assert(floats.capacity() == 100); + assert(properties.size() == 3); + + // Pre-existing elements should contain the default value + assert(floats[0] == 6.0f); + assert(floats[1] == 6.0f); + assert(floats[2] == 6.0f); + + // Update values for a few elements + floats[0] = 1.0f; + floats[1] = 2.0f; + floats[2] = 3.0f; + integers[2] = -2; + assert(floats[0] == 1.0f); + assert(floats[1] == 2.0f); + assert(floats[2] == 3.0f); + assert(integers[2] == -2); + + // Reset an element, and all of its properties should revert to the defaults + properties.reset(2); + assert(floats[2] == 6.0f); + assert(integers[2] == 5); + + // Erase an element, and the size should be reduced + properties.erase(1); + assert(properties.size() == 2); + assert(properties.capacity() == 100); + assert(properties.active_list().size() == 2); + assert(properties.inactive_list().size() == 98); + + // A newly emplaced element should take the empty slot + assert(properties.emplace() == 1); + assert(properties.size() == 3); + // todo: should the new element have default properties? + assert(properties.emplace() == 3); + assert(properties.size() == 4); + + // Swapping a pair of elements swaps all of their properties + properties.swap(0, 3); + assert(integers[0] == 5); + assert(floats[0] == 6.0f); + assert(integers[3] == 5); + assert(floats[3] == 1.0f); + +} + +void test_emplace_group() { + + Property_container properties; + + auto& a = properties.add_property("a", 5); + CGAL_USE(a); + // Insert a group of 100 elements + properties.emplace_group(100); + assert(properties.size() == 100); + + // Eliminate a few regions + properties.erase(3); + assert(properties.is_erased(3)); + assert(properties.size() == 99); + for (int i = 20; i < 25; ++i) + properties.erase(i); + assert(properties.is_erased(23)); + assert(properties.size() == 94); + for (int i = 50; i < 80; ++i) + properties.erase(i); + assert(properties.is_erased(53)); + assert(properties.size() == 64); + + // A group of size 4 should only fit in the empty region fo size 5 + assert(properties.emplace_group(4) == 20); + assert(properties.size() == 68); + assert(properties.capacity() == 100); + + // A group of size 16 should only fit in the empty region fo size 30 + assert(properties.emplace_group(16) == 50); + assert(properties.size() == 84); + assert(properties.capacity() == 100); + + // Another group of size 16 should require the storage to expand, because the largest empty region is mostly full now + assert(properties.emplace_group(16) == 100); + assert(properties.size() == 100); + assert(properties.capacity() == 116); + +} + +void test_append() { + + // Create a pair of property containers with similar contents + Property_container properties_a, properties_b; + properties_a.add_property("ints", 1); + properties_b.add_property("ints", 2); + properties_a.add_property("floats", 3.0f); + properties_b.add_property("floats", 4.0f); + + // One container will also contain an extra property + properties_a.add_property("bools", true); + + // Add some values to both property sets + properties_a.emplace_group(10); + properties_b.emplace_group(5); + assert(properties_a.size() == 10); + assert(properties_b.size() == 5); + + // Add the second group to the end of the first + properties_a.append(properties_b); + assert(properties_a.size() == 15); + assert(properties_b.size() == 5); + + // Initialized values from the second group should appear after those of the first + assert(properties_a.get_property("ints")[5] == 1); + assert(properties_a.get_property("ints")[12] == 2); + assert(properties_a.get_property("floats")[5] == 3.0f); + assert(properties_a.get_property("floats")[12] == 4.0f); + + // Additional properties in the first group should have expanded too, and been filled with defaults + // note: the property array must be const, because non const operator[] doesn't work for vector! + assert(std::as_const(properties_a).get_property("bools")[12] == true); +} + +void test_constructors() { + + // Default constructor should have no properties + Property_container a{}; + assert(a.num_properties() == 0); + + // Copy constructor should duplicate all properties + auto& a_ints = a.add_property("ints", 0); + auto& a_floats = a.add_property("floats", 0.0f); + a.emplace_group(10); + a.get_property("ints")[3] = 1; + a.get_property("floats")[3] = 1.0f; + Property_container b{a}; + assert(b.num_properties() == a.num_properties() && b.num_properties() == 2); + assert(b.get_property("ints")[3] == a.get_property("ints")[3] && b.get_property("ints")[3] == 1); + assert(b.get_property("floats")[3] == a.get_property("floats")[3] && b.get_property("floats")[3] == 1.0f); + + // Copy-assignment operator should do effectively the same thing as the copy constructor + Property_container c; + c = a; + assert(c.num_properties() == a.num_properties() && c.num_properties() == 2); + assert(c.get_property("ints")[3] == a.get_property("ints")[3] && c.get_property("ints")[3] == 1); + assert(c.get_property("floats")[3] == a.get_property("floats")[3] && c.get_property("floats")[3] == 1.0f); + + // Copied property containers should not be synced with the original + a.add_property("more_ints", 2); + assert(a.num_properties() == 3); + assert(b.num_properties() == 2); + assert(c.num_properties() == 2); + a.get_property("ints")[4] = 2; + assert(a.get_property("ints")[4] == 2); + assert(b.get_property("ints")[4] == 0); + assert(c.get_property("ints")[4] == 0); + + // Copy assignment should not invalidate previously obtained array references, + // but it should update their values + auto &b_ints = b.get_property("ints"); + auto &b_floats = b.get_property("floats"); + assert(b_ints[4] == 0); + b = a; + assert(b.num_properties() == 3); + assert(b_ints[4] == 2); + + // Move assignment shouldn't invalidate references either + Property_container d{c}; + auto &d_ints = d.get_property("ints"); + assert(d_ints[4] == 0); + d = std::move(a); + assert(d.num_properties() == 3); + assert(d_ints[4] == 2); + + // Moved-from should be empty + // All properties are preserved, though + assert(a.num_properties() == 3); + assert(a.size() == 0); + assert(a_ints.capacity() == 0); + assert(a_floats.capacity() == 0); + + // Move constructor should behave like move assignment + Property_container e{std::move(b)}; + assert(e.num_properties() == 3); + assert(b.num_properties() == 3); + assert(b.size() == 0); + assert(b_ints.capacity() == 0); + assert(b_floats.capacity() == 0); +} + + +int main() { + + test_property_creation(); + test_element_access(); + test_emplace_group(); + test_append(); + test_constructors(); + + return 0; +} diff --git a/QP_solver/test/QP_solver/master_mps_to_derivatives.cpp b/QP_solver/test/QP_solver/master_mps_to_derivatives.cpp index b0af787b72e..ad6fd7abc89 100644 --- a/QP_solver/test/QP_solver/master_mps_to_derivatives.cpp +++ b/QP_solver/test/QP_solver/master_mps_to_derivatives.cpp @@ -32,7 +32,9 @@ #include #include +#ifdef CGAL_USE_BOOST_MP #include +#endif //Currently already included in boost_mp.h //#ifdef CGAL_USE_BOOST_MP //# include diff --git a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h index ef7c6164f2d..c58110c9b32 100644 --- a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h +++ b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h @@ -1422,28 +1422,32 @@ public: } } - void clear_cells_and_facets_from_c3t3() { - for (typename Tr::Finite_cells_iterator - cit = this->triangulation().finite_cells_begin(), - end = this->triangulation().finite_cells_end(); - cit != end; ++cit) + void clear_cells_and_facets_from_c3t3() + { + //clear cells + for (typename Tr::All_cells_iterator cit = this->triangulation().all_cells_begin(); + cit != this->triangulation().all_cells_end(); + ++cit) { set_subdomain_index(cit, Subdomain_index()); } this->number_of_cells_ = 0; - for (typename Tr::Finite_facets_iterator - fit = this->triangulation().finite_facets_begin(), - end = this->triangulation().finite_facets_end(); - fit != end; ++fit) + + //clear facets + for (typename Tr::All_facets_iterator fit = this->triangulation().all_facets_begin(); + fit != this->triangulation().all_facets_end(); + ++fit) { - Facet facet = *fit; + const auto& facet = *fit; set_surface_patch_index(facet.first, facet.second, Surface_patch_index()); if (this->triangulation().dimension() > 2) { - Facet mirror = tr_.mirror_facet(facet); + const Facet& mirror = tr_.mirror_facet(facet); set_surface_patch_index(mirror.first, mirror.second, Surface_patch_index()); } } this->number_of_facets_ = 0; + + //clear manifold info clear_manifold_info(); } diff --git a/SMDS_3/package_info/SMDS_3/dependencies b/SMDS_3/package_info/SMDS_3/dependencies index bebf22165bb..749f4764b55 100644 --- a/SMDS_3/package_info/SMDS_3/dependencies +++ b/SMDS_3/package_info/SMDS_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Scale_space_reconstruction_3/package_info/Scale_space_reconstruction_3/dependencies b/Scale_space_reconstruction_3/package_info/Scale_space_reconstruction_3/dependencies index 114d4ba5acd..4582cf68fe6 100644 --- a/Scale_space_reconstruction_3/package_info/Scale_space_reconstruction_3/dependencies +++ b/Scale_space_reconstruction_3/package_info/Scale_space_reconstruction_3/dependencies @@ -3,6 +3,7 @@ Algebraic_foundations Alpha_shapes_3 Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Scripts/developer_scripts/cgal_create_package_dir.py b/Scripts/developer_scripts/cgal_create_package_dir.py index 1847fada3bc..90900b1304d 100755 --- a/Scripts/developer_scripts/cgal_create_package_dir.py +++ b/Scripts/developer_scripts/cgal_create_package_dir.py @@ -34,23 +34,20 @@ INPUT = ${CMAKE_SOURCE_DIR}/PACKAGENAME/doc/PACKAGENAME/ \ descrstring = \ r"""// PRETTY PACKAGE NAME should equal the project title in Doxyfile.in -/// \defgroup PkgPACKAGE PRETTY PACKAGE NAME Reference +/// \defgroup PkgPACKAGERef PRETTY PACKAGE NAME Reference /// \defgroup PkgPACKAGEConcepts Concepts -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGEAlgorithmClasses Algorithm Classes -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGETraitsClasses Traits Classes -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGEMiscellaneous Miscellaneous -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /*! -\addtogroup PkgPACKAGE -\todo check generated documentation - \cgalPkgDescriptionBegin{PACKAGE NAME,PkgPACKAGE} \cgalPkgPicture{pkg-small.png} @@ -58,7 +55,7 @@ r"""// PRETTY PACKAGE NAME should equal the project title in Doxyfile.in \cgalPkgAuthors{PACKAGE AUTHOR} \cgalPkgDesc{PACKAGE DESCRIPTION. The package provides ... } -\cgalPkgManuals{Chapter_PACKAGE_NAME,PkgPACKAGE} +\cgalPkgManuals{Chapter_PACKAGE_NAME,PkgPACKAGERef} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin diff --git a/Segment_Delaunay_graph_2/package_info/Segment_Delaunay_graph_2/dependencies b/Segment_Delaunay_graph_2/package_info/Segment_Delaunay_graph_2/dependencies index c3083729fef..62981bc0f23 100644 --- a/Segment_Delaunay_graph_2/package_info/Segment_Delaunay_graph_2/dependencies +++ b/Segment_Delaunay_graph_2/package_info/Segment_Delaunay_graph_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Apollonius_graph_2 Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Segment_Delaunay_graph_Linf_2/package_info/Segment_Delaunay_graph_Linf_2/dependencies b/Segment_Delaunay_graph_Linf_2/package_info/Segment_Delaunay_graph_Linf_2/dependencies index 783d3ee4cb2..d680be9c38d 100644 --- a/Segment_Delaunay_graph_Linf_2/package_info/Segment_Delaunay_graph_Linf_2/dependencies +++ b/Segment_Delaunay_graph_Linf_2/package_info/Segment_Delaunay_graph_Linf_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Apollonius_graph_2 Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h index 88089e89e01..6f0ab44c7ac 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h @@ -253,12 +253,12 @@ public: /*! Retrieves the point property map. */ - const Point_map &point_map() const { return m_point_pmap; } + const Point_map &point_map() const { return *m_point_pmap; } /*! Retrieves the normal property map. */ - const Normal_map &normal() const { return m_normal_pmap; } + const Normal_map &normal() const { return *m_normal_pmap; } Input_iterator input_iterator_first() const { return m_input_iterator_first; @@ -361,13 +361,13 @@ public: m_direct_octrees[s] = new Direct_octree( m_traits, last + 1, last + subsetSize + 1, - m_point_pmap, + *m_point_pmap, remainingPoints - subsetSize); } else m_direct_octrees[0] = new Direct_octree( m_traits, m_input_iterator_first, m_input_iterator_first + (subsetSize), - m_point_pmap, + *m_point_pmap, 0); m_available_octree_sizes[s] = subsetSize; @@ -378,7 +378,7 @@ public: m_global_octree = new Indexed_octree( m_traits, m_input_iterator_first, m_input_iterator_beyond, - m_point_pmap + *m_point_pmap ); m_global_octree->refine(m_options.cluster_epsilon); @@ -496,7 +496,7 @@ public: } // Use bounding box diagonal as reference for default values - Bbox_3 bbox = m_global_octree->boundingBox(); + auto bbox = m_global_octree->boundingBox(); FT bbox_diagonal = (FT) CGAL::sqrt( (bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) @@ -574,14 +574,14 @@ public: static_cast(m_num_available_points)); while (m_shape_index[first_sample] != -1); - done = drawSamplesFromCellContainingPoint - (m_global_octree, - get(m_point_pmap, - *(m_input_iterator_first + first_sample)), - select_random_octree_level(), - indices, - m_shape_index, - m_required_samples); + done = drawSamplesFromCellContainingPoint( + m_global_octree, + get(*m_point_pmap, *(m_input_iterator_first + first_sample)), + select_random_octree_level(), + indices, + m_shape_index, + m_required_samples + ); if (callback && !callback(num_invalid / double(m_num_total_points))) { clear(num_invalid, candidates); @@ -605,8 +605,8 @@ public: p->compute(indices, m_input_iterator_first, m_traits, - m_point_pmap, - m_normal_pmap, + *m_point_pmap, + *m_normal_pmap, m_options.epsilon, m_options.normal_threshold); @@ -1086,7 +1086,7 @@ private: Cell cell = stack.top(); stack.pop(); - FT width = octree->width() / (1 << (cell.depth())); + FT width = octree->width() / (1 << (octree->depth(cell))); FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon; @@ -1097,10 +1097,10 @@ private: // differ between full or partial overlap? // if full overlap further traversal of this branch is not necessary - if (cell.is_leaf()) { + if (octree->is_leaf(cell)) { std::vector indices; - indices.reserve(cell.size()); - for (std::size_t i = 0; i < cell.size(); i++) { + indices.reserve(octree->points(cell).size()); + for (std::size_t i = 0; i < octree->points(cell).size(); i++) { if (shapeIndex[octree->index(cell, i)] == -1) { indices.push_back(octree->index(cell, i)); } @@ -1111,10 +1111,10 @@ private: indices); } else { - if (!cell.is_leaf()) { + if (!octree->is_leaf(cell)) { for (std::size_t i = 0; i < 8; i++) { - if (!cell[i].empty()) - stack.push(cell[i]); + if (octree->points(octree->child(cell, i)).size() != 0) + stack.push(octree->child(cell, i)); } } } @@ -1129,30 +1129,11 @@ private: const typename Octree::Node node_containing_point(const Octree *octree, const Point &p, std::size_t level) { // Find the node containing the point - typename Octree::Node cur = octree->root(); - while (!cur.is_null() && cur.depth() < level) { + typename Octree::Node n = octree->locate(p); + while (octree->depth(n) > level) + n = octree->parent(n); - // If cur is a leaf node, its child is null - if (cur.is_leaf()) - return typename Octree::Node(); - - // If that child is empty, return null - if (cur.empty()) - return typename Octree::Node(); - - // Determine the coordinate of the child - Point center = octree->barycenter(cur); - std::bitset<3> coordinate; - coordinate[0] = center.x() <= p.x(); - coordinate[1] = center.y() <= p.y(); - coordinate[2] = center.z() <= p.z(); - - // Otherwise, return the correct child of cur - cur = cur[coordinate.to_ulong()]; - - } - - return cur; + return n; } template @@ -1167,13 +1148,9 @@ private: const Cell cur = node_containing_point(octree, p, level); - // Stop if the node we need doesn't exist - if (cur.is_null()) - return false; - // Count point indices that map to -1 in the shape index std::size_t enough = 0; - for (auto j : cur) { + for (const auto j : octree->points(cur)) { if (shapeIndex[j] == -1) enough++; if (enough >= requiredSamples) @@ -1186,7 +1163,7 @@ private: do { std::size_t p = CGAL::get_default_random(). - uniform_int(0, cur.size() - 1); + uniform_int(0, octree->points(cur).size() - 1); std::size_t j = octree->index(cur, p); if (shapeIndex[j] == -1) @@ -1225,8 +1202,8 @@ private: // iterators of input data bool m_valid_iterators; Input_iterator m_input_iterator_first, m_input_iterator_beyond; - Point_map m_point_pmap; - Normal_map m_normal_pmap; + std::optional m_point_pmap; + std::optional m_normal_pmap; }; diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h index 37df7a5bcc0..aa14af527c1 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h @@ -39,6 +39,8 @@ namespace CGAL { class InputPointMap, class InputNormalMap> struct Efficient_RANSAC_traits { + /// + typedef Gt GeomTraits; /// typedef typename Gt::FT FT; /// diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index 8e3e261e990..acb782b70a6 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -32,7 +32,7 @@ namespace CGAL { namespace Shape_detection { // Forward declaration needed for automatic traits detection without -// including the deprecated header itself… +// including the deprecated header itself template struct Shape_detection_traits; @@ -56,8 +56,9 @@ class RANSAC_octree { typedef std::vector Input_range; typedef Random_index_access_property_map Indexed_point_map; - typedef CGAL::Octree::type, - Input_range, Indexed_point_map> Octree; + typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, false, 3> OTraits; + + typedef CGAL::Orthtree Octree; Traits m_traits; Input_range m_input_range; @@ -70,7 +71,8 @@ class RANSAC_octree { public: - typedef typename Octree::Node Node; + typedef typename Octree::Node_index Node; + typedef typename OTraits::Node_data Node_data; RANSAC_octree(const Traits &traits, Input_iterator begin, @@ -81,18 +83,26 @@ public: m_input_range(boost::counting_iterator(0), boost::counting_iterator(end - begin)), m_index_map(begin, point_map), - m_octree(m_input_range, m_index_map, 1.0), + m_octree(OTraits(m_input_range, m_index_map)), m_bBox (bbox_3(make_transform_iterator_from_property_map(begin, point_map), make_transform_iterator_from_property_map(end, point_map))), m_offset(offset) {} std::size_t index (Node node, std::size_t i) const { - return m_offset + *(node.begin() + i); + return m_offset + *(m_octree.data(node).begin() + i); + } + + std::size_t depth(const Node& node) const { + return m_octree.depth(node); + } + + bool is_leaf(const Node& node) const { + return m_octree.is_leaf(node); } std::size_t size() const { - return m_octree.root().size(); + return m_input_range.size(); } std::size_t maxLevel() const { @@ -127,17 +137,27 @@ public: return m_width; } + Node child(const Node& node, std::size_t i) const { + return m_octree.child(node, i); + } + + Node parent(const Node& node) const { + return m_octree.parent(node); + } + Node locate(const typename Traits::Point_3 &p) const { return m_octree.locate(p); } Node root() const { return m_octree.root(); } + Node_data points(const Node& n) const { return m_octree.data(n); } + typename Traits::Point_3 barycenter(const Node &node) const { return m_octree.barycenter(node); } - Bbox_3 boundingBox() const { + typename Traits::GeomTraits::Iso_cuboid_3 boundingBox() const { return m_octree.bbox(m_octree.root()); } }; diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h index 8a23794aa42..99fef90cc24 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h @@ -374,7 +374,7 @@ namespace CGAL { */ typename boost::property_traits< typename Traits::Point_map >::reference point(std::size_t i) const { - return get(this->m_point_pmap, *(this->m_first + i)); + return get(this->m_point_pmap.value(), *(this->m_first + i)); } /*! @@ -382,7 +382,7 @@ namespace CGAL { */ typename boost::property_traits< typename Traits::Normal_map >::reference normal(std::size_t i) const { - return get(this->m_normal_pmap, *(this->m_first + i)); + return get(this->m_normal_pmap.value(), *(this->m_first + i)); } /*! @@ -694,8 +694,8 @@ namespace CGAL { Input_iterator m_first; Traits m_traits; - Point_map m_point_pmap; - Normal_map m_normal_pmap; + std::optional m_point_pmap; + std::optional m_normal_pmap; /// \endcond }; } diff --git a/Shape_detection/package_info/Shape_detection/dependencies b/Shape_detection/package_info/Shape_detection/dependencies index 63e6337e4fb..23bbe655dfa 100644 --- a/Shape_detection/package_info/Shape_detection/dependencies +++ b/Shape_detection/package_info/Shape_detection/dependencies @@ -1,11 +1,13 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel +Hash_map Homogeneous_kernel Installation Intersections_2 @@ -16,6 +18,7 @@ Kernel_d Modular_arithmetic Number_types Orthtree +Point_set_2 Principal_component_analysis Principal_component_analysis_LGPL Profiling_tools @@ -25,4 +28,7 @@ STL_Extension Shape_detection Solver_interface Spatial_searching +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 \ No newline at end of file diff --git a/Shape_regularization/package_info/Shape_regularization/dependencies b/Shape_regularization/package_info/Shape_regularization/dependencies index 5cf917f32ed..b07b869d715 100644 --- a/Shape_regularization/package_info/Shape_regularization/dependencies +++ b/Shape_regularization/package_info/Shape_regularization/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations BGL +Cartesian_kernel Circulator Distance_2 Distance_3 @@ -14,6 +15,7 @@ Kernel_d Modular_arithmetic Number_types Orthtree +Point_set_2 Principal_component_analysis_LGPL Profiling_tools Property_map diff --git a/Skin_surface_3/package_info/Skin_surface_3/dependencies b/Skin_surface_3/package_info/Skin_surface_3/dependencies index fb635c6334c..e04beb03945 100644 --- a/Skin_surface_3/package_info/Skin_surface_3/dependencies +++ b/Skin_surface_3/package_info/Skin_surface_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Snap_rounding_2/package_info/Snap_rounding_2/dependencies b/Snap_rounding_2/package_info/Snap_rounding_2/dependencies index 2d53f573e0d..9b654c5c0cc 100644 --- a/Snap_rounding_2/package_info/Snap_rounding_2/dependencies +++ b/Snap_rounding_2/package_info/Snap_rounding_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Stream_support/examples/Stream_support/Polygon_WKT.cpp b/Stream_support/examples/Stream_support/Polygon_WKT.cpp index f417e4a48ee..9bf7a37cf4b 100644 --- a/Stream_support/examples/Stream_support/Polygon_WKT.cpp +++ b/Stream_support/examples/Stream_support/Polygon_WKT.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -11,7 +12,7 @@ int main(int argc, char* argv[]) { typedef CGAL::Polygon_with_holes_2 Polygon; typedef std::deque MultiPolygon; - + typedef CGAL::Multipolygon_with_holes_2 Multipolygon_with_holes_2; { std::ifstream is((argc>1)?argv[1]:"data/polygons.wkt"); std::list polys; @@ -33,5 +34,14 @@ int main(int argc, char* argv[]) for(Polygon p : mp) std::cout<2)?argv[2]:"data/multipolygon.wkt"); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(is, mp); + std::cout << mp << std::endl; + CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + std::cout << std::endl; + } return 0; } diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index 266939338fd..3a3eea129d6 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,7 @@ void pop_back_if_equal_to_front(CGAL::Polygon_with_holes_2& pwh) //! //! \tparam Point can be a `CGAL::Point_2` or `CGAL::Point_3`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` //! \see `CGAL::Point_3` @@ -112,10 +113,10 @@ bool read_point_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes the same point type, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` //! \see `CGAL::Point_3` @@ -159,10 +160,10 @@ bool read_multi_point_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `CGAL::Point_2`. //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -203,10 +204,10 @@ bool read_linestring_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `Linestring`, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -258,7 +259,7 @@ bool read_multi_linestring_WKT(std::istream& in, //! //! \tparam Polygon is a `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -302,10 +303,10 @@ bool read_polygon_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `CGAL::General_polygon_with_holes_2`, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -345,13 +346,22 @@ bool read_multi_polygon_WKT(std::istream& in, return !in.fail(); } + +template +bool read_multi_polygon_WKT(std::istream& in, + Multipolygon_with_holes_2& mp) +{ + return read_multi_polygon_WKT(in, mp.polygons_with_holes()); +} + + //! \ingroup PkgStreamSupportIoFuncsWKT //! //! \brief writes `point` into a WKT stream. //! //! \tparam Point is a `CGAL::Point_2` //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -371,7 +381,7 @@ std::ostream& write_point_WKT(std::ostream& out, //! //! \tparam Polygon must be a `CGAL::General_polygon_with_holes_2` //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -391,7 +401,7 @@ std::ostream& write_polygon_WKT(std::ostream& out, //! //! \tparam LineString must be a `RandomAccessRange` of `CGAL::Point_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::Point_2` template @@ -412,7 +422,7 @@ std::ostream& write_linestring_WKT(std::ostream& out, //! //! \tparam MultiPoint must be a `RandomAccessRange` of `CGAL::Point_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::Point_2` template @@ -433,7 +443,7 @@ std::ostream& write_multi_point_WKT(std::ostream& out, //! //! \tparam MultiPolygon must be a `RandomAccessRange` of `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::General_polygon_with_holes_2` template @@ -448,13 +458,20 @@ std::ostream& write_multi_polygon_WKT(std::ostream& out, return out; } +template +std::ostream& write_multi_polygon_WKT(std::ostream& out, + Multipolygon_with_holes_2& mp) +{ + return write_multi_polygon_WKT(out, mp.polygons_with_holes()); +} + //! \ingroup PkgStreamSupportIoFuncsWKT //! //! \brief writes the content of `mls` into a WKT stream. //! //! \tparam MultiLineString must be a `RandomAccessRange` of `LineString`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::IO::write_linestring_WKT()` template @@ -489,7 +506,7 @@ std::ostream& write_multi_linestring_WKT(std::ostream& out, //! \tparam MultiLineString must be a `RandomAccessRange` of `Linestring`. //! \tparam MultiPolygon must be a model of `RandomAccessRange` of `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::IO::read_linestring_WKT()` template + +#include + +namespace CGAL +{ +namespace Tetrahedral_remeshing +{ + template + struct Complex_cells_selector + { + using key_type = typename Tr::Cell_handle; + using value_type = bool; + using reference = bool; + using category = boost::read_write_property_map_tag; + + friend value_type get(const Complex_cells_selector&, const key_type& c) + { + using SI = typename Tr::Cell::Subdomain_index; + return c->subdomain_index() != SI(); + } + friend void put(Complex_cells_selector&, const key_type&, const value_type) + {} //nothing to do : subdomain indices are updated in remeshing}; + }; + + +} // end namespace Tetrahedral_remeshing +} // end namespace CGAL + +#endif // CGAL_INTERNAL_C3T3_CELLS_SELECTOR_H diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h index 9bb87bf88ce..a3fe4c35a18 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h @@ -16,11 +16,15 @@ #include #include +#include namespace CGAL { namespace Tetrahedral_remeshing { +template +std::size_t peel_slivers(C3T3& c3t3, + const typename C3T3::Triangulation::Geom_traits::FT& sliver_angle); template std::size_t peel_slivers(C3T3& c3t3, @@ -41,7 +45,8 @@ std::size_t peel_slivers(C3T3& c3t3, #endif for (Cell_handle cit : c3t3.cells_in_complex()) { - if(!get(cell_selector, cit)) + const bool selected = get(cell_selector, cit); + if(!selected) continue; std::array facets_on_surface; @@ -112,6 +117,16 @@ std::size_t peel_slivers(C3T3& c3t3, return nb_slivers_peel; } +template +std::size_t peel_slivers(C3T3 & c3t3, + const typename C3T3::Triangulation::Geom_traits::FT & sliver_angle) +{ + using Tr = typename C3T3::Triangulation; + return peel_slivers(c3t3, sliver_angle, + CGAL::Tetrahedral_remeshing::Complex_cells_selector()); +} + + } // end namespace Tetrahedral_remeshing } // end namespace CGAL diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index a814a734926..ba33e7503c2 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -19,6 +19,8 @@ #include #include +#include + #include #include diff --git a/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies b/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies index fd7f85021f9..ae3cfac7514 100644 --- a/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies +++ b/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies @@ -2,6 +2,7 @@ AABB_tree Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Triangulation/doc/Triangulation/Triangulation.txt b/Triangulation/doc/Triangulation/Triangulation.txt index b239a2c05b0..2e7c8e538bd 100644 --- a/Triangulation/doc/Triangulation/Triangulation.txt +++ b/Triangulation/doc/Triangulation/Triangulation.txt @@ -405,7 +405,7 @@ of an infinite full cell is the empty half-space bounded by the affine hull of the finite facet of this cell. The set of full cells that are in conflict with `p` form the conflict zone. The full cells in the conflict zone are removed, leaving a hole that contains `p`. That -hole is ``star shaped'' around `p` and thus is re-triangulated using +hole is “star shaped” around `p` and thus is re-triangulated using `p` as a center vertex. Delaunay triangulations support insertion of points, removal of vertices, diff --git a/Triangulation_2/package_info/Triangulation_2/dependencies b/Triangulation_2/package_info/Triangulation_2/dependencies index c8d90b721ca..e5682c45523 100644 --- a/Triangulation_2/package_info/Triangulation_2/dependencies +++ b/Triangulation_2/package_info/Triangulation_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Triangulation_3/package_info/Triangulation_3/dependencies b/Triangulation_3/package_info/Triangulation_3/dependencies index a68982ddaa5..15e6860497e 100644 --- a/Triangulation_3/package_info/Triangulation_3/dependencies +++ b/Triangulation_3/package_info/Triangulation_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Triangulation_on_sphere_2/package_info/Triangulation_on_sphere_2/dependencies b/Triangulation_on_sphere_2/package_info/Triangulation_on_sphere_2/dependencies index 5b77a741e4d..bf3594adb9b 100644 --- a/Triangulation_on_sphere_2/package_info/Triangulation_on_sphere_2/dependencies +++ b/Triangulation_on_sphere_2/package_info/Triangulation_on_sphere_2/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Algebraic_kernel_for_spheres Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circular_kernel_3 Circulator diff --git a/Visibility_2/package_info/Visibility_2/dependencies b/Visibility_2/package_info/Visibility_2/dependencies index e8367c22417..2199ba4f76d 100644 --- a/Visibility_2/package_info/Visibility_2/dependencies +++ b/Visibility_2/package_info/Visibility_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 +CGAL_Core Cartesian_kernel Circulator Distance_2