diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index 1ddd0a77644..f76a99b18dd 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -12,15 +12,15 @@ jobs: steps: - uses: actions/checkout@v4 - name: REUSE version - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --version - name: REUSE lint - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --include-submodules lint - name: REUSE SPDX SBOM - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: spdx - name: install dependencies @@ -30,6 +30,6 @@ jobs: mkdir -p ./release cmake -DDESTINATION=./release -DCGAL_VERSION=9.9 -P ./Scripts/developer_scripts/cgal_create_release_with_cmake.cmake - name: REUSE lint release tarball - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --root ./release/CGAL-9.9 --include-submodules lint 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..e48c91b7e6a 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 @@ -22,7 +23,6 @@ Number_types Polyhedron Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Stream_support 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/Descartes.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h index 0adc7652b04..bc2287665f0 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h @@ -68,9 +68,9 @@ private: Polynomial poly_; int number_of_real_roots_; - IT* numerator; - IT* denominator_exponent; - bool* is_exact; + std::vector numerator; + std::vector denominator_exponent; + std::vector is_exact; IT LEFT,SCALE,DENOM; bool is_strong_; int k; @@ -91,9 +91,9 @@ public: k(kk), interval_given(false) { - numerator = new IT[CGAL::degree(P)]; - denominator_exponent = new IT[CGAL::degree(P)]; - is_exact = new bool[CGAL::degree(P)]; + numerator.resize(CGAL::degree(P)); + denominator_exponent.resize(CGAL::degree(P)); + is_exact.resize(CGAL::degree(P)); number_of_real_roots_ = 0; if(CGAL::degree(P) == 0) { @@ -116,9 +116,9 @@ public: k(kk), interval_given(true) { - numerator = new IT[CGAL::degree(P)]; - denominator_exponent = new IT[CGAL::degree(P)]; - is_exact = new bool[CGAL::degree(P)]; + numerator.resize(CGAL::degree(P)); + denominator_exponent.resize(CGAL::degree(P)); + is_exact.resize(CGAL::degree(P)); number_of_real_roots_ = 0; if(CGAL::degree(P) == 0) { @@ -153,9 +153,9 @@ public: k(D.k), interval_given(D.interval_given) { - numerator = new IT[CGAL::degree(poly_)]; - denominator_exponent = new IT[CGAL::degree(poly_)]; - is_exact = new bool[CGAL::degree(poly_)]; + numerator.resize(CGAL::degree(poly_)); + denominator_exponent.resize(CGAL::degree(poly_)); + is_exact.resize(CGAL::degree(poly_)); for(int i=0; i m_alpha) std::cerr << "Warning: refining with an alpha greater than the last iteration's!" << std::endl; if(refining && offset != m_offset) std::cerr << "Warning: refining with a different offset value!" << std::endl; +#endif m_alpha = FT(alpha); m_sq_alpha = square(m_alpha); 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/doc/Arrangement_on_surface_2/CGAL/draw_arrangement_2.h b/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/CGAL/draw_arrangement_2.h index a22a398115e..cf7782447aa 100644 --- a/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/CGAL/draw_arrangement_2.h +++ b/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/CGAL/draw_arrangement_2.h @@ -17,36 +17,63 @@ #ifndef CGAL_DRAW_ARRANGEMENT_2_H #define CGAL_DRAW_ARRANGEMENT_2_H -#include +#include #ifdef DOXYGEN_RUNNING namespace CGAL { /*! \ingroup PkgArrangementOnSurface2Draw - * - * opens a new window and draws `arr`, an instance of the `CGAL::Arrangement_2` - * class template. A call to this function is blocking; that is, the program - * continues only after 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 GeometryTraits_2 a geometry traits type, a model of a 2D arrangement - * traits concept. At this point it must be an instance of either - * `CGAL::Arr_segment_traits_2` or `CGAL::Arr_conic_traits_2`. - * - * \tparam Dcel the \dcel type, a model of the `ArrangementDcel` concept. - * - * \param arr the 2D arrangement to draw. - * \param title the window title. - * - * \sa `ArrangementDcel` - * \sa `ArrangementTraits_2` + + opens a new window and draws `arr`, an instance of the `CGAL::Arrangement_2` class template. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 GeometryTraits_2 a geometry traits type, a model of a 2D arrangement traits concept. At this point it must be an instance of either `CGAL::Arr_segment_traits_2` or `CGAL::Arr_conic_traits_2`. +\tparam Dcel the \dcel type, a model of the `ArrangementDcel` concept. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param arr the 2D arrangement to draw. +\param gso the graphics scene options parameter. + +\sa `ArrangementDcel` +\sa `ArrangementTraits_2` +*/ +template +void draw(const Arrangement_2& arr, const GSOptions& gso); + +/*! \ingroup PkgArrangementOnSurface2Draw + + A shortcut to `CGAL::draw(arr, Graphics_scene_options{})`. +*/ +template +void draw(const Arrangement_2& arr); + +/*! \ingroup PkgArrangementOnSurface2Draw + +adds the vertices, edges and faces of `arr` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam GeometryTraits_2 a geometry traits type, a model of a 2D arrangement traits concept. At this point it must be an instance of either `CGAL::Arr_segment_traits_2` or `CGAL::Arr_conic_traits_2`. +\tparam Dcel the \dcel type, a model of the `ArrangementDcel` concept. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param arr the 2D arrangement to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + */ +template +void add_to_graphics_scene(const Arrangement_2& arr, + CGAL::Graphics_scene& gs, const GSOptions& gso); + +/*! \ingroup PkgArrangementOnSurface2Draw + + A shortcut to `CGAL::add_to_graphics_scene(arr, gs, Graphics_scene_options{})`. */ template -void draw(const Arrangement_2& arr, - const char* title = "2D Arrangement Basic Viewer"); +void add_to_graphics_scene(const Arrangement_2& arr, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/draw_arr.cpp b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/draw_arr.cpp index c72c0168c04..1b10d7504fa 100644 --- a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/draw_arr.cpp +++ b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/draw_arr.cpp @@ -100,13 +100,24 @@ int main() { << arr.number_of_faces() << std::endl; std::size_t id(0); - CGAL::draw(arr, [&] (Arrangement_2::Face_const_handle) -> CGAL::IO::Color { - float h = 360.0f * id++ / arr.number_of_faces(); - float s = 0.5; - float v = 0.5; - auto [r, g, b] = hsv_to_rgb(h, s, v); - return CGAL::IO::Color(r,g,b); - }, "hsv colors", true); - return EXIT_SUCCESS; + CGAL::Graphics_scene_options gso; + gso.colored_face=[](const Arrangement_2&, Arrangement_2::Face_const_handle) -> bool + { return true; }; + + gso.face_color=[&id](const Arrangement_2& arr, Arrangement_2::Face_const_handle) -> CGAL::IO::Color + { + float h = 360.0f * id++ / arr.number_of_faces(); + float s = 0.5; + float v = 0.5; + auto [r, g, b] = hsv_to_rgb(h, s, v); + return CGAL::IO::Color(r,g,b); + }; + + CGAL::draw(arr, gso, "hsv colors"); + + return EXIT_SUCCESS; } 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/include/CGAL/draw_arrangement_2.h b/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h index 0e28cd478a2..0cc6e60b83d 100644 --- a/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h +++ b/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h @@ -25,349 +25,70 @@ #include #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER +#include +#include +#include +#include #include -#include #include #include #include namespace CGAL { -struct Default_color_generator { - /*! Obtain color - */ - template - CGAL::IO::Color operator()(HalfedgeHandle /* h */) { - static std::random_device rd; - static std::mt19937 rng(rd()); - static std::uniform_int_distribution uni(0, 255); - return CGAL::IO::Color(uni(rng), uni(rng), uni(rng)); - } -}; - -// Viewer class for`< Polygon_2 -template -class Aos_2_basic_viewer_qt : public Basic_viewer_qt { - using Aos = ArrangementOnSurface_2; - using Color_generator = ColorGenerator; - using Base = Basic_viewer_qt; - using Gt = typename Aos::Geometry_traits_2; - using Point = typename Aos::Point_2; - using X_monotone_curve = typename Aos::X_monotone_curve_2; - using Vertex_const_handle = typename Aos::Vertex_const_handle; - using Halfedge_const_handle = typename Aos::Halfedge_const_handle; - using Face_const_handle = typename Aos::Face_const_handle; - using Ccb_halfedge_const_circulator = - typename Aos::Ccb_halfedge_const_circulator; - -public: - /// Construct the viewer. - /// @param arr the arrangement to view - /// @param title the title of the window - Aos_2_basic_viewer_qt(QWidget* parent, const Aos& aos, - Color_generator color_generator, - const char* title = "2D Arrangement Basic Viewer", - bool draw_vertices = false) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, draw_vertices, true, true, false, false), - m_aos(aos), - m_color_generator(color_generator) +namespace draw_function_for_arrangement_2 +{ + template + class Draw_arr_tool { - // 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; - } + public: + using Halfedge_const_handle=typename Arr::Halfedge_const_handle; + using Vertex_const_handle=typename Arr::Vertex_const_handle; + using Face_const_handle=typename Arr::Face_const_handle; + using Ccb_halfedge_const_circulator=typename Arr::Ccb_halfedge_const_circulator; + using Inner_ccb_const_iterator=typename Arr::Inner_ccb_const_iterator; + using Outer_ccb_const_iterator=typename Arr::Outer_ccb_const_iterator; + using Gt=typename Arr::Geometry_traits_2; + using Point=typename Arr::Point_2; + using X_monotone_curve = typename Arr::X_monotone_curve_2; - /*! 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); - if (ratio != m_pixel_ratio) { - m_pixel_ratio = ratio; - add_elements(); - } - } + Draw_arr_tool(Arr& a_aos, CGAL::Graphics_scene& a_gs, const GSOptions& a_gso): + m_aos(a_aos), m_gs(a_gs), m_gso(a_gso) + {} - /*! Compute an approximation of the bounding box of a point. - * \param[in] p the (exact) point. - * Call this member function only if the geometry traits is equipped with - * the coordinate-approximation functionality of a point coordinate. - * This function must be inlined (e.g., a template) to enable the - * compiled-time dispatching in the function `bounding_box()`. - */ - template - CGAL::Bbox_2 approximate_bbox(const Point& p, const Approximate& approx) { - auto x = approx(p, 0); - auto y = approx(p, 1); - return CGAL::Bbox_2(x, y, x, y); - } + /// Add a face. + void add_face(Face_const_handle face) + { + // std::cout << "add_face()\n"; + for (Inner_ccb_const_iterator it = face->inner_ccbs_begin(); + it != face->inner_ccbs_end(); ++it) + { add_ccb(*it); } - /*! Obtain the bounding box of a point. - * \param[in] p the point. - * We assume that if the coordinate-approximation functionality is not - * supported, the point supports the member function `bbox()`. - * This function must be inlined (e.g., a template) to enable the - * compiled-time dispatching in the function `bounding_box()`. - */ - template - CGAL::Bbox_2 exact_bbox(const Point& p) { return p.bbox(); } - - /*! Compile time dispatching - */ -#if 0 - template - void bounding_box_impl2(CGAL::Bbox_2& bbox, const Point& p, T const&, long) - { bbox += exact_bbox(p); } - - template - auto bounding_box_impl2(CGAL::Bbox_2& bbox, const Point& p, T const& approx, - int) -> decltype(approx.operator()(p), void()) - { bbox += approximate_bbox(p, approx); } - - template - void bounding_box_impl1(CGAL::Bbox_2& bbox, const Point& p, T const&, long) - { bbox += exact_bbox(p); } - - template - auto bounding_box_impl1(CGAL::Bbox_2& bbox, const Point& p, T const& traits, - int) -> - decltype(traits.approximate_2_object(), void()) { - using Approximate = typename Gt::Approximate_2; - bounding_box_impl2(bbox, p, traits.approximate_2_object(), 0); - } -#else - template - void bounding_box_impl1(CGAL::Bbox_2& bbox, const Point& p, T const& traits, - int) - { bbox += approximate_bbox(p, traits.approximate_2_object()); } -#endif - - /*! Compute the bounding box. - */ - CGAL::Bbox_2 bounding_box() { - CGAL::Bbox_2 bbox; - const auto* traits = this->m_aos.geometry_traits(); - // At this point we assume that the arrangement is not open, and thus the - // bounding box is defined by the vertices. - for (auto it = m_aos.vertices_begin(); it != m_aos.vertices_end(); ++it) - bounding_box_impl1(bbox, it->point(), *traits, 0); - return bbox; - } - - /*! Add all faces. - */ - template - void add_faces(const Traits&) { - for (auto it = m_aos.unbounded_faces_begin(); - it != m_aos.unbounded_faces_end(); ++it) - add_face(it); - } - - /*! Add all faces. - */ - template - void - add_faces(Arr_geodesic_arc_on_sphere_traits_2 const&) - { add_face(m_aos.faces_begin()); } - - /*! Add all elements to be drawn. - */ - void add_elements() { - // std::cout << "add_elements()\n"; - // std::cout << "ratio: " << this->pixel_ratio() << std::endl; - clear(); - m_visited.clear(); - - if (m_aos.is_empty()) return; - add_faces(*(this->m_aos.geometry_traits())); - - // Add edges that do not separate faces. - for (auto it = m_aos.edges_begin(); it != m_aos.edges_end(); ++it) - if (it->face() == it->twin()->face()) draw_curve(it->curve()); - - // Add all points - for (auto it = m_aos.vertices_begin(); it != m_aos.vertices_end(); ++it) - draw_point(it->point()); - - m_visited.clear(); - } - - /*/ Obtain the pixel ratio - */ - double pixel_ratio() const { return m_pixel_ratio; } - -protected: - template - Halfedge_const_handle - find_smallest(Ccb_halfedge_const_circulator circ, - Arr_geodesic_arc_on_sphere_traits_2 const&) - { return circ; } - - /*! Find the halfedge incident to the lexicographically smallest vertex - * along the CCB, such that there is no other halfedge underneath. - */ - template - Halfedge_const_handle find_smallest(Ccb_halfedge_const_circulator circ, - const Traits&) { - // std::cout << "find_smallest()\n"; - const auto* traits = this->m_aos.geometry_traits(); - auto cmp_xy = traits->compare_xy_2_object(); - auto cmp_y = traits->compare_y_at_x_right_2_object(); - - // Find the first halfedge directed from left to right - auto curr = circ; - do if (curr->direction() == CGAL::ARR_LEFT_TO_RIGHT) break; - while (++curr != circ); - Halfedge_const_handle ext = curr; - - // Find the halfedge incident to the lexicographically smallest vertex, - // such that there is no other halfedge underneath. - do { - // Discard edges not directed from left to right: - if (curr->direction() != CGAL::ARR_LEFT_TO_RIGHT) continue; - - - auto res = cmp_xy(curr->source()->point(), ext->source()->point()); - - // Discard the edges inciden to a point strictly larger than the point - // incident to the stored extreme halfedge: - if (res == LARGER) continue; - - // Store the edge inciden to a point strictly smaller: - if (res == SMALLER) { - ext = curr; - continue; + for (Outer_ccb_const_iterator it = face->outer_ccbs_begin(); + it != face->outer_ccbs_end(); ++it) + { + add_ccb(*it); + draw_region(*it); } - - // The incident points are equal; compare the halfedges themselves: - if (cmp_y(curr->curve(), ext->curve(), curr->source()->point()) == - SMALLER) - ext = curr; - } while (++curr != circ); - - return ext; - } - - /*! Draw a region using aproximate coordinates. - * Call this member function only if the geometry traits is equipped with - * the coordinate-approximation functionality of a curve. - * This function must be inlined (e.g., a template) to enable the - * compiled-time dispatching in the function `draw_region()`. - */ - template - void draw_approximate_region(Halfedge_const_handle curr, - const Approximate& approx) { - // std::cout << "draw_approximate_region()\n"; - std::vector polyline; - double error(this->pixel_ratio()); - bool l2r = curr->direction() == ARR_LEFT_TO_RIGHT; - approx(curr->curve(), error, std::back_inserter(polyline), l2r); - if (polyline.empty()) return; - auto it = polyline.begin(); - auto prev = it++; - for (; it != polyline.end(); prev = it++) { - this->add_segment(*prev, *it); - this->add_point_in_face(*prev); } - } - /*! Draw an exact curve. - */ - template - void draw_exact_curve(const XMonotoneCurve& curve) { - const auto* traits = this->m_aos.geometry_traits(); - auto ctr_min = traits->construct_min_vertex_2_object(); - auto ctr_max = traits->construct_max_vertex_2_object(); - this->add_segment(ctr_min(curve), ctr_max(curve)); - } - - /*! Draw an exact region. - */ - void draw_exact_region(Halfedge_const_handle curr) { - // this->add_point_in_face(curr->source()->point()); - draw_exact_curve(curr->curve()); - } - - /*! Compile time dispatching - */ -#if 0 - template - void draw_region_impl2(Halfedge_const_handle curr, T const&, long) - { draw_exact_region(curr); } - - template - auto draw_region_impl2(Halfedge_const_handle curr, T const& approx, int) -> - decltype(approx.template operator()(X_monotone_curve{}, double{}, I{}, - bool{}), void()) - { draw_approximate_region(curr, approx); } - - template - void draw_region_impl1(Halfedge_const_handle curr, T const&, long) - { draw_exact_region(curr); } - - template - auto draw_region_impl1(Halfedge_const_handle curr, T const& traits, int) -> - decltype(traits.approximate_2_object(), void()) { - using Approximate = typename Gt::Approximate_2; - draw_region_impl2(curr, traits.approximate_2_object(), 0); - } -#else - template - void draw_region_impl1(Halfedge_const_handle curr, T const& traits, int) - { draw_approximate_region(curr, traits.approximate_2_object()); } -#endif - - template - void draw_region_impl1 - (Halfedge_const_handle curr, - Arr_geodesic_arc_on_sphere_traits_2 const& traits, - int) { - // std::cout << "draw_region_impl1()\n"; - auto approx = traits.approximate_2_object(); - using Kernel = Kernel_; - using Traits = Arr_geodesic_arc_on_sphere_traits_2; - using Ak = typename Traits::Approximate_kernel; - using Ap = typename Traits::Approximate_point_2; - using Approx_point_3 = typename Ak::Point_3; - - std::vector polyline; - double error(0.01); - bool l2r = curr->direction() == ARR_LEFT_TO_RIGHT; - approx(curr->curve(), error, std::back_inserter(polyline), l2r); - if (polyline.empty()) return; - auto it = polyline.begin(); - auto x = it->dx(); - auto y = it->dy(); - auto z = it->dz(); - auto l = std::sqrt(x*x + y*y + z*z); - Approx_point_3 prev(x/l, y/l, z/l); - for (++it; it != polyline.end(); ++it) { - auto x = it->dx(); - auto y = it->dy(); - auto z = it->dz(); - auto l = std::sqrt(x*x + y*y + z*z); - Approx_point_3 next(x/l, y/l, z/l); - this->add_segment(prev, next); - prev = next; - // this->add_point_in_face(*prev); + /// Add a Connected Component of the Boundary. + void add_ccb(Ccb_halfedge_const_circulator circ) + { + // std::cout << "add_ccb()\n"; + auto curr = circ; + do { + auto new_face = curr->twin()->face(); + if (m_visited.find(new_face) != m_visited.end()) continue; + m_visited[new_face] = true; + add_face(new_face); + } while (++curr != circ); } - } - /*! Draw a region. - */ - void draw_region(Ccb_halfedge_const_circulator circ) { + ///! Draw a region. + void draw_region(Ccb_halfedge_const_circulator circ) + { // std::cout << "draw_region()\n"; /* Check whether the traits has a member function called * approximate_2_object() and if so check whether the return type, namely @@ -384,21 +105,315 @@ protected: * * For now we use C++14 features. */ - auto color = m_color_generator(circ->face()); - this->face_begin(color); + if(m_gso.colored_face(m_aos, circ->face())) + { m_gs.face_begin(m_gso.face_color(m_aos, circ->face())); } + else + { m_gs.face_begin(); } - const auto* traits = this->m_aos.geometry_traits(); - auto ext = find_smallest(circ, *traits); - auto curr = ext; + const auto* traits = this->m_aos.geometry_traits(); + auto ext = find_smallest(circ, *traits); + auto curr = ext; - do { - // Skip halfedges that are "antenas": - while (curr->face() == curr->twin()->face()) curr = curr->twin()->next(); - draw_region_impl1(curr, *traits, 0); - curr = curr->next(); - } while (curr != ext); + do { + // Skip halfedges that are "antenas": + while (curr->face() == curr->twin()->face()) curr = curr->twin()->next(); + draw_region_impl1(curr, *traits, 0); + curr = curr->next(); + } while (curr != ext); - this->face_end(); + m_gs.face_end(); + } + + /// Compile time dispatching +#if 0 + template + void draw_region_impl2(Halfedge_const_handle curr, T const&, long) + { draw_exact_region(curr); } + + template + auto draw_region_impl2(Halfedge_const_handle curr, T const& approx, int) -> + decltype(approx.template operator()(X_monotone_curve{}, double{}, I{}, + bool{}), void()) + { draw_approximate_region(curr, approx); } + + template + void draw_region_impl1(Halfedge_const_handle curr, T const&, long) + { draw_exact_region(curr); } + + template + auto draw_region_impl1(Halfedge_const_handle curr, T const& traits, int) -> + decltype(traits.approximate_2_object(), void()) { + using Approximate = typename Gt::Approximate_2; + draw_region_impl2(curr, traits.approximate_2_object(), 0); + } +#else + template + void draw_region_impl1(Halfedge_const_handle curr, T const& traits, int) + { draw_approximate_region(curr, traits.approximate_2_object()); } +#endif + + template + void draw_region_impl1 + (Halfedge_const_handle curr, + Arr_geodesic_arc_on_sphere_traits_2 const& traits, + int) + { + if(!m_gso.draw_edge(m_aos, curr)) + { return; } + + // std::cout << "draw_region_impl1()\n"; + auto approx = traits.approximate_2_object(); + using Kernel = Kernel_; + using Traits = Arr_geodesic_arc_on_sphere_traits_2; + using Ak = typename Traits::Approximate_kernel; + using Ap = typename Traits::Approximate_point_2; + using Approx_point_3 = typename Ak::Point_3; + + std::vector polyline; + double error(0.01); + bool l2r = curr->direction() == ARR_LEFT_TO_RIGHT; + approx(curr->curve(), error, std::back_inserter(polyline), l2r); + if (polyline.empty()) return; + auto it = polyline.begin(); + auto x = it->dx(); + auto y = it->dy(); + auto z = it->dz(); + auto l = std::sqrt(x*x + y*y + z*z); + Approx_point_3 prev(x/l, y/l, z/l); + for (++it; it != polyline.end(); ++it) { + auto x = it->dx(); + auto y = it->dy(); + auto z = it->dz(); + auto l = std::sqrt(x*x + y*y + z*z); + Approx_point_3 next(x/l, y/l, z/l); + + if(m_gso.colored_edge(m_aos, curr)) + { m_gs.add_segment(prev, next, m_gso.edge_color(m_aos, curr)); } + else + { m_gs.add_segment(prev, next); } + + prev = next; + // m_gs.add_point_in_face(*prev); + } + } + + /*! Draw a region using aproximate coordinates. + * Call this member function only if the geometry traits is equipped with + * the coordinate-approximation functionality of a curve. + * This function must be inlined (e.g., a template) to enable the + * compiled-time dispatching in the function `draw_region()`. + */ + template + void draw_approximate_region(Halfedge_const_handle curr, + const Approximate& approx) + { + // std::cout << "draw_approximate_region()\n"; + std::vector polyline; + double error(0.01); // TODO? (this->pixel_ratio()); + bool l2r = curr->direction() == ARR_LEFT_TO_RIGHT; + approx(curr->curve(), error, std::back_inserter(polyline), l2r); + if (polyline.empty()) return; + auto it = polyline.begin(); + auto prev = it++; + for (; it != polyline.end(); prev = it++) { + if(m_gso.draw_edge(m_aos, curr)) + { + if(m_gso.colored_edge(m_aos, curr)) + { m_gs.add_segment(*prev, *it, m_gso.edge_color(m_aos, curr)); } + else + { m_gs.add_segment(*prev, *it); } + } + m_gs.add_point_in_face(*prev); + } + } + + /// Draw an exact curve. + template + void draw_exact_curve(const XMonotoneCurve& curve) + { + const auto* traits = this->m_aos.geometry_traits(); + auto ctr_min = traits->construct_min_vertex_2_object(); + auto ctr_max = traits->construct_max_vertex_2_object(); + m_gs.add_segment(ctr_min(curve), ctr_max(curve)); + } + + /// Draw an exact region. + void draw_exact_region(Halfedge_const_handle curr) + { + // this->add_point_in_face(curr->source()->point()); + draw_exact_curve(curr->curve()); + } + + /// Add all faces. + template + void add_faces(const Traits&) + { + for (auto it=m_aos.unbounded_faces_begin(); it!=m_aos.unbounded_faces_end(); ++it) + { add_face(it); } + } + + /// Add all faces. + template + void add_faces(Arr_geodesic_arc_on_sphere_traits_2 const&) + { add_face(m_aos.faces_begin()); } + + /// Compile time dispatching +#if 0 + template + void draw_point_impl2(const Point& p, T const&, long) { m_gs.add_point(p); } + + template + auto draw_point_impl2(const Point& p, T const& approx, int) -> + decltype(approx.operator()(p), void()) + { m_gs.add_point(approx(p)); } + + template + void draw_point_impl1(const Point& p, T const&, long) { m_gs.add_point(p); } + + template + auto draw_point_impl1(const Point& p, T const& traits, int) -> + decltype(traits.approximate_2_object(), void()) { + using Approximate = typename Gt::Approximate_2; + draw_point_impl2(p, traits.approximate_2_object(), true); + } +#else + template + void draw_point_impl1(const Point& p, T const& traits, int, + bool colored, const CGAL::IO::Color& color) + { + if(colored) + { m_gs.add_point(traits.approximate_2_object()(p), color); } + else + { m_gs.add_point(traits.approximate_2_object()(p)); } + } +#endif + + template + void draw_point_impl1 + (const Point& p, + Arr_geodesic_arc_on_sphere_traits_2 const& traits, + int, + bool colored, + const CGAL::IO::Color& color) + { + auto approx = traits.approximate_2_object(); + using Traits = Arr_geodesic_arc_on_sphere_traits_2; + using Ak = typename Traits::Approximate_kernel; + using Approx_point_3 = typename Ak::Point_3; + auto ap = approx(p); + auto x = ap.dx(); + auto y = ap.dy(); + auto z = ap.dz(); + auto l = std::sqrt(x*x + y*y + z*z); + Approx_point_3 p3(x/l, y/l, z/l); + if(colored) + { m_gs.add_point(p3, color); } + else + { m_gs.add_point(p3); } + } + + /// Draw a point. + void draw_point(Vertex_const_handle vh) + { + const auto* traits = m_aos.geometry_traits(); + if(m_gso.draw_vertex(m_aos, vh)) + { + if(m_gso.colored_vertex(m_aos, vh)) + { draw_point_impl1(vh->point(), *traits, 0, true, + m_gso.vertex_color(m_aos, vh)); } + else + { draw_point_impl1(vh->point(), *traits, 0, false, CGAL::IO::Color()); } // color will be unused + } + } + + template + Halfedge_const_handle + find_smallest(Ccb_halfedge_const_circulator circ, + Arr_geodesic_arc_on_sphere_traits_2 const&) + { return circ; } + + /*! Find the halfedge incident to the lexicographically smallest vertex + * along the CCB, such that there is no other halfedge underneath. + */ + template + Halfedge_const_handle find_smallest(Ccb_halfedge_const_circulator circ, + const Traits&) + { + // std::cout << "find_smallest()\n"; + const auto* traits = this->m_aos.geometry_traits(); + auto cmp_xy = traits->compare_xy_2_object(); + auto cmp_y = traits->compare_y_at_x_right_2_object(); + + // Find the first halfedge directed from left to right + auto curr = circ; + do if (curr->direction() == CGAL::ARR_LEFT_TO_RIGHT) break; + while (++curr != circ); + Halfedge_const_handle ext = curr; + + // Find the halfedge incident to the lexicographically smallest vertex, + // such that there is no other halfedge underneath. + do { + // Discard edges not directed from left to right: + if (curr->direction() != CGAL::ARR_LEFT_TO_RIGHT) continue; + + auto res = cmp_xy(curr->source()->point(), ext->source()->point()); + + // Discard the edges inciden to a point strictly larger than the point + // incident to the stored extreme halfedge: + if (res == LARGER) continue; + + // Store the edge inciden to a point strictly smaller: + if (res == SMALLER) { + ext = curr; + continue; + } + + // The incident points are equal; compare the halfedges themselves: + if (cmp_y(curr->curve(), ext->curve(), curr->source()->point()) == + SMALLER) + ext = curr; + } while (++curr != circ); + + return ext; + } + + /// Add all elements to be drawn. + void add_elements() + { + // std::cout << "add_elements()\n"; + // std::cout << "ratio: " << this->pixel_ratio() << std::endl; + m_visited.clear(); + + if (m_aos.is_empty()) return; + + if(m_gso.are_faces_enabled()) + { add_faces(*(this->m_aos.geometry_traits())); } + + // Add edges that do not separate faces. + if(m_gso.are_edges_enabled()) + { + for (auto it = m_aos.edges_begin(); it != m_aos.edges_end(); ++it) + { if (it->face()==it->twin()->face()) + { + if(m_gso.draw_edge(m_aos, it)) + { + if(m_gso.colored_edge(m_aos, it)) + { draw_curve(it->curve(), true, m_gso.edge_color(m_aos, it)); } + else + { draw_curve(it->curve(), false, CGAL::IO::Color()); } + } + } + } + } + + // Add all points + if(m_gso.are_vertices_enabled()) + { + for (auto it = m_aos.vertices_begin(); it != m_aos.vertices_end(); ++it) + { draw_point(it); } + } + + m_visited.clear(); } /*! Draw a curve using aproximate coordinates. @@ -409,349 +424,195 @@ protected: */ template void draw_approximate_curve(const XMonotoneCurve& curve, - const Approximate& approx) { + const Approximate& approx, + bool colored, const CGAL::IO::Color& c) + { std::vector polyline; - double error(this->pixel_ratio()); + double error(0.01); // TODO? (this->pixel_ratio()); approx(curve, error, std::back_inserter(polyline)); if (polyline.empty()) return; auto it = polyline.begin(); auto prev = it++; - for (; it != polyline.end(); prev = it++) this->add_segment(*prev, *it); + for (; it != polyline.end(); prev = it++) + { + if(colored) + { m_gs.add_segment(*prev, *it, c); } + else + { m_gs.add_segment(*prev, *it); } + } + } /*! Compile time dispatching */ #if 0 - template - void draw_curve_impl2(const X_monotone_curve& xcv, T const&, long) - { draw_exact_curve(xcv); } + template + void draw_curve_impl2(const X_monotone_curve& xcv, T const&, long) + { draw_exact_curve(xcv); } - template - auto draw_curve_impl2(const X_monotone_curve& xcv, T const& approx, int) -> - decltype(approx.template operator()(X_monotone_curve{}, double{}, I{}, - bool{}), void()) - { draw_approximate_curve(xcv, approx); } + template + auto draw_curve_impl2(const X_monotone_curve& xcv, T const& approx, int) -> + decltype(approx.template operator()(X_monotone_curve{}, double{}, I{}, + bool{}), void()) + { draw_approximate_curve(xcv, approx); } - template - void draw_curve_impl1(const X_monotone_curve& xcv, T const&, long) - { draw_exact_curve(xcv); } + template + void draw_curve_impl1(const X_monotone_curve& xcv, T const&, long) + { draw_exact_curve(xcv); } - template - auto draw_curve_impl1(const X_monotone_curve& xcv, T const& traits, int) -> - decltype(traits.approximate_2_object(), void()) { - using Approximate = typename Gt::Approximate_2; - draw_curve_impl2(xcv, traits.approximate_2_object(), 0); - } + template + auto draw_curve_impl1(const X_monotone_curve& xcv, T const& traits, int) -> + decltype(traits.approximate_2_object(), void()) { + using Approximate = typename Gt::Approximate_2; + draw_curve_impl2(xcv, traits.approximate_2_object(), 0); + } #else - template - void draw_curve_impl1(const X_monotone_curve& xcv, T const& traits, int) - { draw_approximate_curve(xcv, traits.approximate_2_object()); } + template + void draw_curve_impl1(const X_monotone_curve& xcv, T const& traits, int, + bool colored, const CGAL::IO::Color& c) + { draw_approximate_curve(xcv, traits.approximate_2_object(), colored, c); } #endif - template - void draw_curve_impl1 - (const X_monotone_curve& xcv, - Arr_geodesic_arc_on_sphere_traits_2 const& traits, - int) { - auto approx = traits.approximate_2_object(); - using Kernel = Kernel_; - using Traits = Arr_geodesic_arc_on_sphere_traits_2; - using Ak = typename Traits::Approximate_kernel; - using Ap = typename Traits::Approximate_point_2; - using Approx_point_3 = typename Ak::Point_3; - std::vector apoints; - double error(0.01); - approx(xcv, error, std::back_inserter(apoints)); - auto it = apoints.begin(); - auto x = it->dx(); - auto y = it->dy(); - auto z = it->dz(); - auto l = std::sqrt(x*x + y*y + z*z); - Approx_point_3 prev(x/l, y/l, z/l); - for (++it; it != apoints.end(); ++it) { + template + void draw_curve_impl1 + (const X_monotone_curve& xcv, + Arr_geodesic_arc_on_sphere_traits_2 const& traits, + int, + bool colored, const CGAL::IO::Color& c) + { + auto approx = traits.approximate_2_object(); + using Kernel = Kernel_; + using Traits = Arr_geodesic_arc_on_sphere_traits_2; + using Ak = typename Traits::Approximate_kernel; + using Ap = typename Traits::Approximate_point_2; + using Approx_point_3 = typename Ak::Point_3; + std::vector apoints; + double error(0.01); + approx(xcv, error, std::back_inserter(apoints)); + auto it = apoints.begin(); auto x = it->dx(); auto y = it->dy(); auto z = it->dz(); auto l = std::sqrt(x*x + y*y + z*z); - Approx_point_3 next(x/l, y/l, z/l); - this->add_segment(prev, next); - prev = next; + Approx_point_3 prev(x/l, y/l, z/l); + for (++it; it != apoints.end(); ++it) { + auto x = it->dx(); + auto y = it->dy(); + auto z = it->dz(); + auto l = std::sqrt(x*x + y*y + z*z); + Approx_point_3 next(x/l, y/l, z/l); + if(colored) + { m_gs.add_segment(prev, next, c); } + else + { m_gs.add_segment(prev, next); } + prev = next; + } } - } - /*! Draw a curve. - */ - template - void draw_curve(const XMonotoneCurve& curve) { - /* Check whether the traits has a member function called - * approximate_2_object() and if so check whether the return type, namely - * `Approximate_2` has an appropriate operator. - * - * C++20 supports concepts and `requires` expression; see, e.g., - * https://en.cppreference.com/w/cpp/language/constraints; thus, the first - * condition above can be elegantly verified as follows: - * constexpr bool has_approximate_2_object = - * requires(const Gt& traits) { traits.approximate_2_object(); }; - * - * C++17 has experimental constructs called is_detected and - * is_detected_v that can be used to achieve the same goal. - * - * For now we use C++14 features. - */ -#if 0 - if constexpr (std::experimental::is_detected_v) + /// Draw a curve. + template + void draw_curve(const XMonotoneCurve& curve, + bool colored, const CGAL::IO::Color& c) { - const auto* traits = this->m_aos.geometry_traits(); - auto approx = traits->approximate_2_object(); - draw_approximate_curve(curve, approx); - return; - } - draw_exact_curve(curve); -#else - const auto* traits = this->m_aos.geometry_traits(); - draw_curve_impl1(curve, *traits, 0); -#endif - } - - /*! Compile time dispatching - */ + /* Check whether the traits has a member function called + * approximate_2_object() and if so check whether the return type, namely + * `Approximate_2` has an appropriate operator. + * + * C++20 supports concepts and `requires` expression; see, e.g., + * https://en.cppreference.com/w/cpp/language/constraints; thus, the first + * condition above can be elegantly verified as follows: + * constexpr bool has_approximate_2_object = + * requires(const Gt& traits) { traits.approximate_2_object(); }; + * + * C++17 has experimental constructs called is_detected and + * is_detected_v that can be used to achieve the same goal. + * + * For now we use C++14 features. + */ #if 0 - template - void draw_point_impl2(const Point& p, T const&, long) { add_point(p); } - - template - auto draw_point_impl2(const Point& p, T const& approx, int) -> - decltype(approx.operator()(p), void()) - { add_point(approx(p)); } - - template - void draw_point_impl1(const Point& p, T const&, long) { add_point(p); } - - template - auto draw_point_impl1(const Point& p, T const& traits, int) -> - decltype(traits.approximate_2_object(), void()) { - using Approximate = typename Gt::Approximate_2; - draw_point_impl2(p, traits.approximate_2_object(), true); - } + if constexpr (std::experimental::is_detected_v) + { + const auto* traits = this->m_aos.geometry_traits(); + auto approx = traits->approximate_2_object(); + draw_approximate_curve(curve, approx); + return; + } + draw_exact_curve(curve); #else - template - void draw_point_impl1(const Point& p, T const& traits, int) - { add_point(traits.approximate_2_object()(p)); } + const auto* traits = this->m_aos.geometry_traits(); + draw_curve_impl1(curve, *traits, 0, colored, c); #endif - - template - void draw_point_impl1 - (const Point& p, - Arr_geodesic_arc_on_sphere_traits_2 const& traits, - int) { - auto approx = traits.approximate_2_object(); - using Traits = Arr_geodesic_arc_on_sphere_traits_2; - using Ak = typename Traits::Approximate_kernel; - using Approx_point_3 = typename Ak::Point_3; - auto ap = approx(p); - auto x = ap.dx(); - auto y = ap.dy(); - auto z = ap.dz(); - auto l = std::sqrt(x*x + y*y + z*z); - Approx_point_3 p3(x/l, y/l, z/l); - add_point(p3); - } - - /*! Draw a point. - */ - void draw_point(const Point& p) { - const auto* traits = m_aos.geometry_traits(); - draw_point_impl1(p, *traits, 0); - } - - /*! Add a Connected Component of the Boundary. - */ - void add_ccb(Ccb_halfedge_const_circulator circ) { - // std::cout << "add_ccb()\n"; - auto curr = circ; - do { - auto new_face = curr->twin()->face(); - if (m_visited.find(new_face) != m_visited.end()) continue; - m_visited[new_face] = true; - add_face(new_face); - } while (++curr != circ); - } - - /*! Add a face. - */ - void add_face(Face_const_handle face) { - // std::cout << "add_face()\n"; - using Inner_ccb_const_iterator = typename Aos::Inner_ccb_const_iterator; - using Outer_ccb_const_iterator = typename Aos::Outer_ccb_const_iterator; - - for (Inner_ccb_const_iterator it = face->inner_ccbs_begin(); - it != face->inner_ccbs_end(); ++it) - add_ccb(*it); - - for (Outer_ccb_const_iterator it = face->outer_ccbs_begin(); - it != face->outer_ccbs_end(); ++it) { - add_ccb(*it); - draw_region(*it); } - } - //! - virtual void keyPressEvent(QKeyEvent* e) { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } + protected: + Arr& m_aos; + CGAL::Graphics_scene& m_gs; + const GSOptions& m_gso; + std::unordered_map m_visited; + }; - // 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 +} // namespace draw_function_for_arrangement_2 - // Call the base method to process others/classicals key - Base::keyPressEvent(e); - } +#define CGAL_ARR_TYPE CGAL::Arrangement_on_surface_2 -protected: - //! The window width in pixels. - int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; +template +void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ + draw_function_for_arrangement_2::Draw_arr_tool dar(aos, graphics_scene, gso); + dar.add_elements(); +} - //! 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 arrangement to draw. - const Aos& m_aos; - - //! The color generator. - Color_generator m_color_generator; - - std::unordered_map m_visited; -}; - -//! Basic viewer of a 2D arrangement. -template -class Aos_2_viewer_qt : public Aos_2_basic_viewer_qt { -public: - using Aos = ArrangementOnSurface_2; - using Color_generator = ColorGenerator; - using Base = Aos_2_basic_viewer_qt; - using Point = typename Aos::Point_2; - using X_monotone_curve = typename Aos::X_monotone_curve_2; - using Halfedge_const_handle = typename Aos::Halfedge_const_handle; - using Face_const_handle = typename Aos::Face_const_handle; - using Ccb_halfedge_const_circulator = - typename Aos::Ccb_halfedge_const_circulator; - - /// Construct the viewer. - /// @param arr the arrangement to view - /// @param title the title of the window - Aos_2_viewer_qt(QWidget* parent, const Aos& aos, - Color_generator color_generator, - const char* title = "2D Arrangement on Surface Basic Viewer", - bool draw_vertices = false) : - Base(parent, aos, color_generator, title, draw_vertices) - {} -}; - -/*! Draw an arrangement on surface. - */ template -void draw(const Arrangement_on_surface_2& aos, - const char* title = "2D Arrangement on Surface Basic Viewer", - bool draw_vertices = false) { -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif +void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, + CGAL::Graphics_scene& graphics_scene) +{ + CGAL::Graphics_scene_options + gso; + gso.colored_face=[](const CGAL_ARR_TYPE&, + typename CGAL_ARR_TYPE::Face_const_handle) -> bool + { return true; }; - if (cgal_test_suite) return; - using Gt = GeometryTraits_2; - using Tt = TopologyTraits; - using Aos = CGAL::Arrangement_on_surface_2; - using Viewer = Aos_2_viewer_qt; + gso.face_color=[](const CGAL_ARR_TYPE&, + typename CGAL_ARR_TYPE::Face_const_handle fh) -> CGAL::IO::Color + { + CGAL::Random random((unsigned int)(std::size_t)(&*fh)); + return get_random_color(random); + }; - CGAL::Qt::init_ogl_context(4,3); - - int argc = 1; - const char* argv[2] = {"t2_viewer", nullptr}; - QApplication app(argc, const_cast(argv)); - Default_color_generator color_generator; - Viewer mainwindow(app.activeWindow(), aos, color_generator, title, - draw_vertices); - mainwindow.add_elements(); - mainwindow.show(); - app.exec(); + add_to_graphics_scene(aos, graphics_scene, gso); } -/*! Draw an arrangement. - */ -template -void draw(const Arrangement_2& arr, - const char* title = "2D Arrangement Basic Viewer", - bool draw_vertices = false) { -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif +#ifdef CGAL_USE_BASIC_VIEWER - if (cgal_test_suite) return; - using Gt = GeometryTraits_2; - using Arr = CGAL::Arrangement_2; - using Viewer = Aos_2_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)); - Default_color_generator color_generator; - Viewer mainwindow(app.activeWindow(), arr, color_generator, title, - draw_vertices); - mainwindow.add_elements(); - mainwindow.show(); - app.exec(); -} - -/*! Draw an arrangement using a given color generator. - */ -template -void draw(const Arrangement_2& arr, - ColorGenerator color_generator, - const char* title = "2D Arrangement Basic Viewer", - bool draw_vertices = false) { -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (cgal_test_suite) return; - - using Color_generator = ColorGenerator; - using Gt = GeometryTraits_2; - using Arr = CGAL::Arrangement_2; - using Viewer = Aos_2_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(), arr, color_generator, title, - draw_vertices); - mainwindow.add_elements(); - mainwindow.show(); - app.exec(); -} +/// Draw an arrangement on surface. +template +void draw(const CGAL_ARR_TYPE& aos, const GSOptions& gso, + const char* title = "2D Arrangement on Surface Basic Viewer") +{ + CGAL::Graphics_scene graphics_scene; + add_to_graphics_scene(aos, graphics_scene, gso); + draw_graphics_scene(graphics_scene, title); } -#endif +template +void draw(const CGAL_ARR_TYPE& aos, + const char* title = "2D Arrangement on Surface Basic Viewer") +{ + CGAL::Graphics_scene graphics_scene; + add_to_graphics_scene(aos, graphics_scene); + draw_graphics_scene(graphics_scene, title); +} + +#endif // CGAL_USE_BASIC_VIEWER + +#undef CGAL_ARR_TYPE + +} // namespace CGAL + #endif diff --git a/Arrangement_on_surface_2/package_info/Arrangement_on_surface_2/dependencies b/Arrangement_on_surface_2/package_info/Arrangement_on_surface_2/dependencies index a723fb58f63..c830d4b62e7 100644 --- a/Arrangement_on_surface_2/package_info/Arrangement_on_surface_2/dependencies +++ b/Arrangement_on_surface_2/package_info/Arrangement_on_surface_2/dependencies @@ -3,6 +3,7 @@ Algebraic_kernel_d Arithmetic_kernel Arrangement_on_surface_2 BGL +Basic_viewer CGAL_Core Cartesian_kernel Circular_kernel_2 @@ -10,7 +11,6 @@ Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView HalfedgeDS Hash_map Homogeneous_kernel 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 130405879e0..fef22ea95c5 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 @@ -928,7 +928,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() @@ -994,7 +994,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() @@ -1110,7 +1110,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() @@ -1254,7 +1254,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() @@ -1301,7 +1301,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() @@ -1382,7 +1382,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/BGL/doc/BGL/BGL.txt b/BGL/doc/BGL/BGL.txt index cf7e8a73903..96be1186c66 100644 --- a/BGL/doc/BGL/BGL.txt +++ b/BGL/doc/BGL/BGL.txt @@ -510,11 +510,12 @@ Dynamic property tags, such as `dynamic_vertex_property_t`, are a generalization value type of the dynamic property map, and a default value. `boost::property_map::%type` is used to obtain the type of the dynamic property map for a graph of type `G`, for a -dynamic property tag `T`. This type must be default constructible and assignable. +dynamic property tag `T`. This type must be assignable, and if no +default is provided it must be default constructible. As for ordinary properties, the function `%get()` is overloaded and serves for retrieving a property map for a given graph and dynamic property tag, as well as for retrieving a value for a given key and -property map. +property map. The default value is provided as third parameter. The following example shows how to attach a `string` property to vertices and a `double` value to the halfedges of a graph. diff --git a/BGL/doc/BGL/Doxyfile.in b/BGL/doc/BGL/Doxyfile.in index 72d3e79168c..f37650c983f 100644 --- a/BGL/doc/BGL/Doxyfile.in +++ b/BGL/doc/BGL/Doxyfile.in @@ -34,6 +34,7 @@ ALIASES += "bgllink{1}= EXTRACT_ALL=NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES +WARN_IF_INCOMPLETE_DOC = NO # macros to be used inside the code ALIASES += "cgalAssociatedTypesBegin=
Associated Types
" diff --git a/BGL/doc/BGL/PackageDescription.txt b/BGL/doc/BGL/PackageDescription.txt index 6032e29a6c9..89497261a96 100644 --- a/BGL/doc/BGL/PackageDescription.txt +++ b/BGL/doc/BGL/PackageDescription.txt @@ -449,6 +449,9 @@ the requirement for traversal of all faces in a graph. /// \defgroup PkgBGLHelperFct Helper Functions /// \ingroup PkgBGLRef +/// \defgroup PkgBGLGeneratorFct Generator Functions +/// \ingroup PkgBGLRef + /// \defgroup PkgBGLIterators Iterators and Circulators /// \ingroup PkgBGLRef @@ -520,7 +523,14 @@ by attaching and initializing external IDs to the elements of the graph. Generic convenience functions for testing if an edge is a border edge, if a mesh is triangular, for conversion between models of different `FaceGraph` concepts, etc. -Most functions are in the header file `` +All functions are in the header file `` +*/ + +/*! +\addtogroup PkgBGLGeneratorrFct +Generic convenience functions for generating meshes such as a triangle, a quad, or a grid. + +All functions are in the header file `` */ /*! @@ -667,6 +677,7 @@ user might encounter. - `CGAL::is_valid_face_graph()` - `CGAL::is_valid_polygon_mesh()` +\cgalCRPSection{Generator Functions} - `CGAL::is_tetrahedron()` - `CGAL::is_hexahedron()` - `CGAL::make_triangle()` diff --git a/BGL/examples/BGL_graphcut/face_selection_borders_regularization_example.cpp b/BGL/examples/BGL_graphcut/face_selection_borders_regularization_example.cpp index c9671dd408e..38de21612d5 100644 --- a/BGL/examples/BGL_graphcut/face_selection_borders_regularization_example.cpp +++ b/BGL/examples/BGL_graphcut/face_selection_borders_regularization_example.cpp @@ -2,7 +2,7 @@ #include #include #include - +#include #include #include diff --git a/BGL/examples/BGL_surface_mesh/gwdwg.cpp b/BGL/examples/BGL_surface_mesh/gwdwg.cpp index f9a7228f711..aaa5eb333bf 100644 --- a/BGL/examples/BGL_surface_mesh/gwdwg.cpp +++ b/BGL/examples/BGL_surface_mesh/gwdwg.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/BGL/examples/BGL_surface_mesh/write_inp.cpp b/BGL/examples/BGL_surface_mesh/write_inp.cpp index 7554bed208d..aedd8f539fb 100644 --- a/BGL/examples/BGL_surface_mesh/write_inp.cpp +++ b/BGL/examples/BGL_surface_mesh/write_inp.cpp @@ -1,5 +1,6 @@ #include #include +#include #include diff --git a/BGL/include/CGAL/boost/graph/generators.h b/BGL/include/CGAL/boost/graph/generators.h index a4e91f177fb..6454253811d 100644 --- a/BGL/include/CGAL/boost/graph/generators.h +++ b/BGL/include/CGAL/boost/graph/generators.h @@ -19,23 +19,15 @@ #include #include +#include +#include +#include + #include #include #include namespace CGAL { -namespace Euler { - -// Some forward declaration to break the helpers.h > generators.h > Euler_operations.h cycle -template< typename Graph> -void fill_hole(typename boost::graph_traits::halfedge_descriptor h, - Graph& g); - -template -typename boost::graph_traits::face_descriptor add_face(const VertexRange& vr, - Graph& g); - -} // namespace Euler namespace internal { @@ -126,7 +118,7 @@ random_face_in_mesh(const Graph& g, CGAL::Random& rnd = get_default_random()) } // namespace internal /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * * \brief creates an isolated triangle * with its vertices initialized to `p0`, `p1` and `p2`, and adds it to the graph `g`. @@ -256,7 +248,7 @@ struct Default_grid_maker } // namespace internal /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * * \brief creates an isolated quad with * its vertices initialized to `p0`, `p1`, `p2`, and `p3`, and adds it to the graph `g`. @@ -287,17 +279,35 @@ make_quad(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g) } /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * \brief creates an isolated hexahedron * with its vertices initialized to `p0`, `p1`, ...\ , and `p7`, and adds it to the graph `g`. * \image html hexahedron.png * \image latex hexahedron.png - * \returns the halfedge that has the target vertex associated with `p0`, in the face with the vertices with the points `p0`, `p1`, `p2`, and `p3`. + * \returns the halfedge that has the target vertex associated with `p0`, + * in the face with the vertices with the points `p0`, `p1`, `p2`, and `p3` + * (or `p0`, `p2` and `p3` when `do_not_triangulate` is set to `false`). + * + * \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" + * \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" + * among the ones listed below + * \cgalNamedParamsBegin + * \cgalParamNBegin{do_not_triangulate_faces} + * \cgalParamDescription{a Boolean used to specify whether the hexadron's faces + * should be triangulated or not. + * The default value is `true`, and faces are not triangulated.} + * \cgalParamDefault{true} + * \cgalParamNEnd + * \cgalNamedParamsEnd **/ -template +template typename boost::graph_traits::halfedge_descriptor make_hexahedron(const P& p0, const P& p1, const P& p2, const P& p3, - const P& p4, const P& p5, const P& p6, const P& p7, Graph& g) + const P& p4, const P& p5, const P& p6, const P& p7, + Graph& g, + const NamedParameters& np = parameters::default_values()) { typedef typename boost::graph_traits Traits; typedef typename Traits::halfedge_descriptor halfedge_descriptor; @@ -306,6 +316,9 @@ make_hexahedron(const P& p0, const P& p1, const P& p2, const P& p3, typedef typename boost::property_map::type Point_property_map; Point_property_map ppmap = get(CGAL::vertex_point, g); + const bool triangulate = !parameters::choose_parameter( + parameters::get_parameter(np, internal_np::do_not_triangulate_faces), true); + vertex_descriptor v0, v1, v2, v3, v4, v5, v6, v7; v0 = add_vertex(g); v1 = add_vertex(g); @@ -326,6 +339,14 @@ make_hexahedron(const P& p0, const P& p1, const P& p2, const P& p3, halfedge_descriptor ht = internal::make_quad(v4, v5, v6, v7, g); halfedge_descriptor hb = prev(internal::make_quad(v0, v3, v2, v1, g), g); + + std::array he_faces; + if(triangulate) + { + he_faces[0] = hb; + he_faces[1] = ht; + } + for(int i=0; i <4; ++i) { halfedge_descriptor h = halfedge(add_edge(g), g); @@ -342,14 +363,72 @@ make_hexahedron(const P& p0, const P& p1, const P& p2, const P& p3, for(int i=0; i <4; ++i) { Euler::fill_hole(opposite(hb, g), g); + if(triangulate) + he_faces[i+2] = opposite(hb, g); hb = next(hb, g); } + if(triangulate) + { + for (halfedge_descriptor hi : he_faces) + { + halfedge_descriptor nnhi = next(next(hi, g), g); + Euler::split_face(hi, nnhi, g); + } + } + return next(next(hb, g), g); } /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct + * \brief creates an isolated hexahedron + * equivalent to `c`, and adds it to the graph `g`. + * \returns the halfedge that has the target vertex associated with `c.min()`, + * aligned with x-axis, + * in the bottom face of the cuboid. + * + * \tparam IsoCuboid a model of `IsoCuboid_3` + * \tparam Graph a model of `MutableFaceGraph` + * \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" + * + * \param c the iso-cuboid describing the geometry of the hexahedron + * \param g the graph to which the hexahedron will be appended + * \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" + * among the ones listed below + * \cgalNamedParamsBegin + * \cgalParamNBegin{do_not_triangulate_faces} + * \cgalParamDescription{a Boolean used to specify whether the hexadron's faces + * should be triangulated or not. + * The default value is `true`, and faces are not triangulated.} + * \cgalParamDefault{true} + * \cgalParamNEnd + * \cgalParamNBegin{geom_traits} + * \cgalParamDescription{an instance of a geometric traits class model of `Kernel`.} + * \cgalParamNEnd + * \cgalNamedParamsEnd + **/ +template +typename boost::graph_traits::halfedge_descriptor +make_hexahedron(const IsoCuboid& c, + Graph& g, + const NamedParameters& np = parameters::default_values()) +{ + using GT = typename GetGeomTraits::type; + GT gt = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::geom_traits)); + typename GT::Construct_vertex_3 v = gt.construct_vertex_3_object(); + + return CGAL::make_hexahedron(v(c, 0), v(c, 1), v(c, 2), v(c, 3), + v(c, 4), v(c, 5), v(c, 6), v(c, 7), + g, + np); +} + +/** + * \ingroup PkgBGLGeneratorFct * \brief creates an isolated tetrahedron * with its vertices initialized to `p0`, `p1`, `p2`, and `p3`, and adds it to the graph `g`. * \image html tetrahedron.png @@ -447,7 +526,7 @@ make_tetrahedron(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g) } /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * * \brief creates a triangulated regular prism, outward oriented, * having `nb_vertices` vertices in each of its bases and adds it to the graph `g`. @@ -547,7 +626,7 @@ make_regular_prism(typename boost::graph_traits::vertices_size_type nb_ve } /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * \brief creates a pyramid, outward oriented, having `nb_vertices` vertices in its base and adds it to the graph `g`. * * If `center` is `(0, 0, 0)`, then the first point of the base is `(radius, 0, 0)` @@ -635,7 +714,7 @@ make_pyramid(typename boost::graph_traits::vertices_size_type nb_vertices } /** - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * * \brief creates an icosahedron, outward oriented, centered in `center` and adds it to the graph `g`. * @@ -730,7 +809,7 @@ make_icosahedron(Graph& g, } /*! - * \ingroup PkgBGLHelperFct + * \ingroup PkgBGLGeneratorFct * * \brief creates a row major ordered grid with `i` cells along the width and `j` cells * along the height and adds it to the graph `g`. diff --git a/BGL/include/CGAL/boost/graph/helpers.h b/BGL/include/CGAL/boost/graph/helpers.h index 43016f9889f..32b99ba397d 100644 --- a/BGL/include/CGAL/boost/graph/helpers.h +++ b/BGL/include/CGAL/boost/graph/helpers.h @@ -1081,8 +1081,5 @@ int halfedge_index_in_face(typename boost::graph_traits::halfedge_descrip } // namespace CGAL -// Here at the bottom because helpers.h must include generators (for backward compatibility reasons), -// and Euler_operations.h needs helpers.h -#include #endif // CGAL_BOOST_GRAPH_HELPERS_H diff --git a/BGL/include/CGAL/draw_face_graph.h b/BGL/include/CGAL/draw_face_graph.h index 7e1de8031ec..11c1da1d618 100644 --- a/BGL/include/CGAL/draw_face_graph.h +++ b/BGL/include/CGAL/draw_face_graph.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2020 GeometryFactory (France) +// Copyright (c) 2018-2022 GeometryFactory (France) // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -9,199 +9,151 @@ // // Author(s) : Guillaume Damiand // Laurent Rineau +// Mostafa Ashraf #ifndef CGAL_DRAW_FACE_GRAPH_H #define CGAL_DRAW_FACE_GRAPH_H -#ifdef CGAL_USE_BASIC_VIEWER - +#include +#include #include #include #include namespace CGAL { -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorFaceGraph +namespace draw_function_for_FG { + +template +void compute_elements(const FG &fg, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) { - template - CGAL::IO::Color operator()(const Graph& /*g*/, - typename boost::graph_traits::face_descriptor /*f*/) const + using Point=typename boost::property_map_value::type; + using Kernel = typename CGAL::Kernel_traits::Kernel; + using Vector = typename Kernel::Vector_3; + + auto vnormals = get(CGAL::dynamic_vertex_property_t(), fg); + auto point_pmap = get(CGAL::vertex_point, fg); + for (auto v : vertices(fg)) { - return get_random_color(CGAL::get_default_random()); - } - - // edges and vertices are black by default - template - CGAL::IO::Color operator()(const Graph& /*g*/, - typename boost::graph_traits::edge_descriptor /*e*/) const - { - return IO::black(); - } - - template - CGAL::IO::Color operator()(const Graph& /*g*/, - typename boost::graph_traits::vertex_descriptor /*v*/) const - { - return IO::black(); - } -}; - -class SimpleFaceGraphViewerQt : public Basic_viewer_qt -{ - using Base = Basic_viewer_qt; - -public: - SimpleFaceGraphViewerQt(QWidget* parent) : - Base(parent, "", false, true, true, true, false), - m_compute_elements_impl([]{}) - { - } - - /// Construct the viewer. - /// @param g the face graph to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big objects where this time could be long) - template - SimpleFaceGraphViewerQt(QWidget* parent, - const Graph& g, - const char* title="Basic Face Graph Viewer", - bool anofaces=false) : - SimpleFaceGraphViewerQt(parent, g, title, anofaces, DefaultColorFunctorFaceGraph()) - { - } - - template - SimpleFaceGraphViewerQt(QWidget* parent, - const Graph& g, - const char* title, - bool anofaces, - ColorFunctor fcolor) : - // First draw: no vertex; edges, faces; mono-color; inverse normal - Base(parent, title, false, true, true, true, false), - m_compute_elements_impl(compute_elements_functor(g, anofaces, fcolor)) - { - } - - void init() override { - compute_elements(); - Base::init(); - } - - void compute_elements() { - m_compute_elements_impl(); - } - - template - void set_face_graph(const Graph& g, - bool anofaces, - ColorFunctor fcolor) { - m_compute_elements_impl = compute_elements_functor(g, anofaces, fcolor); - } - - template - void set_face_graph(const Graph& g, - bool anofaces=false) { - set_mesh(g, anofaces, DefaultColorFunctorFaceGraph()); - } -protected: - template - std::function - compute_elements_functor(const Graph& g, - bool anofaces, - ColorFunctor fcolor) - { - using Point = typename boost::property_map_value::type; - using Kernel = typename CGAL::Kernel_traits::Kernel; - using Vector = typename Kernel::Vector_3; - - auto vnormals = get(CGAL::dynamic_vertex_property_t(), g); - auto point_pmap = get(CGAL::vertex_point, g); - for (auto v : vertices(g)) + Vector n(NULL_VECTOR); + int i = 0; + for (auto h : halfedges_around_target(halfedge(v, fg), fg)) { - Vector n(NULL_VECTOR); - int i=0; - for (auto h : halfedges_around_target(halfedge(v, g), g)) + if (!is_border(h, fg)) { - if (!is_border(h, g)) + Vector ni = CGAL::cross_product( + Vector(get(point_pmap, source(h, fg)), + get(point_pmap, target(h, fg))), + Vector(get(point_pmap, target(h, fg)), + get(point_pmap, target(next(h, fg), fg)))); + if (ni!=NULL_VECTOR) { - Vector ni = CGAL::cross_product( - Vector(get(point_pmap, source(h, g)), get(point_pmap, target(h, g))), - Vector(get(point_pmap, target(h, g)), get(point_pmap, target(next(h, g), g)))); - if (ni != NULL_VECTOR) - { - n+=ni; - ++i; - } + n += ni; + ++i; } } - put(vnormals, v, n/i); } - - // This function return a lambda expression, type-erased in a - // `std::function` object. - return [this, &g, vnormals, anofaces, fcolor, point_pmap]() - { - this->clear(); - - if (!anofaces) - { - for (auto fh: faces(g)) - { - const CGAL::IO::Color& c = fcolor(g, fh); - face_begin(c); - auto hd=halfedge(fh, g); - const auto first_hd = hd; - do - { - auto v = source(hd, g); - add_point_in_face(get(point_pmap, v), get(vnormals, v)); - hd=next(hd, g); - } - while(hd!=first_hd); - face_end(); - } - } - - for (auto e: edges(g)) - { - const CGAL::IO::Color& c = fcolor(g, e); - add_segment(get(point_pmap, source(halfedge(e, g), g)), - get(point_pmap, target(halfedge(e, g), g)), - c); - } - - for (auto v: vertices(g)) - { - const CGAL::IO::Color& c = fcolor(g, v); - this->add_point(get(point_pmap, v), c); - } - }; + put(vnormals, v, n / i); } - void keyPressEvent(QKeyEvent *e) override + if (gs_options.are_faces_enabled()) { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); + for (auto fh : faces(fg)) + { + if (fh != boost::graph_traits::null_face() && // face exists + gs_options.draw_face(fg, fh)) // face is drawn + { + if(gs_options.colored_face(fg, fh)) // face is colored + { graphics_scene.face_begin(gs_options.face_color(fg, fh)); } + else + { graphics_scene.face_begin(); } + auto hd = halfedge(fh, fg); + const auto first_hd = hd; + do + { + auto v = source(hd, fg); + graphics_scene.add_point_in_face(get(point_pmap, v), get(vnormals, v)); + hd = next(hd, fg); + } + while (hd != first_hd); + graphics_scene.face_end(); + } + } } -protected: - std::function m_compute_elements_impl; -}; + if(gs_options.are_edges_enabled()) + { + for (auto e : edges(fg)) + { + if(gs_options.colored_edge(fg, e)) // edge is colored + { + graphics_scene.add_segment(get(point_pmap, source(halfedge(e, fg), fg)), + get(point_pmap, target(halfedge(e, fg), fg)), + gs_options.edge_color(fg, e)); + } + else + { + graphics_scene.add_segment(get(point_pmap, source(halfedge(e, fg), fg)), + get(point_pmap, target(halfedge(e, fg), fg))); + } + } + } + + if(gs_options.are_vertices_enabled()) + { + for (auto v : vertices(fg)) + { + if(gs_options.colored_vertex(fg, v)) // vertex is colored + { + graphics_scene.add_point(get(point_pmap, v), + gs_options.vertex_color(fg, v)); + } + else + { + graphics_scene.add_point(get(point_pmap, v)); + } + } + } +} + +} // draw_function_for_FG + +template +void add_to_graphics_scene_for_fg(const FG &fg, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) +{ + draw_function_for_FG::compute_elements(fg, graphics_scene, gs_options); +} + +template +void add_to_graphics_scene_for_fg(const FG &fg, + CGAL::Graphics_scene &graphics_scene) +{ + Graphics_scene_options::vertex_descriptor, + typename boost::graph_traits::edge_descriptor, + typename boost::graph_traits::face_descriptor> + gs_options; + + gs_options.colored_face = [](const FG&, + typename boost::graph_traits::face_descriptor) -> bool + { return true; }; + + gs_options.face_color = [] (const FG&, + typename boost::graph_traits::face_descriptor fh) -> CGAL::IO::Color + { + if (fh==boost::graph_traits::null_face()) + { return CGAL::IO::Color(100, 125, 200); } + + return get_random_color(CGAL::get_default_random()); + }; + + add_to_graphics_scene_for_fg(fg, graphics_scene, gs_options); +} } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_SURFACE_MESH_H diff --git a/BGL/test/BGL/next.cpp b/BGL/test/BGL/next.cpp index fd69cc00304..4332aa86637 100644 --- a/BGL/test/BGL/next.cpp +++ b/BGL/test/BGL/next.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef K::Point_3 Point_3; diff --git a/BGL/test/BGL/test_Collapse_edge_with_constraints.cpp b/BGL/test/BGL/test_Collapse_edge_with_constraints.cpp index a8f84f6e654..34d704bb679 100644 --- a/BGL/test/BGL/test_Collapse_edge_with_constraints.cpp +++ b/BGL/test/BGL/test_Collapse_edge_with_constraints.cpp @@ -1,6 +1,7 @@ #include #include #include +#include typedef CGAL::Simple_cartesian K; typedef K::Point_3 Point_3; diff --git a/BGL/test/BGL/test_Euler_operations.cpp b/BGL/test/BGL/test_Euler_operations.cpp index fe7c3275dd1..3fdcfe0edec 100644 --- a/BGL/test/BGL/test_Euler_operations.cpp +++ b/BGL/test/BGL/test_Euler_operations.cpp @@ -2,6 +2,7 @@ #include "test_Prefix.h" #include #include +#include #include #include diff --git a/BGL/test/BGL/test_Face_filtered_graph.cpp b/BGL/test/BGL/test_Face_filtered_graph.cpp index 9623d2868bd..9917b7d06a5 100644 --- a/BGL/test/BGL/test_Face_filtered_graph.cpp +++ b/BGL/test/BGL/test_Face_filtered_graph.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "test_Prefix.h" diff --git a/BGL/test/BGL/test_test_face.cpp b/BGL/test/BGL/test_test_face.cpp index aa4e0fa0879..7afbc79385b 100644 --- a/BGL/test/BGL/test_test_face.cpp +++ b/BGL/test/BGL/test_test_face.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies b/Barycentric_coordinates_2/package_info/Barycentric_coordinates_2/dependencies index ee1e1d6538a..09b948be599 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 @@ -20,7 +21,6 @@ Number_types Polygon Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Stream_support diff --git a/Basic_viewer/doc/Basic_viewer/Basic_viewer.txt b/Basic_viewer/doc/Basic_viewer/Basic_viewer.txt new file mode 100644 index 00000000000..970ddbd5f5f --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/Basic_viewer.txt @@ -0,0 +1,173 @@ +namespace CGAL { +/*! + +\mainpage User Manual +\anchor Chapter_Basic_viewer +\anchor ChapterBasicViewer + +\author Guillaume Damiand and Mostafa Ashraf +\cgalAutoToc + + +\section BV_Intro Introduction + +The basic viewer package provides interactive visualization for most \cgal packages, such as +\ref PkgArrangementOnSurface2Ref "2D Arrangements", +\ref PkgBooleanSetOperations2Ref "2D Boolean Set", +\ref PkgLinearCellComplexRef "Linear cell complex", +\ref PkgNef3Ref "3D Nef", +\ref PkgPeriodic2Triangulation2Ref "2D Periodic Triangulations", +\ref PkgPointSet3Ref "3D Point Set", +\ref PkgPolygon2Ref "2D Polygon", +\ref PkgPolyhedronRef "3D Polyhedral Surface", +\ref PkgStraightSkeleton2Ref "2D Straight Skeleton", +\ref PkgSurface_mesh "Surface Mesh", +\ref PkgTriangulation2Ref "2D Triangulation", +\ref PkgTriangulation3Ref "3D Triangulation", +\ref PkgVoronoiDiagram2Ref "2D Voronoi Diagram", +and more. + +The most simple use case of the basic viewer is the call of the global `draw()` function. There is one draw function for each \cgal package. Such a call opens a new interactive window showing the given model and allowing to navigate in the scene, show or hide some specific cells, show the interior of the model if any, etc. + +The drawing can be tuned using `Graphics_scene_options`, for example to change the color of some cells or hide some cells. + +More complex usage can be achieved by using the `Graphics_scene` class. Different \cgal data structures can be added in a same `Graphics_scene` allowing to visualize them simultaneously in a same window. Like for the draw function, the drawing parameters can be tuned thanks to the `Graphics_scene_options`. + +Two classes are based on \qt. + - `CGAL::Qt::Basic_viewer` is a \qt widget that inherits from `QGLViewer` (libQGLViewer is an open source C++ library based on \qt that eases the creation of OpenGL 3D viewers). + - `CGAL::Qt::QApplication_and_basic_viewer` which regroups a `Qt::QApplication` and a `CGAL::Qt::Basic_viewer`. + + +\section BV_SoftwareDesign Software Design + +The diagram in \cgalFigureRef{fig_basic_viewer_diagram_class} shows the main classes and functions of this package. + +\cgalFigureBegin{fig_basic_viewer_diagram_class,basic_viewer_diagramme_class.svg} +UML diagram of the classes and functions of the package (only partial representation). +\cgalFigureEnd + + +\section BV_BasicUsage Basic Usage: The Global Draw Functions + +A first simple solution provided to draw the different data structures of \cgal is the global `draw()` function. This function is templated by the type of the data structure to draw. The following example shows how it can be used. + +\cgalExample{Basic_viewer/draw_lcc.cpp} + +This example creates a 3D linear cell complex of a hexahedron, and draws it. The result is shown in the following figure. + +\cgalFigureBegin{basic_viewer_ex1,lcc_draw.png} +Example of drawing of a 3D LCC. +\cgalFigureEnd + +Users can interact with the viewer using some specific keys or mouse interactions. Pressing key 'h' makes pop up a help window showing the different shortcuts and mouse interactions. The main functionalities of the viewer are: + +
    +
  • Versatile Rendering: + +The viewer supports the rendering of various geometric elements, such as points, edges, faces, rays, and lines. +Different rendering modes, including mono and colored representation, are available for these elements.
  • + +
  • Camera Control: + +The viewer allows users to move the camera, switch between 2D and 3D viewing modes, adjusting the camera accordingly. +Camera settings, such as orthographic or perspective projection, can be configured based on the dimension of the scene.
  • + +
  • User Interaction: + +Users can interact with the viewer through keyboard inputs, enabling them to control rendering options, toggle the display of elements, and adjust visual parameters. +Key presses are mapped to specific actions, such as toggling the clipping plane, changing rendering modes, adjusting the size of elements, and modifying ambient light color.
  • + +
  • Clipping Plane: + +The viewer includes support for a clipping plane, enabling users to selectively render parts of the scene. +The clipping plane can be toggled on and off, and its rendering style can be modified (solid, wireframe, etc.).
  • +
+ +\subsection BV_GraphicSceneOptions Tuning with Graphics Scene Options + +There is one specialization of each draw function that takes graphics scene options as additional parameter, allowing to tune the drawing. + +The `Graphics_scene_options` class provides a set of options and customization parameters for rendering geometric structures in a graphics scene. Its main purpose is to allow users to control the visual appearance of various cells such as vertices, edges, faces, and volumes in a graphical representation of a given data structure. + +The following example shows how to use graphics scene options to tune the drawing of a surface mesh. We define our own class `My_graphics_scene_options` that inherits from `Graphics_scene_options` to get all the default parameters. In this class, we only override the two methods `colored_vertex` and `vertex_color` to draw all vertices in color, and chose randomly green or blue colors for one out of two vertices. + +\cgalExample{Basic_viewer/draw_surface_mesh_vcolor.cpp} + +The result of this example can be shown in \cgalFigureRef{basic_viewer_ex2}. + +\cgalFigureBegin{basic_viewer_ex2,draw_surface_mesh_vcolor.png} +Example of tuned drawing of a 3D surface mesh where some vertex colors are changed. +\cgalFigureEnd + +The drawing options can depend on the cells. In the following example the color of each face depends on its height. + +\cgalExample{Basic_viewer/draw_surface_mesh_height.cpp} + +The result of this example is shown in \cgalFigureRef{basic_viewer_ex3}. + +\cgalFigureBegin{basic_viewer_ex3,draw_surface_height.png} +Example of mesh drawing with a color for each face computed depending on its height. +\cgalFigureEnd + + +\section BV_GraphicsScene The Graphics Scene + +It is possible to do more advanced visualizations by using the class `Graphics_scene`. It is a container class for various geometric elements, such as points, segments, rays, lines, faces and texts. It provides several methods to add elements, possibly with individual colors. Things are a little bit different for faces. You must call `face_begin()` to start a new face (possibly with a color), add all the points in the face by using `add_point_in_face()`, and call `face_end()` to finish the face. Note that the given polygonal face is internally triangulated. + +This class also stores the normal of the faces (computed automatically), and possibly the normal of the vertices that can be provided by the users. + +Users can either fill directly a `Graphics_scene` using these methods, or by using a global function `add_to_graphics_scene()` that fills the scene using all the geometric elements of a given \cgal data structure. Like for the draw functions, there is an overload of `add_to_graphics_scene()` for each \cgal package. + +A `Graphics_scene` can be drawn using the basic viewer thanks to the `draw_graphics_scene()` function. + +The following example shows in a same viewer both a `Point_set_3` and a `Polyhedron_3`. Note that, like the draw functions, each `add_to_graphics_scene()` can use an optional `Graphics_scene_options` as parameter allowing to tune the drawing of the given data structure. In the example, we change the color of all vertices of the point set. + +\cgalExample{Basic_viewer/draw_mesh_and_points.cpp} + +The result of this example is shown in \cgalFigureRef{basic_viewer_ex4} where we can see in green the original points of the point cloud, superposed with the polyhedron surface reconstructed by the Poisson surface reconstruction method. + +\cgalFigureBegin{basic_viewer_ex4,draw_mesh_and_points.png} +Example of drawing of a point cloud and a polyhedron in a same viewer. +\cgalFigureEnd + + +\section BV_BasicViewer The Basic Viewer Class + +The class `CGAL::Qt::Basic_viewer` is a \qt widget that inherits from `QGLViewer` and mainly stores a `Graphics_scene` and allows to visualize it and interact with the scene. Since this class is a \qt widget, it can be used into more complex \qt code to create more advanced demos. + +In the following example, we create two graphics scenes, one filled with a point cloud, a second one filled with the polyhedral surface reconstructed by the Poisson surface reconstruction method. Then, we create two basic viewers associated with these two scenes. The two basic viewers are added into a \qt layout allowing to visualize the two scenes side by side. + +\cgalExample{Basic_viewer/draw_several_windows.cpp} + +The result of this example is shown in \cgalFigureRef{basic_viewer_ex5}. + +\cgalFigureBegin{basic_viewer_ex5,draw_several_windows.png} +Example of drawing of two `Basic_viewer` side by side. +\cgalFigureEnd + + + +\section BV_Interactions Adding Interaction + +In the previous examples, the models are only drawn once and there is no interaction with the user to update the drawing. + +It is possible to define such interactions thanks to the class `CGAL::Qt::QApplication_and_basic_viewer`. In this class, you can define your own function that is called automatically by the viewer when a user presses a key. This can be used to change some parameters and update the drawing accordingly. + +We illustrate this possibility in the following example that shows a surface mesh, while coloring the small faces in red. To do so, we use our own graphics scene options than change the color of a face depending on its size. A face is considered small if its size is below a certain threshold. This threshold can be updated by the user, pressing key 'I' to increase it and 'D' to decrease it. This is done in the key pressed function defined in the `QApplication_and_basic_viewer`. When the threshold changes, the graphics scene is recomputed to take the modification of the size threshold into account. + +\cgalExample{Basic_viewer/draw_surface_mesh_small_faces.cpp} + +The result of this example is shown in \cgalFigureRef{basic_viewer_ex6}, showing to the left the initial drawing of the 3D model, and to the right the same model after having changed the size threshold. + +\cgalFigureBegin{basic_viewer_ex6,draw_surface_mesh_small_faces.png} +Two examples of drawing of a mesh with small faces in red. Left: With the initial threshold. Right: After having increased the threshold. +\cgalFigureEnd + + +\section BV_Design Design and Implementation History + +This package was started by Mostafa Ashraf during his 2022 GSoC project. Guillaume Damiand, who mentored the project, reworked large parts of the package, wrote examples and the manual. + +*/ + +} /* namespace CGAL */ diff --git a/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene.h b/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene.h new file mode 100644 index 00000000000..8d1abd593f8 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene.h @@ -0,0 +1,107 @@ + +namespace CGAL { + +/*! + \ingroup PkgBasicViewerClasses + +The class `Graphics_scene` stores points, segments, triangles, rays, and lines. Elements can be added, possibly with associated colors. Non triangular faces can be directly added and are triangulated internally. + +*/ +class Graphics_scene { +public: + /// adds the given point in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_point(const KPoint &p); + + /// adds the given colored point in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_point(const KPoint &p, const CGAL::IO::Color &color); + + /// adds the given segment in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_segment(const KPoint &p1, const KPoint &p2); + + /// adds the given colored segment in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_segment(const KPoint &p1, const KPoint &p2, + const CGAL::IO::Color &color); + + /// adds the given ray in the scene: a half line starting from `p` and having `v` as direction. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + /// \tparam KVector a model of `Kernel::Vector_2` or `Kernel::Vector_3`. + template + void add_ray(const KPoint &p, const KVector &v); + + /// adds the given colored ray in the scene: a half line starting from `p` and having `v` as direction. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + /// \tparam KVector a model of `Kernel::Vector_2` or `Kernel::Vector_3`. + template + void add_ray(const KPoint &p, const KVector &v, + const CGAL::IO::Color &color); + + /// adds the given line in the scene, defined by `p` and `v` as direction. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + /// \tparam KVector a model of `Kernel::Vector_2` or `Kernel::Vector_3`. + template + void add_line(const KPoint &p, const KVector &v); + + /// adds the given colored line in the scene, defined by `p` and `v` as direction. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + /// \tparam KVector a model of `Kernel::Vector_2` or `Kernel::Vector_3`. + template + void add_line(const KPoint &p, const KVector &v, + const CGAL::IO::Color &color); + + /// starts a new face. + void face_begin(); + + /// starts a new colored face. + void face_begin(const CGAL::IO::Color &color); + + /// return `true` iff a face is started. + bool a_face_started() const; + + /// adds the given point in the current face. + /// @pre `a_face_started()` + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template bool add_point_in_face(const KPoint &kp); + + /// adds the given point in the current face, having the vertex normal. + /// @pre `a_face_started()` + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + /// \tparam KVector a model of `Kernel::Vector_2` or `Kernel::Vector_3`. + template + bool add_point_in_face(const KPoint &kp, const KVector &p_normal); + + /// ends the current face. + /// @pre `a_face_started()` + void face_end(); + + /// adds the given text at the given position in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_text(const KPoint &kp, const char *txt); + + /// adds the given text at the given position in the scene. + /// \tparam KPoint a model of `Kernel::Point_2`, `Kernel::Point_3`, `Kernel::WeightedPoint_2` or `Kernel::WeightedPoint_3`. + template + void add_text(const KPoint &kp, const std::string &txt); + + /// returns `true` iff the scene has no element. + bool empty() const; + + /// clears the scene, i.e., removes all points, segments, triangles, and text. + void clear(); + + /// returns the bounding box of all the elements in the scene. + const CGAL::Bbox_3& bounding_box() const; + + /// returns `true` if the scene is in 2D, i.e., lies on the XY or XZ or YZ plane. + bool is_two_dimensional() const; +}; + +} // namespace CGAL diff --git a/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene_options.h b/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene_options.h new file mode 100644 index 00000000000..ac2fa1dc3ed --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/CGAL/Graphics_scene_options.h @@ -0,0 +1,37 @@ + +namespace CGAL { + +/*! +\ingroup PkgBasicViewerClasses + +The class `Graphics_scene_options` is used to tune the way that the cells of a given data structure of \cgal are considered. +The different `std::function` can be modified to change for example the behavior of the drawing. +`VolumeDescriptor` can be `void` for data structures that do not represent volumes. + +This class is a model of `GraphicsSceneOptions` when `VolumeDescriptor` is `void`, or a model of `GraphicsSceneOptionsWithVolumes` otherwise (`VolumeDescriptor` non `void`). + +\tparam DS a data structure of \cgal. +\tparam VertexDescriptor a descriptor of vertices of `DS`. +\tparam EdgeDescriptor a descriptor of edges of `DS`. +\tparam FaceDescriptor a descriptor of faces of `DS`. +\tparam VolumeDescriptor a descriptor of volumes of `DS`. `void` by default. + +\cgalModels{GraphicsSceneOptions or GraphicsSceneOptionsWithVolumes} +*/ + +template +struct Graphics_scene_options +{ +public: + typedef VertexDescriptor vertex_descriptor; + typedef EdgeDescriptor edge_descriptor; + typedef FaceDescriptor face_descriptor; + typedef VolumeDescriptor volume_descriptor; + +}; + +} // End namespace CGAL diff --git a/Basic_viewer/doc/Basic_viewer/CGAL/Qt/Basic_viewer.h b/Basic_viewer/doc/Basic_viewer/CGAL/Qt/Basic_viewer.h new file mode 100644 index 00000000000..6ff91f29739 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/CGAL/Qt/Basic_viewer.h @@ -0,0 +1,177 @@ +namespace CGAL { + namespace Qt { + +//------------------------------------------------------------------------------ +/*! + \ingroup PkgBasicViewerClasses + +The class `Basic_viewer` is a Qt widget based on `QGLViewer` that allows to visualize 3D elements: points, segments, triangles, rays and lines. This class stores a reference to a `Graphics_scene`. Elements are added through the scene. This class 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`. + +CGAL::QGLViewer is our internal fork of QGLViewer class which is A versatile 3D OpenGL viewer based on QOpenGLWidget. +*/ +class Basic_viewer : public CGAL::QGLViewer +{ +public: + /// Constructor given a pointer on a `QWidget` (can be a `nullptr`) and a `Graphics_scene`. + /// `title` will be the title of the window. + Basic_viewer(QWidget* parent, + const Graphics_scene& scene, + const char* title=""); + + /// enables or disables the drawing of vertices. + void draw_vertices(bool b); + + /// enables or disables the drawing of edges. + void draw_edges(bool b); + + /// enables or disables the drawing of rays. + void draw_rays(bool b); + + /// enables or disables the drawing of lines. + void draw_lines(bool b); + + /// enables or disables the drawing of faces. + void draw_faces(bool b); + + /// enables or disables the use of only one color (if `b` is `true`) or the use of multiple colors (if `b` is `false`). + void use_mono_color(bool b); + + /// enables or disables the drawing of texts. + void draw_text(bool b); + + /// sets the color used for vertices in mono color mode. + void vertices_mono_color(const CGAL::IO::Color& c); + + /// sets the color used for edges in mono color mode. + void edges_mono_color(const CGAL::IO::Color& c); + + /// sets the color used for rays in mono color mode. + void rays_mono_color(const CGAL::IO::Color& c); + + /// sets the color used for lines in mono color mode. + void lines_mono_color(const CGAL::IO::Color& c); + + /// sets the color used for faces in mono color mode. + void faces_mono_color(const CGAL::IO::Color& c); + + /// toggles the drawing of vertices. + void toggle_draw_vertices(); + + /// toggles the drawing of edges. + void toggle_draw_edges(); + + /// toggles the drawing of rays. + void toggle_draw_rays(); + + /// toggles the drawing of lines. + void toggle_draw_lines(); + + /// toggles the drawing of faces. + void toggle_draw_faces(); + + /// toggles the use of mono color mode. + void toggle_use_mono_color(); + + /// toggles the drawing of text. + void toggle_draw_text(); + + /// returns `true` if vertices are drawn. + bool draw_vertices() const; + + /// returns `true` if edges are drawn. + bool draw_edges() const; + + /// returns `true` if rays are drawn. + bool draw_rays() const; + + /// returns `true` if lines are drawn. + bool draw_lines() const; + + /// returns `true` if faces are drawn. + bool draw_faces() const; + + /// returns `true` if mono color mode is used. + bool use_mono_color() const; + + /// returns `true` if normals are reversed. + bool reverse_normal() const; + + /// returns `true` if text are drawn. + bool draw_text() const; + + /// returns the mono color used for vertices. + const CGAL::IO::Color& vertices_mono_color() const; + + /// returns the mono color used for edges. + const CGAL::IO::Color& edges_mono_color() const; + + /// returns the mono color used for rays. + const CGAL::IO::Color& rays_mono_color() const; + + /// returns the mono color used for lines. + const CGAL::IO::Color& lines_mono_color() const; + + /// returns the mono color used for faces. + const CGAL::IO::Color& faces_mono_color() const; + + /// returns `true` if the clipping plane is enabled. + bool clipping_plane_enabled() const; + + /// returns the clipping plane when it is enabled. + CGAL::Exact_predicates_inexact_constructions_kernel::Plane_3 clipping_plane() const; + + /// returns the graphics scene of the viewer. + const Graphics_scene& graphics_scene() const; + + /// reverses all normals of vertices and faces. + void reverse_all_normals(); + + /// draws the viewer without recomputing all internal buffers. + virtual void draw(); + + /// redraws the viewer, i.e., recompute all internal buffers and update the window. + virtual void redraw(); + + /// Function called when a key is pressed. Users can define their own function in order + /// to add specific behavior. + std::function on_key_pressed; +}; + + +//------------------------------------------------------------------------------ +/*! + \ingroup PkgBasicViewerClasses + +The class `QApplication_and_basic_viewer` regroups a `Basic_viewer` and Qt `QApplication`. The `QApplication` is created in the constructor, but started by the `run()` method. This allows for example users to modify the `on_key_pressed` method of the `Basic_viewer` to define their own behavior. + +This class 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`. + +*/ +class QApplication_and_basic_viewer +{ +public: + /// Constructor given a `Graphics_scene` and possibly a title. + QApplication_and_basic_viewer(const CGAL::Graphics_scene& gs, + const char* title="CGAL Basic Viewer"); + + /// runs the `QApplication`, i.e., open the Qt window. A call to this method is blocking, that is the program continues as soon as the user closes the window. + void run(); + + /// returns a reference to the `Basic_viewer` associated with this. + Basic_viewer& basic_viewer(); +}; + +} // End namespace Qt + +//------------------------------------------------------------------------------ +/*! + \ingroup PkgBasicViewerClasses + + opens a new window and draws the given `Graphics_scene` (which must have been filled before). `title` will be the title of the window. A call to this method 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. +*/ +void draw_graphics_scene(const Graphics_scene& graphic_scene, + const char *title="CGAL Basic Viewer") +{} + +} // End namespace CGAL + diff --git a/Basic_viewer/doc/Basic_viewer/Concepts/GraphicsSceneOptions.h b/Basic_viewer/doc/Basic_viewer/Concepts/GraphicsSceneOptions.h new file mode 100644 index 00000000000..f845dc79281 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/Concepts/GraphicsSceneOptions.h @@ -0,0 +1,128 @@ +/*! +\ingroup PkgBasicViewerConcepts + +The concept `GraphicsSceneOptions` defines data and methods used to tune the way that the cells of a given data structure of \cgal are considered for drawing or to be added into a graphics scene. +The different `std::function` can be modified to change for example the behavior of the drawing. + +\cgalHasModelsBegin +\cgalHasModelsBare{\link CGAL::Graphics_scene_options `CGAL::Graphics_scene_options`\endlink} +\cgalHasModelsEnd + +*/ +class GraphicsSceneOptions +{ +public: + /*! + A data structure of \cgal. + */ + typedef unspecified_type DS; + + /*! + A descriptor of vertices of `DS` + */ + typedef unspecified_type vertex_descriptor; + + /*! + A descriptor of edges of `DS` + */ + typedef unspecified_type edge_descriptor; + + /*! + A descriptor of faces of `DS` + */ + typedef unspecified_type face_descriptor; + + /// `std::function` that returns `true` if the given vertex must be ignored, `false` otherwise. + /// Returns `false` by default. + std::function ignore_vertex; + + /// `std::function` that returns `true` if the given edge must be ignored, `false` otherwise. + /// Returns `true` by default. + std::function ignore_edge; + + /// `std::function` that returns `true` if the given face must be ignored, `false` otherwise. + /// Returns `true` by default. + std::function ignore_face; + + /// `std::function` that returns `true` if the given vertex is colored, `false` otherwise. + /// Returns `false` by default. + /// For non colored vertices, this is the role of the user of a graphic scene to decide which color must be used (cf. for example `Basic_viewer`, `vertices_mono_color`). + std::function is_vertex_colored; + + /// `std::function` that returns `true` if the given edge is colored, `false` otherwise. + /// For non colored edges, this is the role of the user of a graphic scene to decide which color must be used (cf. for example `Basic_viewer`, `edges_mono_color`). + /// Returns `false` by default. + std::function is_edge_colored; + + /// `std::function` that returns `true` if the given face is colored, `false` otherwise. + /// For non colored faces, this is the role of the user of a graphic scene to decide which color must be used (cf. for example `Basic_viewer`, `faces_mono_color`). + /// Returns `false` by default. + std::function is_face_colored; + + /// `std::function` that returns `true` if the given face is in wireframe, `false` otherwise. + /// Returns `false` by default. + std::function is_face_wireframe; + + /// `std::function` that returns the color of the given vertex. + /// `nullptr` by default. + std::function vertex_color; + + /// `std::function` that returns the color of the given edge. + /// `nullptr` by default. + std::function edge_color; + + /// `std::function` that returns the color of the given face. + /// `nullptr` by default. + std::function face_color; + + /// ignores all vertices when `b` is `true`; otherwise ignores only vertices for which `ignore_vertex()` returns `true`. + void ignore_all_vertices(bool b); + + /// ignores all edges when `b` is `true`; otherwise ignores only edges for which `ignore_edge()` returns `true`. + void ignore_all_edges(bool b); + + /// ignores all faces when `b` is `true`; otherwise ignores only faces for which `ignore_face()` returns `true`. + void ignore_all_faces(bool b); +}; + +/*! +\ingroup PkgBasicViewerConcepts +\cgalConcept + +The concept `GraphicsSceneOptionsWithVolumes` extends the concept `GraphicsSceneOptions` to deal with data structures that represent volumes. + +\cgalRefines{GraphicsSceneOptions} + +\cgalHasModelsBegin +\cgalHasModelsBare{\link CGAL::Graphics_scene_options `CGAL::Graphics_scene_options`\endlink} +\cgalHasModelsEnd + +*/ +class GraphicsSceneOptionsWithVolumes +{ +public: + /*! + %A descriptor of volumes of `DS`. + */ + typedef unspecified_type volume_descriptor; + + /// `std::function` that returns `true` if the given volume must be ignored, `false` otherwise. + /// Returns `false` by default. + std::function ignore_volume; + + /// `std::function` that returns `true` if the given volume is colored, `false` otherwise. + /// For non colored volumes, this is the role of the user of a graphic scene to decide which color must be used (cf. for example `Basic_viewer`, `faces_mono_color`). + /// Returns `false` by default. + std::function is_volume_colored; + + /// `std::function` that returns `true` if the given volume is in wireframe, `false` otherwise. + /// Returns `false` by default. + std::function is_volume_wireframe; + + /// `std::function` that returns the color of the given volume, i.e. the color of all the faces of this volume. + /// `nullptr` by default. + std::function volume_color; + + /// ignores all volumes when `b` is `true`; otherwise ignore only volumes for which `ignore_volume()` returns `true`. + void ignore_all_volumes(bool b); +}; diff --git a/Basic_viewer/doc/Basic_viewer/Doxyfile.in b/Basic_viewer/doc/Basic_viewer/Doxyfile.in new file mode 100644 index 00000000000..8b325940a19 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/Doxyfile.in @@ -0,0 +1,5 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} + +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - CGAL Basic Viewer" + +EXAMPLE_PATH += ${CGAL_Surface_mesh_EXAMPLE_DIR} \ No newline at end of file diff --git a/Basic_viewer/doc/Basic_viewer/PackageDescription.txt b/Basic_viewer/doc/Basic_viewer/PackageDescription.txt new file mode 100644 index 00000000000..d90a773fed5 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/PackageDescription.txt @@ -0,0 +1,83 @@ +/// \defgroup PkgBasicViewerRef Basic Viewer Reference + +/// \defgroup PkgBasicViewerConcepts Concepts +/// \ingroup PkgBasicViewerRef + +/// \defgroup PkgBasicViewerClasses Classes +/// \ingroup PkgBasicViewerRef + +/*! +\addtogroup PkgBasicViewerRef +\cgalPkgDescriptionBegin{Basic Viewer,PkgBasicViewer} +\cgalPkgPicture{basic_viewer_logo.png} +\cgalPkgSummaryBegin +\cgalPkgAuthor{Guillaume Damiand and Mostafa Ashraf} +\cgalPkgDesc{This package provides interactive visualization for most \cgal packages. +First, the function `draw()` is provided for each data structure. +Several data structures can be added to a same graphics scene and visualized simultaneously. +Rendering can be controlled (color, point size, etc.). +Finally a \qt widget is provided that can be integrated in the GUI of a user application.} +\cgalPkgManuals{Chapter_Basic_viewer,PkgBasicViewerRef} +\cgalPkgSummaryEnd +\cgalPkgShortInfoBegin +\cgalPkgSince{6.0} +\cgalPkgBib{cgal:d-bv} +\cgalPkgLicense{\ref licensesLGPL "LGPL"} +\cgalPkgShortInfoEnd +\cgalPkgDescriptionEnd + +\cgalClassifedRefPages + +\cgalCRPSection{Concepts} +- `GraphicsSceneOptions` +- `GraphicsSceneOptionsWithVolumes` + +\cgalCRPSection{Classes} +- `CGAL::Qt::Basic_viewer` +- `CGAL::Graphics_scene` +- `CGAL::Graphics_scene_options` +- `CGAL::Qt::QApplication_and_basic_viewer` + +\cgalCRPSubsection{Draw a Graphics Scene} + +- `CGAL::draw_graphics_scene()` + +\cgalCRPSubsection{Draw for Different Packages} + +- `CGAL::Arrangement_2` \link PkgArrangementOnSurface2Draw CGAL::draw() \endlink +- `CGAL::Constrained_triangulation_2` \link PkgDrawTriangulation2 CGAL::draw() \endlink +- `CGAL::Linear_cell_complex_for_combinatorial_map` \link PkgDrawLinearCellComplex CGAL::draw() \endlink +- `CGAL::Linear_cell_complex_for_generalized_map` \link PkgDrawLinearCellComplex CGAL::draw() \endlink +- `CGAL::Nef_polyhedron_3` \link PkgDrawNef3 CGAL::draw() \endlink +- `CGAL::Periodic_2_triangulation_2` \link PkgDrawPeriodic2Triangulation2 CGAL::draw() \endlink +- `CGAL::Point_set_3` \link PkgDrawPointSet3D CGAL::draw() \endlink +- `CGAL::Polygon_2` \link PkgDrawPolygon2 CGAL::draw() \endlink +- `CGAL::Polygon_set_2` \link PkgDrawPolygonSet2 CGAL::draw() \endlink +- `CGAL::Polygon_with_holes_2` \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink +- `CGAL::Polyhedron_3` \link PkgDrawPolyhedron CGAL::draw() \endlink +- `CGAL::Surface_mesh` \link PkgDrawSurfaceMesh CGAL::draw() \endlink +- `CGAL::Surface_mesh_topology` \link PkgDrawFaceGraphWithPaths CGAL::draw() \endlink +- `CGAL::Triangulation_2` \link PkgDrawTriangulation2 CGAL::draw() \endlink +- `CGAL::Triangulation_3` \link PkgDrawTriangulation3 CGAL::draw() \endlink +- `CGAL::Voronoi_diagram_2` \link PkgDrawVoronoiDiagram2 CGAL::draw() \endlink + +\cgalCRPSubsection{Fill Graphics Scene for Different Packages} + +- `CGAL::Arrangement_2` \link PkgArrangementOnSurface2Draw CGAL::add_to_graphics_scene() \endlink +- `CGAL::Constrained_triangulation_2` \link PkgDrawTriangulation2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Linear_cell_complex_for_combinatorial_map` \link PkgDrawLinearCellComplex CGAL::add_to_graphics_scene() \endlink +- `CGAL::Linear_cell_complex_for_generalized_map` \link PkgDrawLinearCellComplex CGAL::add_to_graphics_scene() \endlink +- `CGAL::Nef_polyhedron_3` \link PkgDrawNef3 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Periodic_2_triangulation_2` \link PkgDrawPeriodic2Triangulation2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Point_set_3` \link PkgDrawPointSet3D CGAL::add_to_graphics_scene() \endlink +- `CGAL::Polygon_2` \link PkgDrawPolygon2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Polygon_set_2` \link PkgDrawPolygonSet2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Polygon_with_holes_2` \link PkgDrawPolygonWithHoles2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Polyhedron_3` \link PkgDrawPolyhedron CGAL::add_to_graphics_scene() \endlink +- `CGAL::Surface_mesh` \link PkgDrawSurfaceMesh CGAL::add_to_graphics_scene() \endlink +- `CGAL::Surface_mesh_topology` \link PkgDrawFaceGraphWithPaths CGAL::add_to_graphics_scene() \endlink +- `CGAL::Triangulation_2` \link PkgDrawTriangulation2 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Triangulation_3` \link PkgDrawTriangulation3 CGAL::add_to_graphics_scene() \endlink +- `CGAL::Voronoi_diagram_2` \link PkgDrawVoronoiDiagram2 CGAL::add_to_graphics_scene() \endlink + +*/ diff --git a/Basic_viewer/doc/Basic_viewer/dependencies b/Basic_viewer/doc/Basic_viewer/dependencies new file mode 100644 index 00000000000..c684a90bb27 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/dependencies @@ -0,0 +1,16 @@ +Manual +Boolean_set_operations_2 +Linear_cell_complex +Nef_3 +Periodic_2_triangulation_2 +Point_set_3 +Polygon +Polyhedron +Straight_skeleton_2 +Surface_mesh +Surface_mesh_topology +Triangulation_2 +Triangulation_3 +Voronoi_diagram_2 +Kernel_23 +Arrangement_on_surface_2 diff --git a/Basic_viewer/doc/Basic_viewer/examples.txt b/Basic_viewer/doc/Basic_viewer/examples.txt new file mode 100644 index 00000000000..2e18952fcb4 --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/examples.txt @@ -0,0 +1,8 @@ +/*! +\example Basic_viewer/draw_lcc.cpp +\example Basic_viewer/draw_mesh_and_points.cpp +\example Basic_viewer/draw_several_windows.cpp +\example Basic_viewer/draw_surface_mesh_small_faces.cpp +\example Basic_viewer/draw_surface_mesh_height.cpp +\example Basic_viewer/draw_surface_mesh_vcolor.cpp +*/ diff --git a/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_diagramme_class.svg b/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_diagramme_class.svg new file mode 100644 index 00000000000..3ec94e9d1af --- /dev/null +++ b/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_diagramme_class.svg @@ -0,0 +1,530 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Basic_viewer + + + + + Graphics_scene + + + void add_point(apoint) + + + void add_point(apoint, acolor) + + + + + + + + void add_segment(apoint1, apoint2, acolor) + + void add_segment(apoint1, apoint2) + + void face_begin(acolor) + + void add_point_in_face(apoint) + + void face_begin() + + void face_end() + ... + + Basic_viewer (QWidget *parent, const Graphics_scene &scene)+ void draw()+ void redraw()... + + + 1 + + + + QApplication_and_basic_viewer + + QApplication_and_basic_viewer (const Graphics_scene &scene)+ void run()+ Basic_viewer & basic_viewer () + + + 1 + void draw_graphics_scene(const Graphics_scene& gs) + void draw(const DS& ds) + + void draw(const DS& ds, const Graphics_scene_option& gso) + void add_to_graphics_scene(const DS& ds, Graphics_scene& gs) + void add_to_graphics_scene(const DS& ds, Graphics_scene& gs, const Graphics_scene_option& gso) + + diff --git a/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_logo.png b/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_logo.png new file mode 100644 index 00000000000..05df4ad850c Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/basic_viewer_logo.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/draw_mesh_and_points.png b/Basic_viewer/doc/Basic_viewer/fig/draw_mesh_and_points.png new file mode 100644 index 00000000000..4fd65f7d502 Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/draw_mesh_and_points.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/draw_several_windows.png b/Basic_viewer/doc/Basic_viewer/fig/draw_several_windows.png new file mode 100644 index 00000000000..3a7cb06c6eb Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/draw_several_windows.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/draw_surface_height.png b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_height.png new file mode 100644 index 00000000000..082b3a29228 Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_height.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_small_faces.png b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_small_faces.png new file mode 100644 index 00000000000..d6b4066ca7f Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_small_faces.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_vcolor.png b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_vcolor.png new file mode 100644 index 00000000000..74f1e959f9c Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/draw_surface_mesh_vcolor.png differ diff --git a/Basic_viewer/doc/Basic_viewer/fig/lcc_draw.png b/Basic_viewer/doc/Basic_viewer/fig/lcc_draw.png new file mode 100644 index 00000000000..b86f9323c57 Binary files /dev/null and b/Basic_viewer/doc/Basic_viewer/fig/lcc_draw.png differ diff --git a/Basic_viewer/examples/Basic_viewer/CMakeLists.txt b/Basic_viewer/examples/Basic_viewer/CMakeLists.txt new file mode 100644 index 00000000000..f758661d6bb --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/CMakeLists.txt @@ -0,0 +1,41 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +#/!\ /!\ +#/!\ /!\ +# Used in /CGAL/Documentation/doc/Documentation/Developer_manual/create_and_use_a_cmakelist.txt. +# Careful when modifying + +cmake_minimum_required(VERSION 3.1...3.23) +project(Basic_viewer_Examples) + +#CGAL_Qt6 is needed for the drawing. +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6) +find_package(Eigen3 3.1.0) +include(CGAL_Eigen3_support) + +create_single_source_cgal_program("draw_lcc.cpp") +create_single_source_cgal_program("draw_mesh_and_points.cpp") +create_single_source_cgal_program("draw_several_windows.cpp") +create_single_source_cgal_program("draw_surface_mesh_height.cpp") +create_single_source_cgal_program("draw_surface_mesh_small_faces.cpp") +create_single_source_cgal_program("draw_surface_mesh_vcolor.cpp") + +if(CGAL_Qt6_FOUND) + #link it with the required CGAL libraries + target_link_libraries(draw_lcc PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_mesh_and_points PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_several_windows PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_surface_mesh_height PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_surface_mesh_small_faces PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_surface_mesh_vcolor PUBLIC CGAL::CGAL_Basic_viewer) +else() + message("CGAL_Qt6 not configured: examples that require Qt will not be compiled.") +endif() + +if(TARGET CGAL::Eigen3_support) + target_link_libraries(draw_mesh_and_points PUBLIC CGAL::Eigen3_support) + target_link_libraries(draw_several_windows PUBLIC CGAL::Eigen3_support) +endif() + +#end of the file diff --git a/Basic_viewer/examples/Basic_viewer/draw_lcc.cpp b/Basic_viewer/examples/Basic_viewer/draw_lcc.cpp new file mode 100644 index 00000000000..019db26519a --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_lcc.cpp @@ -0,0 +1,17 @@ +#include +#include + +using LCC=CGAL::Linear_cell_complex_for_combinatorial_map<3>; +using Point=LCC::Point; + +int main() +{ + LCC lcc; + lcc.make_hexahedron(Point(0,0,0), Point(5,0,0), + Point(5,5,0), Point(0,5,0), + Point(0,5,4), Point(0,0,4), + Point(5,0,4), Point(5,5,4)); + CGAL::draw(lcc); + + return EXIT_SUCCESS; +} diff --git a/Basic_viewer/examples/Basic_viewer/draw_mesh_and_points.cpp b/Basic_viewer/examples/Basic_viewer/draw_mesh_and_points.cpp new file mode 100644 index 00000000000..e104f016447 --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_mesh_and_points.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using Kernel=CGAL::Exact_predicates_inexact_constructions_kernel; +using Point=Kernel::Point_3; +using Vector=Kernel::Vector_3; +using Pwn=std::pair; +using Polyhedron=CGAL::Polyhedron_3; +using PS3=CGAL::Point_set_3; + +struct Graphics_scene_options_green_points: + public CGAL::Graphics_scene_options +{ + bool colored_vertex(const PS3&, typename PS3::const_iterator) const + { return true; } + CGAL::IO::Color vertex_color(const PS3&, typename PS3::const_iterator) const + { return CGAL::IO::Color(0,220,0); } +}; + +int main(void) +{ + std::vector points; + + if(!CGAL::IO::read_points(CGAL::data_file_path("points_3/kitten.xyz"), std::back_inserter(points), + CGAL::parameters::point_map(CGAL::First_of_pair_property_map()) + .normal_map(CGAL::Second_of_pair_property_map()))) + { + std::cerr << "Error: cannot read input file " << CGAL::data_file_path("points_3/kitten.xyz") << std::endl; + return EXIT_FAILURE; + } + + Polyhedron output_mesh; + + double average_spacing = CGAL::compute_average_spacing + (points, 6, CGAL::parameters::point_map(CGAL::First_of_pair_property_map())); + + if (CGAL::poisson_surface_reconstruction_delaunay + (points.begin(), points.end(), + CGAL::First_of_pair_property_map(), + CGAL::Second_of_pair_property_map(), + output_mesh, average_spacing)) + { + PS3 point_set; + for(Pwn& it: points) + { point_set.insert(it.first); } + + CGAL::Graphics_scene scene; + CGAL::add_to_graphics_scene(point_set, scene, Graphics_scene_options_green_points()); + CGAL::add_to_graphics_scene(output_mesh, scene); + CGAL::draw_graphics_scene(scene); + } + else + { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} diff --git a/Basic_viewer/examples/Basic_viewer/draw_several_windows.cpp b/Basic_viewer/examples/Basic_viewer/draw_several_windows.cpp new file mode 100644 index 00000000000..2ac2ff2d66d --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_several_windows.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CGAL_USE_BASIC_VIEWER +#include +#endif + +#include +#include + +using Kernel=CGAL::Exact_predicates_inexact_constructions_kernel; +using Point=Kernel::Point_3; +using Vector=Kernel::Vector_3; +using Pwn=std::pair; +using Polyhedron=CGAL::Polyhedron_3; +using PS3=CGAL::Point_set_3; + +int main(void) +{ + /// (1) Some CGAL code that create data structures and fill two Graphics_scene. + std::vector points; + + if(!CGAL::IO::read_points(CGAL::data_file_path("points_3/kitten.xyz"), std::back_inserter(points), + CGAL::parameters::point_map(CGAL::First_of_pair_property_map()) + .normal_map(CGAL::Second_of_pair_property_map()))) + { + std::cerr << "Error: cannot read input file " << CGAL::data_file_path("points_3/kitten.xyz") << std::endl; + return EXIT_FAILURE; + } + + Polyhedron output_mesh; + + double average_spacing = CGAL::compute_average_spacing + (points, 6, CGAL::parameters::point_map(CGAL::First_of_pair_property_map())); + + if (!CGAL::poisson_surface_reconstruction_delaunay + (points.begin(), points.end(), + CGAL::First_of_pair_property_map(), + CGAL::Second_of_pair_property_map(), + output_mesh, average_spacing)) + { return EXIT_FAILURE; } + + PS3 point_set; + for(Pwn& it: points) + { point_set.insert(it.first); } + + CGAL::Graphics_scene scene1, scene2; + CGAL::add_to_graphics_scene(point_set, scene1); + CGAL::add_to_graphics_scene(output_mesh, scene2); + + /// (2) Qt code that create windows, add them in a layout, and create app. +#ifdef CGAL_USE_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) { return EXIT_SUCCESS; } + + int argc=1; + const char* argv[2]={"Draw several windows example","\0"}; + QApplication app(argc,const_cast(argv)); + + QMainWindow* mainWindow=new QMainWindow; + QWidget *centralWidget = new QWidget(mainWindow); + QHBoxLayout* layout = new QHBoxLayout(mainWindow); + + CGAL::Qt::Basic_viewer bv1(mainWindow, scene1); + CGAL::Qt::Basic_viewer bv2(mainWindow, scene2); + bv1.draw_vertices(true); + + layout->addWidget(&bv1); + layout->addWidget(&bv2); + + centralWidget->setLayout(layout); + mainWindow->setCentralWidget(centralWidget); + + mainWindow->show(); + app.exec(); +#endif + + return EXIT_SUCCESS; +} diff --git a/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_height.cpp b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_height.cpp new file mode 100644 index 00000000000..719ac3254ed --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_height.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian Kernel; +typedef Kernel::Point_3 Point; +typedef CGAL::Surface_mesh Mesh; + +struct Colored_faces_given_height: + public CGAL::Graphics_scene_options +{ + Colored_faces_given_height(const Mesh& sm) + { + if(sm.is_empty()) return; + + double m_min_y=0., m_max_y=0.; + bool first=true; + for(typename Mesh::Vertex_index vi: sm.vertices()) + { + if(first) + { m_min_y=sm.point(vi).y(); m_max_y=m_min_y; first=false; } + else + { + m_min_y=(std::min)(m_min_y, sm.point(vi).y()); + m_max_y=(std::max)(m_max_y, sm.point(vi).y()); + } + } + + this->colored_face=[](const Mesh &, typename Mesh::Face_index)->bool { return true; }; + + this->face_color=[m_min_y, m_max_y] + (const Mesh& sm, typename Mesh::Face_index fi)->CGAL::IO::Color + { + double res=0.; + std::size_t n=0; + for(typename Mesh::Vertex_index vi: vertices_around_face(sm.halfedge(fi), sm)) + { + res+=sm.point(vi).y(); + ++n; + } + // Random color depending on the "height" of the facet + CGAL::Random random(static_cast(30*((res/n)-m_min_y)/(m_max_y-m_min_y))); + return CGAL::get_random_color(random); + }; + } +}; + +int main(int argc, char* argv[]) +{ + const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); + + Mesh sm; + if(!CGAL::IO::read_polygon_mesh(filename, sm)) + { + std::cerr << "Invalid input file: " << filename << std::endl; + return EXIT_FAILURE; + } + + CGAL::draw(sm, Colored_faces_given_height(sm)); + + return EXIT_SUCCESS; +} diff --git a/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_small_faces.cpp b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_small_faces.cpp new file mode 100644 index 00000000000..396f3a9a814 --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_small_faces.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; +typedef CGAL::Surface_mesh Mesh; +typedef Mesh::Vertex_index vertex_descriptor; +typedef Mesh::Face_index face_descriptor; +typedef K::FT FT; + +template +struct Graphics_scene_options_small_faces: + public CGAL::Graphics_scene_options::vertex_descriptor, + typename boost::graph_traits::edge_descriptor, + typename boost::graph_traits::face_descriptor> +{ + using Base=CGAL::Graphics_scene_options::vertex_descriptor, + typename boost::graph_traits::edge_descriptor, + typename boost::graph_traits::face_descriptor>; + + Graphics_scene_options_small_faces(const SM& sm): Base(), m_sm(sm) + { + typename SM::template Property_map faces_size; + boost::tie(faces_size, m_with_size)=sm.template property_map("f:size"); + if(!m_with_size) + { return; } + + m_min_size=faces_size[*(sm.faces().begin())]; + m_max_size=m_min_size; + FT cur_size; + for (typename SM::Face_range::iterator f=sm.faces().begin(); f!=sm.faces().end(); ++f) + { + cur_size=faces_size[*f]; + if (cur_sizem_max_size) m_max_size=cur_size; + } + + this->face_color=[=](const SM& sm, + typename boost::graph_traits::face_descriptor fh) -> CGAL::IO::Color + { return this->get_face_color(sm, fh); }; + + this->colored_face = [](const SM&, + typename boost::graph_traits::face_descriptor) -> bool + { return true; }; + } + + CGAL::IO::Color get_face_color(const SM& sm, + typename boost::graph_traits::face_descriptor fh) + { + // Default color of faces + CGAL::IO::Color c(75,160,255); + if(!m_with_size) { return c; } + + // Compare the size of the face with the % m_threshold + bool exist; + typename SM::template Property_map faces_size; + boost::tie(faces_size, exist)=sm.template property_map("f:size"); + assert(exist); + + // If the face is small, color it in red. + if (get(faces_size, fh)1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); + + Mesh sm; + if(!CGAL::IO::read_polygon_mesh(filename, sm)) + { + std::cerr << "Invalid input file: " << filename << std::endl; + return EXIT_FAILURE; + } + + CGAL::Polygon_mesh_processing::triangulate_faces(sm); + + Mesh::Property_map faces_size; + bool created; + boost::tie(faces_size, created)=sm.add_property_map("f:size",0.); + assert(created); + + for(face_descriptor fd : sm.faces()) + { faces_size[fd]=CGAL::Polygon_mesh_processing::face_area(fd, sm); } + + Graphics_scene_options_small_faces gsosm(sm); + CGAL::Graphics_scene gs; + + add_to_graphics_scene(sm, gs, gsosm); + +#ifdef CGAL_USE_BASIC_VIEWER + + CGAL::Qt::QApplication_and_basic_viewer app(gs, "Small faces"); + if(app) + { + app.basic_viewer().on_key_pressed= + [&sm, &gsosm, &gs] (QKeyEvent* e, CGAL::Qt::Basic_viewer* basic_viewer) -> bool + { + const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + if ((e->key() == ::Qt::Key_I) && (modifiers == ::Qt::NoButton)) + { + gsosm.m_threshold+=5; + if(gsosm.m_threshold>100) { gsosm.m_threshold=100; } + basic_viewer->displayMessage + (QString("Small faces threshold=%1.").arg(gsosm.m_threshold)); + + gs.clear(); + add_to_graphics_scene(sm, gs, gsosm); + basic_viewer->redraw(); + } + else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) + { + if(gsosm.m_threshold<5) { gsosm.m_threshold=0; } + else { gsosm.m_threshold-=5; } + basic_viewer->displayMessage + (QString("Small faces threshold=%1.").arg(gsosm.m_threshold)); + + gs.clear(); + add_to_graphics_scene(sm, gs, gsosm); + basic_viewer->redraw(); + } + else + { + // Return false will call the base method to process others/classicals key + return false; + } + return true; + }; + + // Here we add shortcut descriptions + app.basic_viewer().setKeyDescription(::Qt::Key_I, "Increase threshold for small faces"); + app.basic_viewer().setKeyDescription(::Qt::Key_D, "Decrease threshold for small faces"); + + // Then we run the app + app.run(); + } + +#endif + + sm.remove_property_map(faces_size); + + return EXIT_SUCCESS; +} diff --git a/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_vcolor.cpp b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_vcolor.cpp new file mode 100644 index 00000000000..209d73bd71a --- /dev/null +++ b/Basic_viewer/examples/Basic_viewer/draw_surface_mesh_vcolor.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +using Kernel=CGAL::Simple_cartesian; +using Point=Kernel::Point_3; +using Mesh=CGAL::Surface_mesh; + +// Inherit from CGAL::Graphics_scene_options to get all the default values. +struct My_graphics_scene_options: + public CGAL::Graphics_scene_options::vertex_descriptor, + typename boost::graph_traits::edge_descriptor, + typename boost::graph_traits::face_descriptor> +{ + // All vertices are colored. + bool colored_vertex(const Mesh&, + typename boost::graph_traits::vertex_descriptor) const + { return true; } + + // Change the color of vertices randomly. + CGAL::IO::Color vertex_color(const Mesh&, + typename boost::graph_traits::vertex_descriptor) const + { + static bool v_green=true; + v_green=!v_green; + if(v_green) // 1 vertex out of two green (randomly) + { return CGAL::IO::Color(0,220,0); } + else // the others are blue + { return CGAL::IO::Color(0,0,220); } + } +}; + + +int main(int argc, char* argv[]) +{ + const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); + + Mesh sm; + if(!CGAL::IO::read_polygon_mesh(filename, sm)) + { + std::cerr << "Invalid input file: " << filename << std::endl; + return EXIT_FAILURE; + } + + // Draw the mesh using the new graphics scene option. + CGAL::draw(sm, My_graphics_scene_options()); + return EXIT_SUCCESS; +} diff --git a/GraphicsView/include/CGAL/Basic_shaders.h b/Basic_viewer/include/CGAL/Basic_shaders.h similarity index 100% rename from GraphicsView/include/CGAL/Basic_shaders.h rename to Basic_viewer/include/CGAL/Basic_shaders.h diff --git a/GraphicsView/include/CGAL/Buffer_for_vao.h b/Basic_viewer/include/CGAL/Buffer_for_vao.h similarity index 90% rename from GraphicsView/include/CGAL/Buffer_for_vao.h rename to Basic_viewer/include/CGAL/Buffer_for_vao.h index dde60abb832..d7c9619619a 100644 --- a/GraphicsView/include/CGAL/Buffer_for_vao.h +++ b/Basic_viewer/include/CGAL/Buffer_for_vao.h @@ -10,8 +10,8 @@ // // Author(s) : Guillaume Damiand -#ifndef CGAL_VBO_BUFFER_FILLER_H -#define CGAL_VBO_BUFFER_FILLER_H +#ifndef CGAL_BUFFER_FOR_VAO_H +#define CGAL_BUFFER_FOR_VAO_H #include @@ -125,13 +125,43 @@ namespace internal static const typename Local_kernel::Ray_2& get_local_ray(const typename Local_kernel::Ray_2& r) { return r; } }; + + template + void mark_domains(CDT& tri, + typename CDT::Face_handle start, int index, + std::queue& border) + { + if (start->info().m_nesting_level!=-1) return; + std::queue queue; + queue.push(start); + while (!queue.empty()) + { + auto fh=queue.front(); + queue.pop(); + if (fh->info().m_nesting_level==-1) + { + fh->info().m_nesting_level=index; + for (int i = 0; i < 3; i++) + { + typename CDT::Edge e(fh,i); + auto n = fh->neighbor(i); + if (n->info().m_nesting_level==-1) + { + if (tri.is_constrained(e)) { border.push(e); } + else { queue.push(n); } + } + } + } + } + } } // End namespace internal //------------------------------------------------------------------------------ -template class Buffer_for_vao { public: + using BufferType=float; + using IndexType=std::size_t; typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; typedef Local_kernel::Point_3 Local_point; typedef Local_kernel::Vector_3 Local_vector; @@ -163,6 +193,7 @@ public: if (m_index_buffer!=nullptr) { m_index_buffer->clear(); } if (m_flat_normal_buffer!=nullptr) { m_flat_normal_buffer->clear(); } if (m_gouraud_normal_buffer!=nullptr) { m_gouraud_normal_buffer->clear(); } + if (m_bb!=nullptr) { (*m_bb)=CGAL::Bbox_3(); } m_zero_x=true; m_zero_y=true; @@ -203,11 +234,10 @@ public: bool has_zero_z() const { return m_zero_z; } - void negate_normals() + void negate_normals() const { m_inverse_normal=!m_inverse_normal; - for (std::vector*array=m_flat_normal_buffer; array!=nullptr; - array=(array==m_gouraud_normal_buffer?nullptr:m_gouraud_normal_buffer)) + for (std::vector*array: {m_flat_normal_buffer, m_gouraud_normal_buffer}) { for (std::size_t i=0; isize(); ++i) { (*array)[i]=-(*array)[i]; } @@ -458,31 +488,31 @@ public: /// adds `kp` coordinates to `buffer` template - static void add_point_in_buffer(const KPoint& kp, std::vector& buffer) + static void add_point_in_buffer(const KPoint& kp, std::vector& buffer) { Local_point p=get_local_point(kp); - buffer.push_back(static_cast(p.x())); - buffer.push_back(static_cast(p.y())); - buffer.push_back(static_cast(p.z())); + buffer.push_back(static_cast(p.x())); + buffer.push_back(static_cast(p.y())); + buffer.push_back(static_cast(p.z())); } /// adds `kv` coordinates to `buffer` template - static void add_normal_in_buffer(const KVector& kv, std::vector& buffer, + static void add_normal_in_buffer(const KVector& kv, std::vector& buffer, bool inverse_normal=false) { Local_vector n=(inverse_normal?-get_local_vector(kv):get_local_vector(kv)); - buffer.push_back(static_cast(n.x())); - buffer.push_back(static_cast(n.y())); - buffer.push_back(static_cast(n.z())); + buffer.push_back(static_cast(n.x())); + buffer.push_back(static_cast(n.y())); + buffer.push_back(static_cast(n.z())); } ///adds `acolor` RGB components to `buffer` - static void add_color_in_buffer(const CGAL::IO::Color& acolor, std::vector& buffer) + static void add_color_in_buffer(const CGAL::IO::Color& acolor, std::vector& buffer) { - buffer.push_back((float)acolor.red()/(float)255); - buffer.push_back((float)acolor.green()/(float)255); - buffer.push_back((float)acolor.blue()/(float)255); + buffer.push_back((BufferType)acolor.red()/(BufferType)255); + buffer.push_back((BufferType)acolor.green()/(BufferType)255); + buffer.push_back((BufferType)acolor.blue()/(BufferType)255); } /// @return true iff the points of 'facet' form a convex face @@ -737,62 +767,20 @@ protected: { cdt.insert_constraint(previous, first); } } - // (2) We mark all external triangles - // (2.1) We initialize is_external and is_process values + // (2.1) We initialize nesting_level for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(), fitend = cdt.all_faces_end(); fit!=fitend; ++fit) - { - fit->info().is_external = true; - fit->info().is_process = false; - } - // (2.2) We check if the facet is external or internal - std::queue face_queue, faces_internal; - if (cdt.infinite_vertex()->face()!=nullptr) - { - typename CDT::Face_circulator - incident_faces(cdt.infinite_vertex()), end_incident_faces(incident_faces); - do - { face_queue.push(incident_faces); } - while(++incident_faces!=end_incident_faces); - } - // std::cout<<"# faces PUSHED "<info().is_process) - { - fh->info().is_process=true; - for(int i=0; i<3; ++i) - { - if(!cdt.is_constrained(std::make_pair(fh, i))) - { - if (fh->neighbor(i)!=nullptr) - { face_queue.push(fh->neighbor(i)); } - } - else - { faces_internal.push(fh->neighbor(i)); } - } - } - } + { fit->info().m_nesting_level=-1; } - while(!faces_internal.empty()) + std::queue border; + internal::mark_domains(cdt, cdt.infinite_face(), 0, border); + while(!border.empty()) { - typename CDT::Face_handle fh=faces_internal.front(); - faces_internal.pop(); - if(!fh->info().is_process) - { - fh->info().is_process = true; - fh->info().is_external = false; - for(unsigned int i=0; i<3; ++i) - { - if(!cdt.is_constrained(std::make_pair(fh, i))) - { - if (fh->neighbor(i)!=nullptr) - { faces_internal.push(fh->neighbor(i)); } - } - } - } + auto e=border.front(); + border.pop(); + auto n=e.first->neighbor(e.second); + if (n->info().m_nesting_level==-1) + { internal::mark_domains(cdt, n, e.first->info().m_nesting_level+1, border); } } // (3) Now we iterates on the internal faces to add the vertices @@ -800,7 +788,7 @@ protected: for(typename CDT::Finite_faces_iterator ffit=cdt.finite_faces_begin(), ffitend = cdt.finite_faces_end(); ffit!=ffitend; ++ffit) { - if(!ffit->info().is_external) + if(ffit->info().in_domain()) { for(unsigned int i=0; i<3; ++i) { @@ -882,9 +870,8 @@ protected: struct Face_info { - bool exist_edge[3]; - bool is_external; - bool is_process; + int m_nesting_level=-1; + bool in_domain() { return m_nesting_level%2==1; } }; typedef CGAL::Projection_traits_3 P_traits; @@ -899,8 +886,8 @@ protected: std::vector* m_pos_buffer; std::vector* m_index_buffer; std::vector* m_color_buffer; - std::vector* m_flat_normal_buffer; - std::vector* m_gouraud_normal_buffer; + mutable std::vector* m_flat_normal_buffer; + mutable std::vector* m_gouraud_normal_buffer; CGAL::Bbox_3* m_bb; @@ -908,7 +895,7 @@ protected: bool m_zero_y; /// True iff all points have y==0 bool m_zero_z; /// True iff all points have z==0 - bool m_inverse_normal; + mutable bool m_inverse_normal; // Local variables, used when we started a new face.g bool m_face_started; @@ -923,4 +910,4 @@ protected: } // End namespace CGAL -#endif // CGAL_VBO_BUFFER_FILLER_H +#endif // CGAL_BUFFER_FOR_VAO_H diff --git a/Basic_viewer/include/CGAL/Graphics_scene.h b/Basic_viewer/include/CGAL/Graphics_scene.h new file mode 100644 index 00000000000..155a471b217 --- /dev/null +++ b/Basic_viewer/include/CGAL/Graphics_scene.h @@ -0,0 +1,436 @@ +// Copyright (c) 2022 GeometryFactory Sarl (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): Guillaume Damiand +// Mostafa Ashraf + +#ifndef CGAL_GRAPHICS_SCENE_H +#define CGAL_GRAPHICS_SCENE_H + +// TODO #include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace CGAL { + +//------------------------------------------------------------------------------ +inline CGAL::IO::Color get_random_color(CGAL::Random& random) +{ + CGAL::IO::Color res; + do + { + res=CGAL::IO::Color(random.get_int(0,256), + random.get_int(0,256), + random.get_int(0,256)); + } + while(res.red()==255 && res.green()==255 && res.blue()==255); + return res; +} +//------------------------------------------------------------------------------ +// This class is responsible for dealing with available CGAL data structures and +// handling buffers. +class Graphics_scene +{ +public: + using BufferType=float; + + typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; + typedef Local_kernel::Point_3 Local_point; + typedef Local_kernel::Vector_3 Local_vector; + + Graphics_scene() + : m_buffer_for_mono_points(&arrays[POS_MONO_POINTS], nullptr, + &m_bounding_box, nullptr, nullptr, nullptr), + m_buffer_for_colored_points(&arrays[POS_COLORED_POINTS], nullptr, + &m_bounding_box, &arrays[COLOR_POINTS], + nullptr, nullptr), + m_buffer_for_mono_segments(&arrays[POS_MONO_SEGMENTS], nullptr, + &m_bounding_box, nullptr, nullptr, nullptr), + m_buffer_for_colored_segments(&arrays[POS_COLORED_SEGMENTS], nullptr, + &m_bounding_box, &arrays[COLOR_SEGMENTS], + nullptr, nullptr), + m_buffer_for_mono_rays(&arrays[POS_MONO_RAYS], nullptr, &m_bounding_box, + nullptr, nullptr), + m_buffer_for_colored_rays(&arrays[POS_COLORED_RAYS], nullptr, + &m_bounding_box, &arrays[COLOR_RAYS], nullptr, + nullptr), + m_buffer_for_mono_lines(&arrays[POS_MONO_RAYS], nullptr, + &m_bounding_box, nullptr, nullptr), + m_buffer_for_colored_lines(&arrays[POS_COLORED_LINES], nullptr, + &m_bounding_box, &arrays[COLOR_LINES], + nullptr, nullptr), + m_buffer_for_mono_faces( + &arrays[POS_MONO_FACES], nullptr, &m_bounding_box, nullptr, + &arrays[FLAT_NORMAL_MONO_FACES], &arrays[SMOOTH_NORMAL_MONO_FACES]), + m_buffer_for_colored_faces(&arrays[POS_COLORED_FACES], nullptr, + &m_bounding_box, &arrays[COLOR_FACES], + &arrays[FLAT_NORMAL_COLORED_FACES], + &arrays[SMOOTH_NORMAL_COLORED_FACES]) + {} + + const Buffer_for_vao &get_buffer_for_mono_points() const + { return m_buffer_for_mono_points; } + + const Buffer_for_vao &get_buffer_for_colored_points() const + { return m_buffer_for_colored_points; } + + const Buffer_for_vao &get_buffer_for_mono_segments() const + { return m_buffer_for_mono_segments; } + + const Buffer_for_vao &get_buffer_for_colored_segments() const + { return m_buffer_for_colored_segments; } + + const Buffer_for_vao &get_buffer_for_mono_rays() const + { return m_buffer_for_mono_rays; } + + const Buffer_for_vao &get_buffer_for_colored_rays() const + { return m_buffer_for_colored_rays; } + + const Buffer_for_vao &get_buffer_for_mono_lines() const + { return m_buffer_for_mono_lines; } + + const Buffer_for_vao &get_buffer_for_colored_lines() const + { return m_buffer_for_colored_lines; } + + const Buffer_for_vao &get_buffer_for_mono_faces() const + { return m_buffer_for_mono_faces; } + + const Buffer_for_vao &get_buffer_for_colored_faces() const + { return m_buffer_for_colored_faces; } + + const CGAL::Bbox_3 &bounding_box() const { return m_bounding_box; } + + const std::vector &get_array_of_index(int index) const + { assert(index(arrays[index].size()*sizeof(BufferType)); } + + unsigned int number_of_elements(int index) const + { return static_cast(arrays[index].size()/3); } + + void initiate_bounding_box(const CGAL::Bbox_3& new_bounding_box) + { m_bounding_box = new_bounding_box; } + + void update_bounding_box(const CGAL::Bbox_3 &box) { m_bounding_box+=box; } + + template + void update_bounding_box_for_ray(const KPoint &p, const KVector &v) + { + Local_point lp = get_local_point(p); + Local_vector lv = get_local_vector(v); + update_bounding_box((lp + lv).bbox()); + } + + template + void update_bounding_box_for_line(const KPoint &p, const KVector &v, + const KVector &pv) + { + Local_point lp = get_local_point(p); + Local_vector lv = get_local_vector(v); + Local_vector lpv = get_local_vector(pv); + update_bounding_box(lp.bbox() + (lp + lv).bbox() + (lp + lpv).bbox()); + } + + void reverse_all_normals() const + { + m_buffer_for_mono_faces.negate_normals(); + m_buffer_for_colored_faces.negate_normals(); + } + + template void add_point(const KPoint &p) + { m_buffer_for_mono_points.add_point(p); } + + template + void add_point(const KPoint &p, const CGAL::IO::Color &acolor) + { m_buffer_for_colored_points.add_point(p, acolor); } + + template + void add_segment(const KPoint &p1, const KPoint &p2) + { m_buffer_for_mono_segments.add_segment(p1, p2); } + + template + void add_segment(const KPoint &p1, const KPoint &p2, + const CGAL::IO::Color &acolor) + { m_buffer_for_colored_segments.add_segment(p1, p2, acolor); } + + template + void add_ray(const KPoint &p, const KVector &v) + { + double bigNumber = 1e30; + m_buffer_for_mono_rays.add_ray_segment(p, (p + (bigNumber)*v)); + } + + template + void add_ray(const KPoint &p, const KVector &v, + const CGAL::IO::Color &acolor) + { + double bigNumber = 1e30; + m_buffer_for_colored_rays.add_ray_segment(p, (p + (bigNumber)*v), acolor); + } + + template + void add_line(const KPoint &p, const KVector &v) + { + double bigNumber = 1e30; + m_buffer_for_mono_lines.add_line_segment((p - (bigNumber)*v), + (p + (bigNumber)*v)); + } + + template + void add_line(const KPoint &p, const KVector &v, + const CGAL::IO::Color &acolor) + { + double bigNumber = 1e30; + m_buffer_for_colored_lines.add_line_segment((p - (bigNumber)*v), + (p + (bigNumber)*v), acolor); + } + + template bool add_point_in_face(const KPoint &kp) + { + if (m_buffer_for_mono_faces.is_a_face_started()) + { return m_buffer_for_mono_faces.add_point_in_face(kp); } + else if (m_buffer_for_colored_faces.is_a_face_started()) + { return m_buffer_for_colored_faces.add_point_in_face(kp); } + return false; + } + + template + bool add_point_in_face(const KPoint &kp, const KVector &p_normal) + { + if (m_buffer_for_mono_faces.is_a_face_started()) + { return m_buffer_for_mono_faces.add_point_in_face(kp, p_normal); } + else if (m_buffer_for_colored_faces.is_a_face_started()) + { return m_buffer_for_colored_faces.add_point_in_face(kp, p_normal); } + return false; + } + + bool a_face_started() const + { + return m_buffer_for_mono_faces.is_a_face_started() || + m_buffer_for_colored_faces.is_a_face_started(); + } + + void face_begin() + { + if (a_face_started()) + { + std::cerr + << "You cannot start a new face before to finish the previous one." + << std::endl; + } + else + { m_buffer_for_mono_faces.face_begin(); } + } + + void face_begin(const CGAL::IO::Color &acolor) + { + if (a_face_started()) + { + std::cerr + << "You cannot start a new face before to finish the previous one." + << std::endl; + } + else + { m_buffer_for_colored_faces.face_begin(acolor); } + } + + void face_end() + { + if (m_buffer_for_mono_faces.is_a_face_started()) + { m_buffer_for_mono_faces.face_end(); } + else if (m_buffer_for_colored_faces.is_a_face_started()) + { m_buffer_for_colored_faces.face_end(); } + } + + template + void add_text(const KPoint &kp, const std::string &txt) + { + Local_point p = get_local_point(kp); + m_texts.push_back(std::make_tuple(p, txt)); + } + + template + void add_text(const KPoint &kp, const char *txt) + { add_text(kp, std::string(txt)); } + + bool empty() const + { + return (m_buffer_for_mono_points.is_empty() && + m_buffer_for_colored_points.is_empty() && + m_buffer_for_mono_segments.is_empty() && + m_buffer_for_colored_segments.is_empty() && + m_buffer_for_mono_rays.is_empty() && + m_buffer_for_colored_rays.is_empty() && + m_buffer_for_mono_lines.is_empty() && + m_buffer_for_colored_lines.is_empty() && + m_buffer_for_mono_faces.is_empty() && + m_buffer_for_colored_faces.is_empty()); + } + + bool has_zero_x() const + { + return m_buffer_for_mono_points.has_zero_x() && + m_buffer_for_colored_points.has_zero_x() && + m_buffer_for_mono_segments.has_zero_x() && + m_buffer_for_colored_segments.has_zero_x() && + m_buffer_for_mono_faces.has_zero_x() && + m_buffer_for_colored_faces.has_zero_x() && + m_buffer_for_mono_rays.has_zero_x() && + m_buffer_for_colored_rays.has_zero_x() && + m_buffer_for_mono_lines.has_zero_x() && + m_buffer_for_colored_lines.has_zero_x(); + } + + bool has_zero_y() const + { + return m_buffer_for_mono_points.has_zero_y() && + m_buffer_for_colored_points.has_zero_y() && + m_buffer_for_mono_segments.has_zero_y() && + m_buffer_for_colored_segments.has_zero_y() && + m_buffer_for_mono_faces.has_zero_y() && + m_buffer_for_colored_faces.has_zero_y() && + m_buffer_for_mono_rays.has_zero_y() && + m_buffer_for_colored_rays.has_zero_y() && + m_buffer_for_mono_lines.has_zero_y() && + m_buffer_for_colored_lines.has_zero_y(); + } + + bool has_zero_z() const + { + return m_buffer_for_mono_points.has_zero_z() && + m_buffer_for_colored_points.has_zero_z() && + m_buffer_for_mono_segments.has_zero_z() && + m_buffer_for_colored_segments.has_zero_z() && + m_buffer_for_mono_faces.has_zero_z() && + m_buffer_for_colored_faces.has_zero_z() && + m_buffer_for_mono_rays.has_zero_z() && + m_buffer_for_colored_rays.has_zero_z() && + m_buffer_for_mono_lines.has_zero_z() && + m_buffer_for_colored_lines.has_zero_z(); + } + + // Returns true if the data structure lies on a XY or XZ or YZ plane + bool is_two_dimensional() const + { + return (!empty() && (has_zero_x() || has_zero_y() || has_zero_z())); + } + + void clear() + { + m_buffer_for_mono_points.clear(); + m_buffer_for_colored_points.clear(); + m_buffer_for_mono_segments.clear(); + m_buffer_for_colored_segments.clear(); + m_buffer_for_mono_rays.clear(); + m_buffer_for_colored_rays.clear(); + m_buffer_for_mono_lines.clear(); + m_buffer_for_colored_lines.clear(); + m_buffer_for_mono_faces.clear(); + m_buffer_for_colored_faces.clear(); + m_texts.clear(); + m_bounding_box=CGAL::Bbox_3(); + } + + template + static Local_point get_local_point(const KPoint &p) + { + return internal::Geom_utils::Kernel, + Local_kernel>::get_local_point(p); + } + + template + static Local_vector get_local_vector(const KVector &v) + { + return internal::Geom_utils::Kernel, + Local_kernel>::get_local_vector(v); + } + + void m_texts_clear() + { m_texts.clear(); } + + std::size_t m_texts_size() const + { return m_texts.size(); } + + const std::vector>& get_m_texts() const + { return m_texts; } + +public: + // The following enum gives the indices of different elements of arrays + // vectors. + enum Buffers { + BEGIN_POS = 0, + POS_MONO_POINTS = BEGIN_POS, + POS_COLORED_POINTS, + POS_MONO_SEGMENTS, + POS_COLORED_SEGMENTS, + POS_MONO_RAYS, + POS_COLORED_RAYS, + POS_MONO_LINES, + POS_COLORED_LINES, + POS_MONO_FACES, + POS_COLORED_FACES, + END_POS, + BEGIN_COLOR = END_POS, + COLOR_POINTS = BEGIN_COLOR, + COLOR_SEGMENTS, + COLOR_RAYS, + COLOR_LINES, + COLOR_FACES, + END_COLOR, + BEGIN_NORMAL = END_COLOR, + SMOOTH_NORMAL_MONO_FACES = BEGIN_NORMAL, + FLAT_NORMAL_MONO_FACES, + SMOOTH_NORMAL_COLORED_FACES, + FLAT_NORMAL_COLORED_FACES, + END_NORMAL, + LAST_INDEX = END_NORMAL + }; + +protected: + Buffer_for_vao m_buffer_for_mono_points; + Buffer_for_vao m_buffer_for_colored_points; + Buffer_for_vao m_buffer_for_mono_segments; + Buffer_for_vao m_buffer_for_colored_segments; + Buffer_for_vao m_buffer_for_mono_rays; + Buffer_for_vao m_buffer_for_colored_rays; + Buffer_for_vao m_buffer_for_mono_lines; + Buffer_for_vao m_buffer_for_colored_lines; + Buffer_for_vao m_buffer_for_mono_faces; + Buffer_for_vao m_buffer_for_colored_faces; + + std::vector> m_texts; + + std::vector arrays[LAST_INDEX]; + + CGAL::Bbox_3 m_bounding_box; +}; + +} // namespace CGAL + +#endif // CGAL_GRAPHICS_SCENE_H diff --git a/Basic_viewer/include/CGAL/Graphics_scene_options.h b/Basic_viewer/include/CGAL/Graphics_scene_options.h new file mode 100644 index 00000000000..b610665ed40 --- /dev/null +++ b/Basic_viewer/include/CGAL/Graphics_scene_options.h @@ -0,0 +1,127 @@ +// Copyright (c) 2022 GeometryFactory Sarl (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): Guillaume Damiand +// Mostafa Ashraf + +#ifndef CGAL_GRAPHICS_SCENE_OPTIONS_H +#define CGAL_GRAPHICS_SCENE_OPTIONS_H + +#include +#include +#include + +namespace CGAL { + +template +struct Graphics_scene_options; + +// Drawing functor for a 2D combinatorial data structure +// (with vertices, edges and faces) +template +struct Graphics_scene_options +{ + + typedef VertexDescriptor vertex_descriptor; + typedef EdgeDescriptor edge_descriptor; + typedef FaceDescriptor face_descriptor; + + Graphics_scene_options(): m_enabled_vertices(true), + m_enabled_edges(true), + m_enabled_faces(true) + { + draw_vertex=[](const DS &, vertex_descriptor)->bool { return true; }; + draw_edge=[](const DS &, edge_descriptor)->bool { return true; }; + draw_face=[](const DS &, face_descriptor)->bool { return true; }; + + colored_vertex=[](const DS &, vertex_descriptor)->bool { return false; }; + colored_edge=[](const DS &, edge_descriptor)->bool { return false; }; + colored_face=[](const DS &, face_descriptor)->bool { return false; }; + + face_wireframe=[](const DS &, face_descriptor)->bool { return false; }; + } + + // The seven following functions should not be null + std::function draw_vertex; + std::function draw_edge; + std::function draw_face; + + std::function colored_vertex; + std::function colored_edge; + std::function colored_face; + + std::function face_wireframe; + + // These functions must be non null if the corresponding colored_XXX function + // returns true. + std::function vertex_color; + std::function edge_color; + std::function face_color; + + void disable_vertices() { m_enabled_vertices=false; } + void enable_vertices() { m_enabled_vertices=true; } + bool are_vertices_enabled() const { return m_enabled_vertices; } + + void disable_edges() { m_enabled_edges=false; } + void enable_edges() { m_enabled_edges=true; } + bool are_edges_enabled() const { return m_enabled_edges; } + + void disable_faces() { m_enabled_faces=false; } + void enable_faces() { m_enabled_faces=true; } + bool are_faces_enabled() const { return m_enabled_faces; } + +protected: + bool m_enabled_vertices, m_enabled_edges, m_enabled_faces; +}; + +// Drawing functor for a 3D combinatorial data structure +// (with vertices, edges, faces and volumes) +template +struct Graphics_scene_options: + public Graphics_scene_options +{ + typedef VertexDescriptor vertex_descriptor; + typedef EdgeDescriptor edge_descriptor; + typedef FaceDescriptor face_descriptor; + typedef VolumeDescriptor volume_descriptor; + + Graphics_scene_options() : m_enabled_volumes(true) + { + draw_volume=[](const DS &, volume_descriptor)->bool { return true; }; + colored_volume=[](const DS &, volume_descriptor)->bool { return false; }; + volume_wireframe=[](const DS &, volume_descriptor)->bool { return false; }; + } + + std::function draw_volume; + std::function colored_volume; + std::function volume_wireframe; + std::function volume_color; + + void disable_volumes() { m_enabled_volumes=false; } + void enable_volumes() { m_enabled_volumes=true; } + bool are_volumes_enabled() const { return m_enabled_volumes; } + +protected: + bool m_enabled_volumes; +}; + +} // End namespace CGAL + +#endif // CGAL_GRAPHICS_SCENE_OPTIONS_H diff --git a/GraphicsView/include/CGAL/Qt/Basic_viewer_qt.h b/Basic_viewer/include/CGAL/Qt/Basic_viewer.h similarity index 58% rename from GraphicsView/include/CGAL/Qt/Basic_viewer_qt.h rename to Basic_viewer/include/CGAL/Qt/Basic_viewer.h index 9cb3c235a22..20ef729341d 100644 --- a/GraphicsView/include/CGAL/Qt/Basic_viewer_qt.h +++ b/Basic_viewer/include/CGAL/Qt/Basic_viewer.h @@ -9,14 +9,17 @@ // // // Author(s) : Guillaume Damiand +// Mostafa Ashraf -#ifndef CGAL_BASIC_VIEWER_QT_H -#define CGAL_BASIC_VIEWER_QT_H +#ifndef CGAL_BASIC_VIEWER_H +#define CGAL_BASIC_VIEWER_H -#include +// TODO #include +#include #include #include #include +#include #ifdef CGAL_USE_BASIC_VIEWER @@ -47,53 +50,42 @@ #include #include -#include #include #include #include -#include #include +#include #define CGAL_BASIC_VIEWER_INIT_SIZE_X 500 #define CGAL_BASIC_VIEWER_INIT_SIZE_Y 450 -namespace CGAL -{ - +namespace CGAL { +namespace Qt { //------------------------------------------------------------------------------ -inline CGAL::IO::Color get_random_color(CGAL::Random& random) -{ - CGAL::IO::Color res; - do - { - res=CGAL::IO::Color(random.get_int(0,256), - random.get_int(0,256), - random.get_int(0,256)); - } - while(res.red()==255 && res.green()==255 && res.blue()==255); - return res; -} -//------------------------------------------------------------------------------ -class Basic_viewer_qt : public CGAL::QGLViewer +class Basic_viewer : public CGAL::QGLViewer { public: + using BufferType=float; typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; typedef Local_kernel::Point_3 Local_point; typedef Local_kernel::Vector_3 Local_vector; + using GS=Graphics_scene; // Constructor/Destructor - Basic_viewer_qt(QWidget* parent, - const char* title="", - bool draw_vertices=false, - bool draw_edges=true, - bool draw_faces=true, - bool use_mono_color=false, - bool inverse_normal=false, - bool draw_rays=true, - bool draw_lines=true, - bool draw_text=true, - bool no_2D_mode=false) : + Basic_viewer(QWidget* parent, + const Graphics_scene& buf, + const char* title="", + bool draw_vertices=false, + bool draw_edges=true, + bool draw_faces=true, + bool use_mono_color=false, + bool inverse_normal=false, + bool draw_rays=true, + bool draw_lines=true, + bool draw_text=true, + bool no_2D_mode=false) : CGAL::QGLViewer(parent), + gBuffer(buf), m_draw_vertices(draw_vertices), m_draw_edges(draw_edges), m_draw_rays(draw_rays), @@ -114,55 +106,7 @@ public: m_lines_mono_color(0, 0, 0), m_faces_mono_color(60, 60, 200), m_ambient_color(0.6f, 0.5f, 0.5f, 0.5f), - m_are_buffers_initialized(false), - m_buffer_for_mono_points(&arrays[POS_MONO_POINTS], - nullptr, - &m_bounding_box, - nullptr, nullptr, nullptr), - m_buffer_for_colored_points(&arrays[POS_COLORED_POINTS], - nullptr, - &m_bounding_box, - &arrays[COLOR_POINTS], - nullptr, nullptr), - m_buffer_for_mono_segments(&arrays[POS_MONO_SEGMENTS], - nullptr, - &m_bounding_box, - nullptr, nullptr, nullptr), - m_buffer_for_colored_segments(&arrays[POS_COLORED_SEGMENTS], - nullptr, - &m_bounding_box, - &arrays[COLOR_SEGMENTS], - nullptr, nullptr), - m_buffer_for_mono_rays(&arrays[POS_MONO_RAYS], - nullptr, - &m_bounding_box, - nullptr, nullptr), - m_buffer_for_colored_rays(&arrays[POS_COLORED_RAYS], - nullptr, - &m_bounding_box, - &arrays[COLOR_RAYS], - nullptr, nullptr), - m_buffer_for_mono_lines(&arrays[POS_MONO_RAYS], - nullptr, - &m_bounding_box, - nullptr, nullptr), - m_buffer_for_colored_lines(&arrays[POS_COLORED_LINES], - nullptr, - &m_bounding_box, - &arrays[COLOR_LINES], - nullptr, nullptr), - m_buffer_for_mono_faces(&arrays[POS_MONO_FACES], - nullptr, - &m_bounding_box, - nullptr, - &arrays[FLAT_NORMAL_MONO_FACES], - &arrays[SMOOTH_NORMAL_MONO_FACES]), - m_buffer_for_colored_faces(&arrays[POS_COLORED_FACES], - nullptr, - &m_bounding_box, - &arrays[COLOR_FACES], - &arrays[FLAT_NORMAL_COLORED_FACES], - &arrays[SMOOTH_NORMAL_COLORED_FACES]) + m_are_buffers_initialized(false) { // Define 'Control+Q' as the new exit shortcut (default was 'Escape') setShortcut(qglviewer::EXIT_VIEWER, ::Qt::CTRL, ::Qt::Key_Q); @@ -187,9 +131,12 @@ public: setKeyDescription(::Qt::Key_O, "Toggles 2D mode only"); // Add custom mouse description - setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::LeftButton, "Rotate the clipping plane when enabled"); - setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::RightButton, "Translate the clipping plane when enabled"); - setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::MiddleButton, "Control the clipping plane transparency when enabled"); + setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::LeftButton, + "Rotate the clipping plane when enabled"); + setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::RightButton, + "Translate the clipping plane when enabled"); + setMouseBindingDescription(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::MiddleButton, + "Control the clipping plane transparency when enabled"); setMouseBinding(::Qt::ControlModifier, ::Qt::LeftButton, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); setMouseBinding(::Qt::ControlModifier, ::Qt::RightButton, qglviewer::FRAME, qglviewer::NO_MOUSE_ACTION); @@ -209,117 +156,95 @@ public: resize(CGAL_BASIC_VIEWER_INIT_SIZE_X, CGAL_BASIC_VIEWER_INIT_SIZE_Y); if (inverse_normal) - { negate_all_normals(); } + { reverse_all_normals(); } } - ~Basic_viewer_qt() + ~Basic_viewer() { makeCurrent(); - for (unsigned int i=0; i - void add_point(const KPoint& p) - { m_buffer_for_mono_points.add_point(p); } - - template - void add_point(const KPoint& p, const CGAL::IO::Color& acolor) - { m_buffer_for_colored_points.add_point(p, acolor); } - - template - void add_segment(const KPoint& p1, const KPoint& p2) - { m_buffer_for_mono_segments.add_segment(p1, p2); } - - template - void add_segment(const KPoint& p1, const KPoint& p2, - const CGAL::IO::Color& acolor) - { m_buffer_for_colored_segments.add_segment(p1, p2, acolor); } - - template - void update_bounding_box_for_ray(const KPoint &p, const KVector &v) - { - Local_point lp = get_local_point(p); - Local_vector lv = get_local_vector(v); - CGAL::Bbox_3 b = (lp + lv).bbox(); - m_bounding_box += b; - } - - template - void update_bounding_box_for_line(const KPoint &p, const KVector &v, - const KVector &pv) - { - Local_point lp = get_local_point(p); - Local_vector lv = get_local_vector(v); - Local_vector lpv = get_local_vector(pv); - - CGAL::Bbox_3 b = lp.bbox() + (lp + lv).bbox() + (lp + lpv).bbox(); - m_bounding_box += b; - } - - template - void add_ray(const KPoint &p, const KVector &v) - { - double bigNumber = 1e30; - m_buffer_for_mono_rays.add_ray_segment(p, (p + (bigNumber)*v)); - } - - template - void add_ray(const KPoint &p, const KVector &v, const CGAL::IO::Color &acolor) - { - double bigNumber = 1e30; - m_buffer_for_colored_rays.add_ray_segment(p, (p + (bigNumber)*v), acolor); - } - - template - void add_line(const KPoint &p, const KVector &v) - { - double bigNumber = 1e30; - m_buffer_for_mono_lines.add_line_segment((p - (bigNumber)*v), - (p + (bigNumber)*v)); - } - - template - void add_line(const KPoint &p, const KVector &v, const CGAL::IO::Color &acolor) - { - double bigNumber = 1e30; - m_buffer_for_colored_lines.add_line_segment((p - (bigNumber)*v), - (p + (bigNumber)*v), acolor); - } - - template - void add_text(const KPoint& kp, const QString& txt) - { - Local_point p=get_local_point(kp); - m_texts.push_back(std::make_tuple(p, txt)); - } - - template - void add_text(const KPoint& kp, const char* txt) - { add_text(kp, QString(txt)); } - - template - void add_text(const KPoint& kp, const std::string& txt) - { add_text(kp, txt.c_str()); } - - bool is_a_face_started() const - { - return m_buffer_for_mono_faces.is_a_face_started() || - m_buffer_for_colored_faces.is_a_face_started(); - } - - void face_begin() - { - if (is_a_face_started()) - { - std::cerr<<"You cannot start a new face before to finish the previous one."< - bool add_point_in_face(const KPoint& kp) - { - if (m_buffer_for_mono_faces.is_a_face_started()) - { return m_buffer_for_mono_faces.add_point_in_face(kp); } - else if (m_buffer_for_colored_faces.is_a_face_started()) - { return m_buffer_for_colored_faces.add_point_in_face(kp); } - return false; - } - - template - bool add_point_in_face(const KPoint& kp, const KVector& p_normal) - { - if (m_buffer_for_mono_faces.is_a_face_started()) - { return m_buffer_for_mono_faces.add_point_in_face(kp, p_normal); } - else if (m_buffer_for_colored_faces.is_a_face_started()) - { return m_buffer_for_colored_faces.add_point_in_face(kp, p_normal); } - return false; - } - - void face_end() - { - if (m_buffer_for_mono_faces.is_a_face_started()) - { m_buffer_for_mono_faces.face_end(); } - else if (m_buffer_for_colored_faces.is_a_face_started()) - { return m_buffer_for_colored_faces.face_end(); } - } + const Graphics_scene& graphics_scene() const + { return gBuffer; } virtual void redraw() { @@ -475,21 +266,362 @@ public: update(); } -protected: - // Shortcuts to simplify function calls. - template - static Local_point get_local_point(const KPoint& p) + void reverse_all_normals() { - return internal::Geom_utils::Kernel, Local_kernel>:: - get_local_point(p); - } - template - static Local_vector get_local_vector(const KVector& v) - { - return internal::Geom_utils::Kernel, Local_kernel>:: - get_local_vector(v); + m_inverse_normal=!m_inverse_normal; + gBuffer.reverse_all_normals(); } + // Returns true if the data structure lies on a plane + bool is_two_dimensional() const + { return !m_no_2D_mode && gBuffer.is_two_dimensional(); } + + virtual void draw() + { + glEnable(GL_DEPTH_TEST); + + QMatrix4x4 clipping_mMatrix; + clipping_mMatrix.setToIdentity(); + for(int i=0; i< 16 ; i++) + { clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; } + QVector4D clipPlane = clipping_mMatrix * QVector4D(0.0, 0.0, 1.0, 0.0); + QVector4D plane_point = clipping_mMatrix * QVector4D(0,0,0,1); + if(!m_are_buffers_initialized) + { initialize_buffers(); } + + QColor color; + attrib_buffers(this); + + if(m_draw_vertices) + { + rendering_program_p_l.bind(); + + // rendering_mode == -1: draw all + // rendering_mode == 0: draw inside clipping plane + // rendering_mode == 1: draw outside clipping plane + auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { + vao[VAO_MONO_POINTS].bind(); + color.setRgbF((double)m_vertices_mono_color.red()/(double)255, + (double)m_vertices_mono_color.green()/(double)255, + (double)m_vertices_mono_color.blue()/(double)255); + rendering_program_p_l.setAttributeValue("color",color); + rendering_program_p_l.setUniformValue("point_size", GLfloat(m_size_points)); + rendering_program_p_l.setUniformValue("clipPlane", clipPlane); + rendering_program_p_l.setUniformValue("pointPlane", plane_point); + rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); + glDrawArrays(GL_POINTS, 0, static_cast(gBuffer.number_of_elements(GS::POS_MONO_POINTS))); + vao[VAO_MONO_POINTS].release(); + + vao[VAO_COLORED_POINTS].bind(); + if (m_use_mono_color) + { + color.setRgbF((double)m_vertices_mono_color.red()/(double)255, + (double)m_vertices_mono_color.green()/(double)255, + (double)m_vertices_mono_color.blue()/(double)255); + rendering_program_p_l.disableAttributeArray("color"); + rendering_program_p_l.setAttributeValue("color",color); + } + else + { + rendering_program_p_l.enableAttributeArray("color"); + } + rendering_program_p_l.setUniformValue("point_size", GLfloat(m_size_points)); + rendering_program_p_l.setUniformValue("clipPlane", clipPlane); + rendering_program_p_l.setUniformValue("pointPlane", plane_point); + rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); + glDrawArrays(GL_POINTS, 0, static_cast(gBuffer.number_of_elements(GS::POS_COLORED_POINTS))); + vao[VAO_COLORED_POINTS].release(); + }; + + enum { + DRAW_ALL = -1, // draw all + DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane + DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane + }; + + if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) + { + renderer(DRAW_INSIDE_ONLY); + } + else + { + renderer(DRAW_ALL); + } + + rendering_program_p_l.release(); + } + + if(m_draw_edges) + { + rendering_program_p_l.bind(); + + // rendering_mode == -1: draw all + // rendering_mode == 0: draw inside clipping plane + // rendering_mode == 1: draw outside clipping plane + auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { + vao[VAO_MONO_SEGMENTS].bind(); + color.setRgbF((double)m_edges_mono_color.red()/(double)255, + (double)m_edges_mono_color.green()/(double)255, + (double)m_edges_mono_color.blue()/(double)255); + rendering_program_p_l.setAttributeValue("color",color); + rendering_program_p_l.setUniformValue("clipPlane", clipPlane); + rendering_program_p_l.setUniformValue("pointPlane", plane_point); + rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); + glLineWidth(m_size_edges); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_MONO_SEGMENTS))); + vao[VAO_MONO_SEGMENTS].release(); + + vao[VAO_COLORED_SEGMENTS].bind(); + if (m_use_mono_color) + { + color.setRgbF((double)m_edges_mono_color.red()/(double)255, + (double)m_edges_mono_color.green()/(double)255, + (double)m_edges_mono_color.blue()/(double)255); + rendering_program_p_l.disableAttributeArray("color"); + rendering_program_p_l.setAttributeValue("color",color); + } + else + { + rendering_program_p_l.enableAttributeArray("color"); + } + rendering_program_p_l.setUniformValue("clipPlane", clipPlane); + rendering_program_p_l.setUniformValue("pointPlane", plane_point); + rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); + glLineWidth(m_size_edges); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_COLORED_SEGMENTS))); + vao[VAO_COLORED_SEGMENTS].release(); + }; + + enum { + DRAW_ALL = -1, // draw all + DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane + DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane + }; + + if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) + { + renderer(DRAW_INSIDE_ONLY); + } + else + { + renderer(DRAW_ALL); + } + + rendering_program_p_l.release(); + } + + if(m_draw_rays) + { + rendering_program_p_l.bind(); + + vao[VAO_MONO_RAYS].bind(); + color.setRgbF((double)m_rays_mono_color.red()/(double)255, + (double)m_rays_mono_color.green()/(double)255, + (double)m_rays_mono_color.blue()/(double)255); + rendering_program_p_l.setAttributeValue("color",color); + glLineWidth(m_size_rays); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_MONO_RAYS))); + vao[VAO_MONO_RAYS].release(); + + vao[VAO_COLORED_RAYS].bind(); + if (m_use_mono_color) + { + color.setRgbF((double)m_rays_mono_color.red()/(double)255, + (double)m_rays_mono_color.green()/(double)255, + (double)m_rays_mono_color.blue()/(double)255); + rendering_program_p_l.disableAttributeArray("color"); + rendering_program_p_l.setAttributeValue("color",color); + } + else + { + rendering_program_p_l.enableAttributeArray("color"); + } + glLineWidth(m_size_rays); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_COLORED_RAYS))); + vao[VAO_COLORED_RAYS].release(); + + rendering_program_p_l.release(); + } + + if(m_draw_lines) + { + rendering_program_p_l.bind(); + + vao[VAO_MONO_LINES].bind(); + color.setRgbF((double)m_lines_mono_color.red()/(double)255, + (double)m_lines_mono_color.green()/(double)255, + (double)m_lines_mono_color.blue()/(double)255); + rendering_program_p_l.setAttributeValue("color",color); + glLineWidth(m_size_lines); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_MONO_LINES))); + vao[VAO_MONO_LINES].release(); + + rendering_program_p_l.release(); + + vao[VAO_COLORED_LINES].bind(); + if (m_use_mono_color) + { + color.setRgbF((double)m_lines_mono_color.red()/(double)255, + (double)m_lines_mono_color.green()/(double)255, + (double)m_lines_mono_color.blue()/(double)255); + rendering_program_p_l.disableAttributeArray("color"); + rendering_program_p_l.setAttributeValue("color",color); + } + else + { + rendering_program_p_l.enableAttributeArray("color"); + } + glLineWidth(m_size_lines); + glDrawArrays(GL_LINES, 0, static_cast(gBuffer.number_of_elements(GS::POS_COLORED_LINES))); + vao[VAO_COLORED_LINES].release(); + + rendering_program_p_l.release(); + } + + // Fix Z-fighting by drawing faces at a depth + GLfloat offset_factor; + GLfloat offset_units; + if (is_two_dimensional()) { + glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor); + glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units); + glPolygonOffset(0.1f, 0.9f); + } + + if (m_draw_faces) + { + rendering_program_face.bind(); + + // reference: https://stackoverflow.com/questions/37780345/opengl-how-to-create-order-independent-transparency + // rendering_mode == -1: draw all as solid; + // rendering_mode == 0: draw solid only; + // rendering_mode == 1: draw transparent only; + auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { + + vao[VAO_MONO_FACES].bind(); + color.setRgbF((double)m_faces_mono_color.red()/(double)255, + (double)m_faces_mono_color.green()/(double)255, + (double)m_faces_mono_color.blue()/(double)255); + rendering_program_face.setAttributeValue("color",color); + rendering_program_face.setUniformValue("rendering_mode", rendering_mode); + rendering_program_face.setUniformValue("rendering_transparency", clipping_plane_rendering_transparency); + rendering_program_face.setUniformValue("clipPlane", clipPlane); + rendering_program_face.setUniformValue("pointPlane", plane_point); + glDrawArrays(GL_TRIANGLES, 0, static_cast(gBuffer.number_of_elements(GS::POS_MONO_FACES))); + vao[VAO_MONO_FACES].release(); + + vao[VAO_COLORED_FACES].bind(); + if (m_use_mono_color) + { + color.setRgbF((double)m_faces_mono_color.red()/(double)255, + (double)m_faces_mono_color.green()/(double)255, + (double)m_faces_mono_color.blue()/(double)255); + rendering_program_face.disableAttributeArray("color"); + rendering_program_face.setAttributeValue("color",color); + } + else + { + rendering_program_face.enableAttributeArray("color"); + } + rendering_program_face.setUniformValue("rendering_mode", rendering_mode); + rendering_program_face.setUniformValue("rendering_transparency", clipping_plane_rendering_transparency); + rendering_program_face.setUniformValue("clipPlane", clipPlane); + rendering_program_face.setUniformValue("pointPlane", plane_point); + glDrawArrays(GL_TRIANGLES, 0, static_cast(gBuffer.number_of_elements(GS::POS_COLORED_FACES))); + vao[VAO_COLORED_FACES].release(); + }; + + auto renderer_clipping_plane = [this](bool clipping_plane_rendering) { + if (!isOpenGL_4_3()) return; + if (!clipping_plane_rendering) return; + // render clipping plane here + rendering_program_clipping_plane.bind(); + vao[VAO_CLIPPING_PLANE].bind(); + glLineWidth(0.1f); + glDrawArrays(GL_LINES, 0, static_cast((m_array_for_clipping_plane.size()/3))); + glLineWidth(1.0f); + vao[VAO_CLIPPING_PLANE].release(); + rendering_program_clipping_plane.release(); + }; + + enum { + DRAW_SOLID_ALL = -1, // draw all mesh in solid mode + DRAW_SOLID_HALF, // draw only the mesh inside the clipping plane as solid + DRAW_TRANSPARENT_HALF // draw only the mesh outside the clipping plane as transparent + }; + + if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF) + { + // The z-buffer will prevent transparent objects from being displayed behind other transparent objects. + // Before rendering all transparent objects, disable z-testing first. + + // 1. draw solid first + renderer(DRAW_SOLID_HALF); + + // 2. draw transparent layer second with back face culling to avoid messy triangles + glDepthMask(false); //disable z-testing + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + renderer(DRAW_TRANSPARENT_HALF); + + // 3. draw solid again without culling and blend to make sure the solid mesh is visible + glDepthMask(true); //enable z-testing + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + renderer(DRAW_SOLID_HALF); + + // 4. render clipping plane here + renderer_clipping_plane(clipping_plane_rendering); + } + else if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_WIRE_HALF || + m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) + { + // 1. draw solid HALF + renderer(DRAW_SOLID_HALF); + + // 2. render clipping plane here + renderer_clipping_plane(clipping_plane_rendering); + } + else + { + // 1. draw solid FOR ALL + renderer(DRAW_SOLID_ALL); + } + + if (is_two_dimensional()) + glPolygonOffset(offset_factor, offset_units); + + rendering_program_face.release(); + } + + if (m_draw_text) + { + glDisable(GL_LIGHTING); + for (std::size_t i=0; iprojectedCoordinatesOf + (CGAL::qglviewer::Vec(std::get<0>(m_texts_vec[i]).x(), + std::get<0>(m_texts_vec[i]).y(), + std::get<0>(m_texts_vec[i]).z())); + + drawText((int)screenPos[0], (int)screenPos[1], + QString(std::get<1>(m_texts_vec[i]).c_str())); + } + glEnable(GL_LIGHTING); + } + + // Multiply matrix to get in the frame coordinate system. + // glMultMatrixd(manipulatedFrame()->matrix()); // Linker error + // Scale down the drawings + // glScalef(0.3f, 0.3f, 0.3f); // Linker error + // Draw an axis using the QGLViewer static function + // drawAxis(); + } + +protected: void compile_shaders() { rendering_program_face.removeAllShaders(); @@ -497,7 +629,7 @@ protected: rendering_program_clipping_plane.removeAllShaders(); // Create the buffers - for (unsigned int i=0; i(arrays[POS_MONO_POINTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_MONO_POINTS).data(), + gBuffer.get_size_of_index(GS::POS_MONO_POINTS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); + // TODO/QUESTION can we use double for BufferType? + // if not remove the template parameter and use float everywhere. buffers[bufn].release(); @@ -635,19 +769,19 @@ protected: vao[VAO_COLORED_POINTS].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_COLORED_POINTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_COLORED_POINTS).data(), + gBuffer.get_size_of_index(GS::POS_COLORED_POINTS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); buffers[bufn].release(); ++bufn; - CGAL_assertion(bufn(arrays[COLOR_POINTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::COLOR_POINTS).data(), + gBuffer.get_size_of_index(GS::COLOR_POINTS)); rendering_program_p_l.enableAttributeArray("color"); rendering_program_p_l.setAttributeBuffer("color",GL_FLOAT,0,3); buffers[bufn].release(); @@ -660,10 +794,10 @@ protected: vao[VAO_MONO_SEGMENTS].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_MONO_SEGMENTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_MONO_SEGMENTS).data(), + gBuffer.get_size_of_index(GS::POS_MONO_SEGMENTS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); @@ -677,20 +811,20 @@ protected: vao[VAO_COLORED_SEGMENTS].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_COLORED_SEGMENTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_COLORED_SEGMENTS).data(), + gBuffer.get_size_of_index(GS::POS_COLORED_SEGMENTS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); buffers[bufn].release(); ++bufn; - CGAL_assertion(bufn(arrays[COLOR_SEGMENTS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::COLOR_SEGMENTS).data(), + gBuffer.get_size_of_index(GS::COLOR_SEGMENTS)); rendering_program_p_l.enableAttributeArray("color"); rendering_program_p_l.setAttributeBuffer("color",GL_FLOAT,0,3); buffers[bufn].release(); @@ -705,10 +839,10 @@ protected: vao[VAO_MONO_RAYS].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_MONO_RAYS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_MONO_RAYS).data(), + gBuffer.get_size_of_index(GS::POS_MONO_RAYS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeArray("vertex",GL_FLOAT,0,3); @@ -723,20 +857,20 @@ protected: vao[VAO_COLORED_RAYS].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_COLORED_RAYS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_COLORED_RAYS).data(), + gBuffer.get_size_of_index(GS::POS_COLORED_RAYS)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); buffers[bufn].release(); ++bufn; - CGAL_assertion(bufn(arrays[COLOR_RAYS].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::COLOR_RAYS).data(), + gBuffer.get_size_of_index(GS::COLOR_RAYS)); rendering_program_p_l.enableAttributeArray("color"); rendering_program_p_l.setAttributeBuffer("color",GL_FLOAT,0,3); buffers[bufn].release(); @@ -750,10 +884,10 @@ protected: vao[VAO_MONO_LINES].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_MONO_LINES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_MONO_LINES).data(), + gBuffer.get_size_of_index(GS::POS_MONO_LINES)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeArray("vertex",GL_FLOAT,0,3); @@ -768,20 +902,20 @@ protected: vao[VAO_COLORED_LINES].bind(); ++bufn; - CGAL_assertion(bufn(arrays[POS_COLORED_LINES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_COLORED_LINES).data(), + gBuffer.get_size_of_index(GS::POS_COLORED_LINES)); rendering_program_p_l.enableAttributeArray("vertex"); rendering_program_p_l.setAttributeBuffer("vertex",GL_FLOAT,0,3); buffers[bufn].release(); ++bufn; - CGAL_assertion(bufn(arrays[COLOR_LINES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::COLOR_LINES).data(), + gBuffer.get_size_of_index(GS::COLOR_LINES)); rendering_program_p_l.enableAttributeArray("color"); rendering_program_p_l.setAttributeBuffer("color",GL_FLOAT,0,3); buffers[bufn].release(); @@ -798,10 +932,10 @@ protected: // 5.1.1) points of the mono faces ++bufn; - CGAL_assertion(bufn(arrays[POS_MONO_FACES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_MONO_FACES).data(), + gBuffer.get_size_of_index(GS::POS_MONO_FACES)); rendering_program_face.enableAttributeArray("vertex"); rendering_program_face.setAttributeBuffer("vertex",GL_FLOAT,0,3); @@ -809,19 +943,17 @@ protected: // 5.1.2) normals of the mono faces ++bufn; - CGAL_assertion(bufn(arrays[FLAT_NORMAL_MONO_FACES].size()* - sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::FLAT_NORMAL_MONO_FACES).data(), + gBuffer.get_size_of_index(GS::FLAT_NORMAL_MONO_FACES)); } else { - buffers[bufn].allocate(arrays[SMOOTH_NORMAL_MONO_FACES].data(), - static_cast(arrays[SMOOTH_NORMAL_MONO_FACES].size()* - sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::SMOOTH_NORMAL_MONO_FACES).data(), + gBuffer.get_size_of_index(GS::SMOOTH_NORMAL_MONO_FACES)); } rendering_program_face.enableAttributeArray("normal"); rendering_program_face.setAttributeBuffer("normal",GL_FLOAT,0,3); @@ -837,10 +969,10 @@ protected: // 5.2.1) points of the color faces ++bufn; - CGAL_assertion(bufn(arrays[POS_COLORED_FACES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::POS_COLORED_FACES).data(), + gBuffer.get_size_of_index(GS::POS_COLORED_FACES)); rendering_program_face.enableAttributeArray("vertex"); rendering_program_face.setAttributeBuffer("vertex",GL_FLOAT,0,3); @@ -848,19 +980,17 @@ protected: // 5.2.2) normals of the color faces ++bufn; - CGAL_assertion(bufn(arrays[FLAT_NORMAL_COLORED_FACES].size()* - sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::FLAT_NORMAL_COLORED_FACES).data(), + gBuffer.get_size_of_index(GS::FLAT_NORMAL_COLORED_FACES)); } else { - buffers[bufn].allocate(arrays[SMOOTH_NORMAL_COLORED_FACES].data(), - static_cast(arrays[SMOOTH_NORMAL_COLORED_FACES].size()* - sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::SMOOTH_NORMAL_COLORED_FACES).data(), + gBuffer.get_size_of_index(GS::SMOOTH_NORMAL_COLORED_FACES)); } rendering_program_face.enableAttributeArray("normal"); rendering_program_face.setAttributeBuffer("normal",GL_FLOAT,0,3); @@ -869,10 +999,10 @@ protected: // 5.2.3) colors of the faces ++bufn; - CGAL_assertion(bufn(arrays[COLOR_FACES].size()*sizeof(float))); + buffers[bufn].allocate(gBuffer.get_array_of_index(GS::COLOR_FACES).data(), + gBuffer.get_size_of_index(GS::COLOR_FACES)); rendering_program_face.enableAttributeArray("color"); rendering_program_face.setAttributeBuffer("color",GL_FLOAT,0,3); @@ -892,10 +1022,10 @@ protected: vao[VAO_CLIPPING_PLANE].bind(); ++bufn; - CGAL_assertion(bufn < NB_VBO_BUFFERS); + CGAL_assertion(bufn < NB_GL_BUFFERS); buffers[bufn].bind(); - buffers[bufn].allocate(arrays[POS_CLIPPING_PLANE].data(), - static_cast(arrays[POS_CLIPPING_PLANE].size() * sizeof(float))); + buffers[bufn].allocate(m_array_for_clipping_plane.data(), + static_cast(m_array_for_clipping_plane.size()*sizeof(BufferType))); rendering_program_clipping_plane.enableAttributeArray("vertex"); rendering_program_clipping_plane.setAttributeBuffer("vertex", GL_FLOAT, 0, 3); @@ -934,13 +1064,13 @@ protected: 1.0f ); CGAL::Bbox_3 bb; - if (bb==bounding_box()) // Case of "empty" bounding box + if (bb==gBuffer.bounding_box()) // Case of "empty" bounding box { bb=Local_point(CGAL::ORIGIN).bbox(); bb=bb + Local_point(1,1,1).bbox(); // To avoid a warning from Qglviewer } else - { bb=bounding_box(); } + { bb=gBuffer.bounding_box(); } QVector4D position((bb.xmax()-bb.xmin())/2, (bb.ymax()-bb.ymin())/2, @@ -988,12 +1118,6 @@ protected: } } - // Returns true if the data structure lies on a plane - bool is_two_dimensional() { - return (!is_empty() && !m_no_2D_mode && - (has_zero_x() || has_zero_y() || has_zero_z())); - } - void set_camera_mode() { if (is_two_dimensional()) @@ -1004,9 +1128,9 @@ protected: constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); double cx=0., cy=0., cz=0.; - if (has_zero_x()) { cx=1.; } - else if (has_zero_y()) { cy=1.; } - else { cz=1.; } + if (gBuffer.has_zero_x()) { cx=1.; } + else if (gBuffer.has_zero_y()) { cy=1.; } + else { cz=1.; } camera()->setViewDirection(CGAL::qglviewer::Vec(-cx,-cy,-cz)); constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(cx, cy, cz)); @@ -1019,349 +1143,6 @@ protected: } } - virtual void draw() - { - glEnable(GL_DEPTH_TEST); - - QMatrix4x4 clipping_mMatrix; - clipping_mMatrix.setToIdentity(); - for(int i=0; i< 16 ; i++) - { clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; } - QVector4D clipPlane = clipping_mMatrix * QVector4D(0.0, 0.0, 1.0, 0.0); - QVector4D plane_point = clipping_mMatrix * QVector4D(0,0,0,1); - if(!m_are_buffers_initialized) - { initialize_buffers(); } - - QColor color; - attrib_buffers(this); - - if(m_draw_vertices) - { - rendering_program_p_l.bind(); - - // rendering_mode == -1: draw all - // rendering_mode == 0: draw inside clipping plane - // rendering_mode == 1: draw outside clipping plane - auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { - vao[VAO_MONO_POINTS].bind(); - color.setRgbF((double)m_vertices_mono_color.red()/(double)255, - (double)m_vertices_mono_color.green()/(double)255, - (double)m_vertices_mono_color.blue()/(double)255); - rendering_program_p_l.setAttributeValue("color",color); - rendering_program_p_l.setUniformValue("point_size", GLfloat(m_size_points)); - rendering_program_p_l.setUniformValue("clipPlane", clipPlane); - rendering_program_p_l.setUniformValue("pointPlane", plane_point); - rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); - glDrawArrays(GL_POINTS, 0, static_cast(arrays[POS_MONO_POINTS].size()/3)); - vao[VAO_MONO_POINTS].release(); - - vao[VAO_COLORED_POINTS].bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_vertices_mono_color.red()/(double)255, - (double)m_vertices_mono_color.green()/(double)255, - (double)m_vertices_mono_color.blue()/(double)255); - rendering_program_p_l.disableAttributeArray("color"); - rendering_program_p_l.setAttributeValue("color",color); - } - else - { - rendering_program_p_l.enableAttributeArray("color"); - } - rendering_program_p_l.setUniformValue("point_size", GLfloat(m_size_points)); - rendering_program_p_l.setUniformValue("clipPlane", clipPlane); - rendering_program_p_l.setUniformValue("pointPlane", plane_point); - rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); - glDrawArrays(GL_POINTS, 0, static_cast(arrays[POS_COLORED_POINTS].size()/3)); - vao[VAO_COLORED_POINTS].release(); - }; - - enum { - DRAW_ALL = -1, // draw all - DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane - DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane - }; - - if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) - { - renderer(DRAW_INSIDE_ONLY); - } - else - { - renderer(DRAW_ALL); - } - - rendering_program_p_l.release(); - } - - if(m_draw_edges) - { - rendering_program_p_l.bind(); - - // rendering_mode == -1: draw all - // rendering_mode == 0: draw inside clipping plane - // rendering_mode == 1: draw outside clipping plane - auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { - vao[VAO_MONO_SEGMENTS].bind(); - color.setRgbF((double)m_edges_mono_color.red()/(double)255, - (double)m_edges_mono_color.green()/(double)255, - (double)m_edges_mono_color.blue()/(double)255); - rendering_program_p_l.setAttributeValue("color",color); - rendering_program_p_l.setUniformValue("clipPlane", clipPlane); - rendering_program_p_l.setUniformValue("pointPlane", plane_point); - rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); - glLineWidth(m_size_edges); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_MONO_SEGMENTS].size()/3)); - vao[VAO_MONO_SEGMENTS].release(); - - vao[VAO_COLORED_SEGMENTS].bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_edges_mono_color.red()/(double)255, - (double)m_edges_mono_color.green()/(double)255, - (double)m_edges_mono_color.blue()/(double)255); - rendering_program_p_l.disableAttributeArray("color"); - rendering_program_p_l.setAttributeValue("color",color); - } - else - { - rendering_program_p_l.enableAttributeArray("color"); - } - rendering_program_p_l.setUniformValue("clipPlane", clipPlane); - rendering_program_p_l.setUniformValue("pointPlane", plane_point); - rendering_program_p_l.setUniformValue("rendering_mode", rendering_mode); - glLineWidth(m_size_edges); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_COLORED_SEGMENTS].size()/3)); - vao[VAO_COLORED_SEGMENTS].release(); - }; - - enum { - DRAW_ALL = -1, // draw all - DRAW_INSIDE_ONLY, // draw only the part inside the clipping plane - DRAW_OUTSIDE_ONLY // draw only the part outside the clipping plane - }; - - if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) - { - renderer(DRAW_INSIDE_ONLY); - } - else - { - renderer(DRAW_ALL); - } - - rendering_program_p_l.release(); - } - - if(m_draw_rays) - { - rendering_program_p_l.bind(); - - vao[VAO_MONO_RAYS].bind(); - color.setRgbF((double)m_rays_mono_color.red()/(double)255, - (double)m_rays_mono_color.green()/(double)255, - (double)m_rays_mono_color.blue()/(double)255); - rendering_program_p_l.setAttributeValue("color",color); - glLineWidth(m_size_rays); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_MONO_RAYS].size()/3)); - vao[VAO_MONO_RAYS].release(); - - vao[VAO_COLORED_RAYS].bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_rays_mono_color.red()/(double)255, - (double)m_rays_mono_color.green()/(double)255, - (double)m_rays_mono_color.blue()/(double)255); - rendering_program_p_l.disableAttributeArray("color"); - rendering_program_p_l.setAttributeValue("color",color); - } - else - { - rendering_program_p_l.enableAttributeArray("color"); - } - glLineWidth(m_size_rays); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_COLORED_RAYS].size()/3)); - vao[VAO_COLORED_RAYS].release(); - - rendering_program_p_l.release(); - } - - if(m_draw_lines) - { - rendering_program_p_l.bind(); - - vao[VAO_MONO_LINES].bind(); - color.setRgbF((double)m_lines_mono_color.red()/(double)255, - (double)m_lines_mono_color.green()/(double)255, - (double)m_lines_mono_color.blue()/(double)255); - rendering_program_p_l.setAttributeValue("color",color); - glLineWidth(m_size_lines); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_MONO_LINES].size()/3)); - vao[VAO_MONO_LINES].release(); - - rendering_program_p_l.release(); - - vao[VAO_COLORED_LINES].bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_rays_mono_color.red()/(double)255, - (double)m_rays_mono_color.green()/(double)255, - (double)m_rays_mono_color.blue()/(double)255); - rendering_program_p_l.disableAttributeArray("color"); - rendering_program_p_l.setAttributeValue("color",color); - } - else - { - rendering_program_p_l.enableAttributeArray("color"); - } - glLineWidth(m_size_lines); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_COLORED_LINES].size()/3)); - vao[VAO_COLORED_LINES].release(); - - rendering_program_p_l.release(); - } - - // Fix Z-fighting by drawing faces at a depth - GLfloat offset_factor; - GLfloat offset_units; - if (is_two_dimensional()) { - glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor); - glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units); - glPolygonOffset(0.1f, 0.9f); - } - - if (m_draw_faces) - { - rendering_program_face.bind(); - - // reference: https://stackoverflow.com/questions/37780345/opengl-how-to-create-order-independent-transparency - // rendering_mode == -1: draw all as solid; - // rendering_mode == 0: draw solid only; - // rendering_mode == 1: draw transparent only; - auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { - - vao[VAO_MONO_FACES].bind(); - color.setRgbF((double)m_faces_mono_color.red()/(double)255, - (double)m_faces_mono_color.green()/(double)255, - (double)m_faces_mono_color.blue()/(double)255); - rendering_program_face.setAttributeValue("color",color); - rendering_program_face.setUniformValue("rendering_mode", rendering_mode); - rendering_program_face.setUniformValue("rendering_transparency", clipping_plane_rendering_transparency); - rendering_program_face.setUniformValue("clipPlane", clipPlane); - rendering_program_face.setUniformValue("pointPlane", plane_point); - glDrawArrays(GL_TRIANGLES, 0, static_cast(arrays[POS_MONO_FACES].size()/3)); - vao[VAO_MONO_FACES].release(); - - vao[VAO_COLORED_FACES].bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_faces_mono_color.red()/(double)255, - (double)m_faces_mono_color.green()/(double)255, - (double)m_faces_mono_color.blue()/(double)255); - rendering_program_face.disableAttributeArray("color"); - rendering_program_face.setAttributeValue("color",color); - } - else - { - rendering_program_face.enableAttributeArray("color"); - } - rendering_program_face.setUniformValue("rendering_mode", rendering_mode); - rendering_program_face.setUniformValue("rendering_transparency", clipping_plane_rendering_transparency); - rendering_program_face.setUniformValue("clipPlane", clipPlane); - rendering_program_face.setUniformValue("pointPlane", plane_point); - glDrawArrays(GL_TRIANGLES, 0, static_cast(arrays[POS_COLORED_FACES].size()/3)); - vao[VAO_COLORED_FACES].release(); - }; - - auto renderer_clipping_plane = [this](bool clipping_plane_rendering) { - if (!isOpenGL_4_3()) return; - if (!clipping_plane_rendering) return; - // render clipping plane here - rendering_program_clipping_plane.bind(); - vao[VAO_CLIPPING_PLANE].bind(); - glLineWidth(0.1f); - glDrawArrays(GL_LINES, 0, static_cast(arrays[POS_CLIPPING_PLANE].size() / 3)); - glLineWidth(1.0f); - vao[VAO_CLIPPING_PLANE].release(); - rendering_program_clipping_plane.release(); - }; - - enum { - DRAW_SOLID_ALL = -1, // draw all mesh in solid mode - DRAW_SOLID_HALF, // draw only the mesh inside the clipping plane as solid - DRAW_TRANSPARENT_HALF // draw only the mesh outside the clipping plane as transparent - }; - - if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF) - { - // The z-buffer will prevent transparent objects from being displayed behind other transparent objects. - // Before rendering all transparent objects, disable z-testing first. - - // 1. draw solid first - renderer(DRAW_SOLID_HALF); - - // 2. draw transparent layer second with back face culling to avoid messy triangles - glDepthMask(false); //disable z-testing - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CW); - renderer(DRAW_TRANSPARENT_HALF); - - // 3. draw solid again without culling and blend to make sure the solid mesh is visible - glDepthMask(true); //enable z-testing - glDisable(GL_CULL_FACE); - glDisable(GL_BLEND); - renderer(DRAW_SOLID_HALF); - - // 4. render clipping plane here - renderer_clipping_plane(clipping_plane_rendering); - } - else if (m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_WIRE_HALF || - m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) - { - // 1. draw solid HALF - renderer(DRAW_SOLID_HALF); - - // 2. render clipping plane here - renderer_clipping_plane(clipping_plane_rendering); - } - else - { - // 1. draw solid FOR ALL - renderer(DRAW_SOLID_ALL); - } - - if (is_two_dimensional()) - glPolygonOffset(offset_factor, offset_units); - - rendering_program_face.release(); - } - - if (m_draw_text) - { - glDisable(GL_LIGHTING); - for (std::size_t i=0; iprojectedCoordinatesOf - (CGAL::qglviewer::Vec(std::get<0>(m_texts[i]).x(), - std::get<0>(m_texts[i]).y(), - std::get<0>(m_texts[i]).z())); - - drawText((int)screenPos[0], (int)screenPos[1], std::get<1>(m_texts[i])); - } - glEnable(GL_LIGHTING); - } - - // Multiply matrix to get in the frame coordinate system. - // glMultMatrixd(manipulatedFrame()->matrix()); // Linker error - // Scale down the drawings - // glScalef(0.3f, 0.3f, 0.3f); // Linker error - // Draw an axis using the QGLViewer static function - // drawAxis(); - } - virtual void init() { set_camera_mode(); @@ -1381,13 +1162,13 @@ protected: compile_shaders(); CGAL::Bbox_3 bb; - if (bb==bounding_box()) // Case of "empty" bounding box + if (bb==gBuffer.bounding_box()) // Case of "empty" bounding box { bb=Local_point(CGAL::ORIGIN).bbox(); bb=bb + Local_point(1,1,1).bbox(); // To avoid a warning from Qglviewer } else - { bb=bounding_box(); } + { bb=gBuffer.bounding_box(); } this->camera()->setSceneBoundingBox(CGAL::qglviewer::Vec(bb.xmin(), bb.ymin(), bb.zmin()), @@ -1402,117 +1183,112 @@ protected: void generate_clipping_plane() { - qreal size=((bounding_box().xmax() - bounding_box().xmin()) + - (bounding_box().ymax() - bounding_box().ymin()) + - (bounding_box().zmax() - bounding_box().zmin())); + qreal size=((gBuffer.bounding_box().xmax()-gBuffer.bounding_box().xmin()) + + (gBuffer.bounding_box().ymax()-gBuffer.bounding_box().ymin()) + + (gBuffer.bounding_box().zmax()-gBuffer.bounding_box().zmin())); const unsigned int nbSubdivisions=30; - arrays[POS_CLIPPING_PLANE].clear(); + auto& array = m_array_for_clipping_plane; + array.clear(); for (unsigned int i=0; i<=nbSubdivisions; ++i) { const float pos = float(size*(2.0*i/nbSubdivisions-1.0)); - arrays[POS_CLIPPING_PLANE].push_back(pos); - arrays[POS_CLIPPING_PLANE].push_back(float(-size)); - arrays[POS_CLIPPING_PLANE].push_back(0.f); + array.push_back(pos); + array.push_back(float(-size)); + array.push_back(0.f); - arrays[POS_CLIPPING_PLANE].push_back(pos); - arrays[POS_CLIPPING_PLANE].push_back(float(+size)); - arrays[POS_CLIPPING_PLANE].push_back(0.f); + array.push_back(pos); + array.push_back(float(+size)); + array.push_back(0.f); - arrays[POS_CLIPPING_PLANE].push_back(float(-size)); - arrays[POS_CLIPPING_PLANE].push_back(pos); - arrays[POS_CLIPPING_PLANE].push_back(0.f); + array.push_back(float(-size)); + array.push_back(pos); + array.push_back(0.f); - arrays[POS_CLIPPING_PLANE].push_back(float(size)); - arrays[POS_CLIPPING_PLANE].push_back(pos); - arrays[POS_CLIPPING_PLANE].push_back(0.f); + array.push_back(float(size)); + array.push_back(pos); + array.push_back(0.f); } } - void negate_all_normals() - { - m_buffer_for_mono_faces.negate_normals(); - m_buffer_for_colored_faces.negate_normals(); - } - virtual void keyPressEvent(QKeyEvent *e) { - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - - if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::NoButton)) + if(!on_key_pressed || !on_key_pressed(e, this)) { - if (!isOpenGL_4_3()) return; - if (!is_two_dimensional()) + const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::NoButton)) { - // toggle clipping plane - m_use_clipping_plane = (m_use_clipping_plane + 1) % CLIPPING_PLANE_END_INDEX; - if (m_use_clipping_plane==CLIPPING_PLANE_OFF) - { setManipulatedFrame(nullptr); } - else - { setManipulatedFrame(m_frame_plane); } - - switch(m_use_clipping_plane) + if (!isOpenGL_4_3()) return; + if (!is_two_dimensional()) { - case CLIPPING_PLANE_OFF: displayMessage(QString("Draw clipping = false")); break; - case CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF: clipping_plane_rendering=true; displayMessage(QString("Draw clipping = solid half & transparent half")); break; - case CLIPPING_PLANE_SOLID_HALF_WIRE_HALF: displayMessage(QString("Draw clipping = solid half & wireframe half")); break; - case CLIPPING_PLANE_SOLID_HALF_ONLY: displayMessage(QString("Draw clipping = solid half only")); break; - default: break; - } - update(); - } - } + // toggle clipping plane + m_use_clipping_plane = (m_use_clipping_plane + 1) % CLIPPING_PLANE_END_INDEX; + if (m_use_clipping_plane==CLIPPING_PLANE_OFF) + { setManipulatedFrame(nullptr); } + else + { setManipulatedFrame(m_frame_plane); } - else if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::AltModifier)) - { - if (!isOpenGL_4_3()) return; - if (m_use_clipping_plane!=CLIPPING_PLANE_OFF) + switch(m_use_clipping_plane) + { + case CLIPPING_PLANE_OFF: displayMessage(QString("Draw clipping = false")); break; + case CLIPPING_PLANE_SOLID_HALF_TRANSPARENT_HALF: clipping_plane_rendering=true; displayMessage(QString("Draw clipping = solid half & transparent half")); break; + case CLIPPING_PLANE_SOLID_HALF_WIRE_HALF: displayMessage(QString("Draw clipping = solid half & wireframe half")); break; + case CLIPPING_PLANE_SOLID_HALF_ONLY: displayMessage(QString("Draw clipping = solid half only")); break; + default: break; + } + update(); + } + } + + else if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::AltModifier)) { - clipping_plane_rendering = !clipping_plane_rendering; - displayMessage(QString("Draw clipping plane=%1.").arg(clipping_plane_rendering?"true":"false")); + if (!isOpenGL_4_3()) return; + if (m_use_clipping_plane!=CLIPPING_PLANE_OFF) + { + clipping_plane_rendering = !clipping_plane_rendering; + displayMessage(QString("Draw clipping plane=%1.").arg(clipping_plane_rendering?"true":"false")); + update(); + } + } + else if ((e->key()==::Qt::Key_E) && (modifiers==::Qt::NoButton)) + { + m_draw_edges=!m_draw_edges; + displayMessage(QString("Draw edges=%1.").arg(m_draw_edges?"true":"false")); update(); } - } - else if ((e->key()==::Qt::Key_E) && (modifiers==::Qt::NoButton)) - { - m_draw_edges=!m_draw_edges; - displayMessage(QString("Draw edges=%1.").arg(m_draw_edges?"true":"false")); - update(); - } - else if ((e->key()==::Qt::Key_M) && (modifiers==::Qt::NoButton)) - { - m_use_mono_color=!m_use_mono_color; - displayMessage(QString("Mono color=%1.").arg(m_use_mono_color?"true":"false")); - update(); - } - else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::NoButton)) - { - m_inverse_normal=!m_inverse_normal; - displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal?"true":"false")); - negate_all_normals(); - redraw(); - } - else if ((e->key()==::Qt::Key_S) && (modifiers==::Qt::NoButton)) - { - m_flatShading=!m_flatShading; - if (m_flatShading) - displayMessage("Flat shading."); - else - displayMessage("Gouraud shading."); - redraw(); - } - else if ((e->key()==::Qt::Key_T) && (modifiers==::Qt::NoButton)) - { - m_draw_text=!m_draw_text; - displayMessage(QString("Draw text=%1.").arg(m_draw_text?"true":"false")); - update(); - } - else if ((e->key()==::Qt::Key_U) && (modifiers==::Qt::NoButton)) - { - if (is_two_dimensional()) + else if ((e->key()==::Qt::Key_M) && (modifiers==::Qt::NoButton)) { - displayMessage(QString("Move camera direction upside down.")); - /* CGAL::qglviewer::Vec cur=camera()->viewDirection(); + m_use_mono_color=!m_use_mono_color; + displayMessage(QString("Mono color=%1.").arg(m_use_mono_color?"true":"false")); + update(); + } + else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::NoButton)) + { + reverse_all_normals(); + displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal?"true":"false")); + redraw(); + } + else if ((e->key()==::Qt::Key_S) && (modifiers==::Qt::NoButton)) + { + m_flatShading=!m_flatShading; + if (m_flatShading) + displayMessage("Flat shading."); + else + displayMessage("Gouraud shading."); + redraw(); + } + else if ((e->key()==::Qt::Key_T) && (modifiers==::Qt::NoButton)) + { + m_draw_text=!m_draw_text; + displayMessage(QString("Draw text=%1.").arg(m_draw_text?"true":"false")); + update(); + } + else if ((e->key()==::Qt::Key_U) && (modifiers==::Qt::NoButton)) + { + if (is_two_dimensional()) + { + displayMessage(QString("Move camera direction upside down.")); + /* CGAL::qglviewer::Vec cur=camera()->viewDirection(); // TODO ! double cx=cur.x, cy=cur.y, cz=cur.z; if (has_zero_x()) { cx=-cx; } else if (has_zero_y()) { cy=-cy; } @@ -1522,137 +1298,139 @@ protected: else if (has_zero_y()) { cy=(cur.y<0?-1.:1); } else { cz=(cur.z<0?-1.:1); }*/ - camera()->setUpVector(-camera()->upVector()); + camera()->setUpVector(-camera()->upVector()); //camera()->frame()->setConstraint(NULL); // camera()->setViewDirection(CGAL::qglviewer::Vec(-cx,-cy,-cz)); //constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(cx, cy, cz)); //camera()->frame()->setConstraint(&constraint); //update(); - redraw(); + redraw(); + } } - } - else if ((e->key()==::Qt::Key_V) && (modifiers==::Qt::NoButton)) - { - m_draw_vertices=!m_draw_vertices; - displayMessage(QString("Draw vertices=%1.").arg(m_draw_vertices?"true":"false")); - update(); - } - else if ((e->key()==::Qt::Key_W) && (modifiers==::Qt::NoButton)) - { - m_draw_faces=!m_draw_faces; - displayMessage(QString("Draw faces=%1.").arg(m_draw_faces?"true":"false")); - update(); - } - else if ((e->key()==::Qt::Key_Plus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl - { - m_size_edges+=.5; - displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); - update(); - } - else if ((e->key()==::Qt::Key_Minus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl - { - if (m_size_edges>.5) m_size_edges-=.5; - displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); - update(); - } - else if ((e->key()==::Qt::Key_Plus) && (modifiers.testFlag(::Qt::ControlModifier))) - { - m_size_points+=.5; - displayMessage(QString("Size of points=%1.").arg(m_size_points)); - update(); - } - else if ((e->key()==::Qt::Key_Minus) && (modifiers.testFlag(::Qt::ControlModifier))) - { - if (m_size_points>.5) m_size_points-=.5; - displayMessage(QString("Size of points=%1.").arg(m_size_points)); - update(); - } - else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::NoButton)) - { - m_ambient_color.setX(m_ambient_color.x()+.1); - if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); - m_ambient_color.setY(m_ambient_color.x()+.1); - if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); - m_ambient_color.setZ(m_ambient_color.x()+.1); - if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::NoButton)) - { - m_ambient_color.setX(m_ambient_color.x()-.1); - if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); - m_ambient_color.setY(m_ambient_color.y()-.1); - if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); - m_ambient_color.setZ(m_ambient_color.z()-.1); - if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ShiftModifier)) - { - m_ambient_color.setX(m_ambient_color.x()+.1); - if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::AltModifier)) - { - m_ambient_color.setY(m_ambient_color.y()+.1); - if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ControlModifier)) - { - m_ambient_color.setZ(m_ambient_color.z()+.1); - if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ShiftModifier)) - { - m_ambient_color.setX(m_ambient_color.x()-.1); - if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::AltModifier)) - { - m_ambient_color.setY(m_ambient_color.y()-.1); - if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ControlModifier)) - { - m_ambient_color.setZ(m_ambient_color.z()-.1); - if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - update(); - } - else if ((e->key()==::Qt::Key_O) && (modifiers==::Qt::NoButton)) - { - bool old_2D=is_two_dimensional(); - m_no_2D_mode=!m_no_2D_mode; - if (old_2D!=is_two_dimensional()) + else if ((e->key()==::Qt::Key_V) && (modifiers==::Qt::NoButton)) { - if (is_two_dimensional()) - { displayMessage(QString("Viewer is in 2D mode.")); } - else { displayMessage(QString("Viewer is in 3D mode.")); } + m_draw_vertices=!m_draw_vertices; + displayMessage(QString("Draw vertices=%1.").arg(m_draw_vertices?"true":"false")); update(); } + else if ((e->key()==::Qt::Key_W) && (modifiers==::Qt::NoButton)) + { + m_draw_faces=!m_draw_faces; + displayMessage(QString("Draw faces=%1.").arg(m_draw_faces?"true":"false")); + update(); + } + else if ((e->key()==::Qt::Key_Plus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl + { + m_size_edges+=.5; + displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); + update(); + } + else if ((e->key()==::Qt::Key_Minus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl + { + if (m_size_edges>.5) m_size_edges-=.5; + displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); + update(); + } + else if ((e->key()==::Qt::Key_Plus) && (modifiers.testFlag(::Qt::ControlModifier))) + { + m_size_points+=.5; + displayMessage(QString("Size of points=%1.").arg(m_size_points)); + update(); + } + else if ((e->key()==::Qt::Key_Minus) && (modifiers.testFlag(::Qt::ControlModifier))) + { + if (m_size_points>.5) m_size_points-=.5; + displayMessage(QString("Size of points=%1.").arg(m_size_points)); + update(); + } + else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::NoButton)) + { + m_ambient_color.setX(m_ambient_color.x()+.1); + if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); + m_ambient_color.setY(m_ambient_color.x()+.1); + if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); + m_ambient_color.setZ(m_ambient_color.x()+.1); + if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::NoButton)) + { + m_ambient_color.setX(m_ambient_color.x()-.1); + if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); + m_ambient_color.setY(m_ambient_color.y()-.1); + if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); + m_ambient_color.setZ(m_ambient_color.z()-.1); + if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ShiftModifier)) + { + m_ambient_color.setX(m_ambient_color.x()+.1); + if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::AltModifier)) + { + m_ambient_color.setY(m_ambient_color.y()+.1); + if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageUp) && (modifiers==::Qt::ControlModifier)) + { + m_ambient_color.setZ(m_ambient_color.z()+.1); + if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ShiftModifier)) + { + m_ambient_color.setX(m_ambient_color.x()-.1); + if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::AltModifier)) + { + m_ambient_color.setY(m_ambient_color.y()-.1); + if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_PageDown) && (modifiers==::Qt::ControlModifier)) + { + m_ambient_color.setZ(m_ambient_color.z()-.1); + if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); + displayMessage(QString("Light color=(%1 %2 %3)."). + arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); + update(); + } + else if ((e->key()==::Qt::Key_O) && (modifiers==::Qt::NoButton)) + { + bool old_2D=is_two_dimensional(); + m_no_2D_mode=!m_no_2D_mode; + if (old_2D!=is_two_dimensional()) + { + if (is_two_dimensional()) + { displayMessage(QString("Viewer is in 2D mode.")); } + else { displayMessage(QString("Viewer is in 3D mode.")); } + set_camera_mode(); + update(); + } + } + else + { CGAL::QGLViewer::keyPressEvent(e); } // By default call QGLViewer key press } - else - CGAL::QGLViewer::keyPressEvent(e); } virtual QString helpString() const @@ -1689,8 +1467,12 @@ protected: text += "Press Escape to exit the viewer."; return text; } +public: + std::function on_key_pressed; protected: + const Graphics_scene& gBuffer; + bool m_draw_vertices; bool m_draw_edges; bool m_draw_rays; @@ -1713,6 +1495,10 @@ protected: int m_use_clipping_plane=CLIPPING_PLANE_OFF; CGAL::qglviewer::ManipulatedFrame* m_frame_plane=nullptr; + // Buffer for clipping plane is not stored in the scene because it is not + // filled by users but by the basic viewer. + std::vector m_array_for_clipping_plane; + double m_size_points; double m_size_edges; double m_size_rays; @@ -1726,60 +1512,14 @@ protected: QVector4D m_ambient_color; bool m_are_buffers_initialized; - CGAL::Bbox_3 m_bounding_box; // CGAL::qglviewer::LocalConstraint constraint; CGAL::qglviewer::WorldConstraint constraint; - // The following enum gives the indices of different elements of arrays vectors. - enum - { - BEGIN_POS=0, - POS_MONO_POINTS=BEGIN_POS, - POS_COLORED_POINTS, - POS_MONO_SEGMENTS, - POS_COLORED_SEGMENTS, - POS_MONO_RAYS, - POS_COLORED_RAYS, - POS_MONO_LINES, - POS_COLORED_LINES, - POS_MONO_FACES, - POS_COLORED_FACES, - POS_CLIPPING_PLANE, - END_POS, - BEGIN_COLOR=END_POS, - COLOR_POINTS=BEGIN_COLOR, - COLOR_SEGMENTS, - COLOR_RAYS, - COLOR_LINES, - COLOR_FACES, - END_COLOR, - BEGIN_NORMAL=END_COLOR, - SMOOTH_NORMAL_MONO_FACES=BEGIN_NORMAL, - FLAT_NORMAL_MONO_FACES, - SMOOTH_NORMAL_COLORED_FACES, - FLAT_NORMAL_COLORED_FACES, - END_NORMAL, - LAST_INDEX=END_NORMAL - }; - std::vector arrays[LAST_INDEX]; + static const unsigned int NB_GL_BUFFERS=(GS::END_POS-GS::BEGIN_POS)+ + (GS::END_COLOR-GS::BEGIN_COLOR)+3; // +2 for normals (mono and color), +1 for clipping plane - Buffer_for_vao m_buffer_for_mono_points; - Buffer_for_vao m_buffer_for_colored_points; - Buffer_for_vao m_buffer_for_mono_segments; - Buffer_for_vao m_buffer_for_colored_segments; - Buffer_for_vao m_buffer_for_mono_rays; - Buffer_for_vao m_buffer_for_colored_rays; - Buffer_for_vao m_buffer_for_mono_lines; - Buffer_for_vao m_buffer_for_colored_lines; - Buffer_for_vao m_buffer_for_mono_faces; - Buffer_for_vao m_buffer_for_colored_faces; - Buffer_for_vao m_buffer_for_clipping_plane; - - static const unsigned int NB_VBO_BUFFERS=(END_POS-BEGIN_POS)+ - (END_COLOR-BEGIN_COLOR)+2; // +2 for 2 vectors of normals - - QOpenGLBuffer buffers[NB_VBO_BUFFERS]; + QOpenGLBuffer buffers[NB_GL_BUFFERS]; // +1 for the buffer of clipping plane // The following enum gives the indices of the different vao. enum @@ -1806,9 +1546,99 @@ protected: bool clipping_plane_rendering = true; // will be toggled when alt+c is pressed, which is used for indicating whether or not to render the clipping plane ; float clipping_plane_rendering_transparency = 0.5f; // to what extent the transparent part should be rendered; - std::vector > m_texts; }; +//------------------------------------------------------------------------------ +class QApplication_and_basic_viewer +{ +public: + QApplication_and_basic_viewer(const CGAL::Graphics_scene& buffer, + const char* title="CGAL Basic Viewer"): + m_application(nullptr), + m_basic_viewer(nullptr), + m_argc(1) + { + m_argv[0]=new char[strlen(title)+1]; + memcpy(m_argv[0], title, strlen(title)+1); + m_argv[1]=nullptr; + +#if defined(CGAL_TEST_SUITE) + bool cgal_test_suite = true; +#else + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); +#endif + + if (cgal_test_suite) + { return; } + + Qt::init_ogl_context(4, 3); + m_application=new QApplication(m_argc, const_cast(m_argv)); + m_basic_viewer=new Basic_viewer(m_application->activeWindow(), + buffer, title); + } + + ~QApplication_and_basic_viewer() + { + delete[] m_argv[0]; + delete m_basic_viewer; + delete m_application; + } + + operator bool() const + { return m_application!=nullptr; } + + void run() + { + if (m_application!=nullptr) + { + m_basic_viewer->show(); + m_application->exec(); + } + } + + Basic_viewer& basic_viewer() + { + CGAL_assertion(m_basic_viewer!=nullptr); + return *m_basic_viewer; + } + +protected: + QApplication* m_application; + Basic_viewer* m_basic_viewer; + char *m_argv[2]; + int m_argc; +}; + +} // End namespace Qt + +// A shortcut to use directly CGAL::Basic_viewer instead of CGAL::Qt::Basic_viewer. +// Can be changed later if we have several viewers. +using Qt::Basic_viewer; + +inline +void draw_graphics_scene(const Graphics_scene& graphics_scene, + const char *title="CGAL 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) + { + Qt::init_ogl_context(4, 3); + + int argc = 1; + const char *argv[2] = {title, nullptr}; + QApplication app(argc, const_cast(argv)); + Basic_viewer basic_viewer(app.activeWindow(), graphics_scene, title); + + basic_viewer.show(); + app.exec(); + } +} + } // End namespace CGAL #else // CGAL_USE_BASIC_VIEWER @@ -1822,8 +1652,15 @@ namespace CGAL std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< -void draw(const PS& aps); + +template +struct Graphics_scene_options_polygon_set_2: public CGAL::Graphics_scene_options +{}; + +/*! +\ingroup PkgDrawPolygonSet2 + +opens a new window and draws a 2D polygon set. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 PS2 which must be an instanciation of a `CGAL::Polygon_set_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsPolygonSet2` concept. + +\param ps2 the polygon set to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Polygon_set_2& ps2, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const PS2& ps2, const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygonSet2 + +A shortcut to `CGAL::draw(ps2, Graphics_scene_options_polygon_set_2{})`. +*/ +template +void draw(const PS2& ps2); + +/*! +\ingroup PkgDrawPolygonSet2 + +adds the vertices, edges and faces of `ps2` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam PS2 which must be an instanciation of a `CGAL::Polygon_set_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsPolygonSet2` concept. + +\param ps2 the polygon set to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Polygon_set_2& ps2, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const PS2& ps2, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygonSet2 + +A shortcut to `CGAL::add_to_graphics_scene(ps2, gs, Graphics_scene_options_polygon_set_2{})`. +*/ +template +void add_to_graphics_scene(const PS2& ps2, + CGAL::Graphics_scene& gs); } /* end namespace CGAL */ diff --git a/Boolean_set_operations_2/doc/Boolean_set_operations_2/Concepts/GraphicsSceneOptionsPolygonSet2.h b/Boolean_set_operations_2/doc/Boolean_set_operations_2/Concepts/GraphicsSceneOptionsPolygonSet2.h new file mode 100644 index 00000000000..458b9baa283 --- /dev/null +++ b/Boolean_set_operations_2/doc/Boolean_set_operations_2/Concepts/GraphicsSceneOptionsPolygonSet2.h @@ -0,0 +1,38 @@ +/*! +\ingroup PkgBooleanSetOperations2Concepts + +The concept `GraphicsSceneOptionsPolygonSet2` defines data and methods used to tune the way that the cells of a `Polygon_set_2` are considered for drawing or to be added into a graphics scene. + +\cgalRefines{GraphicsSceneOptions} + +\cgalHasModelsBegin +\cgalHasModelsBare{\link CGAL::Graphics_scene_options_polygon_set_2 `CGAL::Graphics_scene_options_polygon_set_2`\endlink} +\cgalHasModelsEnd + +*/ +class GraphicsSceneOptionsPolygonSet2 +{ +public: + /// returns the color of the unbounded face. + const CGAL::IO::Color& unbounded_face_color() const; + /// sets the color of the unbounded face to `c`. + void unbounded_face_color(const CGAL::IO::Color& c); + + /// returns `true` if the unbounded face must be drawn, `false` otherwise. + /// Returns `false` by default. + bool draw_unbounded() const; + /// sets the draw of unbounded face to `b`. + void draw_unbounded(bool b); + /// toggles the draw unbounded face value. + void toggle_draw_unbounded(); + + /// returns the height of the box used to draw the unbounded face. + int height() const; + /// returns the width of the box used to draw the unbounded face. + int width() const; + + /// sets the height of the box used to draw the unbounded face to `i`. + void height(int i); + /// sets the width of the box used to draw the unbounded face to `i`. + void width(int i); +}; diff --git a/Boolean_set_operations_2/doc/Boolean_set_operations_2/PackageDescription.txt b/Boolean_set_operations_2/doc/Boolean_set_operations_2/PackageDescription.txt index f67e57f7e1a..a562dd4e48a 100644 --- a/Boolean_set_operations_2/doc/Boolean_set_operations_2/PackageDescription.txt +++ b/Boolean_set_operations_2/doc/Boolean_set_operations_2/PackageDescription.txt @@ -69,6 +69,8 @@ containment predicates. - \link boolean_connect_holes `CGAL::connect_holes()` \endlink \cgalCRPSection{Draw a Polygon_set_2} -- \link PkgDrawPolygonSet2 CGAL::draw() \endlink +- \link PkgDrawPolygonSet2 CGAL::draw() \endlink +- `GraphicsSceneOptionsPolygonSet2` +- `CGAL::Graphics_scene_options_polygon_set_2` */ diff --git a/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h b/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h index bd9cfec7f7c..3ae8c90a999 100644 --- a/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h +++ b/Boolean_set_operations_2/include/CGAL/draw_polygon_set_2.h @@ -19,40 +19,159 @@ #include - -#include - -#ifdef DOXYGEN_RUNNING -namespace CGAL { - -/*! - * \ingroup PkgDrawPolygonSet2 - * - * opens a new window and draws `aps`, an instance of the `CGAL::Polygon_set_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 PS an instance of the `CGAL::Polygon_set_2` class. - * \param aps the polygon set to draw. - */ -template -void draw(const PS& aps); - -} /* namespace CGAL */ -#endif - -#ifdef CGAL_USE_BASIC_VIEWER - -#include +#include +#include +#include #include namespace CGAL { -template -class Polygon_set_2_basic_viewer_qt : public Basic_viewer_qt { - using Base = Basic_viewer_qt; +template +struct Graphics_scene_options_polygon_set_2 : + public CGAL::Graphics_scene_options +{ + void unbounded_face_color(const CGAL::IO::Color& c) + { m_unbounded_face_color=c; } + const CGAL::IO::Color& unbounded_face_color() const + { return m_unbounded_face_color; } + + bool draw_unbounded() const + { return m_draw_unbounded; } + void draw_unbounded(bool b) + { m_draw_unbounded=b; } + void toggle_draw_unbounded() + { m_draw_unbounded=!m_draw_unbounded; } + + int height() const + { return m_height; } + int width() const + { return m_width; } + + void height(int i) + { m_height=i; } + void width(int i) + { m_width=i; } + +protected: + bool m_draw_unbounded=true; + CGAL::IO::Color m_unbounded_face_color=CGAL::IO::Color(75,160,255); + int m_width=0, m_height=0; +}; + +namespace draw_function_for_boolean_set_2 { + +template +void compute_loop(const typename PS2::Polygon_2& p, bool hole, + CGAL::Graphics_scene& gs, + const GSOptions& gso) +{ + if (gso.are_faces_enabled() && hole) + { gs.add_point_in_face(p.vertex(p.size()-1)); } + + auto prev = p.vertices_begin(); + auto it = prev; + + // First vertex + if (gso.are_vertices_enabled() && gso.draw_vertex(p, it)) + { + if(gso.colored_vertex(p, it)) + { gs.add_point(*it, gso.vertex_color(p, it)); } + else + { gs.add_point(*it); } + } + + if (gso.are_faces_enabled()) + { gs.add_point_in_face(*it); } + + // Other vertices + for (++it; it!=p.vertices_end(); ++it) + { + if (gso.are_vertices_enabled() && gso.draw_vertex(p, it)) + { // Add point + if(gso.colored_vertex(p, it)) + { gs.add_point(*it, gso.vertex_color(p, it)); } + else + { gs.add_point(*it); } + } + + if (gso.are_edges_enabled() && gso.draw_edge(p, prev)) + { // Add segment with previous point + if(gso.colored_edge(p, prev)) + { gs.add_segment(*prev, *it, gso.edge_color(p, prev)); } + else + { gs.add_segment(*prev, *it); } + } + + if (gso.are_faces_enabled()) + { gs.add_point_in_face(*it); } // add point in face + prev = it; + } + + // Last segment (between last and first point) + if (gso.are_edges_enabled() && gso.draw_edge(p, prev)) + { + if(gso.colored_edge(p, prev)) + { gs.add_segment(*prev, *(p.vertices_begin()), gso.edge_color(p, prev)); } + else + { gs.add_segment(*prev, *(p.vertices_begin())); } + } +} + +/// Compute the elements of a polygon with holes. +template +void compute_elements(const PWH& pwh, + CGAL::Graphics_scene& gs, + const GSOptions& gso) +{ + if (!gso.draw_unbounded() && pwh.outer_boundary().is_empty()) return; + + if (gso.are_faces_enabled()) + { gs.face_begin(gso.unbounded_face_color()); } + + using Pnt=typename PWH::Polygon_2::Point_2; + const Pnt* point_in_face=nullptr; + if (pwh.outer_boundary().is_empty()) + { + if(gso.width()!=0 && gso.height()!=0) + { + typename PWH::Polygon_2 pgn; + pgn.push_back(Pnt(-gso.width(), -gso.height())); + pgn.push_back(Pnt(gso.width(), -gso.height())); + pgn.push_back(Pnt(gso.width(), gso.height())); + pgn.push_back(Pnt(-gso.width(), gso.height())); + draw_function_for_boolean_set_2::compute_loop(pgn, false, gs, gso); + point_in_face = &(pgn.vertex(pgn.size()-1)); + } + } + else + { + const auto& outer_boundary = pwh.outer_boundary(); + draw_function_for_boolean_set_2::compute_loop(outer_boundary, false, gs, gso); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + } + + for (auto it = pwh.holes_begin(); it != pwh.holes_end(); ++it) + { + draw_function_for_boolean_set_2::compute_loop(*it, true, gs, gso); + if (gso.are_faces_enabled() && point_in_face && point_in_face!=nullptr) + { gs.add_point_in_face(*point_in_face); } + } + + if (gso.are_faces_enabled()) + { gs.face_end(); } +} + +} // End namespace draw_function_for_boolean_set_2 + +#ifdef CGAL_USE_BASIC_VIEWER + +template +class Polygon_set_2_basic_viewer_qt : public Basic_viewer +{ + using Base = Basic_viewer; using Ps = PolygonSet_2; using Pwh = typename Ps::Polygon_with_holes_2; using Pgn = typename Ps::Polygon_2; @@ -60,13 +179,15 @@ class Polygon_set_2_basic_viewer_qt : public Basic_viewer_qt { public: Polygon_set_2_basic_viewer_qt(QWidget* parent, const Ps& ps, - const char* title = "Basic Polygon_set_2 Viewer", - bool draw_unbounded = false, - bool draw_vertices = false) : - Base(parent, title, draw_vertices), + GSOptions& gs_options, + const char* title = "Basic Polygon_set_2 Viewer") : + Base(parent, graphics_scene, title), m_ps(ps), - m_draw_unbounded(draw_unbounded) + gso(gs_options) { + gso.width(CGAL_BASIC_VIEWER_INIT_SIZE_X); + gso.height(CGAL_BASIC_VIEWER_INIT_SIZE_Y); + if (ps.is_empty()) return; // mimic the computation of Camera::pixelGLRatio() @@ -74,37 +195,39 @@ public: 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; + m_pixel_ratio = diameter / gso.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; + gso.width(width); + gso.height(height); CGAL::qglviewer::Vec p; - auto ratio = camera()->pixelGLRatio(p); - if (ratio != m_pixel_ratio) { + auto ratio = this->camera()->pixelGLRatio(p); + if (ratio != m_pixel_ratio) + { m_pixel_ratio = ratio; add_elements(); } } - /*! Compute the elements of a polygon set. - */ - virtual void add_elements() { - clear(); - - std::vector pwhs; - m_ps.polygons_with_holes(std::back_inserter(pwhs)); - for (const auto& pwh : pwhs) add_elements(pwh); - } - /*! Obtain the pixel ratio. */ double pixel_ratio() const { return m_pixel_ratio; } + /*! Compute the elements of a polygon set. + */ + virtual void add_elements() + { + graphics_scene.clear(); + std::vector pwhs; + m_ps.polygons_with_holes(std::back_inserter(pwhs)); + for (const auto& pwh : pwhs) + { draw_function_for_boolean_set_2::compute_elements(pwh, graphics_scene, gso); } + } + /*! Compute the bounding box. */ CGAL::Bbox_2 bounding_box() { @@ -122,64 +245,7 @@ public: return bbox; } -protected: - /*! Compute the elements of a polygon with holes. - */ - void add_elements(const Pwh& pwh) { - if (! m_draw_unbounded && pwh.outer_boundary().is_empty()) return; - - CGAL::IO::Color c(75,160,255); - face_begin(c); - - const Pnt* point_in_face; - if (pwh.outer_boundary().is_empty()) { - Pgn pgn; - pgn.push_back(Pnt(-m_width, -m_height)); - pgn.push_back(Pnt(m_width, -m_height)); - pgn.push_back(Pnt(m_width, m_height)); - pgn.push_back(Pnt(-m_width, m_height)); - compute_loop(pgn, false); - point_in_face = &(pgn.vertex(pgn.size()-1)); - } - else { - const auto& outer_boundary = pwh.outer_boundary(); - compute_loop(outer_boundary, false); - point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); - } - - for (auto it = pwh.holes_begin(); it != pwh.holes_end(); ++it) { - compute_loop(*it, true); - add_point_in_face(*point_in_face); - } - - face_end(); - } - - 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_point(*it); // add vertex - add_segment(*prev, *it); // add segment with previous point - 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())); - } - 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; @@ -188,14 +254,39 @@ private: //! Indicates whether to draw unbounded polygons with holes. bool m_draw_unbounded = false; + + Graphics_scene graphics_scene; + GSOptions& gso; }; -// Specialization of draw function. +#endif // CGAL_USE_BASIC_VIEWER + +#define CGAL_PS2_TYPE CGAL::Polygon_set_2 + +// Specializations of add_to_graphics_scene function +template +void add_to_graphics_scene(const CGAL_PS2_TYPE& ap2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ draw_function_for_boolean_set_2::compute_elements(ap2, graphics_scene, gso); } + template -void draw(const CGAL::Polygon_set_2& ps, - const char* title = "Polygon_set_2 Basic Viewer", - bool draw_vertices = false, - bool draw_unbounded = false) +void add_to_graphics_scene(const CGAL_PS2_TYPE& ap2, + CGAL::Graphics_scene &graphics_scene) +{ + CGAL::Graphics_scene_options_polygon_set_2 gso; + draw_function_for_boolean_set_2::compute_elements(ap2, graphics_scene, gso); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function. +template +void draw(const CGAL_PS2_TYPE& ps, GSOptions& gso, + const char* title = "Polygon_set_2 Basic Viewer") { #if defined(CGAL_TEST_SUITE) bool cgal_test_suite = true; @@ -203,22 +294,35 @@ void draw(const CGAL::Polygon_set_2& ps, bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif - if (! cgal_test_suite) { + if (! cgal_test_suite) + { using Ps = CGAL::Polygon_set_2; - using Viewer = Polygon_set_2_basic_viewer_qt; + using Viewer = Polygon_set_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(), ps, title, draw_unbounded, draw_vertices); - mainwindow.add_elements(); - mainwindow.show(); + Viewer basic_viewer(app.activeWindow(), ps, gso, title); + basic_viewer.show(); app.exec(); } } -} // End namespace CGAL +template +void draw(const CGAL_PS2_TYPE& ps, + const char* title = "Polygon_set_2 Basic Viewer") +{ + CGAL::Graphics_scene_options_polygon_set_2 gso; + draw(ps, gso, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_PS2_TYPE + +} // End namespace CGAL + #endif // CGAL_DRAW_POLYGON_SET_2_H 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..4f01f6dce65 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 @@ -1,14 +1,16 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 +BGL +Basic_viewer Boolean_set_operations_2 +CGAL_Core Cartesian_kernel Circular_kernel_2 Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView HalfedgeDS Hash_map Homogeneous_kernel @@ -22,7 +24,12 @@ Modular_arithmetic Number_types Polygon Profiling_tools +Property_map +Random_numbers STL_Extension +Spatial_sorting Stream_support Surface_sweep_2 +TDS_2 +Triangulation_2 Union_find diff --git a/CGAL_Core/examples/Core/CMakeLists.txt b/CGAL_Core/examples/Core/CMakeLists.txt index d4513e22dea..6d9e732db93 100644 --- a/CGAL_Core/examples/Core/CMakeLists.txt +++ b/CGAL_Core/examples/Core/CMakeLists.txt @@ -9,4 +9,5 @@ if(NOT CGAL_Core_FOUND) return() endif() +create_single_source_cgal_program("zero-one.cpp") create_single_source_cgal_program("delaunay.cpp") diff --git a/CGAL_Core/examples/Core/zero-one.cpp b/CGAL_Core/examples/Core/zero-one.cpp new file mode 100644 index 00000000000..cfc5a903c19 --- /dev/null +++ b/CGAL_Core/examples/Core/zero-one.cpp @@ -0,0 +1,32 @@ + +#include + +typedef CORE::Expr Real; + +int main() +{ + Real r(3.14); + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = CGAL::sqrt(r); + + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = r * r; + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = r - r; + + CGAL::is_zero(r); + + return 0; +} 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..e4b0bcc4263 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 @@ -19,6 +20,5 @@ Modular_arithmetic Number_types Profiling_tools Property_map -Random_numbers STL_Extension Stream_support diff --git a/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies b/Convex_decomposition_3/package_info/Convex_decomposition_3/dependencies index fa1556add87..cbdbc3424ad 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 @@ -27,7 +28,6 @@ Polygon Polyhedron Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Stream_support diff --git a/Convex_hull_3/include/CGAL/convex_hull_3.h b/Convex_hull_3/include/CGAL/convex_hull_3.h index 51555a5d57b..eac14144073 100644 --- a/Convex_hull_3/include/CGAL/convex_hull_3.h +++ b/Convex_hull_3/include/CGAL/convex_hull_3.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include 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/CMakeLists.txt b/Documentation/doc/CMakeLists.txt index 0e7ddd3b95a..f9efeb99354 100644 --- a/Documentation/doc/CMakeLists.txt +++ b/Documentation/doc/CMakeLists.txt @@ -354,6 +354,10 @@ configure_file(${CGAL_DOC_RESOURCE_DIR}/BaseDoxyfile.in set(CGAL_DOC_DOXY_DEFAULT "${CGAL_DOC_DXY_DIR}/BaseDoxyfile") +if (BE_QUIET) + file(APPEND ${CGAL_DOC_DOXY_DEFAULT} "WARN_LOGFILE=doxygen.log\n") +endif() + # pkglist_filter gets the path to the pkglist_filter of this source # directory. if(WIN32) 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..af93af92d6a 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} @@ -167,5 +168,6 @@ \package_listing{GraphicsView} \package_listing{CGAL_ipelets} +\package_listing{Basic_viewer} */ 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/Documentation/doc/scripts/html_output_post_processing.py b/Documentation/doc/scripts/html_output_post_processing.py index 90e8547eb58..a253f314715 100755 --- a/Documentation/doc/scripts/html_output_post_processing.py +++ b/Documentation/doc/scripts/html_output_post_processing.py @@ -184,7 +184,7 @@ def automagically_number_figures(): for el in d('a.elRef'): text = pq(el).attr('href') if text.find("index.html")!=-1: - re_pkg_index=re.compile("\.\./([A-Z_a-z0-9]+)/index\.html") + re_pkg_index=re.compile(r'\.\./([A-Z_a-z0-9]+)/index\.html') res=re_pkg_index.match(text) if res: all_packages.append(res.group(1)) @@ -248,7 +248,7 @@ removes some unneeded files, and performs minor repair on some glitches.''') #workaround issue with operator<< in pyquery all_pages=glob.glob('*/*.html') for f in all_pages: - re_replace_in_file("operator<<\(\)", "operator<<()", f) + re_replace_in_file(r'operator<<\(\)', "operator<<()", f) # number figure automagically_number_figures() @@ -319,7 +319,7 @@ removes some unneeded files, and performs minor repair on some glitches.''') filesjs_files=package_glob('./*/files.js') for fn in filesjs_files: - re_replace_in_file('^.*\[ "Concepts",.*$', '', fn) + re_replace_in_file(r'^.*\[ "Concepts",.*$', '', fn) #Rewrite the path of some images re_replace_in_file("'src','ftv2", @@ -329,7 +329,7 @@ removes some unneeded files, and performs minor repair on some glitches.''') # external is placed by doxygen to mark a class from a tagfile, this # is more confusing then helpful in our case if path.isfile(os.path.join('Manual','annotated.html')): - re_replace_in_file('\[external\]', '', os.path.join('Manual','annotated.html')) + re_replace_in_file(r'\[external\]', '', os.path.join('Manual','annotated.html')) else: stderr.write("Error: ./Manual/annotated.html does not exists\n") # fix class/concept mismatch in generated pages 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/GraphicsView/include/CGAL/Qt/PolygonWithHolesGraphicsItem.h b/GraphicsView/include/CGAL/Qt/PolygonWithHolesGraphicsItem.h index 1348f5d2932..a44bd71044f 100644 --- a/GraphicsView/include/CGAL/Qt/PolygonWithHolesGraphicsItem.h +++ b/GraphicsView/include/CGAL/Qt/PolygonWithHolesGraphicsItem.h @@ -19,7 +19,6 @@ #include #include -#include #include #include #include diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h index 3cca531c5fb..7387b1f4874 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h @@ -1047,6 +1047,9 @@ static QString mouseButtonsString(::Qt::MouseButtons b) { CGAL_INLINE_FUNCTION void CGAL::QGLViewer::performClickAction(qglviewer::ClickAction ca, const QMouseEvent *const e) { + // the following call is needed to update the pixel ratio + camera()->setScreenWidthAndHeight(this->width(), this->height(), this->devicePixelRatio()); + // Note: action that need it should call update(). switch (ca) { // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in diff --git a/Triangulation_2/include/CGAL/apply_to_range.h b/GraphicsView/include/CGAL/apply_to_range.h similarity index 100% rename from Triangulation_2/include/CGAL/apply_to_range.h rename to GraphicsView/include/CGAL/apply_to_range.h diff --git a/GraphicsView/package_info/GraphicsView/dependencies b/GraphicsView/package_info/GraphicsView/dependencies index f74d1ba2e46..b4a464ef5e7 100644 --- a/GraphicsView/package_info/GraphicsView/dependencies +++ b/GraphicsView/package_info/GraphicsView/dependencies @@ -11,4 +11,3 @@ Number_types Profiling_tools STL_Extension Stream_support -Triangulation_2 diff --git a/HalfedgeDS/include/CGAL/boost/graph/properties_HalfedgeDS_base.h b/HalfedgeDS/include/CGAL/boost/graph/properties_HalfedgeDS_base.h index d2a1f11653b..a39cfd90715 100644 --- a/HalfedgeDS/include/CGAL/boost/graph/properties_HalfedgeDS_base.h +++ b/HalfedgeDS/include/CGAL/boost/graph/properties_HalfedgeDS_base.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace CGAL { @@ -126,11 +127,20 @@ typename boost::property_map::type get(PropertyTag,CGAL_HDS_CLASS&) { return typename boost::property_map::type(); } + // generalized 3-ary get functions -template +template> && + !std::is_same_v> && + !std::is_same_v> && + !std::is_same_v> + > +> typename boost::property_traits< typename boost::property_map::type >::reference get(PropertyTag p,CGAL_HDS_CLASS& g, const Key& key) -{ return get(get(p, g), key); } +{ + return get(get(p, g), key); +} template typename boost::property_traits< typename boost::property_map::const_type >::reference @@ -139,13 +149,13 @@ get(PropertyTag p,CGAL_HDS_CLASS const& g, const Key& key) -#define DECLARE_HDS_DYNAMIC_PM(TAG, DESCRIPTOR) \ +#define DECLARE_HDS_DYNAMIC_PM(TAG, DESCRIPTOR) \ template \ typename boost::property_map::const_type \ -get(const TAG&, const CGAL_HDS_CLASS&) \ -{ \ +get(const TAG&, const CGAL_HDS_CLASS&, const T& dv = T()) \ +{ \ typedef typename boost::graph_traits< CGAL_HDS_CLASS >::DESCRIPTOR descriptor; \ - return internal::Dynamic_property_map(); \ + return internal::Dynamic_property_map(dv); \ } DECLARE_HDS_DYNAMIC_PM(dynamic_vertex_property_t, vertex_descriptor) diff --git a/Hash_map/test/Hash_map/Hash.cpp b/Hash_map/test/Hash_map/Hash.cpp index fc12a7571fb..e73d4c3033d 100644 --- a/Hash_map/test/Hash_map/Hash.cpp +++ b/Hash_map/test/Hash_map/Hash.cpp @@ -11,9 +11,9 @@ #include #include #include +#include #include #include -#include typedef CGAL::Simple_cartesian Kernel; 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/Heat_method_3/package_info/Heat_method_3/dependencies b/Heat_method_3/package_info/Heat_method_3/dependencies index 42f0881808e..fe8fe1b0af7 100644 --- a/Heat_method_3/package_info/Heat_method_3/dependencies +++ b/Heat_method_3/package_info/Heat_method_3/dependencies @@ -12,7 +12,6 @@ Modular_arithmetic Number_types Profiling_tools Property_map -Random_numbers STL_Extension Solver_interface Stream_support diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 922549a9831..6b208d92520 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -10,11 +10,32 @@ Release date: October 2023 - **Breaking change**: C++17 is now required - Support for Visual `C++` 14.0 (Visual studio 2015) is dropped. -- The demos as well as the `draw()` functions using the `Basic_viewer` are based on Qt6 +- The demos as well as the `Basic_viewer` are based on Qt6. +- The `Basic_viewer` is improved and documented. - **Breaking change**: The usage of `boost::shared_ptr` has been replaced by `std::shared_ptr`. Packages affected are 2D Straight Line Skeleton and Shape Detection. - **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 + +- **Breaking change**: The type of the result of point location queries changed to + `std::variant`. The support for the old macro `CGAL_ARR_POINT_LOCATION_VERSION` + has been removed. + +- Eliminated the error-prone c-type casting that was used to define observers. In general, backward compatibility was maintained; however, the former class template `Arr_observer` was replaced by an alias template. (The former class Arr_observer was renamed to Aos_observer). + +- Introduced `Arr_dcel`, which essentially replaces the former `Arr_default_dcel`. Backward compatibility was maintained by the introduction of the alias template `Arr_default_dcel`. `Arr_dcel`, as opposed to the former `Arr_default_dcel` is templated (in addition to the geometry traits) by Vertex, Halfedge, and Face template parameters, and they have default type values. All this enables the layered extension of DCEL records. #### Envelopes of Surfaces in 3D - **Breaking change**: Construct_projected_boundary_2 in `EnvelopeTraits_3` is now using `std::variant` instead of `Object` @@ -33,6 +54,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 @@ -44,6 +71,8 @@ Release date: October 2023 `CGAL::Polygon_mesh_processing::autorefine_triangle_soup()` that refines a soup of triangles so that no pair of triangles intersects in their interiors. Also added, the function `autorefine()` operating directly on a triangle mesh and updating it using the aforementioned function on a triangle soup. +- Added the function `CGAL::Polygon_mesh_processing::add_bbox()` that enables to add a tight or extended, triangulated or not, + bounding box to a face graph. ### [2D Arrangements](https://doc.cgal.org/6.0/Manual/packages.html#PkgArrangementOnSurface2) - **Breaking change**: The type of the result of point location queries changed to diff --git a/Installation/CMakeLists.txt b/Installation/CMakeLists.txt index fa2102ff7c0..98d27538371 100644 --- a/Installation/CMakeLists.txt +++ b/Installation/CMakeLists.txt @@ -73,6 +73,8 @@ if(CGAL_BRANCH_BUILD) set(CGAL_GRAPHICSVIEW_PACKAGE_DIR "${CGAL_SOURCE_DIR}/GraphicsView" CACHE INTERNAL "Directory containing the GraphicsView package") + file(GLOB INCLUDE_CGAL_QT_DIRECTORIES "${CGAL_SOURCE_DIR}/*/include/CGAL/Qt") + message(STATUS "Include CGAL/Qt directories: ${INCLUDE_CGAL_QT_DIRECTORIES}") message(STATUS "Installation package directory: ${CGAL_INSTALLATION_PACKAGE_DIR}") message(STATUS "Maintenance package directory: ${CGAL_MAINTENANCE_PACKAGE_DIR}") @@ -104,6 +106,7 @@ else(CGAL_BRANCH_BUILD) set(CGAL_GRAPHICSVIEW_PACKAGE_DIR "${CGAL_SOURCE_DIR}" CACHE INTERNAL "Directory containing the GraphicsView package") + set(INCLUDE_CGAL_QT_DIRECTORIES "${CGAL_SOURCE_DIR}/include/CGAL/Qt") # Enable testing with CGAL_ENABLE_TESTING. Before CGAL-6.0, users would enable # the tests by specifying BUILD_TESTING. For compatibility, If BUILD_TESTING is @@ -115,6 +118,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) @@ -649,7 +655,7 @@ cache_get(CGAL_3RD_PARTY_INCLUDE_DIRS ) cache_get(CGAL_3RD_PARTY_LIBRARIES ) cache_get(CGAL_3RD_PARTY_LIBRARIES_DIRS) -install(DIRECTORY "${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/include/CGAL/Qt/" DESTINATION "${CGAL_INSTALL_INC_DIR}/CGAL/Qt" COMPONENT CGAL_Qt6) +install(DIRECTORY ${INCLUDE_CGAL_QT_DIRECTORIES} DESTINATION "${CGAL_INSTALL_INC_DIR}/CGAL/" COMPONENT CGAL_Qt6) if(CGAL_BRANCH_BUILD) install(DIRECTORY "${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/demo/resources/" DESTINATION "${CGAL_INSTALL_CMAKE_DIR}/demo/resources" COMPONENT CGAL_Qt6) install(DIRECTORY "${CGAL_GRAPHICSVIEW_PACKAGE_DIR}/demo/icons/" DESTINATION "${CGAL_INSTALL_CMAKE_DIR}/demo/icons" COMPONENT CGAL_Qt6) @@ -1049,7 +1055,7 @@ You must disable CGAL_ENABLE_CHECK_HEADERS.") execute_process( COMMAND "${CMAKE_COMMAND}" -DCGAL_BUILD_THREE_DOC=TRUE - -DDOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE} + -DDOXYGEN_EXECUTABLE=${DOXYGEN_EXECUTABLE} -DBE_QUIET=TRUE "${CGAL_SOURCE_DIR}/Documentation/doc" WORKING_DIRECTORY "${DOC_DIR}") execute_process( 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_SetupCGAL_Qt6Dependencies.cmake b/Installation/cmake/modules/CGAL_SetupCGAL_Qt6Dependencies.cmake index f4c961ab97d..e8bc2f8c788 100644 --- a/Installation/cmake/modules/CGAL_SetupCGAL_Qt6Dependencies.cmake +++ b/Installation/cmake/modules/CGAL_SetupCGAL_Qt6Dependencies.cmake @@ -25,7 +25,7 @@ set(CGAL_SetupCGAL_Qt6Dependencies_included TRUE) # ^^^^^^^^^^^^ # - :module:`Qt6Config` -find_package(Qt6 QUIET COMPONENTS Widgets OpenGL OpenGLWidgets OPTIONAL_COMPONENTS Svg) +find_package(Qt6 QUIET COMPONENTS OpenGL OpenGLWidgets Widgets OPTIONAL_COMPONENTS Svg) set(CGAL_Qt6_MISSING_DEPS "") if(NOT Qt6OpenGLWidgets_FOUND) 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/Kernel_23/doc/Kernel_23/CGAL/Bbox_2.h b/Kernel_23/doc/Kernel_23/CGAL/Bbox_2.h index 1905d336703..600314c0330 100644 --- a/Kernel_23/doc/Kernel_23/CGAL/Bbox_2.h +++ b/Kernel_23/doc/Kernel_23/CGAL/Bbox_2.h @@ -102,6 +102,12 @@ dilates the bounding box by a specified number of ULP. */ void dilate(int dist); +/*! +scales the bounding box by `factor`, while keeping its center fixed. +\pre `factor > 0` +*/ +void scale(double factor); + /// @} }; /* end Bbox_2 */ diff --git a/Kernel_23/doc/Kernel_23/CGAL/Bbox_3.h b/Kernel_23/doc/Kernel_23/CGAL/Bbox_3.h index f44a44e4812..734f79bc76e 100644 --- a/Kernel_23/doc/Kernel_23/CGAL/Bbox_3.h +++ b/Kernel_23/doc/Kernel_23/CGAL/Bbox_3.h @@ -115,6 +115,13 @@ Bbox_3& operator+=(const Bbox_3 &c); dilates the bounding box by a specified number of ULP. */ void dilate(int dist); + +/*! +scales the bounding box by `factor`, while keeping its center fixed. +\pre `factor > 0` +*/ +void scale(double factor); + /// @} }; /* end Bbox_3 */ diff --git a/Kernel_23/include/CGAL/Bbox_2.h b/Kernel_23/include/CGAL/Bbox_2.h index 57e482e8b0c..0222cf94778 100644 --- a/Kernel_23/include/CGAL/Bbox_2.h +++ b/Kernel_23/include/CGAL/Bbox_2.h @@ -71,6 +71,7 @@ public: inline Bbox_2& operator+=(const Bbox_2 &b); inline void dilate(int dist); + inline void scale(double factor); }; inline @@ -170,6 +171,25 @@ Bbox_2::dilate(int dist) rep[3] = float_advance(rep[3],dist); } +inline +void +Bbox_2::scale(double factor) +{ + CGAL_precondition(factor > 0); + + if(factor == 1.) + return; + + std::array half_width = { (xmax() - xmin()) * 0.5, + (ymax() - ymin()) * 0.5 }; + std::array center = { xmin() + half_width[0], + ymin() + half_width[1] }; + rep[0] = center[0] - factor * half_width[0]; + rep[1] = center[1] - factor * half_width[1]; + rep[2] = center[0] + factor * half_width[0]; + rep[3] = center[1] + factor * half_width[1]; +} + inline bool do_overlap(const Bbox_2 &bb1, const Bbox_2 &bb2) diff --git a/Kernel_23/include/CGAL/Bbox_3.h b/Kernel_23/include/CGAL/Bbox_3.h index b5bb3640800..dc750e4d0e0 100644 --- a/Kernel_23/include/CGAL/Bbox_3.h +++ b/Kernel_23/include/CGAL/Bbox_3.h @@ -76,7 +76,8 @@ public: Bbox_3 operator+(const Bbox_3& b) const; Bbox_3& operator+=(const Bbox_3& b); - void dilate(int dist); + inline void dilate(int dist); + inline void scale(double factor); }; inline @@ -200,6 +201,28 @@ Bbox_3::dilate(int dist) rep[5] = float_advance(rep[5],dist); } +inline +void +Bbox_3::scale(double factor) +{ + CGAL_precondition(factor > 0); + + if (factor == 1.) + return; + + std::array half_width = { (xmax() - xmin()) * 0.5, + (ymax() - ymin()) * 0.5, + (zmax() - zmin()) * 0.5 }; + std::array center = { xmin() + half_width[0], + ymin() + half_width[1], + zmin() + half_width[2] }; + rep[0] = center[0] - factor * half_width[0]; + rep[1] = center[1] - factor * half_width[1]; + rep[2] = center[2] - factor * half_width[2]; + rep[3] = center[0] + factor * half_width[0]; + rep[4] = center[1] + factor * half_width[1]; + rep[5] = center[2] + factor * half_width[2]; +} inline bool 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/demo/Linear_cell_complex/MainWindow.ui b/Linear_cell_complex/demo/Linear_cell_complex/MainWindow.ui index d63b9d7c9b1..6a9f06c68d2 100644 --- a/Linear_cell_complex/demo/Linear_cell_complex/MainWindow.ui +++ b/Linear_cell_complex/demo/Linear_cell_complex/MainWindow.ui @@ -38,7 +38,7 @@ - &File + File @@ -55,7 +55,7 @@ - &Operations + Operations @@ -75,7 +75,7 @@ - &Creations + Creations @@ -88,7 +88,7 @@ - &View + View @@ -103,67 +103,67 @@ - &Import OFF + Import OFF - &Quit + Quit - &Subdivide all volumes + Subdivide all volumes - &Subdivide all volumes - by Pqq + Subdivide all volumes - by Pqq - Create &3 cubes + Create 3 cubes - Import 3D&TS + Import 3DTS - &Clear + Clear - Create &2 volumes + Create 2 volumes - &Dual + Dual - &Close all volumes + Close all volumes - Create &cube + Create cube - S&ew3 all same facets + Sew3 all same facets - Create &mesh + Create mesh @@ -173,22 +173,22 @@ - &Remove volumes + Remove volumes - &Triangulate facets + Triangulate facets - &Unsew3 volumes + Unsew3 volumes - Reverse &orientation + Reverse orientation @@ -201,12 +201,12 @@ - Extend &filled volumes + Extend filled volumes - Extend &hidden volumes + Extend hidden volumes @@ -216,7 +216,7 @@ - &Merge volumes + Merge volumes @@ -231,12 +231,12 @@ - &Load + Load - &Save + Save diff --git a/Linear_cell_complex/demo/Linear_cell_complex/Viewer.cpp b/Linear_cell_complex/demo/Linear_cell_complex/Viewer.cpp index dafcccb1695..e7ff28d2ef2 100644 --- a/Linear_cell_complex/demo/Linear_cell_complex/Viewer.cpp +++ b/Linear_cell_complex/demo/Linear_cell_complex/Viewer.cpp @@ -9,37 +9,73 @@ // // Author(s) : Guillaume Damiand // Contributor(s): Kumar Snehasish +// Mostafa Ashraf // #include "Viewer.h" #include -Viewer::Viewer(QWidget* parent) : - Base(parent, nullptr, ""), +Viewer::Viewer(QWidget *parent) + : Base(parent, m_graphic_buffer, ""), m_previous_scene_empty(true) -{} +{ + m_gs_options.face_color=[](const LCC & alcc, + Dart_const_descriptor dh)->CGAL::IO::Color + { + if(alcc.template is_free<3>(dh)) + { return alcc.template info<3>(dh).color(); } -void Viewer::setScene(Scene* scene_, bool doredraw) + if(!alcc.template info<3>(dh).is_visible() || + !alcc.template info<3>(dh).is_filled()) + { return alcc.template info<3>(alcc.template beta<3>(dh)).color(); } + + if(!alcc.template info<3>(alcc.template beta<3>(dh)).is_visible() || + !alcc.template info<3>(alcc.template beta<3>(dh)).is_filled()) + { return alcc.template info<3>(dh).color(); } + + const CGAL::IO::Color& c1=alcc.template info<3>(dh).color(); + const CGAL::IO::Color& c2=alcc.template info<3>(alcc.template beta<3>(dh)).color(); + return CGAL::IO::Color((c1[0]+c2[0])/2, (c1[1]+c2[1])/2, (c1[2]+c2[2])/2); + }; + + m_gs_options.colored_face=[](const LCC &, Dart_const_descriptor)->bool + { return true; }; + + m_gs_options.draw_volume=[](const LCC & alcc, Dart_const_descriptor dh)->bool + { return alcc.template info<3>(dh).is_visible(); }; + + m_gs_options.volume_wireframe=[](const LCC& alcc, Dart_const_descriptor dh)->bool + { return !(alcc.template info<3>(dh).is_filled()); }; +} + +void Viewer::setScene(Scene *scene_, bool doredraw) { scene = scene_; - set_lcc(scene->lcc, doredraw); + + if (scene->lcc!=nullptr) + { CGAL::add_to_graphics_scene(*scene->lcc, m_graphic_buffer, m_gs_options); } + + if (doredraw) + { Base::redraw(); } } void Viewer::sceneChanged() { - Base::compute_elements(); - this->camera()-> - setSceneBoundingBox(CGAL::qglviewer::Vec(m_bounding_box.xmin(), - m_bounding_box.ymin(), - m_bounding_box.zmin()), - CGAL::qglviewer::Vec(m_bounding_box.xmax(), - m_bounding_box.ymax(), - m_bounding_box.zmax())); + m_graphic_buffer.clear(); + CGAL::add_to_graphics_scene(*scene->lcc, m_graphic_buffer, m_gs_options); + + this->camera()->setSceneBoundingBox( + CGAL::qglviewer::Vec(gBuffer.bounding_box().xmin(), + gBuffer.bounding_box().ymin(), + gBuffer.bounding_box().zmin()), + CGAL::qglviewer::Vec(gBuffer.bounding_box().xmax(), + gBuffer.bounding_box().ymax(), + gBuffer.bounding_box().zmax())); Base::redraw(); if (m_previous_scene_empty) { this->showEntireScene(); } - m_previous_scene_empty = scene->lcc->is_empty(); // for the next call to sceneChanged + m_previous_scene_empty=scene->lcc->is_empty(); // for the next call to sceneChanged } void Viewer::keyPressEvent(QKeyEvent *e) diff --git a/Linear_cell_complex/demo/Linear_cell_complex/Viewer.h b/Linear_cell_complex/demo/Linear_cell_complex/Viewer.h index ff1501ba2cd..95167205459 100644 --- a/Linear_cell_complex/demo/Linear_cell_complex/Viewer.h +++ b/Linear_cell_complex/demo/Linear_cell_complex/Viewer.h @@ -9,106 +9,20 @@ // // Author(s) : Guillaume Damiand // Kumar Snehasish +// Mostafa Ashraf // #ifndef VIEWER_H #define VIEWER_H #include "typedefs.h" #include +#include -// Functor used by SimpleLCCViewerQt to colorize of not elements. -struct MyDrawingFunctorLCC -{ - /// @return true iff the volume containing dh is drawn. - template - bool draw_volume(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { return alcc.template info<3>(dh).is_visible(); } - /// @return true iff the face containing dh is drawn. - template - bool draw_face(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the edge containing dh is drawn. - template - bool draw_edge(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the vertex containing dh is drawn. - template - bool draw_vertex(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - - /// @return true iff the volume containing dh is drawn in wireframe. - template - bool volume_wireframe(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { return !(alcc.template info<3>(dh).is_filled()); } - /// @return true iff the face containing dh is drawn in wireframe. - template - bool face_wireframe(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - - /// @return true iff the volume containing dh is colored. - template - bool colored_volume(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the face containing dh is colored. - /// if we have also colored_volume(alcc, dh), the volume color is - /// ignored and only the face color is considered. - template - bool colored_face(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - /// @return true iff the edge containing dh is colored. - template - bool colored_edge(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - /// @return true iff the vertex containing dh is colored. - template - bool colored_vertex(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - - /// @return the color of the volume containing dh - /// used only if colored_volume(alcc, dh) and !colored_face(alcc, dh) - template - CGAL::IO::Color volume_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { return alcc.template info<3>(dh).color(); } - /// @return the color of the face containing dh - /// used only if colored_face(alcc, dh) - template - CGAL::IO::Color face_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { - CGAL::Random random((unsigned int)(alcc.darts().index(dh))); - return get_random_color(random); - } - /// @return the color of the edge containing dh - /// used only if colored_edge(alcc, dh) - template - CGAL::IO::Color edge_color(const LCC&, - typename LCC::Dart_const_descriptor) const - { return CGAL::IO::Color(0, 0, 0); } - /// @return the color of the vertex containing dh - /// used only if colored_vertex(alcc, dh) - template - CGAL::IO::Color vertex_color(const LCC&, - typename LCC::Dart_const_descriptor) const - { return CGAL::IO::Color(0, 0, 0); } -}; - - -class Viewer : public CGAL::SimpleLCCViewerQt +class Viewer : public CGAL::Basic_viewer { Q_OBJECT - typedef CGAL::SimpleLCCViewerQt Base; + typedef CGAL::Basic_viewer Base; public: Viewer(QWidget* parent); @@ -120,6 +34,12 @@ public Q_SLOTS: void sceneChanged(); private: + CGAL::Graphics_scene_options m_gs_options; + CGAL::Graphics_scene m_graphic_buffer; Scene* scene; bool m_previous_scene_empty; }; diff --git a/Linear_cell_complex/demo/Linear_cell_complex/typedefs.h b/Linear_cell_complex/demo/Linear_cell_complex/typedefs.h index 31fde91f697..36bcfc807f7 100644 --- a/Linear_cell_complex/demo/Linear_cell_complex/typedefs.h +++ b/Linear_cell_complex/demo/Linear_cell_complex/typedefs.h @@ -165,8 +165,9 @@ public: }; typedef CGAL::Linear_cell_complex_for_combinatorial_map<3,3,Mytraits,Myitems> LCC; -typedef LCC::Dart_descriptor Dart_descriptor; -typedef LCC::Vertex_attribute Vertex; +typedef LCC::Dart_descriptor Dart_descriptor; +typedef LCC::Dart_const_descriptor Dart_const_descriptor; +typedef LCC::Vertex_attribute Vertex; typedef LCC::Point Point_3; typedef LCC::Vector Vector_3; diff --git a/Linear_cell_complex/doc/Linear_cell_complex/CGAL/draw_linear_cell_complex.h b/Linear_cell_complex/doc/Linear_cell_complex/CGAL/draw_linear_cell_complex.h index 648d2c81ef3..6dc9b021133 100644 --- a/Linear_cell_complex/doc/Linear_cell_complex/CGAL/draw_linear_cell_complex.h +++ b/Linear_cell_complex/doc/Linear_cell_complex/CGAL/draw_linear_cell_complex.h @@ -3,14 +3,73 @@ namespace CGAL { /*! \ingroup PkgDrawLinearCellComplex -opens a new window and draws `alcc`, a model of the `LinearCellComplex` concept. 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 LCC a model of the `LinearCellComplex` concept. -\param alcc the linear cell complex to draw. +opens a new window and draws a linear cell complex. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 LCC which must be an instanciation of a `CGAL::Linear_cell_complex_for_combinatorial_map<...>` or `CGAL::Linear_cell_complex_for_generalized_map<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param lcc the linear cell complex to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Linear_cell_complex_base& lcc, const GSOptions& gso); + +\cgalAdvancedEnd +*/ + template + void draw(const LCC& lcc, const GSOptions& gso); + +/*! +\ingroup PkgDrawLinearCellComplex + +A shortcut to `CGAL::draw(lcc, Graphics_scene_options{})`. +*/ + template + void draw(const LCC& lcc); + +/*! +\ingroup PkgDrawLinearCellComplex + +adds the vertices, edges and faces of `lcc` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam LCC which must be an instanciation of a `CGAL::Linear_cell_complex_for_combinatorial_map<...>` or `CGAL::Linear_cell_complex_for_generalized_map<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param lcc the linear cell complex to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Linear_cell_complex_base& lcc, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const LCC& lcc, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawLinearCellComplex + +A shortcut to `CGAL::add_to_graphics_scene(lcc, gs, Graphics_scene_options{})`. */ template -void draw(const LCC& alcc); +void add_to_graphics_scene(const LCC& lcc, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Linear_cell_complex/doc/Linear_cell_complex/PackageDescription.txt b/Linear_cell_complex/doc/Linear_cell_complex/PackageDescription.txt index ad0479d5128..a9ead2ceac0 100644 --- a/Linear_cell_complex/doc/Linear_cell_complex/PackageDescription.txt +++ b/Linear_cell_complex/doc/Linear_cell_complex/PackageDescription.txt @@ -72,5 +72,6 @@ \cgalCRPSubsection{Draw a Linear Cell Complex} - \link PkgDrawLinearCellComplex CGAL::draw() \endlink +- \link PkgDrawLinearCellComplex CGAL::add_in_graphics_scene() \endlink */ diff --git a/Linear_cell_complex/examples/Linear_cell_complex/basic_viewer.h b/Linear_cell_complex/examples/Linear_cell_complex/basic_viewer.h deleted file mode 100644 index f01ab8208f2..00000000000 --- a/Linear_cell_complex/examples/Linear_cell_complex/basic_viewer.h +++ /dev/null @@ -1,1190 +0,0 @@ -// Copyright (c) 2011 CNRS and LIRIS' Establishments (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) : Guillaume Damiand - -#ifndef CGAL_BASIC_VIEWER_H -#define CGAL_BASIC_VIEWER_H - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define NB_VBO_BUFFERS 8 -#define NB_VAO_BUFFERS 4 - -typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; -typedef Local_kernel::Point_3 Local_point; -typedef Local_kernel::Vector_3 Local_vector; - -//Vertex source code -const char vertex_source_mono[] = - { - "#version 150 \n" - "in highp vec4 vertex;\n" - "in highp vec3 normal;\n" - - "uniform highp mat4 mvp_matrix;\n" - "uniform highp mat4 mv_matrix; \n" - - "out highp vec4 fP; \n" - "out highp vec3 fN; \n" - "void main(void)\n" - "{\n" - " fP = mv_matrix * vertex; \n" - " fN = mat3(mv_matrix)* normal; \n" - " gl_Position = mvp_matrix * vertex;\n" - "}" - }; - -const char vertex_source_color[] = - { - "#version 150 \n" - "in highp vec4 vertex;\n" - "in highp vec3 normal;\n" - "in highp vec3 color;\n" - - "uniform highp mat4 mvp_matrix;\n" - "uniform highp mat4 mv_matrix; \n" - - "out highp vec4 fP; \n" - "out highp vec3 fN; \n" - "out highp vec4 fColor; \n" - "void main(void)\n" - "{\n" - " fP = mv_matrix * vertex; \n" - " fN = mat3(mv_matrix)* normal; \n" - " fColor = vec4(color, 1.0); \n" - " gl_Position = mvp_matrix * vertex;\n" - "}" - }; - -//Vertex source code -const char fragment_source_mono[] = - { - "#version 150 \n" - "in highp vec4 fP; \n" - "in highp vec3 fN; \n" - "uniform highp vec4 color; \n" - "uniform highp vec4 light_pos; \n" - "uniform highp vec4 light_diff; \n" - "uniform highp vec4 light_spec; \n" - "uniform highp vec4 light_amb; \n" - "uniform float spec_power ; \n" - "out highp vec4 out_color; \n" - - "void main(void) { \n" - - " highp vec3 L = light_pos.xyz - fP.xyz; \n" - " highp vec3 V = -fP.xyz; \n" - - " highp vec3 N = normalize(fN); \n" - " L = normalize(L); \n" - " V = normalize(V); \n" - - " highp vec3 R = reflect(-L, N); \n" - " highp vec4 diffuse = max(dot(N,L), 0.0) * light_diff * color; \n" - " highp vec4 specular = pow(max(dot(R,V), 0.0), spec_power) * light_spec; \n" - - "out_color = light_amb*color + diffuse ; \n" - "} \n" - "\n" - }; - -const char fragment_source_color[] = - { - "#version 150 \n" - "in highp vec4 fP; \n" - "in highp vec3 fN; \n" - "in highp vec4 fColor; \n" - "uniform highp vec4 light_pos; \n" - "uniform highp vec4 light_diff; \n" - "uniform highp vec4 light_spec; \n" - "uniform highp vec4 light_amb; \n" - "uniform float spec_power ; \n" - "out highp vec4 out_color; \n" - - "void main(void) { \n" - - " highp vec3 L = light_pos.xyz - fP.xyz; \n" - " highp vec3 V = -fP.xyz; \n" - - " highp vec3 N = normalize(fN); \n" - " L = normalize(L); \n" - " V = normalize(V); \n" - - " highp vec3 R = reflect(-L, N); \n" - " highp vec4 diffuse = max(dot(N,L), 0.0) * light_diff * fColor; \n" - " highp vec4 specular = pow(max(dot(R,V), 0.0), spec_power) * light_spec; \n" - - "out_color = light_amb*fColor + diffuse ; \n" - "} \n" - "\n" - }; - -//Vertex source code -const char vertex_source_p_l[] = - { - "#version 150 \n" - "in highp vec4 vertex;\n" - "uniform highp mat4 mvp_matrix;\n" - "void main(void)\n" - "{\n" - " gl_Position = mvp_matrix * vertex;\n" - "}" - }; -//Vertex source code -const char fragment_source_p_l[] = - { - "#version 150 \n" - "uniform highp vec4 color; \n" - "out highp vec4 out_color; \n" - "void main(void) { \n" - "out_color = color; \n" - "} \n" - "\n" - }; - -namespace internal { - template - void newell_single_step_3(const Point& p, const Point& q, Vector& n) - { - // Compute normal of the face by using Newell's method: for each edge PQ - // Nx += (Py - Qy) * (Pz + Qz); - // Ny += (Pz - Qz) * (Px + Qx); - // Nz += (Px - Qx) * (Py + Qy); - n = Vector(n.x()+((p.y()-q.y())*(p.z()+q.z())), - n.y()+((p.z()-q.z())*(p.x()+q.x())), - n.z()+((p.x()-q.x())*(p.y()+q.y()))); - } -} // End namespace internal - -template -typename K::Vector_3 compute_normal_of_face(const std::vector& points) -{ - typename K::Vector_3 normal(CGAL::NULL_VECTOR); - unsigned int nb = 0; - for (std::size_t i=0; i0); - return (typename K::Construct_scaled_vector_3()(normal, 1.0/nb)); -} - -class Basic_viewer : public CGAL::QGLViewer, public QOpenGLFunctions -{ - struct Vertex_info - { - Local_vector v; - }; - - struct Face_info - { - bool exist_edge[3]; - bool is_external; - bool is_process; - }; - - typedef CGAL::Projection_traits_3 P_traits; - typedef CGAL::Triangulation_vertex_base_with_info_2 Vb; - - typedef CGAL::Triangulation_face_base_with_info_2 Fb1; - - typedef CGAL::Constrained_triangulation_face_base_2 Fb; - typedef CGAL::Triangulation_data_structure_2 TDS; - typedef CGAL::Exact_predicates_tag Itag; - typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; - -public: - // Constructor/Destructor - Basic_viewer(const char* title="") : - CGAL::QGLViewer(CGAL::Qt::createOpenGLContext()), - m_draw_vertices(true), - m_draw_edges(true), - m_draw_faces(true), - m_flatShading(true), - m_use_mono_color(false), - m_inverse_normal(false), - m_empty(true), - m_size_points(7.), - m_size_edges(3.1), - m_vertices_mono_color(51, 51, 178), - m_edges_mono_color(51, 51, 148), - m_faces_mono_color(180, 125, 200), - m_ambient_color(0.6f, 0.5f, 0.5f, 0.5f), - m_are_buffers_initialized(false), - m_face_started(false) - { - if (title[0]==0) - setWindowTitle("CGAL Basic Viewer"); - else - setWindowTitle(title); - - resize(500, 450); - - if ( is_empty() ) - { - bb=Local_point(CGAL::ORIGIN).bbox(); - bb=bb + Local_point(1,1,1).bbox(); // To avoid a warning from Qglviewer - } - } - - ~Basic_viewer() - { - for (int i=0; i& point_vector) - { - point_vector.push_back(p.x()); - point_vector.push_back(p.y()); - point_vector.push_back(p.z()); - - if (is_empty()) - { bb=p.bbox(); m_empty=false; } - else - { bb=bb+p.bbox(); } - } - - void add_color(const CGAL::IO::Color& acolor, std::vector& color_vector) - { - color_vector.push_back((double)color_of_face.red()/(double)255); - color_vector.push_back((double)color_of_face.green()/(double)255); - color_vector.push_back((double)color_of_face.blue()/(double)255); - } - - void add_normal(const Local_vector& n, std::vector& normal_vector) - { - normal_vector.push_back(n.x()); - normal_vector.push_back(n.y()); - normal_vector.push_back(n.z()); - } - - void add_mono_point(const Local_point& p) - { add_point(p, arrays[POS_MONO_POINTS]); } - - void add_colored_point(const Local_point& p, const CGAL::IO::Color& acolor) - { - add_point(p, arrays[POS_COLORED_POINTS]); - add_color(acolor, arrays[COLOR_POINTS]); - } - - void add_mono_segment(const Local_point& p1, const Local_point& p2) - { - add_point(p1, arrays[POS_MONO_SEGMENTS]); - add_point(p2, arrays[POS_MONO_SEGMENTS]); - } - - void add_colored_segment(const Local_point& p1, const Local_point& p2, - const CGAL::IO::Color& acolor) - { - add_point(p1, arrays[POS_COLORED_SEGMENTS]); - add_point(p2, arrays[POS_COLORED_SEGMENTS]); - add_color(acolor, arrays[COLOR_SEGMENTS]); - } - - void face_begin() - { - if (m_face_started) - { - std::cerr<<"You cannot start a new face before to finish the previous one."<(points_of_face); - - if (points_of_face.size()==3) // Triangle: no need to triangulate - { - for (int i=0; i<3; ++i) - { - // The point - add_point(points_of_face[i], arrays[m_started_face_is_colored? - POS_COLORED_FACES: - POS_MONO_FACES]); - - // Its color - if (m_started_face_is_colored) - { add_color(color_of_face, arrays[COLOR_FACES]); } - - // Its flat normal - add_normal(normal, arrays[m_started_face_is_colored? - FLAT_NORMAL_COLORED_FACES: - FLAT_NORMAL_MONO_FACES]); - - // Its smoth normal (if given by the user) - if (vertex_normals_for_face.size()==3) - { // Here we have 3 vertex normals; we can use Gourod - add_normal(vertex_normals_for_face[i], arrays[m_started_face_is_colored? - SMOOTH_NORMAL_COLORED_FACES: - SMOOTH_NORMAL_MONO_FACES]); - } - else - { // Here user does not provide all vertex normals: we use face normal instead - // and thus we will not be able to use Gourod - add_normal(normal, arrays[m_started_face_is_colored? - SMOOTH_NORMAL_COLORED_FACES: - SMOOTH_NORMAL_MONO_FACES]); - } - } - } - // TODO CASE OF 4 POINTS ? PB HOW TO FIND (EASILY) THE TWO POINTS TO LINK ? - // else if (points_of_face.size()==4) - else - { // More than 3 points: we triangulate - try - { - P_traits cdt_traits(normal); - CDT cdt(cdt_traits); - - bool with_vertex_normal=(vertex_normals_for_face.size()==points_of_face.size()); - - // (1) We insert all the edges as constraint in the CDT. - typename CDT::Vertex_descriptor previous=NULL, first=NULL; - for (int i=0; iinfo().v=vertex_normals_for_face[i]; } - else - { vh->info().v=normal; } - - if(previous!=NULL && previous!=vh) - { cdt.insert_constraint(previous, vh); } - previous=vh; - } - - if (previous!=NULL && previous!=first) - cdt.insert_constraint(previous, first); - - // (2) We mark all external triangles - // (2.1) We initialize is_external and is_process values - for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(), - fitend = cdt.all_faces_end(); fit!=fitend; ++fit) - { - fit->info().is_external = true; - fit->info().is_process = false; - } - // (2.2) We check if the facet is external or internal - std::queue face_queue; - typename CDT::Face_descriptor face_internal = NULL; - if (cdt.infinite_vertex()->face()!=NULL) - face_queue.push(cdt.infinite_vertex()->face()); - while(! face_queue.empty() ) - { - typename CDT::Face_descriptor fh = face_queue.front(); - face_queue.pop(); - if(!fh->info().is_process) - { - fh->info().is_process = true; - for(int i=0; i<3; ++i) - { - if(!cdt.is_constrained(std::make_pair(fh, i))) - { - if (fh->neighbor(i)!=NULL) - face_queue.push(fh->neighbor(i)); - } - else if (face_internal==NULL) - { - face_internal = fh->neighbor(i); - } - } - } - } - - if ( face_internal!=NULL ) - face_queue.push(face_internal); - - while(! face_queue.empty() ) - { - typename CDT::Face_descriptor fh = face_queue.front(); - face_queue.pop(); - if(!fh->info().is_process) - { - fh->info().is_process = true; - fh->info().is_external = false; - for(int i=0; i<3; ++i) - { - if(!cdt.is_constrained(std::make_pair(fh, i))) - { - if (fh->neighbor(i)!=NULL) - face_queue.push(fh->neighbor(i)); - } - } - } - } - - // (3) Now we iterates on the internal faces to add the vertices to the - // positions and the normals to the appropriate vectors - for(typename CDT::Finite_faces_iterator ffit=cdt.finite_faces_begin(), - ffitend = cdt.finite_faces_end(); ffit!=ffitend; ++ffit) - { - if(!ffit->info().is_external) - { - for(int i=0; i<3; ++i) - { - // The point - add_point(ffit->vertex(i)->point(), arrays[m_started_face_is_colored? - POS_COLORED_FACES: - POS_MONO_FACES]); - - // Its color - if (m_started_face_is_colored) - { add_color(color_of_face, arrays[COLOR_FACES]); } - - // Its flat normal - add_normal(normal, arrays[m_started_face_is_colored? - FLAT_NORMAL_COLORED_FACES: - FLAT_NORMAL_MONO_FACES]); - - // Its smoth normal (if given by the user) - add_normal(ffit->vertex(i)->info().v, arrays[m_started_face_is_colored? - SMOOTH_NORMAL_COLORED_FACES: - SMOOTH_NORMAL_MONO_FACES]); - } - } - } - } - catch(...) - { // Triangulation crash: the face is not filled - std::cout<<"Catch: face not filled."<compileSourceCode(vertex_source_mono)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(fragment_source_mono)) - { std::cerr<<"Compiling fragmentsource FAILED"<compileSourceCode(vertex_source_mono)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(fragment_source_mono)) - { std::cerr<<"Compiling fragmentsource FAILED"<compileSourceCode(vertex_source_color)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(fragment_source_color)) - { std::cerr<<"Compiling fragmentsource FAILED"<compileSourceCode(vertex_source_p_l)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(fragment_source_p_l)) - { std::cerr<<"Compiling fragmentsource FAILED"<(arrays[POS_MONO_FACES].size()*sizeof(float))); - vertexLocation[vaon] = rendering_program_mono.attributeLocation("vertex"); - rendering_program_mono.bind(); - rendering_program_mono.enableAttributeArray(vertexLocation[vaon]); - rendering_program_mono.setAttributeBuffer(vertexLocation[vaon],GL_FLOAT,0,3); - rendering_program_mono.release(); - - buffers[bufn].release(); - ++bufn; - - // 3.1.2) normals of the mono faces - assert(bufn(arrays[FLAT_NORMAL_MONO_FACES].size()* - sizeof(float))); - } - else - { - buffers[bufn].allocate(arrays[SMOOTH_NORMAL_MONO_FACES].data(), - static_cast(arrays[SMOOTH_NORMAL_MONO_FACES].size()* - sizeof(float))); - } - normalsLocation = rendering_program_mono.attributeLocation("normal"); - rendering_program_mono.bind(); - rendering_program_mono.enableAttributeArray(normalsLocation); - rendering_program_mono.setAttributeBuffer(normalsLocation,GL_FLOAT,0,3); - rendering_program_mono.release(); - - buffers[bufn].release(); - ++bufn; - - vao[vaon].release(); - ++vaon; - - // 3.2) Color faces - assert(vaon(arrays[POS_COLORED_FACES].size()*sizeof(float))); - vertexLocation[vaon] = rendering_program_color.attributeLocation("vertex"); - rendering_program_color.bind(); - rendering_program_color.enableAttributeArray(vertexLocation[vaon]); - rendering_program_color.setAttributeBuffer(vertexLocation[vaon],GL_FLOAT,0,3); - rendering_program_color.release(); - - buffers[bufn].release(); - ++bufn; - - // 3.2.2) normals of the color faces - assert(bufn(arrays[FLAT_NORMAL_COLORED_FACES].size()* - sizeof(float))); - } - else - { - buffers[bufn].allocate(arrays[SMOOTH_NORMAL_COLORED_FACES].data(), - static_cast(arrays[SMOOTH_NORMAL_COLORED_FACES].size()* - sizeof(float))); - } - normalsLocation = rendering_program_color.attributeLocation("normal"); - rendering_program_color.bind(); - rendering_program_color.enableAttributeArray(normalsLocation); - rendering_program_color.setAttributeBuffer(normalsLocation,GL_FLOAT,0,3); - rendering_program_color.release(); - - buffers[bufn].release(); - ++bufn; - - // 3.2.3) colors of the faces - if (!m_use_mono_color) - { - assert(bufn(arrays[COLOR_FACES].size()*sizeof(float))); - colorsLocation = rendering_program_color.attributeLocation("color"); - rendering_program_color.bind(); - rendering_program_color.enableAttributeArray(colorsLocation); - rendering_program_color.setAttributeBuffer(colorsLocation,GL_FLOAT,0,3); - rendering_program_color.release(); - - buffers[bufn].release(); - ++bufn; - } - - vao[vaon].release(); - ++vaon; - - m_are_buffers_initialized = true; - } - - void attrib_buffers(CGAL::QGLViewer* viewer) - { - QMatrix4x4 mvpMatrix; - QMatrix4x4 mvMatrix; - double mat[16]; - viewer->camera()->getModelViewProjectionMatrix(mat); - for(int i=0; i < 16; i++) - { - mvpMatrix.data()[i] = (float)mat[i]; - } - viewer->camera()->getModelViewMatrix(mat); - for(int i=0; i < 16; i++) - { - mvMatrix.data()[i] = (float)mat[i]; - } - // define material - QVector4D diffuse( 0.9f, - 0.9f, - 0.9f, - 0.9f ); - - QVector4D specular( 0.0f, - 0.0f, - 0.0f, - 1.0f ); - - QVector4D position((bb.xmax()-bb.xmin())/2, (bb.ymax()-bb.ymin())/2,bb.zmax(), 0.0 ); - GLfloat shininess = 1.0f; - - rendering_program_mono.bind(); - mvpLocation[0] = rendering_program_mono.uniformLocation("mvp_matrix"); - mvLocation = rendering_program_mono.uniformLocation("mv_matrix"); - lightLocation[0] = rendering_program_mono.uniformLocation("light_pos"); - lightLocation[1] = rendering_program_mono.uniformLocation("light_diff"); - lightLocation[2] = rendering_program_mono.uniformLocation("light_spec"); - lightLocation[3] = rendering_program_mono.uniformLocation("light_amb"); - lightLocation[4] = rendering_program_mono.uniformLocation("spec_power"); - - rendering_program_mono.setUniformValue(lightLocation[0], position); - rendering_program_mono.setUniformValue(lightLocation[1], diffuse); - rendering_program_mono.setUniformValue(lightLocation[2], specular); - rendering_program_mono.setUniformValue(lightLocation[3], m_ambient_color); - rendering_program_mono.setUniformValue(lightLocation[4], shininess); - rendering_program_mono.setUniformValue(mvpLocation[0], mvpMatrix); - rendering_program_mono.setUniformValue(mvLocation, mvMatrix); - - colorLocation1 = rendering_program_mono.uniformLocation("color"); - rendering_program_mono.release(); - - rendering_program_color.bind(); - mvpLocation[0] = rendering_program_color.uniformLocation("mvp_matrix"); - mvLocation = rendering_program_color.uniformLocation("mv_matrix"); - lightLocation[0] = rendering_program_color.uniformLocation("light_pos"); - lightLocation[1] = rendering_program_color.uniformLocation("light_diff"); - lightLocation[2] = rendering_program_color.uniformLocation("light_spec"); - lightLocation[3] = rendering_program_color.uniformLocation("light_amb"); - lightLocation[4] = rendering_program_color.uniformLocation("spec_power"); - - rendering_program_color.setUniformValue(lightLocation[0], position); - rendering_program_color.setUniformValue(lightLocation[1], diffuse); - rendering_program_color.setUniformValue(lightLocation[2], specular); - rendering_program_color.setUniformValue(lightLocation[3], m_ambient_color); - rendering_program_color.setUniformValue(lightLocation[4], shininess); - rendering_program_color.setUniformValue(mvpLocation[0], mvpMatrix); - rendering_program_color.setUniformValue(mvLocation, mvMatrix); - - if (m_use_mono_color) - { colorLocation2 = rendering_program_color.uniformLocation("color"); } - rendering_program_color.release(); - - /* rendering_program_p_l_.bind(); - mvpLocation[1] = rendering_program_p_l.uniformLocation("mvp_matrix"); - colorLocation = rendering_program_p_l.uniformLocation("color"); - rendering_program.setUniformValue(mvpLocation[1], mvpMatrix); - rendering_program_p_l.release();*/ - } - - virtual void draw() - { - glEnable(GL_DEPTH_TEST); - if(!m_are_buffers_initialized) - initialize_buffers(); - - QColor color; - - if (m_draw_faces) - { - vao[0].bind(); - attrib_buffers(this); - rendering_program_mono.bind(); - color.setRgbF((double)m_faces_mono_color.red()/(double)255, - (double)m_faces_mono_color.green()/(double)255, - (double)m_faces_mono_color.blue()/(double)255); - rendering_program_mono.setUniformValue(colorLocation1,color); - glDrawArrays(GL_TRIANGLES, 0, static_cast(arrays[POS_MONO_FACES].size()/3)); - rendering_program_mono.release(); - vao[0].release(); - - vao[1].bind(); - attrib_buffers(this); - rendering_program_color.bind(); - if (m_use_mono_color) - { - color.setRgbF((double)m_faces_mono_color.red()/(double)255, - (double)m_faces_mono_color.green()/(double)255, - (double)m_faces_mono_color.blue()/(double)255); - rendering_program_color.setUniformValue(colorLocation2,color); - } - glDrawArrays(GL_TRIANGLES, 0, static_cast(arrays[POS_COLORED_FACES].size()/3)); - rendering_program_color.release(); - vao[1].release(); - } - } - - virtual void init() - { - // Restore previous viewer state. - initializeOpenGLFunctions(); - - // Define 'Control+Q' as the new exit shortcut (default was 'Escape') - setShortcut(EXIT_VIEWER, Qt::CTRL+Qt::Key_Q); - - // Add custom key description (see keyPressEvent). - setKeyDescription(Qt::Key_E, "Toggles edges display"); - setKeyDescription(Qt::Key_F, "Toggles faces display"); - setKeyDescription(Qt::Key_G, "Switch between flat/Gouraud shading display"); - setKeyDescription(Qt::Key_M, "Toggles mono color for all faces"); - setKeyDescription(Qt::Key_N, "Inverse direction of normals"); - setKeyDescription(Qt::Key_V, "Toggles vertices display"); - setKeyDescription(Qt::Key_Plus, "Increase size of edges"); - setKeyDescription(Qt::Key_Minus, "Decrease size of edges"); - setKeyDescription(Qt::Key_Plus+Qt::ShiftModifier, "Increase size of vertices"); - setKeyDescription(Qt::Key_Minus+Qt::ShiftModifier, "Decrease size of vertices"); - setKeyDescription(Qt::Key_PageDown, "Increase light (all colors, use shift/alt/ctrl for one rgb component)"); - setKeyDescription(Qt::Key_PageUp, "Decrease light (all colors, use shift/alt/ctrl for one rgb component)"); - - // Light default parameters - ::glLineWidth(m_size_edges); - ::glEnable(GL_POLYGON_OFFSET_FILL); - ::glPolygonOffset(1.f,1.f); - ::glClearColor(1.0f,1.0f,1.0f,0.0f); - ::glDisable(GL_BLEND); - ::glEnable(GL_LINE_SMOOTH); - ::glDisable(GL_POLYGON_SMOOTH_HINT); - ::glBlendFunc(GL_ONE, GL_ZERO); - ::glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); - - compile_shaders(); - - this->camera()->setSceneBoundingBox(CGAL::qglviewer::Vec(bb.xmin(), - bb.ymin(), - bb.zmin()), - CGAL::qglviewer::Vec(bb.xmax(), - bb.ymax(), - bb.zmax())); - - this->showEntireScene(); - } - - void negate_all_normals() - { - for (unsigned int k=BEGIN_NORMAL; kmodifiers(); - - if ((e->key()==Qt::Key_E) && (modifiers==Qt::NoButton)) - { - m_draw_edges=!m_draw_edges; - displayMessage(QString("Draw edges=%1.").arg(m_draw_edges?"true":"false")); - updateGL(); - } - else if ((e->key()==Qt::Key_F) && (modifiers==Qt::NoButton)) - { - m_draw_faces=!m_draw_faces; - displayMessage(QString("Draw faces=%1.").arg(m_draw_faces?"true":"false")); - updateGL(); - } - else if ((e->key()==Qt::Key_G) && (modifiers==Qt::NoButton)) - { - m_flatShading=!m_flatShading; - if (m_flatShading) - displayMessage("Flat shading."); - else - displayMessage("Gouraud shading."); - compile_shaders(); - initialize_buffers(); - updateGL(); - } - else if ((e->key()==Qt::Key_M) && (modifiers==Qt::NoButton)) - { - m_use_mono_color=!m_use_mono_color; - displayMessage(QString("Mono color=%1.").arg(m_use_mono_color?"true":"false")); - compile_shaders(); - initialize_buffers(); - updateGL(); - } - else if ((e->key()==Qt::Key_N) && (modifiers==Qt::NoButton)) - { - m_inverse_normal=!m_inverse_normal; - negate_all_normals(); - compile_shaders(); - initialize_buffers(); - displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal?"true":"false")); - updateGL(); - } - else if ((e->key()==Qt::Key_V) && (modifiers==Qt::NoButton)) - { - m_draw_vertices=!m_draw_vertices; - displayMessage(QString("Draw vertices=%1.").arg(m_draw_vertices?"true":"false")); - updateGL(); - } - else if ((e->key()==Qt::Key_Plus) && (modifiers==Qt::KeypadModifier)) - { - m_size_edges+=.5; - displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); - updateGL(); - } - else if ((e->key()==Qt::Key_Minus) && (modifiers==Qt::KeypadModifier)) - { - if (m_size_edges>.5) m_size_edges-=.5; - displayMessage(QString("Size of edges=%1.").arg(m_size_edges)); - updateGL(); - } - else if ((e->key()==Qt::Key_Plus) && (modifiers==(Qt::ShiftModifier|Qt::KeypadModifier))) - { - m_size_points+=.5; - displayMessage(QString("Size of points=%1.").arg(m_size_points)); - updateGL(); - } - else if ((e->key()==Qt::Key_Minus) && (modifiers==(Qt::ShiftModifier|Qt::KeypadModifier))) - { - if (m_size_points>.5) m_size_points-=.5; - displayMessage(QString("Size of points=%1.").arg(m_size_points)); - updateGL(); - } - else if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) - { - m_ambient_color.setX(m_ambient_color.x()+.1); - if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); - m_ambient_color.setY(m_ambient_color.x()+.1); - if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); - m_ambient_color.setZ(m_ambient_color.x()+.1); - if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageDown) && (modifiers==Qt::NoButton)) - { - m_ambient_color.setX(m_ambient_color.x()-.1); - if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); - m_ambient_color.setY(m_ambient_color.y()-.1); - if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); - m_ambient_color.setZ(m_ambient_color.z()-.1); - if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::ShiftModifier)) - { - m_ambient_color.setX(m_ambient_color.x()+.1); - if (m_ambient_color.x()>1.) m_ambient_color.setX(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::AltModifier)) - { - m_ambient_color.setY(m_ambient_color.y()+.1); - if (m_ambient_color.y()>1.) m_ambient_color.setY(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::ControlModifier)) - { - m_ambient_color.setZ(m_ambient_color.z()+.1); - if (m_ambient_color.z()>1.) m_ambient_color.setZ(1.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageDown) && (modifiers==Qt::ShiftModifier)) - { - m_ambient_color.setX(m_ambient_color.x()-.1); - if (m_ambient_color.x()<0.) m_ambient_color.setX(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageDown) && (modifiers==Qt::AltModifier)) - { - m_ambient_color.setY(m_ambient_color.y()-.1); - if (m_ambient_color.y()<0.) m_ambient_color.setY(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else if ((e->key()==Qt::Key_PageDown) && (modifiers==Qt::ControlModifier)) - { - m_ambient_color.setZ(m_ambient_color.z()-.1); - if (m_ambient_color.z()<0.) m_ambient_color.setZ(0.); - displayMessage(QString("Light color=(%1 %2 %3)."). - arg(m_ambient_color.x()).arg(m_ambient_color.y()).arg(m_ambient_color.z())); - updateGL(); - } - else - CGAL::QGLViewer::keyPressEvent(e); - } - - virtual QString helpString() const - { - QString text("

C G A L B a s i c V i e w e r

"); - text += "Use the mouse to move the camera around the object. "; - text += "You can respectively revolve around, zoom and translate with " - "the three mouse buttons. "; - text += "Left and middle buttons pressed together rotate around the " - "camera view direction axis

"; - text += "Pressing Alt and one of the function keys " - "(F1..F12) defines a camera keyFrame. "; - text += "Simply press the function key again to restore it. " - "Several keyFrames define a "; - text += "camera path. Paths are saved when you quit the application " - "and restored at next start.

"; - text += "Press F to display the frame rate, A for the " - "world axis, "; - text += "Alt+Return for full screen mode and Control+S " - "to save a snapshot. "; - text += "See the Keyboard tab in this window for a complete " - "shortcut list.

"; - text += "Double clicks automates single click actions: A left button " - "double click aligns the closer axis with the camera (if close enough). "; - text += "A middle button double click fits the zoom of the camera and " - "the right button re-centers the scene.

"; - text += "A left button double click while holding right button pressed " - "defines the camera Revolve Around Point. "; - text += "See the Mouse tab and the documentation web pages for " - "details.

"; - text += "Press Escape to exit the viewer."; - return text; - } - -private: - bool m_draw_vertices; - bool m_draw_edges; - bool m_draw_faces; - bool m_flatShading; - bool m_use_mono_color; - bool m_inverse_normal; - bool m_empty; - - double m_size_points; - double m_size_edges; - - CGAL::IO::Color m_vertices_mono_color; - CGAL::IO::Color m_edges_mono_color; - CGAL::IO::Color m_faces_mono_color; - QVector4D m_ambient_color; - - bool m_are_buffers_initialized; - CGAL::Bbox_3 bb; - - //Shaders elements - int vertexLocation[NB_VAO_BUFFERS]; - int normalsLocation; - int mvpLocation[2]; - int mvLocation; - int colorLocation1; - int colorLocation2; - int lightLocation[5]; - - enum - { POS_MONO_POINTS=0, - POS_COLORED_POINTS, - POS_MONO_SEGMENTS, - POS_COLORED_SEGMENTS, - POS_MONO_FACES, - POS_COLORED_FACES, - BEGIN_NORMAL, - SMOOTH_NORMAL_MONO_FACES=BEGIN_NORMAL, - FLAT_NORMAL_MONO_FACES, - SMOOTH_NORMAL_COLORED_FACES, - FLAT_NORMAL_COLORED_FACES, - END_NORMAL, - COLOR_POINTS=END_NORMAL, - COLOR_SEGMENTS, - COLOR_FACES, - LAST_INDEX - }; - - std::vector arrays[LAST_INDEX]; - - QGLBuffer buffers[NB_VBO_BUFFERS]; - QOpenGLVertexArrayObject vao[NB_VAO_BUFFERS]; - int colorsLocation; - - QOpenGLShaderProgram rendering_program_mono; - QOpenGLShaderProgram rendering_program_color; - QOpenGLShaderProgram rendering_program_p_l_mono; - QOpenGLShaderProgram rendering_program_p_l_color; - - // Local variables, used when we started a new face. - bool m_face_started; - bool m_started_face_is_colored; - std::vector points_of_face; - std::vector vertex_normals_for_face; - CGAL::IO::Color color_of_face; -}; - -#endif // CGAL_BASIC_VIEWER_H diff --git a/Linear_cell_complex/include/CGAL/Linear_cell_complex_base.h b/Linear_cell_complex/include/CGAL/Linear_cell_complex_base.h index 4669765f493..6681a4bb8b3 100644 --- a/Linear_cell_complex/include/CGAL/Linear_cell_complex_base.h +++ b/Linear_cell_complex/include/CGAL/Linear_cell_complex_base.h @@ -806,7 +806,7 @@ namespace CGAL { /** Compute the dual of a Linear_cell_complex. * @param alcc the lcc in which we build the dual of this lcc. - * @param adart a dart of the initial lcc, nullptr by default. + * @param adart a dart of the initial lcc, `nullptr` by default. * @return adart of the dual lcc, the dual of adart if adart!=nullptr, * any dart otherwise. * As soon as we don't modify this lcc and alcc lcc, we can iterate diff --git a/Linear_cell_complex/include/CGAL/draw_linear_cell_complex.h b/Linear_cell_complex/include/CGAL/draw_linear_cell_complex.h index 84fc7564bf2..9034d419963 100644 --- a/Linear_cell_complex/include/CGAL/draw_linear_cell_complex.h +++ b/Linear_cell_complex/include/CGAL/draw_linear_cell_complex.h @@ -8,135 +8,45 @@ // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_LCC_H #define CGAL_DRAW_LCC_H -#include - -#ifdef CGAL_USE_BASIC_VIEWER -#include - +#include +#include +#include +#include #include #include -namespace CGAL +namespace CGAL { + +namespace draw_function_for_lcc { -// Default color functor; user can change it to have its own face color -struct DefaultDrawingFunctorLCC -{ - /// @return true iff the volume containing dh is drawn. - template - bool draw_volume(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the face containing dh is drawn. - template - bool draw_face(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the edge containing dh is drawn. - template - bool draw_edge(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the vertex containing dh is drawn. - template - bool draw_vertex(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } +typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; +typedef Local_kernel::Point_3 Local_point; +typedef Local_kernel::Vector_3 Local_vector; - /// @return true iff the volume containing dh is drawn in wireframe. - template - bool volume_wireframe(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - /// @return true iff the face containing dh is drawn in wireframe. - template - bool face_wireframe(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - - /// @return true iff the volume containing dh is colored. - template - bool colored_volume(const LCC&, - typename LCC::Dart_const_descriptor) const - { return true; } - /// @return true iff the face containing dh is colored. - /// if we have also colored_volume(alcc, dh), the volume color is - /// ignored and only the face color is considered. - template - bool colored_face(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - /// @return true iff the edge containing dh is colored. - template - bool colored_edge(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - /// @return true iff the vertex containing dh is colored. - template - bool colored_vertex(const LCC&, - typename LCC::Dart_const_descriptor) const - { return false; } - - /// @return the color of the volume containing dh - /// used only if colored_volume(alcc, dh) and !colored_face(alcc, dh) - template - CGAL::IO::Color volume_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { - CGAL::Random random((unsigned int)(alcc.darts().index(dh))); - return get_random_color(random); - } - /// @return the color of the face containing dh - /// used only if colored_face(alcc, dh) - template - CGAL::IO::Color face_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { - CGAL::Random random((unsigned int)(alcc.darts().index(dh))); - return get_random_color(random); - } - /// @return the color of the edge containing dh - /// used only if colored_edge(alcc, dh) - template - CGAL::IO::Color edge_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { - CGAL::Random random((unsigned int)(alcc.darts().index(dh))); - return get_random_color(random); - } - /// @return the color of the vertex containing dh - /// used only if colored_vertex(alcc, dh) - template - CGAL::IO::Color vertex_color(const LCC& alcc, - typename LCC::Dart_const_descriptor dh) const - { - CGAL::Random random((unsigned int)(alcc.darts().index(dh))); - return get_random_color(random); - } -}; - -template +template struct LCC_geom_utils; -template +template struct LCC_geom_utils { static typename Local_kernel::Vector_3 get_vertex_normal(const LCC& lcc, typename LCC::Dart_const_descriptor dh) { - typename Local_kernel::Vector_3 n = internal::Geom_utils - :: - get_local_vector(CGAL::compute_normal_of_cell_0(lcc,dh)); - n = n/(CGAL::sqrt(n*n)); + typename Local_kernel::Vector_3 n = + internal::Geom_utils:: + get_local_vector(CGAL::compute_normal_of_cell_0(lcc, dh)); + n = n / (CGAL::sqrt(n * n)); return n; } }; -template +template struct LCC_geom_utils { static typename Local_kernel::Vector_3 @@ -147,181 +57,161 @@ struct LCC_geom_utils } }; -// Viewer class for LCC -template -class SimpleLCCViewerQt : public Basic_viewer_qt +template +void compute_face(const LCC& lcc, + typename LCC::Dart_const_handle dh, + typename LCC::Dart_const_handle voldh, + CGAL::Graphics_scene& graphics_scene, + const GSOptionsLCC& gso) { - typedef Basic_viewer_qt Base; - typedef typename LCC::Dart_const_descriptor Dart_const_descriptor; - typedef typename LCC::Traits Kernel; - typedef typename Kernel::Point Point; - typedef typename Kernel::Vector Vector; + if(!gso.are_faces_enabled() || !gso.draw_face(lcc, dh)) + { return; } -public: - /// Construct the viewer. - /// @param alcc the lcc to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big object where this time could be long) - SimpleLCCViewerQt(QWidget* parent, - const LCC* alcc=nullptr, - const char* title="Basic LCC Viewer", - bool anofaces=false, - const DrawingFunctorLCC& drawing_functor=DrawingFunctorLCC()) : - // First draw: vertices; edges, faces; multi-color; inverse normal - Base(parent, title, true, true, true, false, false), - lcc(alcc), - m_nofaces(anofaces), - m_random_face_color(false), - m_drawing_functor(drawing_functor) + // We fill only closed faces. + typename LCC::Dart_const_handle cur=dh; + do { - if (lcc!=nullptr) - { compute_elements(); } + if (!lcc.is_next_exist(cur)) + { return; } // open face=>not filled + cur = lcc.next(cur); } + while (cur!=dh); - ~SimpleLCCViewerQt() - {} + if (gso.colored_volume(lcc, voldh)) + { graphics_scene.face_begin(gso.volume_color(lcc, voldh)); } + else if (gso.colored_face(lcc, dh)) + { graphics_scene.face_begin(gso.face_color(lcc, dh)); } + else + { graphics_scene.face_begin(); } -protected: - void set_lcc(const LCC* alcc, bool doredraw=true) + cur=dh; + do { - if (lcc==alcc) - { return; } - - lcc=alcc; - if (lcc!=nullptr) - { compute_elements(); } - - if (doredraw) { redraw(); } + graphics_scene.add_point_in_face + (lcc.point(cur), + LCC_geom_utils::get_vertex_normal(lcc, cur)); + cur=lcc.next(cur); } + while (cur!=dh); - void compute_face(Dart_const_descriptor dh, Dart_const_descriptor voldh) + graphics_scene.face_end(); +} + +template +void compute_edge(const LCC& lcc, + typename LCC::Dart_const_handle dh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ + if(!gso.are_edges_enabled() || !gso.draw_edge(lcc, dh)) + { return; } + + const typename LCC::Point& p1=lcc.point(dh); + typename LCC::Dart_const_handle d2=lcc.other_extremity(dh); + if (d2!=LCC::null_descriptor) { - if (m_nofaces || !m_drawing_functor.draw_face(*lcc, dh)) return; - - // We fill only closed faces. - Dart_const_descriptor cur=dh; - Dart_const_descriptor min=dh; - do + if (gso.colored_edge(lcc, dh)) { - if (!lcc->is_next_exist(cur)) return; // open face=>not filled - if (curnext(cur); - } - while(cur!=dh); - - if (m_random_face_color) - { - CGAL::Random random((unsigned int)(lcc->darts().index(dh))); - CGAL::IO::Color c=get_random_color(random); - face_begin(c); - } - else if (m_drawing_functor.colored_face(*lcc, dh)) - { - CGAL::IO::Color c=m_drawing_functor.face_color(*lcc, dh); - face_begin(c); - } - else if (m_drawing_functor.colored_volume(*lcc, voldh)) - { - CGAL::IO::Color c=m_drawing_functor.volume_color(*lcc, voldh); - face_begin(c); + graphics_scene.add_segment(p1, lcc.point(d2), + gso.edge_color(lcc, dh)); } else - { face_begin(); } - - cur=dh; - do - { - add_point_in_face(lcc->point(cur), LCC_geom_utils:: - get_vertex_normal(*lcc, cur)); - cur=lcc->next(cur); - } - while(cur!=dh); - - face_end(); + { graphics_scene.add_segment(p1, lcc.point(d2)); } } +} - void compute_edge(Dart_const_descriptor dh) +template +void compute_vertex(const LCC& lcc, + typename LCC::Dart_const_handle dh, + CGAL::Graphics_scene& graphics_scene, + const GSOptionsLCC& gso) +{ + if (!gso.are_vertices_enabled() || !gso.draw_vertex(lcc, dh)) + { return; } + + if (gso.colored_vertex(lcc, dh)) { - if (!m_drawing_functor.draw_edge(*lcc, dh)) return; - - Point p1 = lcc->point(dh); - Dart_const_descriptor d2 = lcc->other_extremity(dh); - if (d2!=lcc->null_descriptor) - { - if (m_drawing_functor.colored_edge(*lcc, dh)) - { add_segment(p1, lcc->point(d2), m_drawing_functor.edge_color(*lcc, dh)); } - else - { add_segment(p1, lcc->point(d2)); } - } + graphics_scene.add_point(lcc.point(dh), + gso.vertex_color(lcc, dh)); } + else + { graphics_scene.add_point(lcc.point(dh)); } +} - void compute_vertex(Dart_const_descriptor dh) +template +struct Test_opposite_draw_lcc +{ + template + static bool run(const LCC& lcc, const GSOptions& gso, + typename LCC::Dart_const_descriptor dh) + { return (!lcc.template is_free<3>(dh) && + !gso.volume_wireframe(lcc, lcc.template opposite<3>(dh))); } +}; + +template +struct Test_opposite_draw_lcc +{ + template + static bool run(const LCC&, const GSOptions&, + typename LCC::Dart_const_descriptor) + { return true; } +}; + + +template +void compute_elements(const LCC& lcc, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ + typename LCC::size_type markvolumes = lcc.get_new_mark(); + typename LCC::size_type markfaces = lcc.get_new_mark(); + typename LCC::size_type markedges = lcc.get_new_mark(); + typename LCC::size_type markvertices = lcc.get_new_mark(); + typename LCC::size_type oriented_mark = lcc.get_new_mark(); + + lcc.orient(oriented_mark); + + for(typename LCC::Dart_range::const_iterator it=lcc.darts().begin(), + itend=lcc.darts().end(); it!=itend; ++it) { - if (!m_drawing_functor.draw_vertex(*lcc, dh)) return; - - if (m_drawing_functor.colored_vertex(*lcc, dh)) - { add_point(lcc->point(dh), m_drawing_functor.vertex_color(*lcc, dh)); } - else - { add_point(lcc->point(dh)); } - } - - void compute_elements() - { - clear(); - if (lcc==nullptr) return; - - typename LCC::size_type markvolumes =lcc->get_new_mark(); - typename LCC::size_type markfaces =lcc->get_new_mark(); - typename LCC::size_type markedges =lcc->get_new_mark(); - typename LCC::size_type markvertices =lcc->get_new_mark(); - typename LCC::size_type oriented_mark=lcc->get_new_mark(); - - lcc->orient(oriented_mark); - - for (typename LCC::Dart_range::const_iterator it=lcc->darts().begin(), - itend=lcc->darts().end(); it!=itend; ++it ) + if (!lcc.is_marked(it, markvolumes) && + gso.draw_volume(lcc, it)) { - if (!lcc->is_marked(it, markvolumes) && - m_drawing_functor.draw_volume(*lcc, it)) + for(typename LCC::template Dart_of_cell_basic_range<3>::const_iterator + itv=lcc.template darts_of_cell_basic<3>(it, markvolumes).begin(), + itvend=lcc.template darts_of_cell_basic<3>(it, markvolumes).end(); + itv!=itvend; ++itv) { - for (typename LCC::template Dart_of_cell_basic_range<3>:: - const_iterator itv=lcc->template darts_of_cell_basic<3>(it, markvolumes).begin(), - itvend=lcc->template darts_of_cell_basic<3>(it, markvolumes).end(); - itv!=itvend; ++itv) + lcc.mark(itv, markvolumes); + if (!lcc.is_marked(itv, markfaces) && + lcc.is_marked(itv, oriented_mark) && + gso.draw_face(lcc, itv)) { - lcc->mark(itv, markvolumes); // To be sure that all darts of the basic iterator will be marked - if (!lcc->is_marked(itv, markfaces) && - lcc->is_marked(itv, oriented_mark) && - m_drawing_functor.draw_face(*lcc, itv)) + if ((!gso.volume_wireframe(lcc, itv) || + Test_opposite_draw_lcc::run(lcc, gso, itv)) && + !gso.face_wireframe(lcc, itv)) + { compute_face(lcc, itv, it, graphics_scene, gso); } + for(typename LCC::template Dart_of_cell_basic_range<2>::const_iterator + itf=lcc.template darts_of_cell_basic<2>(itv, markfaces).begin(), + itfend=lcc.template darts_of_cell_basic<2>(itv, markfaces).end(); + itf!=itfend; ++itf) { - if (!m_drawing_functor.volume_wireframe(*lcc, itv) && - !m_drawing_functor.face_wireframe(*lcc, itv)) - { compute_face(itv, it); } - for (typename LCC::template Dart_of_cell_basic_range<2>:: - const_iterator itf=lcc->template darts_of_cell_basic<2>(itv, markfaces).begin(), - itfend=lcc->template darts_of_cell_basic<2>(itv, markfaces).end(); - itf!=itfend; ++itf) + lcc.mark(itf, markfaces); + if (!lcc.is_marked(itf, markedges) && + gso.draw_edge(lcc, itf)) { - if (!m_drawing_functor.volume_wireframe(*lcc, itv) && - !m_drawing_functor.face_wireframe(*lcc, itv)) - { lcc->mark(itf, markfaces); } // To be sure that all darts of the basic iterator will be marked - if ( !lcc->is_marked(itf, markedges) && - m_drawing_functor.draw_edge(*lcc, itf)) + compute_edge(lcc, itf, graphics_scene, gso); + for(typename LCC::template Dart_of_cell_basic_range<1>::const_iterator + ite=lcc.template darts_of_cell_basic<1>(itf, markedges).begin(), + iteend=lcc.template darts_of_cell_basic<1>(itf, markedges).end(); + ite!=iteend; ++ite) { - compute_edge(itf); - for (typename LCC::template Dart_of_cell_basic_range<1>:: - const_iterator ite=lcc->template darts_of_cell_basic<1>(itf, markedges).begin(), - iteend=lcc->template darts_of_cell_basic<1>(itf, markedges).end(); - ite!=iteend; ++ite) + lcc.mark(ite, markedges); + if (!lcc.is_marked(ite, markvertices) && + gso.draw_vertex(lcc, ite)) { - lcc->mark(ite, markedges); // To be sure that all darts of the basic iterator will be marked - if ( !lcc->is_marked(ite, markvertices) && - m_drawing_functor.draw_vertex(*lcc, ite)) - { - compute_vertex(ite); - CGAL::mark_cell(*lcc, ite, markvertices); - } + compute_vertex(lcc, ite, graphics_scene, gso); + CGAL::mark_cell(lcc, ite, markvertices); } } } @@ -329,99 +219,108 @@ protected: } } } - - for (typename LCC::Dart_range::const_iterator it=lcc->darts().begin(), - itend=lcc->darts().end(); it!=itend; ++it ) - { - lcc->unmark(it, markvertices); - lcc->unmark(it, markedges); - lcc->unmark(it, markfaces); - lcc->unmark(it, markvolumes); - lcc->unmark(it, oriented_mark); - } - - lcc->free_mark(markvolumes); - lcc->free_mark(markfaces); - lcc->free_mark(markedges); - lcc->free_mark(markvertices); - lcc->free_mark(oriented_mark); } - virtual void init() + for (typename LCC::Dart_range::const_iterator it = lcc.darts().begin(), + itend = lcc.darts().end(); it != itend; ++it) { - Base::init(); - setKeyDescription(::Qt::Key_R, "Toggles random face colors"); + lcc.unmark(it, markvertices); + lcc.unmark(it, markedges); + lcc.unmark(it, markfaces); + lcc.unmark(it, markvolumes); + lcc.unmark(it, oriented_mark); } - virtual void keyPressEvent(QKeyEvent *e) - { - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - if ((e->key()==::Qt::Key_R) && (modifiers==::Qt::NoButton)) - { - m_random_face_color=!m_random_face_color; - displayMessage(QString("Random face color=%1.").arg(m_random_face_color?"true":"false")); - compute_elements(); - redraw(); - } - else - { Base::keyPressEvent(e); } // Call the base method to process others/classicals key - - // Call: * compute_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 - } - -protected: - const LCC* lcc; - bool m_nofaces; - bool m_random_face_color; - const DrawingFunctorLCC& m_drawing_functor; -}; - -// Specialization of draw function. -#define CGAL_LCC_TYPE CGAL::Linear_cell_complex_base \ - - -template < unsigned int d_, unsigned int ambient_dim, - class Traits_, - class Items_, - class Alloc_, - template - class Map, - class Refs, - class Storage_, - class DrawingFunctorLCC=DefaultDrawingFunctorLCC> -void draw(const CGAL_LCC_TYPE& alcc, - const char* title="LCC for CMap Basic Viewer", - bool nofill=false, - const DrawingFunctorLCC& drawing_functor=DrawingFunctorLCC()) -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"lccviewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimpleLCCViewerQt - mainwindow(app.activeWindow(), &alcc, title, nofill, drawing_functor); - mainwindow.show(); - app.exec(); - } + lcc.free_mark(markvolumes); + lcc.free_mark(markfaces); + lcc.free_mark(markedges); + lcc.free_mark(markvertices); + lcc.free_mark(oriented_mark); } -// Todo a function taking a const DrawingFunctorLCC& drawing_functor as parameter +} // namespace draw_function_for_lcc + +#define CGAL_LCC_TYPE \ + CGAL::Linear_cell_complex_base + +// add_to_graphics_scene: to add a LCC in the given graphic buffer, with a +// graphics scene options. +template class Map, + class Refs, class Storage_, + class GSOptions> +void add_to_graphics_scene(const CGAL_LCC_TYPE& alcc, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ + draw_function_for_lcc::compute_elements(static_cast(alcc), + graphics_scene, gso); +} + +// add_to_graphics_scene: to add a LCC in the given graphic buffer, without a +// graphics scene options. Use default drawing values. +template class Map, + class Refs, class Storage_> +void add_to_graphics_scene(const CGAL_LCC_TYPE& alcc, + CGAL::Graphics_scene& graphics_scene) +{ + CGAL::Graphics_scene_options + gso; + + gso.colored_volume = [](const CGAL_LCC_TYPE&, + typename CGAL_LCC_TYPE::Dart_const_handle) -> bool + { return true; }; + + gso.volume_color = [] (const CGAL_LCC_TYPE& alcc, + typename CGAL_LCC_TYPE::Dart_const_handle dh) -> CGAL::IO::Color + { + CGAL::Random random((unsigned int)(alcc.darts().index(dh))); + return get_random_color(random); + }; + + add_to_graphics_scene(alcc, graphics_scene, gso); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function for a LCC, with a drawing graphics scene options. +template class Map, + class Refs, class Storage_, + class GSOptions> +void draw(const CGAL_LCC_TYPE& alcc, const GSOptions& gso, + const char *title="LCC Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(alcc, buffer, gso); + draw_graphics_scene(buffer, title); +} + +// Specialization of draw function for a LCC, without a graphics scene options. +template class Map, + class Refs, class Storage_> +void draw(const CGAL_LCC_TYPE& alcc, const char *title="LCC Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(alcc, buffer); + draw_graphics_scene(buffer, title); +} + +#endif // CGAL_USE_BASIC_VIEWER + #undef CGAL_LCC_TYPE } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_LCC_H diff --git a/Linear_cell_complex/package_info/Linear_cell_complex/dependencies b/Linear_cell_complex/package_info/Linear_cell_complex/dependencies index 53e5ce3f153..817476384ef 100644 --- a/Linear_cell_complex/package_info/Linear_cell_complex/dependencies +++ b/Linear_cell_complex/package_info/Linear_cell_complex/dependencies @@ -1,6 +1,8 @@ Algebraic_foundations Arithmetic_kernel BGL +Basic_viewer +CGAL_Core Cartesian_kernel Circulator Combinatorial_map @@ -8,7 +10,6 @@ Distance_2 Distance_3 Filtered_kernel Generalized_map -GraphicsView HalfedgeDS Hash_map Homogeneous_kernel @@ -21,8 +22,12 @@ Kernel_d Linear_cell_complex Modular_arithmetic Number_types +Polygon Profiling_tools Property_map Random_numbers STL_Extension +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 diff --git a/Linear_cell_complex/test/Linear_cell_complex/Linear_cell_complex_2_test.h b/Linear_cell_complex/test/Linear_cell_complex/Linear_cell_complex_2_test.h index b76bbf1d7e6..f22bd0a480a 100644 --- a/Linear_cell_complex/test/Linear_cell_complex/Linear_cell_complex_2_test.h +++ b/Linear_cell_complex/test/Linear_cell_complex/Linear_cell_complex_2_test.h @@ -17,6 +17,7 @@ #include #include #include +#include #include // #define LCC_TRACE_TEST_BEGIN 1 diff --git a/Mesh_2/examples/Mesh_2/mesh_marked_domain.cpp b/Mesh_2/examples/Mesh_2/mesh_marked_domain.cpp index 5313871f527..9649f2076e5 100644 --- a/Mesh_2/examples/Mesh_2/mesh_marked_domain.cpp +++ b/Mesh_2/examples/Mesh_2/mesh_marked_domain.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include diff --git a/Mesh_2/examples/Mesh_2/mesh_with_seeds.cpp b/Mesh_2/examples/Mesh_2/mesh_with_seeds.cpp index 2b30e792014..79a52e9034c 100644 --- a/Mesh_2/examples/Mesh_2/mesh_with_seeds.cpp +++ b/Mesh_2/examples/Mesh_2/mesh_with_seeds.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include diff --git a/Mesh_2/include/CGAL/Mesh_2/Mesh_global_optimizer_2.h b/Mesh_2/include/CGAL/Mesh_2/Mesh_global_optimizer_2.h index 380696549c1..d2df50df3fa 100644 --- a/Mesh_2/include/CGAL/Mesh_2/Mesh_global_optimizer_2.h +++ b/Mesh_2/include/CGAL/Mesh_2/Mesh_global_optimizer_2.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include 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..4be8408d049 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 @@ -28,7 +29,6 @@ Polygon Polyhedron Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Stream_support diff --git a/Nef_3/doc/Nef_3/CGAL/draw_nef_3.h b/Nef_3/doc/Nef_3/CGAL/draw_nef_3.h index d61f502cc4f..1d2528d6079 100644 --- a/Nef_3/doc/Nef_3/CGAL/draw_nef_3.h +++ b/Nef_3/doc/Nef_3/CGAL/draw_nef_3.h @@ -3,14 +3,72 @@ namespace CGAL { /*! \ingroup PkgDrawNef3 -Open a new window and draws `anef3`, the `Nef_polyhedron_3`. 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 Nef3 a model of the `Nef_polyhedron_3` concept. -\param anef3 the nef polyhedron to draw. +opens a new window and draws a nef polyhedron. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 NP3 which must be an instanciation of a `CGAL::Nef_polyhedron_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param np3 the nef polyhedron to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Nef_polyhedron_3& np3, const GSOptions& gso); + +\cgalAdvancedEnd */ -template -void draw(const Nef3& anef3); + template + void draw(const NP3& np3, const GSOptions& gso); + +/*! +\ingroup PkgDrawNef3 + +A shortcut to `CGAL::draw(np3, Graphics_scene_options{})`. +*/ + template + void draw(const NP3& np3); + +/*! +\ingroup PkgDrawNef3 + +adds the vertices, edges and faces of `np3` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam NP3 which must be an instanciation of a `CGAL::Nef_polyhedron_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param np3 the nef polyhedron to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Nef_polyhedron_3& np3, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const NP3& np3, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawNef3 + +A shortcut to `CGAL::add_to_graphics_scene(np3, gs, Graphics_scene_options{})`. +*/ +template +void add_to_graphics_scene(const NP3& np3, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Nef_3/include/CGAL/Nef_3/polygon_mesh_to_nef_3.h b/Nef_3/include/CGAL/Nef_3/polygon_mesh_to_nef_3.h index a20a6643f50..d222d741100 100644 --- a/Nef_3/include/CGAL/Nef_3/polygon_mesh_to_nef_3.h +++ b/Nef_3/include/CGAL/Nef_3/polygon_mesh_to_nef_3.h @@ -27,6 +27,7 @@ #include #include #include +#include #undef CGAL_NEF_DEBUG #define CGAL_NEF_DEBUG 29 diff --git a/Nef_3/include/CGAL/draw_nef_3.h b/Nef_3/include/CGAL/draw_nef_3.h index e561b3fef70..aac531bcd5d 100644 --- a/Nef_3/include/CGAL/draw_nef_3.h +++ b/Nef_3/include/CGAL/draw_nef_3.h @@ -9,16 +9,16 @@ // // // Author(s) : Jasmeet Singh +// Mostafa Ashraf #ifndef DRAW_NEF_3_H #define DRAW_NEF_3_H #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include +#include +#include +#include +#include #include #include #include @@ -28,274 +28,283 @@ namespace CGAL { -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorNefPolyhedron +namespace draw_function_for_nef_polyhedron { - template - static CGAL::IO::Color run(const NefPolyhedron&, - typename NefPolyhedron::Halffacet_const_handle fh) + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; +typedef Local_kernel::Vector_3 Local_vector; + +template +Local_vector get_face_normal(typename Nef_Polyhedron::SHalfedge_const_handle she) +{ + typename Nef_Polyhedron::SHalfedge_around_facet_const_circulator he(she), end(he); + Local_vector normal=CGAL::NULL_VECTOR; + unsigned int nb=0; + + using GU=internal::Geom_utils::Kernel, + Local_kernel>; + CGAL_For_all(he, end) { - if (fh == nullptr) // use to get the mono color - return CGAL::IO::Color(100, 125, 200); // R G B between 0-255 - - CGAL::Random random((unsigned int)(std::size_t)(&(*fh))); - return get_random_color(random); + internal::newell_single_step_3(GU::get_local_point(he->next()->source()-> + center_vertex()->point()), + GU::get_local_point(he->source()->center_vertex()-> + point()), normal); + ++nb; } -}; -// Viewer class for Nef Polyhedron -template -class SimpleNefPolyhedronViewerQt : public Basic_viewer_qt + CGAL_assertion(nb > 0); + return (typename Local_kernel::Construct_scaled_vector_3()(normal, 1.0/nb)); +} + +template +Local_vector get_vertex_normal(typename Nef_Polyhedron::Vertex_const_handle vh) { - typedef Basic_viewer_qt Base; - typedef typename Nef_Polyhedron::Kernel Kernel; + typename Nef_Polyhedron::SHalfedge_const_iterator it=vh->shalfedges_begin(); + typename Nef_Polyhedron::SHalfedge_const_handle end=it; + Local_vector normal=CGAL::NULL_VECTOR; + do + { + Local_vector n=get_face_normal(it); + normal=typename Local_kernel::Construct_sum_of_vectors_3()(normal, n); + it=it->snext(); + } + while(it!=end); - typedef typename Nef_Polyhedron::Halffacet_cycle_const_iterator Halffacet_cycle_const_iterator; - typedef typename Nef_Polyhedron::SHalfedge_around_facet_const_circulator SHalfedge_around_facet_const_circulator; + if (!typename Local_kernel::Equal_3()(normal, CGAL::NULL_VECTOR)) + { + normal=(typename Local_kernel::Construct_scaled_vector_3() + (normal, 1.0/CGAL::sqrt(normal.squared_length()))); + } - typedef typename Nef_Polyhedron::Shell_entry_const_iterator Shell_entry_const_iterator; - typedef typename Nef_Polyhedron::SHalfedge_const_iterator SHalfedge_const_iterator; - typedef typename Nef_Polyhedron::Volume_const_iterator Volume_const_iterator; + return normal; +} + +// Visitor class to iterate through shell objects +template +class Nef_Visitor { - typedef typename Nef_Polyhedron::Vertex_const_handle Vertex_const_handle; - typedef typename Nef_Polyhedron::SFace_const_handle SFace_const_handle; typedef typename Nef_Polyhedron::Halfedge_const_handle Halfedge_const_handle; typedef typename Nef_Polyhedron::Halffacet_const_handle Halffacet_const_handle; typedef typename Nef_Polyhedron::SHalfedge_const_handle SHalfedge_const_handle; + typedef typename Nef_Polyhedron::SHalfedge_around_facet_const_circulator SHalfedge_around_facet_const_circulator; + typedef typename Nef_Polyhedron::Vertex_const_handle Vertex_const_handle; + typedef typename Nef_Polyhedron::SFace_const_handle SFace_const_handle; typedef typename Nef_Polyhedron::SHalfloop_const_handle SHalfloop_const_handle; + typedef typename Nef_Polyhedron::Halffacet_cycle_const_iterator Halffacet_cycle_const_iterator; public: - /// Construct the viewer - /// @param anef the nef polyhedron to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not - /// computed: this can be useful for big objects) - SimpleNefPolyhedronViewerQt(QWidget* parent, - const Nef_Polyhedron& anef, - const char* title="Basic Nef Polyhedron Viewer", - bool anofaces=false, - const ColorFunctor& fcolor=ColorFunctor()) : - //First draw: vertex; edges, faces; mon-color; inverse normal - Base(parent, title, false, true, true, true, false), - nef(anef), - m_nofaces(anofaces), - m_fcolor(fcolor) - { - compute_elements(); - } -protected: - // Visitor class to iterate through shell objects - class Nef_Visitor { - public: - Nef_Visitor(SimpleNefPolyhedronViewerQt &v) - : n_faces(0), n_edges(0), viewer(v) {} + Nef_Visitor(const Nef_Polyhedron&_nef, + CGAL::Graphics_scene& _graphics_scene, + const GSOptions&_gs_options) : + n_faces(0), n_edges(0), + graphics_scene(_graphics_scene), + gs_options(_gs_options), + nef(_nef) + {} - void visit(Vertex_const_handle vh) { - viewer.add_point(vh->point()); + void visit(Vertex_const_handle vh) + { + if (!gs_options.are_vertices_enabled() || + !gs_options.draw_vertex(nef, vh)) + { return; } + + if(gs_options.colored_vertex(nef, vh)) + { graphics_scene.add_point(vh->point(), + gs_options.vertex_color(nef, vh)); } + else + { graphics_scene.add_point(vh->point()); } + } + + void visit(Halffacet_const_handle opposite_facet) + { + if (!gs_options.are_faces_enabled() || + !gs_options.draw_face(nef, opposite_facet)) + { return; } + + Halffacet_const_handle f=opposite_facet->twin(); + if (facets_done.find(f)!=facets_done.end() || + facets_done.find(opposite_facet)!=facets_done.end()) + { return; } + + SHalfedge_const_handle se; + Halffacet_cycle_const_iterator fc = f->facet_cycles_begin(); + + se = SHalfedge_const_handle(fc); // non-zero if shalfedge is returned + if(se==0) + { return; } //return if not-shalfedge + + if(gs_options.colored_face(nef, f)) + { graphics_scene.face_begin(gs_options.face_color(nef, f)); } + else + { graphics_scene.face_begin(); } + + SHalfedge_around_facet_const_circulator hc_start(se); + SHalfedge_around_facet_const_circulator hc_end(hc_start); + Vertex_const_handle lastvh; + CGAL_For_all(hc_start, hc_end) + { + Vertex_const_handle vh=hc_start->source()->center_vertex(); + lastvh=vh; + graphics_scene.add_point_in_face(vh->point(), + get_vertex_normal(vh)); } - void visit(Halffacet_const_handle opposite_facet) + // Now iterate through holes of the face + ++fc; + while(fc!=f->facet_cycles_end()) { - Halffacet_const_handle f = opposite_facet->twin(); - - if (facets_done.find(f) != facets_done.end() || - facets_done.find(opposite_facet) != facets_done.end()) { - return; - } - - - - SHalfedge_const_handle se; - Halffacet_cycle_const_iterator fc=f->facet_cycles_begin(); - - se = SHalfedge_const_handle(fc); // non-zero if shalfedge is returned - if(se == 0) - { //return if not-shalfedge - return; - } - - CGAL::IO::Color c = viewer.run_color(f); - viewer.face_begin(c); - - SHalfedge_around_facet_const_circulator hc_start(se); - SHalfedge_around_facet_const_circulator hc_end(hc_start); - Vertex_const_handle lastvh; - CGAL_For_all(hc_start, hc_end) { - Vertex_const_handle vh=hc_start->source()->center_vertex(); - lastvh=vh; - viewer.add_point_in_face(vh->point(), - viewer.get_vertex_normal(vh)); - } - - // Now iterate through holes of the face - ++fc; - while(fc!=f->facet_cycles_end()) + if(fc.is_shalfedge()) { - if(fc.is_shalfedge()) + se = SHalfedge_const_handle(fc); + hc_start=se; + hc_end=hc_start; + CGAL_For_all(hc_start, hc_end) { - se = SHalfedge_const_handle(fc); - hc_start=se; - hc_end=hc_start; - CGAL_For_all(hc_start, hc_end) { - Vertex_const_handle vh=hc_start->source()->center_vertex(); - viewer.add_point_in_face(vh->point(), - viewer.get_vertex_normal(vh)); - } - viewer.add_point_in_face(hc_start->source()->center_vertex()->point(), - viewer.get_vertex_normal(hc_start->source()->center_vertex())); - viewer.add_point_in_face(lastvh->point(), - viewer.get_vertex_normal(lastvh)); + Vertex_const_handle vh=hc_start->source()->center_vertex(); + graphics_scene.add_point_in_face(vh->point(), + get_vertex_normal(vh)); } - ++fc; + graphics_scene.add_point_in_face(hc_start->source()->center_vertex()->point(), + get_vertex_normal + (hc_start->source()->center_vertex())); + graphics_scene.add_point_in_face(lastvh->point(), + get_vertex_normal(lastvh)); } - viewer.face_end(); - facets_done[f]=true; - n_faces++; + ++fc; } - void visit(Halfedge_const_handle he) - { - Halfedge_const_handle twin = he->twin(); - if (edges_done.find(he) != edges_done.end() || - edges_done.find(twin) != edges_done.end()) - { - // Edge already added - return; - } - - viewer.add_segment(he->source()->point(), he->target()->point()); - edges_done[he] = true; - n_edges++; - } - - void visit(SHalfedge_const_handle ) {} - void visit(SHalfloop_const_handle ) {} - void visit(SFace_const_handle ) {} - int n_faces; - int n_edges; - protected: - std::unordered_map facets_done; - std::unordered_map edges_done; - SimpleNefPolyhedronViewerQt& viewer; - }; - - void compute_elements() - { - clear(); - - Volume_const_iterator c; - - Nef_Visitor V(*this); - CGAL_forall_volumes(c, nef) - { - Shell_entry_const_iterator it; - CGAL_forall_shells_of(it, c) - { - nef.visit_shell_objects(SFace_const_handle(it), V); - } - } - - negate_all_normals(); + graphics_scene.face_end(); + facets_done[f]=true; + ++n_faces; } - CGAL::IO::Color run_color(Halffacet_const_handle fh) + void visit(Halfedge_const_handle he) { - return m_fcolor.run(nef, fh); - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); + if (!gs_options.are_edges_enabled() || + !gs_options.draw_edge(nef, he)) + { return; } + + Halfedge_const_handle twin=he->twin(); + if (edges_done.find(he)!=edges_done.end() || + edges_done.find(twin)!=edges_done.end()) + { return; } // Edge already added + + if(gs_options.colored_edge(nef, he)) + { graphics_scene.add_segment(he->source()->point(), he->target()->point(), + gs_options.edge_color(nef, he)); } + else + { graphics_scene.add_segment(he->source()->point(), he->target()->point()); } + edges_done[he]=true; + ++n_edges; } + void visit(SHalfedge_const_handle ) {} + void visit(SHalfloop_const_handle ) {} + void visit(SFace_const_handle ) {} + int n_faces; + int n_edges; protected: - Local_vector get_face_normal(SHalfedge_const_handle she) - { - SHalfedge_around_facet_const_circulator he(she); - Local_vector normal = CGAL::NULL_VECTOR; - SHalfedge_around_facet_const_circulator end = he; - unsigned int nb = 0; - - CGAL_For_all(he, end) - { - internal::newell_single_step_3(this->get_local_point - (he->next()->source()->center_vertex()->point()), - this->get_local_point(he->source()->center_vertex()-> - point()), normal); - ++nb; - } - - CGAL_assertion(nb > 0); - return (typename Local_kernel::Construct_scaled_vector_3()(normal, 1.0 / nb)); - } - - Local_vector get_vertex_normal(Vertex_const_handle vh) - { - Local_vector normal = CGAL::NULL_VECTOR; - - SHalfedge_const_iterator it = vh->shalfedges_begin(); - SHalfedge_const_handle end = it; - do { - Local_vector n = get_face_normal(it); - normal = typename Local_kernel::Construct_sum_of_vectors_3()(normal, n); - it = it->snext(); - } while( it != end ); - - if (!typename Local_kernel::Equal_3()(normal, CGAL::NULL_VECTOR)) - { - normal = (typename Local_kernel::Construct_scaled_vector_3()( - normal, 1.0 / CGAL::sqrt(normal.squared_length()))); - } - - return normal; - } - -protected: - const Nef_Polyhedron &nef; - bool m_nofaces; - const ColorFunctor &m_fcolor; + std::unordered_map facets_done; + std::unordered_map edges_done; + CGAL::Graphics_scene& graphics_scene; + const GSOptions& gs_options; + const Nef_Polyhedron& nef; }; +template +void compute_elements(const Nef_Polyhedron &nef, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) +{ + + typedef typename Nef_Polyhedron::Volume_const_iterator Volume_const_iterator; + typedef typename Nef_Polyhedron::Shell_entry_const_iterator Shell_entry_const_iterator; + typedef typename Nef_Polyhedron::SFace_const_handle SFace_const_handle; + + Volume_const_iterator c; + Nef_Visitor V(nef, graphics_scene, gs_options); + CGAL_forall_volumes(c, nef) + { + Shell_entry_const_iterator it; + CGAL_forall_shells_of(it, c) + { nef.visit_shell_objects(SFace_const_handle(it), V); } + } + + graphics_scene.reverse_all_normals(); +} + +} // namespace draw_function_for_nef_polyhedron + #define CGAL_NEF3_TYPE Nef_polyhedron_3 -template -void draw(const CGAL_NEF3_TYPE &anef, - const char *title = "Nef Polyhedron Viewer", - bool nofill = false) { -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite = true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc = 1; - const char *argv[2] = {"nef_polyhedron_viewer", nullptr}; - QApplication app(argc, const_cast(argv)); - DefaultColorFunctorNefPolyhedron fcolor; - SimpleNefPolyhedronViewerQt - mainwindow(app.activeWindow(), anef, title, nofill, fcolor); - mainwindow.show(); - app.exec(); - } +// add_to_graphics_scene +template +void add_to_graphics_scene(const CGAL_NEF3_TYPE &anef, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) +{ + draw_function_for_nef_polyhedron::compute_elements(anef, + graphics_scene, + gs_options); } -} // End namespace CGAL +template +void add_to_graphics_scene(const CGAL_NEF3_TYPE &anef, + CGAL::Graphics_scene &graphics_scene) +{ + // Default graphics view options. + Graphics_scene_options + gs_options; + + gs_options.colored_face=[](const CGAL_NEF3_TYPE&, + typename CGAL_NEF3_TYPE::Halffacet_const_handle) -> bool + { return true; }; + + gs_options.face_color=[](const CGAL_NEF3_TYPE&, + typename CGAL_NEF3_TYPE::Halffacet_const_handle fh) -> CGAL::IO::Color + { + if (fh==nullptr) // use to get the mono color + { return CGAL::IO::Color(100, 125, 200); } + + CGAL::Random random((unsigned int)(std::size_t)(&(*fh))); + return get_random_color(random); + }; + + add_to_graphics_scene(anef, graphics_scene, gs_options); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function +template +void draw(const CGAL_NEF3_TYPE &anef, + const GSOptions &gs_options, + const char *title="Nef Polyhedron Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(anef, buffer, gs_options); + draw_graphics_scene(buffer, title); +} + +template +void draw(const CGAL_NEF3_TYPE &anef, + const char *title="Nef Polyhedron Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(anef, buffer); + draw_graphics_scene(buffer, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_NEF3_TYPE + +} // End namespace CGAL + #endif // DRAW_NEF_3_H diff --git a/Nef_3/package_info/Nef_3/dependencies b/Nef_3/package_info/Nef_3/dependencies index 7fec88e2373..6950244ddcd 100644 --- a/Nef_3/package_info/Nef_3/dependencies +++ b/Nef_3/package_info/Nef_3/dependencies @@ -1,13 +1,14 @@ Algebraic_foundations Arithmetic_kernel BGL +Basic_viewer Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView HalfedgeDS Hash_map Homogeneous_kernel 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_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index ae0b603b1ed..efeb5ad9363 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -115,6 +115,14 @@ template <> class Algebraic_structure_traits< CORE::Expr > }; */ }; + class Is_zero + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& x ) const { + return x.isZero(); + } + }; + }; template <> class Real_embeddable_traits< CORE::Expr > 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..bfc1d066f41 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 @@ -315,6 +317,25 @@ template < typename ET > struct Exact_type_selector : Exact_field_selector< ET > {}; #endif +constexpr const char* exact_nt_backend_string() +{ + switch(Default_exact_nt_backend) + { + case GMP_BACKEND: + return "GMP_BACKEND"; + case GMPXX_BACKEND: + return "GMPXX_BACKEND"; + case BOOST_GMP_BACKEND: + return "BOOST_GMP_BACKEND"; + case BOOST_BACKEND: + return "BOOST_BACKEND"; + case LEDA_BACKEND: + return "LEDA_BACKEND"; + default: + return "MP_FLOAT_BACKEND"; + } +} + } } // namespace CGAL::internal #undef CGAL_EXACT_SELECTORS_SPECS 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..f8ff8698b72 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") @@ -65,15 +61,19 @@ create_single_source_cgal_program("unsigned.cpp") create_single_source_cgal_program("utilities.cpp") create_single_source_cgal_program("Exact_rational.cpp") create_single_source_cgal_program("Mpzf_new.cpp") +create_single_source_cgal_program("check_exact_backend.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/check_exact_backend.cpp b/Number_types/test/Number_types/check_exact_backend.cpp new file mode 100644 index 00000000000..6aff3bf2d0a --- /dev/null +++ b/Number_types/test/Number_types/check_exact_backend.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +#include + +using namespace CGAL::internal; + +int main() +{ + static_assert( +#ifdef CGAL_USE_GMP + ( Default_exact_nt_backend!=GMP_BACKEND || (std::is_same_v && std::is_same_v) ) && +#endif +#ifdef CGAL_USE_GMPXX + ( Default_exact_nt_backend!=GMPXX_BACKEND || (std::is_same_v && std::is_same_v) ) && +#endif +#if defined(CGAL_USE_BOOST_MP) && defined(CGAL_USE_GMP) + ( Default_exact_nt_backend!=BOOST_GMP_BACKEND || (std::is_same_v && std::is_same_v) ) && +#endif +#if defined(CGAL_USE_BOOST_MP) + ( Default_exact_nt_backend!=BOOST_BACKEND || (std::is_same_v && std::is_same_v) ) && +#endif +#if defined(CGAL_USE_LEDA) + ( Default_exact_nt_backend!=LEDA_BACKEND || (std::is_same_v && std::is_same_v) ) && +#endif + ( Default_exact_nt_backend!=MP_FLOAT_BACKEND || (std::is_same_v && std::is_same_v>) ) + ); + + std::cout << "Exact backend is " << exact_nt_backend_string() << "\n"; +#ifdef CGAL_USE_CORE +#ifdef CGAL_CORE_USE_GMP_BACKEND + std::cout << "CGAL_CORE_USE_GMP_BACKEND is defined, using gmp backend in BigInt and BigRat\n"; +#else + std::cout << "CGAL_CORE_USE_GMP_BACKEND is NOT defined, using boost-mp backend in BigInt and BigRat\n"; +#endif +#else + std::cout << "CGAL_USE_CORE is not defined\n"; +#endif +} 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/include/CGAL/Optimal_bounding_box/oriented_bounding_box.h b/Optimal_bounding_box/include/CGAL/Optimal_bounding_box/oriented_bounding_box.h index d64b73e699b..c867c190814 100644 --- a/Optimal_bounding_box/include/CGAL/Optimal_bounding_box/oriented_bounding_box.h +++ b/Optimal_bounding_box/include/CGAL/Optimal_bounding_box/oriented_bounding_box.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #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..7937afbe7fb --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits.h @@ -0,0 +1,59 @@ +// 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 + +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..2d1ade67e92 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -0,0 +1,165 @@ +// 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 + +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..f05416b91db --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -0,0 +1,199 @@ +// 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 + +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..fee49d8d44b 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,4 +1,5 @@ Algebraic_foundations +Cartesian_kernel Distance_2 Distance_3 Installation 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_2_triangulation_2/doc/Periodic_2_triangulation_2/CGAL/draw_periodic_2_triangulation_2.h b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/CGAL/draw_periodic_2_triangulation_2.h index cbad54f1f7a..fafd9a4f5f1 100644 --- a/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/CGAL/draw_periodic_2_triangulation_2.h +++ b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/CGAL/draw_periodic_2_triangulation_2.h @@ -3,14 +3,94 @@ namespace CGAL { /*! \ingroup PkgDrawPeriodic2Triangulation2 -opens a new window and draws `ap2t2`, the `Periodic_2_Triangulation_2`. 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 P2T2 a model of the `Periodic_2TriangulationTraits_2` concept. -\param ap2t2 the 2D periodic trinagulation to draw. +The class `Graphics_scene_options_periodic_2_triangulation_2` defines data and methods used to tune the way that the cells of a `Periodic_2_triangulation_2` are considered for drawing or to be added into a graphics scene. +This class is a model of `GraphicsSceneOptionsPeriodic2Triangulation2`. + +\tparam DS a `CGAL::Periodic_2_triangulation_2`. +\tparam VertexDescriptor a descriptor of vertices of `DS`. +\tparam EdgeDescriptor a descriptor of edges of `DS`. +\tparam FaceDescriptor a descriptor of faces of `DS`. + +\cgalModels{GraphicsSceneOptionsPeriodic2Triangulation2} +*/ + +template +struct Graphics_scene_options_periodic_2_triangulation_2: public CGAL::Graphics_scene_options +{}; + +/*! +\ingroup PkgDrawPeriodic2Triangulation2 + +opens a new window and draws a periodic 2D triangulation. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 P2T2 which must be an instanciation of a `CGAL::Periodic_2_triangulation_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsPeriodic2Triangulation2` concept. + +\param p2t2 the periodic triangulation to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Periodic_2_triangulation_2& p2t2, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const P2T2& p2t2, const GSOptions& gso); + +/*! +\ingroup PkgDrawPeriodic2Triangulation2 + +A shortcut to `CGAL::draw(p2t2, Graphics_scene_options_periodic_2_triangulation_2{})`. */ template -void draw(const P2T2& ap2t2); +void draw(const P2T2& p2t2); + +/*! +\ingroup PkgDrawPeriodic2Triangulation2 + +adds the vertices, edges and faces of `p2t2` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam P2T2 which must be an instanciation of a `CGAL::Periodic_2_triangulation_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsPeriodic2Triangulation2` concept. + +\param p2t2 the periodic triangulation to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Periodic_2_triangulation_2& p2t2, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const P2T2& p2t2, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPeriodic2Triangulation2 + +A shortcut to `CGAL::add_to_graphics_scene(p2t2, gs, Graphics_scene_options_periodic_2_triangulation_2{})`. +*/ +template +void add_to_graphics_scene(const P2T2& p2t2, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/Concepts/GraphicsSceneOptionsPeriodic2Triangulation2.h b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/Concepts/GraphicsSceneOptionsPeriodic2Triangulation2.h new file mode 100644 index 00000000000..54b84e2d75a --- /dev/null +++ b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/Concepts/GraphicsSceneOptionsPeriodic2Triangulation2.h @@ -0,0 +1,37 @@ +/*! +\ingroup PkgPeriodic2Triangulation2Concepts + +The concept `GraphicsSceneOptionsPeriodic2Triangulation2` defines data and methods used to tune the way that the cells of a `Periodic_2_triangulation_2` are considered for drawing or to be added into a graphics scene. + +\cgalRefines{GraphicsSceneOptions} + +\cgalHasModelsBegin +\cgalHasModelsBare{\link CGAL::Graphics_scene_options_periodic_2_triangulation_2 `CGAL::Graphics_scene_options_periodic_2_triangulation_2`\endlink} +\cgalHasModelsEnd + +*/ +class GraphicsSceneOptionsPeriodic2Triangulation2 +{ +public: + /// returns `true` if the domain of the Periodic_2_triangulation_2 must be drawn, `false` otherwise. + /// Returns `false` by default. + bool draw_domain() const; + + /// sets the draw domain value to `b`. + void draw_domain(bool b); + + /// toggles the draw domain value. + void toggle_draw_domain(); + + /// returns the type of the display (STORED, UNIQUE, STORED_COVER_DOMAIN or UNIQUE_COVER_DOMAIN). + typename DS::Iterator_type display_type() const; + + /// set the display type to the next type (in the ordered circular list STORED, UNIQUE, STORED_COVER_DOMAIN, UNIQUE_COVER_DOMAIN). + void increase_display_type(); + + /// returns the color used to draw the domain. + const CGAL::IO::Color& domain_color() const; + + /// sets the color used to draw the domain to `c`. + void domain_color(const CGAL::IO::Color& c) +}; diff --git a/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/PackageDescription.txt b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/PackageDescription.txt index 00c17ceec12..3c527f6fdd2 100644 --- a/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/PackageDescription.txt +++ b/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/PackageDescription.txt @@ -110,7 +110,9 @@ of the concept `Periodic_2Offset_2`. - `CGAL::Periodic_2_triangulation_2::Locate_type` \cgalCRPSection{Draw 2D Periodic Triangulation} - - \link PkgDrawPeriodic2Triangulation2 CGAL::draw() \endlink +- \link PkgDrawPeriodic2Triangulation2 CGAL::draw() \endlink +- `GraphicsSceneOptionsPeriodic2Triangulation2` +- `CGAL::Graphics_scene_options_periodic_2_triangulation_2` */ diff --git a/Periodic_2_triangulation_2/include/CGAL/draw_periodic_2_triangulation_2.h b/Periodic_2_triangulation_2/include/CGAL/draw_periodic_2_triangulation_2.h index 21d2156cd21..048494256b3 100644 --- a/Periodic_2_triangulation_2/include/CGAL/draw_periodic_2_triangulation_2.h +++ b/Periodic_2_triangulation_2/include/CGAL/draw_periodic_2_triangulation_2.h @@ -8,274 +8,280 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Jasmeet Singh +// Mostafa Ashraf #ifndef DRAW_PERIODIC_2_TRIANGULATION_2_H #define DRAW_PERIODIC_2_TRIANGULATION_2_H #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include +#include +#include +#include #include -#include +#include namespace CGAL { -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorP2T2 { - template - static CGAL::IO::Color run(const P2T2 &, - const typename P2T2::Periodic_triangle_iterator /*ti*/) { - //CGAL::Random random((unsigned int)(std::size_t)(&*ti)); - //return get_random_color(random); - return CGAL::IO::Color(73, 250, 117); +// We need a specific graphics view options for periodic_2_triangulation_2 for the parameters +// of the domain. +template +struct Graphics_scene_options_periodic_2_triangulation_2 : + public CGAL::Graphics_scene_options +{ + Graphics_scene_options_periodic_2_triangulation_2(): + m_domain_color(CGAL::IO::Color(96, 104, 252)), + m_draw_domain(true), + m_display_type(DS::STORED) + {} + + bool draw_domain() const + { return m_draw_domain; } + void draw_domain(bool b) + { m_draw_domain=b; } + void toggle_draw_domain() + { m_draw_domain=!m_draw_domain; } + + typename DS::Iterator_type display_type() const + { return m_display_type; } + + void increase_display_type() + { + if(m_display_type==DS::UNIQUE_COVER_DOMAIN) + { m_display_type=DS::STORED; } + else + { m_display_type=typename DS::Iterator_type(static_cast(m_display_type)+1); } } + + const CGAL::IO::Color& domain_color() const + { return m_domain_color; } + void domain_color(const CGAL::IO::Color& c) + { m_domain_color=c; } + +protected: + CGAL::IO::Color m_domain_color; + bool m_draw_domain; + typename DS::Iterator_type m_display_type; }; -// Viewer class for P2T2 -template -class SimplePeriodic2Triangulation2ViewerQt : public Basic_viewer_qt +namespace draw_function_for_P2T2 { - typedef Basic_viewer_qt Base; - typedef typename P2T2::Iterator_type Iterator_type; +template +void compute_vertex(const P2T2 &p2t2, + typename P2T2::Periodic_point_iterator pi, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + // Construct the point in 9-sheeted covering space and add to viewer + if(!gs_options.draw_vertex(p2t2, pi)) + { return; } - // Vertex iterators - typedef typename P2T2::Periodic_point_iterator Periodic_point_iterator; + if(gs_options.colored_vertex(p2t2, pi)) + { graphics_scene.add_point(p2t2.point(*pi), + gs_options.vertex_color(p2t2, pi)); } + else + { graphics_scene.add_point(p2t2.point(*pi)); } +} - // Edge iterators - typedef typename P2T2::Periodic_segment_iterator Periodic_segment_iterator; - typedef typename P2T2::Segment Segment; +template +void compute_edge(const P2T2 &p2t2, + typename P2T2::Periodic_segment_iterator si, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if(!gs_options.draw_edge(p2t2, si)) + { return; } - // Face iterators - typedef typename P2T2::Periodic_triangle_iterator Periodic_triangle_iterator; - typedef typename P2T2::Triangle Triangle; + // Construct the segment in 9-sheeted covering space and add to viewer + typename P2T2::Segment s(p2t2.segment(*si)); + if(gs_options.colored_edge(p2t2, si)) + { graphics_scene.add_segment(s[0], s[1], + gs_options.edge_color(p2t2, si)); } + else + { graphics_scene.add_segment(s[0], s[1]); } +} +template +void compute_face(const P2T2 &p2t2, + typename P2T2::Periodic_triangle_iterator ti, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if(!gs_options.draw_face(p2t2, ti)) + { return; } + + // Construct the triangle in 9-sheeted covering space and add to viewer + typename P2T2::Triangle t(p2t2.triangle(*ti)); + + if(gs_options.colored_face(p2t2, ti)) + { graphics_scene.face_begin(gs_options.face_color(p2t2, ti)); } + else + { graphics_scene.face_begin(); } + + graphics_scene.add_point_in_face(t[0]); + graphics_scene.add_point_in_face(t[1]); + graphics_scene.add_point_in_face(t[2]); + graphics_scene.face_end(); +} + +template +void compute_domain(const P2T2& p2t2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -public: - /// Construct the viewer. - /// @param ap2t2 the p2t2 to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big object where this time could be long) - SimplePeriodic2Triangulation2ViewerQt(QWidget* parent, const P2T2& ap2t2, - const char* title="Basic P2T2 Viewer", - bool anofaces=false, - const ColorFunctor& fcolor=ColorFunctor()) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - p2t2(ap2t2), - m_nofaces(anofaces), - m_fcolor(fcolor), - m_display_type(STORED_COVER_DOMAIN), - m_domain(true) + Kernel::Iso_rectangle_2 orig_domain = p2t2.domain(); + std::array covering_sheets = p2t2.number_of_sheets(); + + for(int i = 0; i < covering_sheets[0]; i++) { - // Add custom key description (see keyPressEvent). - setKeyDescription(::Qt::Key_1, "STORED: Display all geometric primitives as they are stored in " - "Triangulation_data_structure_2"); - setKeyDescription(::Qt::Key_2, "UNIQUE: Display only one representative of each geometric primitive even " - "if the triangulation is computed in multiply sheeted covering space."); - setKeyDescription(::Qt::Key_3, "STORED_COVER_DOMAIN: Same as STORED but also display " - "all primitives whose intersection with the original " - "domain of the current covering space is non-empty"); - setKeyDescription(::Qt::Key_4, "UNIQUE_COVER_DOMAIN: Same as UNIQUE but also display " - "all primitives whose intersection with the original " - "domain of the current covering space is non-empty"); - setKeyDescription(::Qt::Key_D, "Toggle 9-sheeted domain display"); + for(int j = 0; j < covering_sheets[1]; j++) + { + Kernel::Vector_2 shift(i * (orig_domain.xmax() - orig_domain.xmin()), + j * orig_domain.ymax() - orig_domain.ymin()); + Kernel::Point_2 p1((orig_domain.min)()); + Kernel::Point_2 p2(orig_domain.xmin(), orig_domain.ymax()); + Kernel::Point_2 p3(orig_domain.xmax(), orig_domain.ymin()); + Kernel::Point_2 p4((orig_domain.max)()); - compute_elements(); - } -protected: - void compute_vertex(Periodic_point_iterator pi) - { - // Construct the point in 9-sheeted covering space and add to viewer - add_point(p2t2.point(*pi)); - } - - void compute_edge(Periodic_segment_iterator si) - { - // Construct the segment in 9-sheeted covering space and add to viewer - Segment s(p2t2.segment(*si)); - add_segment(s[0], s[1]); - } - - void compute_face(Periodic_triangle_iterator ti) - { - // Construct the triangle in 9-sheeted covering space and add to viewer - Triangle t(p2t2.triangle(*ti)); - - CGAL::IO::Color c=m_fcolor.run(p2t2, ti); - face_begin(c); - add_point_in_face(t[0]); - add_point_in_face(t[1]); - add_point_in_face(t[2]); - face_end(); - - // Display the edges of the faces as segments with a - // light gray color for better visualization - add_segment(t[0], t[1], CGAL::IO::Color(207, 213, 211)); - add_segment(t[1], t[2], CGAL::IO::Color(207, 213, 211)); - add_segment(t[2], t[0], CGAL::IO::Color(207, 213, 211)); - } - - void compute_domain() - { - Kernel::Iso_rectangle_2 orig_domain = p2t2.domain(); - std::array covering_sheets = p2t2.number_of_sheets(); - - for(int i = 0; i < covering_sheets[0]; i++){ - for(int j = 0; j < covering_sheets[1]; j++){ - Kernel::Vector_2 shift(i * (orig_domain.xmax() - orig_domain.xmin()), - j * orig_domain.ymax() - orig_domain.ymin()); - Kernel::Point_2 p1((orig_domain.min)()); - Kernel::Point_2 p2(orig_domain.xmin(), orig_domain.ymax()); - Kernel::Point_2 p3(orig_domain.xmax(), orig_domain.ymin()); - Kernel::Point_2 p4((orig_domain.max)()); - - add_segment(p1 + shift, p2 + shift, CGAL::IO::Color(96, 104, 252)); - add_segment(p1 + shift, p3 + shift, CGAL::IO::Color(96, 104, 252)); - add_segment(p2 + shift, p4 + shift, CGAL::IO::Color(96, 104, 252)); - add_segment(p3 + shift, p4 + shift, CGAL::IO::Color(96, 104, 252)); - } + graphics_scene.add_segment(p1 + shift, p2 + shift, gs_options.domain_color()); + graphics_scene.add_segment(p1 + shift, p3 + shift, gs_options.domain_color()); + graphics_scene.add_segment(p2 + shift, p4 + shift, gs_options.domain_color()); + graphics_scene.add_segment(p3 + shift, p4 + shift, gs_options.domain_color()); } } - - void compute_elements() { - // Clear the buffers - clear(); - - // Get the display type, iterate through periodic elements according - // to the display type - Iterator_type it_type = (Iterator_type)m_display_type; - - // Iterate through vertices, edges and faces, add elements to buffer - for (Periodic_point_iterator it = - p2t2.periodic_points_begin(it_type); - it != p2t2.periodic_points_end(it_type); it++) - { - compute_vertex(it); - } - - for (Periodic_segment_iterator it = - p2t2.periodic_segments_begin(it_type); - it != p2t2.periodic_segments_end(it_type); it++) - { - compute_edge(it); - } - - for (Periodic_triangle_iterator it = - p2t2.periodic_triangles_begin(it_type); - it != p2t2.periodic_triangles_end(it_type); it++) - { - compute_face(it); - } - - if(m_domain){ - // Compute the (9-sheet covering space) domain of the periodic triangulation - compute_domain(); - } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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 - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - if (e->text()=="1") - { - m_display_type = Display_type::UNIQUE; - displayMessage(QString("Display type= UNIQUE")); - compute_elements(); - redraw(); - } else if (e->text()=="2") - { - m_display_type = Display_type::UNIQUE_COVER_DOMAIN; - displayMessage(QString("Display type= UNIQUE_COVER_DOMAIN")); - compute_elements(); - redraw(); - } else if (e->text()=="3") - { - m_display_type = Display_type::STORED; - displayMessage(QString("Display type= STORED")); - compute_elements(); - redraw(); - } else if (e->text()=="4") - { - m_display_type = Display_type::STORED_COVER_DOMAIN; - displayMessage(QString("Display type= STORED_COVER_DOMAIN")); - compute_elements(); - redraw(); - } else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) - { - m_domain=!m_domain; - displayMessage(QString("Draw domain=%1.").arg(m_domain?"true":"false")); - compute_elements(); - redraw(); - } else { - Base::keyPressEvent(e); - } - } - -protected: - const P2T2& p2t2; - bool m_nofaces; - const ColorFunctor& m_fcolor; - enum Display_type - { - STORED = 0, - UNIQUE, // 1 - STORED_COVER_DOMAIN, // 2 - UNIQUE_COVER_DOMAIN // 3 - }; - Display_type m_display_type; - bool m_domain; -}; - -// Specialization of draw function -#define CGAL_P2T2_TYPE CGAL::Periodic_2_triangulation_2 \ - - -template < class Gt, - class Tds > -void draw(const CGAL_P2T2_TYPE& ap2t2, - const char* title = "2D Periodic Triangulation Viewer", - bool nofill = false) -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"p2t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - DefaultColorFunctorP2T2 fcolor; - SimplePeriodic2Triangulation2ViewerQt - mainwindow(app.activeWindow(), ap2t2, title, nofill, fcolor); - mainwindow.show(); - app.exec(); - } } -} // namespace CGAL +template +void compute_elements(const P2T2& p2t2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + // Get the display type, iterate through periodic elements according + // to the display type + typedef typename P2T2::Iterator_type Iterator_type; + Iterator_type it_type = (Iterator_type)gs_options.display_type(); + + // Iterate through vertices, edges and faces, add elements to buffer + if(gs_options.are_vertices_enabled()) + { + for (typename P2T2::Periodic_point_iterator it=p2t2.periodic_points_begin(it_type); + it!=p2t2.periodic_points_end(it_type); ++it) + { compute_vertex(p2t2, it, graphics_scene, gs_options); } + } + + if(gs_options.are_edges_enabled()) + { + for (typename P2T2::Periodic_segment_iterator it=p2t2.periodic_segments_begin(it_type); + it!=p2t2.periodic_segments_end(it_type); ++it) + { compute_edge(p2t2, it, graphics_scene, gs_options); } + } + + if (gs_options.are_faces_enabled()) + { + for (typename P2T2::Periodic_triangle_iterator it=p2t2.periodic_triangles_begin(it_type); + it!=p2t2.periodic_triangles_end(it_type); ++it) + { compute_face(p2t2, it, graphics_scene, gs_options); } + } + + if(gs_options.draw_domain()) + { + // Compute the (9-sheet covering space) domain of the periodic triangulation + compute_domain(p2t2, graphics_scene, gs_options); + } +} + +} // namespace draw_function_for_P2T2 + +#define CGAL_P2T2_TYPE CGAL::Periodic_2_triangulation_2 + +template +void add_to_graphics_scene(const CGAL_P2T2_TYPE& p2t2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + draw_function_for_P2T2::compute_elements(p2t2, graphics_scene, gs_options); +} + +template +void add_to_graphics_scene(const CGAL_P2T2_TYPE& p2t2, + CGAL::Graphics_scene& graphics_scene) +{ + CGAL::Graphics_scene_options_periodic_2_triangulation_2 + gs_options; + + add_to_graphics_scene(p2t2, graphics_scene, gs_options); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function +template +void draw(const CGAL_P2T2_TYPE& ap2t2, + GSOptions& gs_options, + const char* title="2D Periodic Triangulation Viewer") +{ + CGAL::Graphics_scene gs; + add_to_graphics_scene(ap2t2, gs, gs_options); + CGAL::Qt::QApplication_and_basic_viewer app(gs, title); + if(app) + { + // Here we define the std::function to capture key pressed. + app.basic_viewer().on_key_pressed= + [&ap2t2, &gs, &gs_options] (QKeyEvent* e, CGAL::Qt::Basic_viewer* basic_viewer) -> bool + { + const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) + { + gs_options.increase_display_type(); + basic_viewer->displayMessage + (QString("Display type=%1.").arg(gs_options.display_type()==0?"Stored": + (gs_options.display_type()==1?"Unique": + (gs_options.display_type()==2?"Stored cover": + "Unique cover")))); + gs.clear(); + add_to_graphics_scene(ap2t2, gs, gs_options); + basic_viewer->redraw(); + } + else + { + // Return false will call the base method to process others/classicals key + return false; + } + return true; // the key was captured + }; + + // Here we add shortcut descriptions + app.basic_viewer().setKeyDescription(::Qt::Key_D, "Next display type"); + + // Then we run the app + app.run(); + } +} + +template +void draw(const CGAL_P2T2_TYPE& ap2t2, + const char* title="2D Periodic Triangulation Viewer") +{ + CGAL::Graphics_scene_options_periodic_2_triangulation_2 + gs_options; + draw(ap2t2, gs_options, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_P2T2_TYPE + +} // namespace CGAL + #endif // DRAW_PERIODIC_2_TRIANGULATION_2_H diff --git a/Periodic_2_triangulation_2/package_info/Periodic_2_triangulation_2/dependencies b/Periodic_2_triangulation_2/package_info/Periodic_2_triangulation_2/dependencies index 22f0ba917df..fa072ac6299 100644 --- a/Periodic_2_triangulation_2/package_info/Periodic_2_triangulation_2/dependencies +++ b/Periodic_2_triangulation_2/package_info/Periodic_2_triangulation_2/dependencies @@ -1,23 +1,30 @@ Algebraic_foundations +Arithmetic_kernel +BGL +Basic_viewer Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView Hash_map +Homogeneous_kernel Installation Intersections_2 Intersections_3 Interval_support Kernel_23 +Kernel_d Modular_arithmetic Number_types Periodic_2_triangulation_2 +Polygon Profiling_tools Property_map +Random_numbers STL_Extension Spatial_sorting Stream_support TDS_2 Triangulation_2 +CGAL_Core 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/Periodic_4_hyperbolic_triangulation_2/demo/Periodic_4_hyperbolic_triangulation_2/include/internal/Qt/TriangulationGraphicsItem.h b/Periodic_4_hyperbolic_triangulation_2/demo/Periodic_4_hyperbolic_triangulation_2/include/internal/Qt/TriangulationGraphicsItem.h index e6b56085125..71e38171d57 100644 --- a/Periodic_4_hyperbolic_triangulation_2/demo/Periodic_4_hyperbolic_triangulation_2/include/internal/Qt/TriangulationGraphicsItem.h +++ b/Periodic_4_hyperbolic_triangulation_2/demo/Periodic_4_hyperbolic_triangulation_2/include/internal/Qt/TriangulationGraphicsItem.h @@ -14,7 +14,6 @@ #define CGAL_QT_TRIANGULATION_GRAPHICS_ITEM_H #include -#include #include #include diff --git a/Point_set_3/examples/Point_set_3/CMakeLists.txt b/Point_set_3/examples/Point_set_3/CMakeLists.txt index 747ba9d7992..833fb509719 100644 --- a/Point_set_3/examples/Point_set_3/CMakeLists.txt +++ b/Point_set_3/examples/Point_set_3/CMakeLists.txt @@ -11,9 +11,7 @@ create_single_source_cgal_program("point_set.cpp") create_single_source_cgal_program("point_set_property.cpp") create_single_source_cgal_program("point_set_read_xyz.cpp") create_single_source_cgal_program("point_set_advanced.cpp") - -set(needed_cxx_features cxx_rvalue_references cxx_variadic_templates) -create_single_source_cgal_program("point_set_read_ply.cpp" CXX_FEATURES ${needed_cxx_features}) +create_single_source_cgal_program("point_set_read_ply.cpp") find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater) include(CGAL_Eigen3_support) diff --git a/Point_set_3/include/CGAL/draw_point_set_3.h b/Point_set_3/include/CGAL/draw_point_set_3.h index ecd747b3cdf..6a8f671296d 100644 --- a/Point_set_3/include/CGAL/draw_point_set_3.h +++ b/Point_set_3/include/CGAL/draw_point_set_3.h @@ -9,12 +9,16 @@ // // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_POINT_SET_3_H #define CGAL_DRAW_POINT_SET_3_H #include -#include +#include +#include +#include +#include #ifdef DOXYGEN_RUNNING namespace CGAL { @@ -22,100 +26,151 @@ namespace CGAL { /*! \ingroup PkgDrawPointSet3D -opens a new window and draws `aps`, an instance of the `CGAL::Point_set_3` 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 PS an instance of the `CGAL::Point_set_3` class. -\param aps the point set to draw. +opens a new window and draws a point set. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 PS which must be an instanciation of a `CGAL::Point_set_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param ps the point set to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Point_set_3& ps, const GSOptions& gso); + +\cgalAdvancedEnd +*/ + template + void draw(const PS& ps, const GSOptions& gso); + +/*! +\ingroup PkgDrawPointSet3D + +A shortcut to `CGAL::draw(ps, Graphics_scene_options{})`. +*/ + template + void draw(const PS& ps); + +/*! +\ingroup PkgDrawPointSet3D + +adds the vertices, edges and faces of `ps` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam PS which must be an instanciation of a `CGAL::Point_set_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param ps the point set to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Point_set_3& ps, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const PS& ps, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPointSet3D + +A shortcut to `CGAL::add_to_graphics_scene(ps, gs, Graphics_scene_options{})`. */ template -void draw(const PS& aps); +void add_to_graphics_scene(const PS& ps, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ #endif -#ifdef CGAL_USE_BASIC_VIEWER +namespace CGAL { -#include -#include -#include +namespace draw_function_for_PointSet { -namespace CGAL +template +void compute_elements(const PointSet& pointset, + Graphics_scene& graphics_scene, + const GSOptions& gs_options) { + if (!gs_options.are_vertices_enabled()) + { return; } -// Viewer class for Point_set -template -class SimplePointSetViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename PointSet::Point_map::value_type Point; - -public: - /// Construct the viewer. - /// @param apointset the point set to view - /// @param title the title of the window - SimplePointSetViewerQt(QWidget* parent, - const PointSet& apointset, const char* title="") : - // First draw: vertices; no-edge, no-face; mono-color; no inverse normal - Base(parent, title, true, false, false, true, false), - pointset(apointset) + for (typename PointSet::const_iterator it=pointset.begin(); + it!=pointset.end(); ++it) { - compute_elements(); - } - -protected: - void compute_vertex(const Point& p) - { - add_point(p); - // We can use add_point(p, c) with c a CGAL::IO::Color to add a colored point - } - - void compute_elements() - { - clear(); - - for (typename PointSet::const_iterator it=pointset.begin(); - it!=pointset.end(); ++it) - { compute_vertex(pointset.point(*it)); } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - Base::keyPressEvent(e); - } - -protected: - const PointSet& pointset; -}; - -// Specialization of draw function. -template -void draw(const Point_set_3& apointset, - const char* title="Point_set_3 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) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"point_set_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimplePointSetViewerQt > mainwindow(app.activeWindow(), - apointset, - title); - mainwindow.show(); - app.exec(); + if(gs_options.draw_vertex(pointset, it)) + { + if (gs_options.colored_vertex(pointset, it)) + { + graphics_scene.add_point(pointset.point(*it), + gs_options.vertex_color(pointset, it)); + } + else + { graphics_scene.add_point(pointset.point(*it)); } + } } } -} // End namespace CGAL +} // namespace draw_function_for_PointSet + +template +void add_to_graphics_scene(const Point_set_3& apointset, + Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + draw_function_for_PointSet::compute_elements(apointset, + graphics_scene, + gs_options); +} + +template +void add_to_graphics_scene(const Point_set_3& apointset, + Graphics_scene& graphics_scene) +{ + CGAL::Graphics_scene_options, + typename Point_set_3::const_iterator, + int, int> gs_options; + add_to_graphics_scene(apointset, graphics_scene, gs_options); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function. + template +void draw(const Point_set_3& apointset, + const GSOptions& gs_options, + const char *title="Point_set_3 Basic Viewer") +{ + Graphics_scene buffer; + add_to_graphics_scene(apointset, buffer, gs_options); + draw_graphics_scene(buffer, title); +} + +template +void draw(const Point_set_3& apointset, + const char *title="Point_set_3 Basic Viewer") +{ + Graphics_scene buffer; + add_to_graphics_scene(apointset, buffer); + draw_graphics_scene(buffer, title); +} #endif // CGAL_USE_BASIC_VIEWER +} // End namespace CGAL + #endif // CGAL_DRAW_POINT_SET_3_H diff --git a/Point_set_3/package_info/Point_set_3/dependencies b/Point_set_3/package_info/Point_set_3/dependencies index 6f0ec394588..5b0eb897d7e 100644 --- a/Point_set_3/package_info/Point_set_3/dependencies +++ b/Point_set_3/package_info/Point_set_3/dependencies @@ -1,16 +1,32 @@ Algebraic_foundations +Arithmetic_kernel BGL +Basic_viewer +Cartesian_kernel Circulator -GraphicsView +Distance_2 +Distance_3 +Filtered_kernel +Hash_map +Homogeneous_kernel Installation +Intersections_2 +Intersections_3 Interval_support Kernel_23 +Kernel_d Modular_arithmetic Number_types Point_set_3 Point_set_processing_3 +Polygon Profiling_tools Property_map +Random_numbers STL_Extension +Spatial_sorting Stream_support Surface_mesh +TDS_2 +Triangulation_2 +CGAL_Core 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..d70f0d7df18 100644 --- a/Polygon/examples/Polygon/CMakeLists.txt +++ b/Polygon/examples/Polygon/CMakeLists.txt @@ -18,4 +18,6 @@ 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_polygon_with_holes_2 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/draw_polygon_with_holes_2.cpp b/Polygon/examples/Polygon/draw_polygon_with_holes_2.cpp new file mode 100644 index 00000000000..3306655a135 --- /dev/null +++ b/Polygon/examples/Polygon/draw_polygon_with_holes_2.cpp @@ -0,0 +1,46 @@ +/*! \file draw_polygon_with_holes_2.cpp + */ + +#include +#include +#include +#include + +using K=CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2=K::Point_2; +using Polygon_2=CGAL::Polygon_2; +using Polygon_with_holes_2=CGAL::Polygon_with_holes_2; + +Polygon_2 triangle(double x, double y) +{ // Create a triangle (x, y) + Polygon_2 P; + P.push_back(Point_2(x, y)); + P.push_back(Point_2(x+0.5, y+1)); + P.push_back(Point_2(x+1, y)); + return P; +} + +Polygon_2 rectangle(double l) +{ // Create a rectangle with given side length. + Polygon_2 P; + P.push_back(Point_2(0, 0)); + P.push_back(Point_2(l, 0)); + P.push_back(Point_2(l, l)); + P.push_back(Point_2(0, l)); + return P; +} + +int main() +{ + // Create a large rectangle A, with 3 triangle holes + Polygon_with_holes_2 A(rectangle(4)); + Polygon_2 H1(triangle(1, 1)); + Polygon_2 H2(triangle(2, 1)); + Polygon_2 H3(triangle(1.5, 2)); + A.add_hole(H1); + A.add_hole(H2); + A.add_hole(H3); + CGAL::draw(A); + + 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..8950956786e --- /dev/null +++ b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h @@ -0,0 +1,212 @@ +// 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 Qt::Basic_viewer { + using Base = Qt::Basic_viewer; + 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, gs, 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() { + gs.clear(); + + for (auto const& p: m_mpwh.polygons_with_holes()) { + gs.face_begin(get_random_color(get_default_random())); + + 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); + gs.add_point_in_face(*point_in_face); + } + + gs.face_end(); + } + } + +protected: + /*! Compute the face + */ + void compute_loop(const Pgn& p, bool hole) { + if (hole) gs.add_point_in_face(p.vertex(p.size()-1)); + + auto prev = p.vertices_begin(); + auto it = prev; + gs.add_point(*it); + gs.add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + gs.add_segment(*prev, *it); // add segment with previous point + gs.add_point(*it); + gs.add_point_in_face(*it); // add point in face + prev = it; + } + + // Add the last segment between the last point and the first one + gs.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: + Graphics_scene gs; + + //! 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/include/CGAL/draw_polygon_2.h b/Polygon/include/CGAL/draw_polygon_2.h index 800e15e18a0..24acaaa7535 100644 --- a/Polygon/include/CGAL/draw_polygon_2.h +++ b/Polygon/include/CGAL/draw_polygon_2.h @@ -13,11 +13,15 @@ // // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_POLYGON_2_H #define CGAL_DRAW_POLYGON_2_H -#include +#include +#include +#include +#include #ifdef DOXYGEN_RUNNING namespace CGAL { @@ -25,117 +29,177 @@ namespace CGAL { /*! \ingroup PkgDrawPolygon2 -opens a new window and draws `ap`, an instance of the `CGAL::Polygon_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 P an instance of the `CGAL::Polygon_2` class. -\param ap the polygon to draw. +opens a new window and draws a 2D polygon. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 P which must be an instanciation of a `CGAL::Polygon_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param p the polygon to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Polygon_2& p, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const P& p, const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygon2 + +A shortcut to `CGAL::draw(p, Graphics_scene_options{})`. +*/ + template + void draw(const P& p); + +/*! +\ingroup PkgDrawPolygon2 + +adds the vertices, edges and faces of `p` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam P which must be an instanciation of a `CGAL::Polygon_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param p the 2D polygon to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Polygon_2& p, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const P& p, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygon2 + +A shortcut to `CGAL::add_to_graphics_scene(p, gs, Graphics_scene_options{})`. */ template -void draw(const P& ap); - +void add_to_graphics_scene(const P& p, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ #endif -#ifdef CGAL_USE_BASIC_VIEWER -#include -#include -#include +namespace CGAL { -namespace CGAL +namespace draw_function_for_p2 { + +template +void compute_elements(const P2& p2, + CGAL::Graphics_scene &graphics_scene, + const GSOptions& gso) { + if (p2.is_empty()) + return; -// Viewer class for Polygon_2 -template -class SimplePolygon2ViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename P2::Point_2 Point; - -public: - /// Construct the viewer. - /// @param ap2 the polygon to view - /// @param title the title of the window - SimplePolygon2ViewerQt(QWidget* parent, const P2& ap2, - const char* title="Basic Polygon_2 Viewer") : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - p2(ap2) + if (gso.are_faces_enabled()) { - compute_elements(); + if(gso.colored_face(p2, nullptr)) + { graphics_scene.face_begin(gso.face_color(p2, nullptr)); } + else + { graphics_scene.face_begin(); } } -protected: - void compute_elements() + typename P2::Vertex_const_iterator prev=p2.vertices_end(); --prev; + for (typename P2::Vertex_const_iterator i=p2.vertices_begin(); + i!=p2.vertices_end(); ++i) { - clear(); - - if (p2.is_empty()) return; - - Point prev=p2.vertex(p2.size()-1); - - CGAL::IO::Color c(75,160,255); - face_begin(c); - - for (typename P2::Vertex_const_iterator i=p2.vertices_begin(); - i!=p2.vertices_end(); ++i) - { - add_point(*i); // Add vertex - add_segment(prev, *i); // Add segment with previous point - add_point_in_face(*i); // Add point in face - prev=*i; + if(gso.are_vertices_enabled() && + gso.draw_vertex(p2, i)) + { // Add vertex + if(gso.colored_vertex(p2, i)) + { graphics_scene.add_point(*i, gso.vertex_color(p2, i)); } + else + { graphics_scene.add_point(*i); } } - face_end(); + if(gso.are_edges_enabled() && + gso.draw_edge(p2, prev)) + { // Add edge with previous point + if(gso.colored_edge(p2, prev)) + { graphics_scene.add_segment(*prev, *i, gso.edge_color(p2, prev)); } + else + { graphics_scene.add_segment(*prev, *i); } + } + + if(gso.are_faces_enabled()) + { graphics_scene.add_point_in_face(*i); } // Add point in face + + prev=i; } - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); - } - -protected: - const P2& p2; -}; - -// Specialization of draw function. -template -void draw(const CGAL::Polygon_2& ap2, - const char* title="Polygon_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) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimplePolygon2ViewerQt > - mainwindow(app.activeWindow(), ap2, title); - mainwindow.show(); - app.exec(); - } + if (gso.are_faces_enabled()) + { graphics_scene.face_end(); } } -} // End namespace CGAL +} // namespace draw_function_for_p2 + +#define CGAL_P2_TYPE CGAL::Polygon_2 + +// Specializations of add_to_graphics_scene function +template +void add_to_graphics_scene(const CGAL_P2_TYPE& ap2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso) +{ draw_function_for_p2::compute_elements(ap2, graphics_scene, gso); } + +template +void add_to_graphics_scene(const CGAL_P2_TYPE& ap2, + CGAL::Graphics_scene &graphics_scene) +{ + CGAL::Graphics_scene_options gso; + draw_function_for_p2::compute_elements(ap2, graphics_scene, gso); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function. +template +void draw(const CGAL_P2_TYPE &ap2, + const char *title="Polygon_2 Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ap2, buffer); + draw_graphics_scene(buffer, title); +} + +template +void draw(const CGAL_P2_TYPE &ap2, + const GSOptions& gso, + const char *title="Polygon_2 Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ap2, buffer, gso); + draw_graphics_scene(buffer, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_P2_TYPE + +} // End namespace CGAL + #endif // CGAL_DRAW_POLYGON_2_H diff --git a/Polygon/include/CGAL/draw_polygon_with_holes_2.h b/Polygon/include/CGAL/draw_polygon_with_holes_2.h index a530ad0a221..3be337aa157 100644 --- a/Polygon/include/CGAL/draw_polygon_with_holes_2.h +++ b/Polygon/include/CGAL/draw_polygon_with_holes_2.h @@ -13,209 +13,230 @@ // // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_POLYGON_WITH_HOLES_2_H #define CGAL_DRAW_POLYGON_WITH_HOLES_2_H -#include +#include +#include +#include +#include #ifdef DOXYGEN_RUNNING namespace CGAL { /*! - * \ingroup PkgDrawPolygonWithHoles2 - * - * opens a new window and draws `aph`, an instance of the - * `CGAL::Polygon_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::Polygon_with_holes_2` class. - * \param aph the polygon with holes to draw. - */ + \ingroup PkgDrawPolygonWithHoles2 -template -void draw(const PH& aph); +opens a new window and draws a 2D polygon with holes. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 which must be an instanciation of a `CGAL::Polygon_with_holes_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param ph the polygon with holes to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Polygon_with_holes_2& ph, const GSOptions& gso); + +\cgalAdvancedEnd + */ +template +void draw(const PH& ph, const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygonWithHoles2 + +A shortcut to `CGAL::draw(ph, Graphics_scene_options{})`. +*/ + template + void draw(const PH& ph); + +/*! +\ingroup PkgDrawPolygonWithHoles2 + +adds the vertices, edges and faces of `ph` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam PH which must be an instanciation of a `CGAL::Polygon_with_holes_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param ph the polygon with holes to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Polygon_with_holes_2& ph, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const PH& ph, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPolygonWithHoles2 + +A shortcut to `CGAL::add_to_graphics_scene(ph, gs, Graphics_scene_options{})`. +*/ +template +void add_to_graphics_scene(const PH& ph, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ #endif -#ifdef CGAL_USE_BASIC_VIEWER - -#include -#include - -namespace CGAL { - -// Viewer class for Polygon_with_holes_2 -template -class Pwh_2_basic_viewer_qt : public Basic_viewer_qt { - using Base = Basic_viewer_qt; - using Pwh = PolygonWidthHoles_2; - using Pgn = typename Pwh::Polygon_2; - using Pnt = typename Pgn::Point_2; - -public: - /// Construct the viewer. - /// @param parent the active window to draw - /// @param pwh the polygon to view - /// @param title the title of the window - Pwh_2_basic_viewer_qt(QWidget* parent, const Pwh& pwh, - const char* title = "Basic Polygon_with_holes_2 Viewer") : - Base(parent, title, true, true, true, false, false), - m_pwh(pwh) - { - if (pwh.is_unbounded() && (0 == pwh.number_of_holes())) 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() { - if (! m_pwh.is_unbounded()) return m_pwh.outer_boundary().bbox(); - - Bbox_2 bbox; - for (auto it = m_pwh.holes_begin(); it != m_pwh.holes_end(); ++it) - bbox += it->bbox(); - return bbox; - } - - /*! Compute the elements of a polygon with holes. - */ - void add_elements() { - clear(); - CGAL::IO::Color c(75,160,255); - face_begin(c); - - const Pnt* point_in_face; - if (m_pwh.outer_boundary().is_empty()) { - Pgn pgn; - - double x = (double)m_width * 0.5 * m_pixel_ratio; - double y = (double)m_height * 0.5 * m_pixel_ratio; - pgn.push_back(Pnt(-x, -y)); - pgn.push_back(Pnt(x, -y)); - pgn.push_back(Pnt(x, y)); - pgn.push_back(Pnt(-x, y)); - compute_loop(pgn, false); - point_in_face = &(pgn.vertex(pgn.size()-1)); - } - else { - const auto& outer_boundary = m_pwh.outer_boundary(); - compute_loop(outer_boundary, false); - point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); - } - - for (auto it = m_pwh.holes_begin(); it != m_pwh.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 Pwh& m_pwh; -}; - -// Specialization of draw function. -template -void draw(const CGAL::Polygon_with_holes_2& pwh, - const char* title = "Polygon_with_holes_2 Basic Viewer") +namespace CGAL { -#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 Pwh = CGAL::Polygon_with_holes_2; - using Viewer = Pwh_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(), pwh, title); - mainwindow.add_elements(); - mainwindow.show(); - app.exec(); +namespace draw_function_for_ph2_with_holes { + +template +void compute_one_loop_elements(const P2& ap2, + const typename P2::General_polygon_2& aloop, + Graphics_scene &graphics_scene, + bool hole, + const GSOptions& gs_options) +{ + if (hole && gs_options.are_faces_enabled()) + { graphics_scene.add_point_in_face(aloop.vertex(aloop.size()-1)); } + + typename P2::General_polygon_2::Vertex_const_iterator prev; + for(typename P2::General_polygon_2::Vertex_const_iterator i=aloop.vertices_begin(); + i!=aloop.vertices_end(); ++i) + { + if(gs_options.are_vertices_enabled() && + gs_options.draw_vertex(ap2, i)) + { // Add vertex + if(gs_options.colored_vertex(ap2, i)) + { graphics_scene.add_point(*i, gs_options.vertex_color(ap2, i)); } + else + { graphics_scene.add_point(*i); } + } + + if(i!=aloop.vertices_begin() && + gs_options.are_edges_enabled() && gs_options.draw_edge(ap2, prev)) + { // Add segment with previous point + if(gs_options.colored_edge(ap2, prev)) + { graphics_scene.add_segment(*prev, *i, gs_options.edge_color(ap2, prev)); } + else + { graphics_scene.add_segment(*prev, *i); } + } + + if(gs_options.are_faces_enabled()) + { graphics_scene.add_point_in_face(*i); } // Add point in face + + prev=i; + } + + // Add the last segment between the last point and the first one + if(gs_options.are_edges_enabled() && + gs_options.draw_edge(ap2, aloop.vertices_begin())) + { + if(gs_options.colored_edge(ap2, prev)) + { graphics_scene.add_segment(*prev, *(aloop.vertices_begin()), + gs_options.edge_color(ap2, prev)); } + else + { graphics_scene.add_segment(*prev, *(aloop.vertices_begin())); } } } -} // End namespace CGAL +template +void compute_elements(const P2& p2, Graphics_scene &graphics_scene, + const GSOptions& gs_options) +{ + if (p2.outer_boundary().is_empty()) return; + + if (gs_options.are_faces_enabled()) + { + if(gs_options.colored_face(p2, nullptr)) + { graphics_scene.face_begin(gs_options.face_color(p2, nullptr)); } + else + { graphics_scene.face_begin(); } + } + + compute_one_loop_elements(p2, p2.outer_boundary(), graphics_scene, + false, gs_options); + + for (typename P2::Hole_const_iterator it=p2.holes_begin(); it!=p2.holes_end(); ++it) + { + compute_one_loop_elements(p2, *it, graphics_scene, + true, gs_options); + if (gs_options.are_faces_enabled()) + { graphics_scene.add_point_in_face(p2.outer_boundary().vertex + (p2.outer_boundary().size()-1)); + } + } + + if (gs_options.are_faces_enabled()) + { graphics_scene.face_end(); } +} + +} // draw_function_for_ph2 + +#define CGAL_P2_WITH_HOLES_TYPE CGAL::Polygon_with_holes_2 + +template +void add_to_graphics_scene(const CGAL_P2_WITH_HOLES_TYPE& p2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions &gs_options) +{ + draw_function_for_ph2_with_holes::compute_elements(p2, graphics_scene, + gs_options); +} + +template +void add_to_graphics_scene(const CGAL_P2_WITH_HOLES_TYPE& p2, + CGAL::Graphics_scene& graphics_scene) +{ + Graphics_scene_options gs_options; + + add_to_graphics_scene(p2, graphics_scene, gs_options); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function. +template +void draw(const CGAL_P2_WITH_HOLES_TYPE& ap2, const GSOptions &gs_options, + const char* title="Polygon with Holes Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ap2, buffer, gs_options); + draw_graphics_scene(buffer, title); +} + +template +void draw(const CGAL_P2_WITH_HOLES_TYPE& ap2, + const char* title="Polygon with Holes Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ap2, buffer); + draw_graphics_scene(buffer, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_P2_WITH_HOLES_TYPE + +} // End namespace CGAL + #endif // CGAL_DRAW_POLYGON_WITH_HOLES_2_H diff --git a/Polygon/package_info/Polygon/dependencies b/Polygon/package_info/Polygon/dependencies index 3312eb37e46..8533556ba08 100644 --- a/Polygon/package_info/Polygon/dependencies +++ b/Polygon/package_info/Polygon/dependencies @@ -1,10 +1,29 @@ Algebraic_foundations +Arithmetic_kernel +BGL +Basic_viewer +Cartesian_kernel Circulator -GraphicsView +Distance_2 +Distance_3 +Filtered_kernel +Hash_map +Homogeneous_kernel Installation +Intersections_2 +Intersections_3 +Interval_support Kernel_23 +Kernel_d +Modular_arithmetic Number_types Polygon Profiling_tools +Property_map +Random_numbers STL_Extension +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 +CGAL_Core diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 455e1ce2be7..d66c727f73d 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -14,8 +14,8 @@ create_single_source_cgal_program("polyhedral_envelope_of_triangle_soup.cpp" ) create_single_source_cgal_program("polyhedral_envelope_mesh_containment.cpp" ) create_single_source_cgal_program("self_intersections_example.cpp" ) create_single_source_cgal_program("stitch_borders_example.cpp" ) -create_single_source_cgal_program("compute_normals_example_Polyhedron.cpp" CXX_FEATURES cxx_range_for ) -create_single_source_cgal_program("compute_normals_example.cpp" CXX_FEATURES cxx_range_for cxx_auto_type ) +create_single_source_cgal_program("compute_normals_example_Polyhedron.cpp") +create_single_source_cgal_program("compute_normals_example.cpp") create_single_source_cgal_program("point_inside_example.cpp") create_single_source_cgal_program("triangulate_faces_example.cpp") create_single_source_cgal_program("triangulate_faces_split_visitor_example.cpp") diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Non_manifold_feature_map.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Non_manifold_feature_map.h index e8901ebd727..f2a522db1e1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Non_manifold_feature_map.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/Non_manifold_feature_map.h @@ -75,6 +75,7 @@ struct Non_manifold_feature_map halfedge_descriptor hd = halfedge(ed, pm); // an edge can be non-manifold only if both its vertices are non-manifold + // THIS IS NOT TRUE! if ( get(v_nm_id, source(hd, pm))==std::size_t(-1) || get(v_nm_id, target(hd, pm))==std::size_t(-1) ) continue; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h index e51999b9c54..0166c85e58f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h @@ -134,7 +134,6 @@ void angle_and_area_smoothing(const FaceRange& faces, TriangleMesh& tmesh, const NamedParameters& np = parameters::default_values()) { - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::edge_descriptor edge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; @@ -209,14 +208,7 @@ void angle_and_area_smoothing(const FaceRange& faces, const bool use_Delaunay_flips = choose_parameter(get_parameter(np, internal_np::use_Delaunay_flips), true); VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), - get(Vertex_property_tag(), tmesh)); - - // If it's the default vcmap, manually set everything to false because the dynamic pmap has no default initialization - if((std::is_same::value)) - { - for(vertex_descriptor v : vertices(tmesh)) - put(vcmap, v, false); - } + get(Vertex_property_tag(), tmesh, false)); ECMap ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), Static_boolean_property_map()); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/bbox.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/bbox.h index 96e61d7bb67..dd19ef4c685 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/bbox.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/bbox.h @@ -15,14 +15,16 @@ #include - #include -#include +#include +#include #include #include +#include + namespace CGAL { namespace Polygon_mesh_processing { @@ -54,6 +56,14 @@ namespace CGAL { * `Construct_bbox_3` must provide the functor `Bbox_3 operator()(Point_3)` * where `%Point_3` is the value type of the vertex point map.} * \cgalParamNEnd + * + * \cgalParamNBegin{bbox_scaling} + * \cgalParamDescription{a double used to scale the bounding box. + * The default value is 1 and the bounding box is the smallest possible + * axis-aligned bounding box.} + * \cgalParamDefault{1.} + * \cgalParamPrecondition{`bbox_scaling > 0`} + * \cgalParamNEnd * \cgalNamedParamsEnd * * @see `vertex_bbox()` @@ -75,6 +85,9 @@ namespace CGAL { GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); typename GT::Construct_bbox_3 get_bbox = gt.construct_bbox_3_object(); + const double factor = choose_parameter(get_parameter(np, internal_np::bbox_scaling), 1.); + CGAL_precondition(factor > 0); + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; CGAL::Bbox_3 bb; @@ -82,6 +95,8 @@ namespace CGAL { { bb += get_bbox( get(vpm, v) ); } + bb.scale(factor); + return bb; } @@ -255,6 +270,66 @@ namespace CGAL { } return bb; } + + /*! + * \ingroup PkgPolygonMeshProcessingRef + * + * adds an axis-aligned bounding box to a polygon mesh. + * + * @tparam PolygonMesh a model of `MutableFaceGraph` + * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" + * + * @param pmesh a polygon mesh + * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamNBegin{bbox_scaling} + * \cgalParamDescription{a double used to scale the bounding box. + * The default value is 1 and the bounding box is the smallest possible + * axis-aligned bounding box.} + * \cgalParamDefault{1.} + * \cgalParamPrecondition{`bbox_scaling > 0`} + * \cgalParamNEnd + * \cgalParamNBegin{do_not_triangulate_faces} + * \cgalParamDescription{a Boolean used to specify whether the bounding box's faces + * should be triangulated or not. + * The default value is `true`, and faces are not triangulated.} + * \cgalParamDefault{true} + * \cgalParamNEnd + * \cgalParamNBegin{vertex_point_map} + * \cgalParamDescription{a property map associating points to the vertices of `pmesh`} + * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` + * as key type and `%Point_3` as value type} + * \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} + * \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` + * must be available in `PolygonMesh`.} + * \cgalParamNEnd + * \cgalParamNBegin{geom_traits} + * \cgalParamDescription{an instance of a geometric traits class model of `Kernel`.} + * \cgalParamNEnd + * \cgalNamedParamsEnd + * + * @see `bbox()` + */ + template + void add_bbox(PolygonMesh& pmesh, + const NamedParameters& np = parameters::default_values()) + { + using parameters::choose_parameter; + using parameters::get_parameter; + + using GT = typename GetGeomTraits::type; + using P = typename GT::Point_3; + GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + typename GT::Construct_iso_cuboid_3 + iso_cuboid = gt.construct_iso_cuboid_3_object(); + + const CGAL::Bbox_3 bb = bbox(pmesh, np); + CGAL::make_hexahedron(iso_cuboid(P(bb.xmin(), bb.ymin(), bb.zmin()), + P(bb.xmax(), bb.ymax(), bb.zmax())), + pmesh, np); + } } } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h index f37105cd6e6..9cf0e4b43a5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -566,7 +566,7 @@ generic_clip_impl( * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` and `clipper` will be + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm` and `clipper` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} @@ -668,7 +668,7 @@ clip(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm` * and `plane` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} @@ -778,7 +778,7 @@ bool clip(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm` * and `iso_cuboid` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} @@ -980,7 +980,7 @@ void split(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm` * and `plane` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} @@ -1069,7 +1069,7 @@ void split(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm` * and `iso_cuboid` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 8cad7f7227c..c955aa8bdc3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -138,7 +138,7 @@ enum Boolean_operation_type {UNION = 0, INTERSECTION=1, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm1` and `tm2` will be + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm1` and `tm2` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} @@ -477,7 +477,7 @@ corefine_and_compute_boolean_operations( * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true` the set of triangles closed to the intersection of `tm1` and `tm2` will be + * \cgalParamDescription{If `true` the set of triangles close to the intersection of `tm1` and `tm2` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} @@ -644,7 +644,7 @@ corefine_and_compute_difference( TriangleMesh& tm1, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true` the set of triangles closed to the intersection of `tm1` and `tm2` will be + * \cgalParamDescription{If `true` the set of triangles close to the intersection of `tm1` and `tm2` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h index e15ae718823..54b15c7777a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h @@ -154,11 +154,8 @@ public: typedef typename boost::property_map::type Marked_edges_map; - Marked_edges_map marks = get(Edge_property_tag(), mesh_); + Marked_edges_map marks = get(Edge_property_tag(), mesh_, false); - // dynamic pmaps do not have default values... - for(edge_descriptor e : edges(mesh_)) - put(marks, e, false); for(edge_descriptor e : edge_range) put(marks, e, true); #ifdef CGAL_PMP_SMOOTHING_DEBUG diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h index b7c0d086c5c..6dd55b9fc62 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -104,9 +105,7 @@ void simplify_range(HalfedgeRange& halfedge_range, typedef CGAL::dynamic_halfedge_property_t Halfedge_bool_tag; typedef typename boost::property_map::type Range_halfedges; - Range_halfedges range_halfedges = get(Halfedge_bool_tag(), tm); - for(halfedge_descriptor h : halfedge_range) - put(range_halfedges, h, true); + Range_halfedges range_halfedges = get(Halfedge_bool_tag(), tm, false); CGAL_postcondition_code(const std::size_t initial_n = halfedge_range.size();) @@ -1233,10 +1232,10 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A, // We keep in memory pairs of source/target edges that are stitchable after vertex-vertex snapping // --> these halfedges should not be considered as targets in non-conformal snapping // Similarly, matching vertices whose incident edges have matching directions are also locked - Locked_vertices locked_vertices_A = get(Vertex_bool_tag(), tm_A); - Locked_vertices locked_vertices_B = get(Vertex_bool_tag(), tm_B); - Locked_halfedges locked_halfedges_A = get(Halfedge_bool_tag(), tm_A); - Locked_halfedges locked_halfedges_B = get(Halfedge_bool_tag(), tm_B); + Locked_vertices locked_vertices_A = get(Vertex_bool_tag(), tm_A, false); + Locked_vertices locked_vertices_B = get(Vertex_bool_tag(), tm_B, false); + Locked_halfedges locked_halfedges_A = get(Halfedge_bool_tag(), tm_A, false); + Locked_halfedges locked_halfedges_B = get(Halfedge_bool_tag(), tm_B, false); std::vector > locked_vertices; std::vector locked_halfedges_A_vector, locked_halfedges_B_vector; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h index a996edae648..650268e1c40 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/intersection.h @@ -1711,7 +1711,7 @@ OutputIterator intersecting_meshes(const TriangleMeshRange& range, * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} - * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm1` and `tm2` will be + * \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm1` and `tm2` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h index de45ca3150e..46ac4ed6cb6 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h @@ -58,9 +58,7 @@ bool is_non_manifold_vertex(typename boost::graph_traits::vertex_de typedef typename boost::property_map::const_type Visited_halfedge_map; // Dynamic pmaps do not have default initialization values (yet) - Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); - for(halfedge_descriptor h : halfedges(pm)) - put(visited_halfedges, h, false); + Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm, false); std::size_t incident_null_faces_counter = 0; for(halfedge_descriptor h : halfedges_around_target(v, pm)) @@ -324,20 +322,11 @@ OutputIterator non_manifold_vertices(const PolygonMesh& pm, typedef CGAL::dynamic_halfedge_property_t Halfedge_property_tag; typedef typename boost::property_map::const_type Visited_halfedge_map; - Known_manifold_vertex_map known_nm_vertices = get(Vertex_bool_tag(), pm); - Visited_vertex_map visited_vertices = get(Vertex_halfedge_tag(), pm); - Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); - halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); - // Dynamic pmaps do not have default initialization values (yet) - for(vertex_descriptor v : vertices(pm)) - { - put(known_nm_vertices, v, false); - put(visited_vertices, v, null_h); - } - for(halfedge_descriptor h : halfedges(pm)) - put(visited_halfedges, h, false); + Known_manifold_vertex_map known_nm_vertices = get(Vertex_bool_tag(), pm, false); + Visited_vertex_map visited_vertices = get(Vertex_halfedge_tag(), pm, null_h); + Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm, false); for(halfedge_descriptor h : halfedges(pm)) { 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/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h index c71a5d4c806..1dde0a5b20d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h @@ -639,7 +639,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, // Vertex property map that combines the VCM and the fact that extremities of a constrained edge should be constrained typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; typedef typename boost::property_map::type DVCM; - DVCM vcm = get(Vertex_property_tag(), tmesh); + DVCM vcm = get(Vertex_property_tag(), tmesh, false); // parameters const double cap_threshold = diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h index e30f95fd289..343007a83b2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h @@ -190,7 +190,7 @@ struct Boundary_cycle_rep_maintainer Boundary_cycle_rep_maintainer(PolygonMesh& pmesh) : m_pmesh(pmesh) { - m_candidate_halfedges = get(Candidate_tag(), pmesh); + m_candidate_halfedges = get(Candidate_tag(), pmesh, false); } public: 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_mesh_processing/test/Polygon_mesh_processing/connected_component_surface_mesh.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_surface_mesh.cpp index bff0cb6d083..f308a4ce35a 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_surface_mesh.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_surface_mesh.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_transform.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_transform.cpp index 695d4862623..c45de576ffb 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_transform.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_transform.cpp @@ -1,7 +1,7 @@ #include +#include #include -#include #include diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp index d5d2fa63b9d..540aeb84639 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include 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..0c061fc6375 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,13 +22,12 @@ 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 Property_map -Random_numbers STL_Extension Solver_interface Spatial_searching diff --git a/Polyhedron/demo/Polyhedron/Plugins/AABB_tree/Scene_movable_sm_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/AABB_tree/Scene_movable_sm_item.cpp index 96051a607c4..049cfd6611b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/AABB_tree/Scene_movable_sm_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/AABB_tree/Scene_movable_sm_item.cpp @@ -92,7 +92,7 @@ void Scene_movable_sm_item_priv::compute_elements() const typedef boost::graph_traits::face_descriptor face_descriptor; typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef boost::graph_traits::edge_descriptor edge_descriptor; - typedef CGAL::Buffer_for_vao CPF; + typedef CGAL::Buffer_for_vao CPF; flat_vertices.clear(); flat_normals.clear(); edges_vertices.clear(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp index e901d5f3dab..a1c13d1c6a8 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp @@ -131,7 +131,7 @@ public: void computeElements() const override { - typedef CGAL::Buffer_for_vao CPF; + typedef CGAL::Buffer_for_vao CPF; QApplication::setOverrideCursor(Qt::WaitCursor); diff --git a/Polyhedron/demo/Polyhedron/Plugins/IO/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/IO/CMakeLists.txt index ec7b583d8a2..d80560ab5d4 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/IO/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/IO/CMakeLists.txt @@ -79,30 +79,18 @@ endif() polyhedron_demo_plugin(xyz_plugin XYZ_io_plugin KEYWORDS Viewer PointSetProcessing Classification) target_link_libraries(xyz_plugin PUBLIC scene_points_with_normal_item) -list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_rvalue_references has_cxx_rvalues) -list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_variadic_templates has_cxx_variadic) - -if(has_cxx_rvalues LESS 0 OR has_cxx_variadic LESS 0) - message(STATUS "NOTICE: LAS/PLY IO examples require a C++11 compiler and will not be compiled.") -else() - set(needed_cxx_features cxx_rvalue_references cxx_variadic_templates) - - polyhedron_demo_plugin(ply_plugin PLY_io_plugin KEYWORDS Viewer PointSetProcessing Classification PMP) - target_link_libraries(ply_plugin PUBLIC scene_points_with_normal_item scene_polygon_soup_item scene_surface_mesh_item scene_textured_item) - target_compile_features(ply_plugin PRIVATE ${needed_cxx_features}) - - if (TARGET CGAL::LASLIB_support) - polyhedron_demo_plugin(las_plugin LAS_io_plugin KEYWORDS Viewer PointSetProcessing Classification) - target_link_libraries(las_plugin PUBLIC scene_points_with_normal_item CGAL::LASLIB_support) - target_compile_features(las_plugin PRIVATE ${needed_cxx_features}) - if(MSVC) - target_compile_definitions( - las_plugin - PUBLIC "-D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS") - endif() - else() - message(STATUS "NOTICE: the LAS IO plugin needs LAS libraries and will not be compiled.") +polyhedron_demo_plugin(ply_plugin PLY_io_plugin KEYWORDS Viewer PointSetProcessing Classification PMP) +target_link_libraries(ply_plugin PUBLIC scene_points_with_normal_item scene_polygon_soup_item scene_surface_mesh_item scene_textured_item) +if (TARGET CGAL::LASLIB_support) + polyhedron_demo_plugin(las_plugin LAS_io_plugin KEYWORDS Viewer PointSetProcessing Classification) + target_link_libraries(las_plugin PUBLIC scene_points_with_normal_item CGAL::LASLIB_support) + if(MSVC) + target_compile_definitions( + las_plugin + PUBLIC "-D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS") endif() +else() + message(STATUS "NOTICE: the LAS IO plugin needs LAS libraries and will not be compiled.") endif() find_path( diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp index 51355a07b5f..7c4b491ce0b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Clipping_box_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Clipping_box_plugin.cpp index aa32c500594..079100d1fb6 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Clipping_box_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Clipping_box_plugin.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "Selection_visualizer.h" #include "Scene_plane_item.h" diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Create_bbox_mesh_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Create_bbox_mesh_plugin.cpp index ee591ddc04a..c624727032e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Create_bbox_mesh_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Create_bbox_mesh_plugin.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -164,7 +166,7 @@ bbox(bool extended) Scene_item* item; EPICK::Iso_cuboid_3 ic(bbox); SMesh* p = new SMesh; - CGAL::make_hexahedron(ic[0], ic[1], ic[2], ic[3], ic[4], ic[5], ic[6], ic[7], *p); + CGAL::make_hexahedron(ic, *p); item = new Scene_surface_mesh_item(p); item->setName(name + (extended ? " (Extended Bbox)" : " (Bbox)")); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Edit_box_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Edit_box_plugin.cpp index 77569a7b810..dc96135c003 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Edit_box_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Edit_box_plugin.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Pca_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Pca_plugin.cpp index 2fcecd22abd..66331a4a6d2 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Pca_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Pca_plugin.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "Kernel_type.h" #include diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/AddBboxDialog.ui b/Polyhedron/demo/Polyhedron/Plugins/PMP/AddBboxDialog.ui new file mode 100644 index 00000000000..1de3d65e9eb --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/AddBboxDialog.ui @@ -0,0 +1,105 @@ + + + AddBboxDialog + + + + 0 + 0 + 413 + 108 + + + + Dialog + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Triangulate bbox + + + + + + + + + Scaling : + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + DoubleEdit + QLineEdit +

CGAL_double_edit.h
+ + + + + buttonBox + accepted() + AddBboxDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddBboxDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt index 232e6f364d6..412126f18ea 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt @@ -131,14 +131,13 @@ target_link_libraries( PUBLIC scene_surface_mesh_item scene_polylines_item scene_points_with_normal_item) -qt6_wrap_ui( repairUI_FILES RemoveNeedlesDialog.ui SelfSnapDialog.ui) +qt6_wrap_ui( repairUI_FILES RemoveNeedlesDialog.ui SelfSnapDialog.ui AddBboxDialog.ui) polyhedron_demo_plugin(repair_polyhedron_plugin Repair_polyhedron_plugin ${repairUI_FILES} KEYWORDS PMP) target_link_libraries(repair_polyhedron_plugin PUBLIC scene_points_with_normal_item scene_surface_mesh_item scene_polygon_soup_item) if(TARGET CGAL::TBB_support) target_link_libraries(repair_polyhedron_plugin PUBLIC CGAL::TBB_support) endif() - if(TARGET CGAL::Eigen3_support) qt6_wrap_ui(isotropicRemeshingUI_FILES Isotropic_remeshing_dialog.ui) polyhedron_demo_plugin(isotropic_remeshing_plugin Isotropic_remeshing_plugin diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp index 5d87af53f59..d585b374084 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp @@ -25,6 +25,7 @@ #include "ui_RemoveNeedlesDialog.h" #include "ui_SelfSnapDialog.h" +#include "ui_AddBboxDialog.h" #include #include #include @@ -60,6 +61,7 @@ public: actionAutorefineAndRMSelfIntersections = new QAction(tr("Autorefine and Remove Self-Intersections (Deprecated)"), mw); actionRemoveNeedlesAndCaps = new QAction(tr("Remove Needles And Caps")); actionSnapBorders = new QAction(tr("Snap Boundaries")); + actionAddBbox = new QAction(tr("Add Bounding Box")); actionRemoveIsolatedVertices->setObjectName("actionRemoveIsolatedVertices"); actionRemoveDegenerateFaces->setObjectName("actionRemoveDegenerateFaces"); @@ -73,6 +75,7 @@ public: actionAutorefineAndRMSelfIntersections->setObjectName("actionAutorefineAndRMSelfIntersections"); actionRemoveNeedlesAndCaps->setObjectName("actionRemoveNeedlesAndCaps"); actionSnapBorders->setObjectName("actionSnapBorders"); + actionAddBbox->setObjectName("actionAddBbox"); actionRemoveDegenerateFaces->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); actionStitchCloseBorderHalfedges->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); @@ -85,6 +88,7 @@ public: actionNewAutorefine->setProperty("subMenuName", "Polygon Mesh Processing/Repair"); actionAutorefineAndRMSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); actionSnapBorders->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); + actionAddBbox->setProperty("subMenuName", "Polygon Mesh Processing"); autoConnectActions(); } @@ -102,7 +106,8 @@ public: << actionNewAutorefine << actionAutorefineAndRMSelfIntersections << actionRemoveNeedlesAndCaps - << actionSnapBorders; + << actionSnapBorders + << actionAddBbox; } bool applicable(QAction* action) const @@ -127,6 +132,8 @@ public: template void on_actionRemoveDegenerateFaces_triggered(Scene_interface::Item_id index); template + void on_actionAddBbox_triggered(Scene_interface::Item_id index); + template void on_actionRemoveSelfIntersections_triggered(Scene_interface::Item_id index); template void on_actionStitchCloseBorderHalfedges_triggered(Scene_interface::Item_id index); @@ -156,6 +163,7 @@ public Q_SLOTS: void on_actionAutorefineAndRMSelfIntersections_triggered(); void on_actionRemoveNeedlesAndCaps_triggered(); void on_actionSnapBorders_triggered(); + void on_actionAddBbox_triggered(); private: QAction* actionRemoveIsolatedVertices; @@ -170,6 +178,7 @@ private: QAction* actionAutorefineAndRMSelfIntersections; QAction* actionRemoveNeedlesAndCaps; QAction* actionSnapBorders; + QAction* actionAddBbox; Messages_interface* messages; }; // end Polyhedron_demo_repair_polyhedron_plugin @@ -365,6 +374,41 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionSnapBorders_triggered() sm_item->itemChanged(); } +template +void Polyhedron_demo_repair_polyhedron_plugin::on_actionAddBbox_triggered(Scene_interface::Item_id index) +{ + Item* poly_item = + qobject_cast(scene->item(index)); + if (poly_item) + { + QDialog dialog; + Ui::AddBboxDialog ui; + ui.setupUi(&dialog); + ui.triangulate_bbox->setChecked(true); + ui.bbox_scaling->setValue(1.0); + + if(dialog.exec() != QDialog::Accepted) + return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + const double scaling = ui.bbox_scaling->value(); + CGAL::Polygon_mesh_processing::add_bbox(*poly_item->face_graph(), + CGAL::parameters::bbox_scaling(scaling). + do_not_triangulate_faces(!ui.triangulate_bbox->isChecked())); + + poly_item->invalidateOpenGLBuffers(); + Q_EMIT poly_item->itemChanged(); + QApplication::restoreOverrideCursor(); + CGAL::Three::Three::information(tr("Bbox has been added (%1 @%)").arg(scaling)); + } +} + +void Polyhedron_demo_repair_polyhedron_plugin::on_actionAddBbox_triggered() +{ + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + on_actionAddBbox_triggered(index); +} + template void Polyhedron_demo_repair_polyhedron_plugin::on_actionRemoveDegenerateFaces_triggered(Scene_interface::Item_id index) { diff --git a/Polyhedron/demo/Polyhedron/Scene_lcc_item.cpp b/Polyhedron/demo/Polyhedron/Scene_lcc_item.cpp index 1247ab267f1..917a75bb646 100644 --- a/Polyhedron/demo/Polyhedron/Scene_lcc_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_lcc_item.cpp @@ -88,7 +88,7 @@ struct lcc_priv{ faces.push_back(pt->z() + offset.z); } } - else if (CGAL::Buffer_for_vao::is_facet_convex(f.points,f.normal)) + else if (CGAL::Buffer_for_vao::is_facet_convex(f.points,f.normal)) { if (f.size()==4) { diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 4bdd7f76581..d17c3b6f248 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -447,7 +447,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: idx_data_.reserve(num_faces(*smesh_) * 3); - typedef CGAL::Buffer_for_vao CPF; + typedef CGAL::Buffer_for_vao CPF; typedef boost::graph_traits::face_descriptor face_descriptor; typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef boost::graph_traits::edge_descriptor edge_descriptor; @@ -2524,7 +2524,7 @@ void Scene_surface_mesh_item::fill_flat_vertex_map() typedef EPICK::Point_3 Point; typedef CGAL::Surface_mesh SMesh; typedef boost::graph_traits::face_descriptor face_descriptor; - typedef CGAL::Buffer_for_vao CPF; + typedef CGAL::Buffer_for_vao CPF; if(d->flat_vertex_map_ready) return; diff --git a/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp b/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp index 507122e3de6..58c66145fce 100644 --- a/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp @@ -2198,7 +2198,7 @@ void Scene_triangulation_3_item::computeIntersection() void Scene_triangulation_3_item::set_cut_edge(bool b) { d->cut_edges = b; - d->intersection->setCutEdges(b); + if(d->intersection) d->intersection->setCutEdges(b); Q_EMIT redraw(); } diff --git a/Polyhedron/doc/Polyhedron/CGAL/draw_polyhedron.h b/Polyhedron/doc/Polyhedron/CGAL/draw_polyhedron.h index 9ed56fe232c..6d30faa6562 100644 --- a/Polyhedron/doc/Polyhedron/CGAL/draw_polyhedron.h +++ b/Polyhedron/doc/Polyhedron/CGAL/draw_polyhedron.h @@ -3,14 +3,73 @@ namespace CGAL { /*! \ingroup PkgDrawPolyhedron -opens a new window and draws `apoly`, an instance of the `CGAL::Polyhedron_3` 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 POLY an instance of the `CGAL::Polyhedron_3` class. -\param apoly the polyhedron to draw. +opens a new window and draws a polyhedron. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 P which must be an instanciation of a `CGAL::Polyhedron_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param p the polyhedron to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Polyhedron_3& p, const GSOptions& gso); + +\cgalAdvancedEnd */ -template -void draw(const POLY& apoly); +template +void draw(const P& p, const GSOptions& gso); + +/*! +\ingroup PkgDrawPolyhedron + +A shortcut to `CGAL::draw(p, Graphics_scene_options{})`. +*/ + template + void draw(const P& p); + +/*! +\ingroup PkgDrawPolyhedron + +adds the vertices, edges and faces of `p` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam P which must be an instanciation of a `CGAL::Polyhedron_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param p the polyhedron to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Polyhedron_3& p, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const P& p, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawPolyhedron + +A shortcut to `CGAL::add_to_graphics_scene(p, gs, Graphics_scene_options{})`. +*/ +template +void add_to_graphics_scene(const P& p, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Polyhedron/include/CGAL/draw_polyhedron.h b/Polyhedron/include/CGAL/draw_polyhedron.h index 0866e27ebc0..c45ea4e0dd4 100644 --- a/Polyhedron/include/CGAL/draw_polyhedron.h +++ b/Polyhedron/include/CGAL/draw_polyhedron.h @@ -8,58 +8,79 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_POLYHEDRON_H #define CGAL_DRAW_POLYHEDRON_H #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER -#include +#include +#include #include #include -#include +#include namespace CGAL { -// Specialization of draw function. #define CGAL_POLY_TYPE CGAL::Polyhedron_3 \ +// Specialization of add_to_graphics_scene function. +template + class T_HDS, + class Alloc, + class GSOptions> +void add_to_graphics_scene(const CGAL_POLY_TYPE& apoly, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) +{ add_to_graphics_scene_for_fg(apoly, graphics_scene, gs_options); } + +template + class T_HDS, + class Alloc> +void add_to_graphics_scene(const CGAL_POLY_TYPE& apoly, + CGAL::Graphics_scene &graphics_scene) +{ add_to_graphics_scene_for_fg(apoly, graphics_scene); } + +// Specialization of draw function: require Qt and the CGAL basic viewer. +#ifdef CGAL_USE_BASIC_VIEWER + template class T_HDS, class Alloc> void draw(const CGAL_POLY_TYPE& apoly, - const char* title="Polyhedron Basic Viewer", - bool nofill=false) + const char* title="Polyhedron 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) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"polyhedron_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimpleFaceGraphViewerQt - mainwindow(app.activeWindow(), apoly, title, nofill); - mainwindow.show(); - app.exec(); - } + CGAL::Graphics_scene buffer; + add_to_graphics_scene_for_fg(apoly, buffer); + draw_graphics_scene(buffer, title); } +template + class T_HDS, + class Alloc, + class GSOptions> +void draw(const CGAL_POLY_TYPE& apoly, + const GSOptions &gs_options, + const char* title="Polyhedron Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene_for_fg(apoly, buffer, gs_options); + draw_graphics_scene(buffer, title); +} +#endif // CGAL_USE_BASIC_VIEWER + #undef CGAL_POLY_TYPE } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_POLYHEDRON_H diff --git a/Polyhedron/package_info/Polyhedron/dependencies b/Polyhedron/package_info/Polyhedron/dependencies index 53bd7918c4d..7ab360cb471 100644 --- a/Polyhedron/package_info/Polyhedron/dependencies +++ b/Polyhedron/package_info/Polyhedron/dependencies @@ -1,10 +1,12 @@ Algebraic_foundations +Arithmetic_kernel BGL +Basic_viewer Cartesian_kernel Circulator Distance_2 Distance_3 -GraphicsView +Filtered_kernel HalfedgeDS Hash_map Homogeneous_kernel @@ -13,12 +15,18 @@ Intersections_2 Intersections_3 Interval_support Kernel_23 +Kernel_d Modifier Modular_arithmetic Number_types +Polygon Polyhedron Profiling_tools Property_map Random_numbers STL_Extension +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 +CGAL_Core diff --git a/Polyline_simplification_2/include/CGAL/Polyline_simplification_2/Stop_above_cost_threshold.h b/Polyline_simplification_2/include/CGAL/Polyline_simplification_2/Stop_above_cost_threshold.h index fd4648fa01d..5c8b0e6e207 100644 --- a/Polyline_simplification_2/include/CGAL/Polyline_simplification_2/Stop_above_cost_threshold.h +++ b/Polyline_simplification_2/include/CGAL/Polyline_simplification_2/Stop_above_cost_threshold.h @@ -36,7 +36,7 @@ public : /// Initializes it with the given threshold value. Stop_above_cost_threshold( double threshold ) : mThres(threshold) {} - /// Returns `true` when `cost` is smaller or equal than the threshold. + /// Returns `true` when `cost` is greater or equal than the threshold. /// \tparam CDT must be `CGAL::Constrained_Delaunay_triangulation_2` with a vertex type that /// is model of `PolylineSimplificationVertexBase_2`. @@ -61,4 +61,3 @@ private: } //namespace CGAL #endif // CGAL_POLYLINE_SIMPLIFICATION_2_STOP_ABOVE_COST_THRESHOLD_H - 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 @@ -14,16 +15,24 @@ int main() CGAL::make_triangle(Point_3(0,0,0),Point_3(1,0,0),Point_3(1,1,0), mesh); typedef boost::property_map >::type VertexNameMap; - VertexNameMap vnm = get(CGAL::dynamic_vertex_property_t(), mesh); + VertexNameMap vnm = get(CGAL::dynamic_vertex_property_t(), mesh, std::string("default")); put(vnm, *(vertices(mesh).first), "Paris"); + assert(get(vnm, *(vertices(mesh).first))=="Paris"); + assert(get(vnm, *(std::next(vertices(mesh).first)))=="default"); + std::cout << get(vnm, *(vertices(mesh).first)) << std::endl; + std::cout << get(vnm, *(std::next(vertices(mesh).first))) << std::endl; typedef boost::property_map >::type TrafficDensityMap; - TrafficDensityMap tdm = get(CGAL::dynamic_halfedge_property_t(), mesh); + TrafficDensityMap tdm = get(CGAL::dynamic_halfedge_property_t(), mesh, -1.); put(tdm, *(halfedges(mesh).first), 0.7); + assert(get(tdm, *(halfedges(mesh).first))==0.7); + assert(get(tdm, *(std::next(halfedges(mesh).first)))==-1.); + std::cout << get(tdm, *(halfedges(mesh).first)) << std::endl; + std::cout << get(tdm, *(std::next(halfedges(mesh).first))) << std::endl; return 0; } diff --git a/Property_map/include/CGAL/Dynamic_property_map.h b/Property_map/include/CGAL/Dynamic_property_map.h index bca414e19b0..949eed33382 100644 --- a/Property_map/include/CGAL/Dynamic_property_map.h +++ b/Property_map/include/CGAL/Dynamic_property_map.h @@ -135,8 +135,8 @@ struct Dynamic_with_index : m_values() {} - Dynamic_with_index(std::size_t num_features) - : m_values( new std::vector(num_features) ) + Dynamic_with_index(std::size_t num_features, Value default_value = Value()) + : m_values( new std::vector(num_features, default_value) ) {} friend reference get(const Dynamic_with_index& m, const key_type& k) @@ -228,34 +228,34 @@ namespace CGAL { template typename boost::property_map >::const_type -get(const CGAL::dynamic_vertex_property_t&, const G&) +get(const CGAL::dynamic_vertex_property_t&, const G&, const T& default_value = T()) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - return internal::Dynamic_property_map(); + return internal::Dynamic_property_map(default_value); } template typename boost::property_map >::const_type -get(const CGAL::dynamic_halfedge_property_t&, const G&) +get(const CGAL::dynamic_halfedge_property_t&, const G&, const T& default_value = T()) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - return internal::Dynamic_property_map(); + return internal::Dynamic_property_map(default_value); } template typename boost::property_map >::const_type -get(const CGAL::dynamic_edge_property_t&, const G&) +get(const CGAL::dynamic_edge_property_t&, const G&, const T& default_value = T()) { typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - return internal::Dynamic_property_map(); + return internal::Dynamic_property_map(default_value); } template typename boost::property_map >::const_type -get(const CGAL::dynamic_face_property_t&, const G&) +get(const CGAL::dynamic_face_property_t&, const G&, const T& default_value = T()) { typedef typename boost::graph_traits::face_descriptor face_descriptor; - return internal::Dynamic_property_map(); + return internal::Dynamic_property_map(default_value); } template diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h new file mode 100644 index 00000000000..739f652b318 --- /dev/null +++ b/Property_map/include/CGAL/Property_container.h @@ -0,0 +1,630 @@ +// Copyright (c) 2023 INRIA +// 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) : Jackson Campolattaro + +#ifndef CGAL_PROPERTY_CONTAINTER_H +#define CGAL_PROPERTY_CONTAINTER_H + +#include + +#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/dynamic_properties_test.cpp b/Property_map/test/Property_map/dynamic_properties_test.cpp index 41a4ea8abce..07fd4751c4c 100644 --- a/Property_map/test/Property_map/dynamic_properties_test.cpp +++ b/Property_map/test/Property_map/dynamic_properties_test.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #if defined(CGAL_USE_OPENMESH) #include diff --git a/Property_map/test/Property_map/kernel_converter_properties_test.cpp b/Property_map/test/Property_map/kernel_converter_properties_test.cpp index ebe871bc3e3..05cf177b325 100644 --- a/Property_map/test/Property_map/kernel_converter_properties_test.cpp +++ b/Property_map/test/Property_map/kernel_converter_properties_test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include 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/Ridges_3/package_info/Ridges_3/dependencies b/Ridges_3/package_info/Ridges_3/dependencies index 3d5486b85be..f4b40909932 100644 --- a/Ridges_3/package_info/Ridges_3/dependencies +++ b/Ridges_3/package_info/Ridges_3/dependencies @@ -9,7 +9,6 @@ Number_types Principal_component_analysis_LGPL Profiling_tools Property_map -Random_numbers Ridges_3 STL_Extension Stream_support 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..cc7e6969e77 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 @@ -19,7 +20,6 @@ Number_types Polygon_mesh_processing Profiling_tools Property_map -Random_numbers SMDS_3 STL_Extension Spatial_sorting diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index 9b172f5b4c1..7f84dd628e3 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -163,6 +163,7 @@ CGAL_add_named_parameter(patch_normal_map_t, patch_normal_map, patch_normal_map) CGAL_add_named_parameter(region_primitive_map_t, region_primitive_map, region_primitive_map) CGAL_add_named_parameter(postprocess_regions_t, postprocess_regions, postprocess_regions) CGAL_add_named_parameter(sizing_function_t, sizing_function, sizing_function) +CGAL_add_named_parameter(bbox_scaling_t, bbox_scaling, bbox_scaling) // List of named parameters that we use in the package 'Surface Mesh Simplification' CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) 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_2/test/Segment_Delaunay_graph_2/issue7972.cpp b/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp index 6dd4a4e5e2b..dc320717cf9 100644 --- a/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp +++ b/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp @@ -7,15 +7,13 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef CGAL::Segment_Delaunay_graph_traits_2 Gt; typedef CGAL::Segment_Delaunay_graph_2 SDG2; -int main() { - auto segments = std::vector({ - CGAL::Segment_2( - CGAL::Point_2(0.0, 0.0), - CGAL::Point_2(1.0, 0.0)) - }); +int main() +{ + std::vector> segments; + segments.emplace_back(CGAL::Point_2(0.0, 0.0),CGAL::Point_2(1.0, 0.0)); - SDG2 delaunay; - delaunay.insert_segments(segments.begin(), segments.end()); + SDG2 delaunay; + delaunay.insert_segments(segments.begin(), segments.end()); - return 0; + return 0; } 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/Polygon_mesh_processing/region_growing.h b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h index fc594b9122f..d6f78085c44 100644 --- a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h +++ b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h @@ -354,6 +354,7 @@ detect_corners_of_regions( using parameters::get_parameter; using parameters::is_default_parameter; + using VPM = typename GetVertexPointMap < PolygonMesh, NamedParameters>::const_type; using Traits = typename GetGeomTraits::type; using Graph_traits = boost::graph_traits; using halfedge_descriptor = typename Graph_traits::halfedge_descriptor; @@ -377,7 +378,7 @@ detect_corners_of_regions( } Ecm ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), dynamic_ecm); - using Polyline_graph = CGAL::Shape_detection::Polygon_mesh::Polyline_graph; + using Polyline_graph = CGAL::Shape_detection::Polygon_mesh::Polyline_graph; using Segment_map = typename Polyline_graph::Segment_map; using Item = typename Polyline_graph::Item; @@ -424,7 +425,7 @@ detect_corners_of_regions( filtered_edges.push_back(e); } - Polyline_graph pgraph(mesh, filtered_edges, region_map); + Polyline_graph pgraph(mesh, filtered_edges, region_map, np); const auto& segment_range = pgraph.segment_range(); Line_region line_region(np.segment_map(pgraph.segment_map())); 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..26fdaaadd2f 100644 --- a/Shape_detection/package_info/Shape_detection/dependencies +++ b/Shape_detection/package_info/Shape_detection/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Shape_regularization/package_info/Shape_regularization/dependencies b/Shape_regularization/package_info/Shape_regularization/dependencies index 5cf917f32ed..353a79ca562 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 diff --git a/Skin_surface_3/package_info/Skin_surface_3/dependencies b/Skin_surface_3/package_info/Skin_surface_3/dependencies index fb635c6334c..c65e98b4cf0 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 @@ -21,7 +22,6 @@ Number_types Polyhedron Profiling_tools Property_map -Random_numbers STL_Extension Skin_surface_3 Spatial_sorting 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/Straight_skeleton_2/include/CGAL/draw_straight_skeleton_2.h b/Straight_skeleton_2/include/CGAL/draw_straight_skeleton_2.h index 3238ea1e0b1..8ff502ba5e3 100644 --- a/Straight_skeleton_2/include/CGAL/draw_straight_skeleton_2.h +++ b/Straight_skeleton_2/include/CGAL/draw_straight_skeleton_2.h @@ -8,176 +8,202 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_SS2_H #define CGAL_DRAW_SS2_H #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER - +#include +#include +#include #include -#include - #include namespace CGAL { -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorSS2 +namespace draw_function_for_ss2 { + +template +void compute_edge(const SS2& ss2, typename SS2::Halfedge_const_handle eh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) { - template - static CGAL::IO::Color run(const SS2&, - const typename SS2::Finite_faces_iterator fh) - { - CGAL::Random random((unsigned int)(std::size_t)(&*fh)); - return get_random_color(random); - } -}; + if (!gs_options.draw_edge(ss2, eh)) + { return; } -// Viewer class for SS2 -template -class SimpleStraightSkeleton2ViewerQt : public Basic_viewer_qt + if (gs_options.colored_edge(ss2, eh)) + { + graphics_scene.add_segment(eh->opposite()->vertex()->point(), + eh->vertex()->point(), + gs_options.edge_color(ss2, eh)); + } + else + { + graphics_scene.add_segment(eh->opposite()->vertex()->point(), + eh->vertex()->point()); + } +} + +template +void print_halfedge_labels(const SS2& ss2, + typename SS2::Halfedge_const_handle h, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) { - typedef Basic_viewer_qt Base; - typedef typename SS2::Vertex_const_handle Vertex_const_handle; - typedef typename SS2::Halfedge_const_handle Halfedge_const_handle; + // TODO? an option different from draw_edge to allow to show only some labels ?? + if(!gs_options.draw_edge(ss2, h)) + { return; } - // typedef typename SS2::Point Point; + std::stringstream label; + label << "H" << h->id() << " (V" << h->vertex()->id() << ") "; + label << "H" << h->opposite()->id() << " (V" << h->opposite()->vertex()->id() + << ") "; -public: - /// Construct the viewer. - /// @param ass2 the ss2 to view - /// @param title the title of the window - SimpleStraightSkeleton2ViewerQt(QWidget* parent, const SS2& ass2, - const char* title="Basic SS2 Viewer", - const ColorFunctor& fcolor=ColorFunctor()) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - ss2(ass2), - m_fcolor(fcolor) + graphics_scene.add_text( + CGAL::midpoint(h->opposite()->vertex()->point(), h->vertex()->point()), + label.str()); +} + +template +void compute_vertex(const SS2& ss2, typename SS2::Vertex_const_handle vh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (!gs_options.draw_vertex(ss2, vh)) + { return; } + + if (gs_options.colored_vertex(ss2, vh)) { - compute_elements(); + graphics_scene.add_point(vh->point(), gs_options.vertex_color(ss2, vh)); } + else + { graphics_scene.add_point(vh->point()); } +} -protected: - /* - void compute_face(Facet_const_handle fh) - { - CGAL::IO::Color c=m_fcolor.run(ss2, fh); - face_begin(c); - - add_point_in_face(fh->vertex(0)->point()); - add_point_in_face(fh->vertex(1)->point()); - add_point_in_face(fh->vertex(2)->point()); - - face_end(); - } - */ - void compute_edge(Halfedge_const_handle eh) - { - if(eh->is_bisector()) - add_segment(eh->opposite()->vertex()->point(), eh->vertex()->point(), CGAL::IO::red()); - else - add_segment(eh->opposite()->vertex()->point(), eh->vertex()->point(), CGAL::IO::black()); - } - void print_halfedge_labels(Halfedge_const_handle h) - { - std::stringstream label; - label << "H" << h->id() << " (V" << h->vertex()->id() << ") "; - label << "H" << h->opposite()->id() << " (V" << h->opposite()->vertex()->id() << ") "; - add_text(CGAL::midpoint(h->opposite()->vertex()->point(), h->vertex()->point()), label.str()); - } - - void compute_vertex(Vertex_const_handle vh) - { - if(vh->is_split()) - add_point(vh->point(), CGAL::IO::Color(10,10,180)); // blue, but not flashy - else if(vh->has_infinite_time()) - add_point(vh->point(), CGAL::IO::orange()); - else if(vh->is_contour()) - add_point(vh->point(), CGAL::IO::black()); - else - add_point(vh->point(), CGAL::IO::Color(10,180,10)); // green, but not flashy - } - void print_vertex_label(Vertex_const_handle vh) +template +void print_vertex_label(const SS2& ss2, + typename SS2::Vertex_const_handle vh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + // TODO? an option different from draw_vertex to allow to show only some labels ?? + if (gs_options.draw_vertex(ss2, vh)) { std::stringstream label; label << "V" << vh->id() << std::ends; - add_text(vh->point(), label.str()); - } - - void compute_elements() - { - clear(); - - for (typename SS2::Halfedge_const_iterator it=ss2.halfedges_begin(); it!=ss2.halfedges_end(); ++it) - { - if(it->id() < it->opposite()->id()) - { - compute_edge(it); - print_halfedge_labels(it); - } - } - for (typename SS2::Vertex_const_iterator it=ss2.vertices_begin(); it!=ss2.vertices_end(); ++it) - { - compute_vertex(it); - print_vertex_label(it); - } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); - } - -protected: - const SS2& ss2; - const ColorFunctor& m_fcolor; -}; - -// Specialization of draw function. -#define CGAL_SS_TYPE CGAL::Straight_skeleton_2 - -template -void draw(const CGAL_SS_TYPE& ass2, - const char* title="Straight Skeleton 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) - { - int argc=1; - const char* argv[2]={"ss2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - DefaultColorFunctorSS2 fcolor; - SimpleStraightSkeleton2ViewerQt - mainwindow(app.activeWindow(), ass2, title, fcolor); - mainwindow.show(); - app.exec(); + graphics_scene.add_text(vh->point(), label.str()); } } +template +void compute_elements(const SS2& ss2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (gs_options.are_edges_enabled()) + { + for (typename SS2::Halfedge_const_iterator it=ss2.halfedges_begin(); + it != ss2.halfedges_end(); ++it) + { + if (it->id()opposite()->id()) + { + compute_edge(ss2, it, graphics_scene, gs_options); + print_halfedge_labels(ss2, it, graphics_scene, gs_options); + } + } + } + + if (gs_options.are_vertices_enabled()) + { + for (typename SS2::Vertex_const_iterator it=ss2.vertices_begin(); + it!=ss2.vertices_end(); ++it) + { + compute_vertex(ss2, it, graphics_scene, gs_options); + print_vertex_label(ss2, it, graphics_scene, gs_options); + } + } +} + +} // namespace draw_function_for_ss2 + +#define CGAL_SS_TYPE CGAL::Straight_skeleton_2 + +template +void add_to_graphics_scene(const CGAL_SS_TYPE &ass2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + draw_function_for_ss2::compute_elements(ass2, graphics_scene, + gs_options); +} + +template +void add_to_graphics_scene(const CGAL_SS_TYPE& ass2, + CGAL::Graphics_scene& graphics_scene) +{ + Graphics_scene_options + drawingFunctor; + + drawingFunctor.colored_edge = [] + (const CGAL_SS_TYPE&, typename CGAL_SS_TYPE::Halfedge_const_handle) -> bool + { return true; }; + + drawingFunctor.colored_vertex = [] + (const CGAL_SS_TYPE&, typename CGAL_SS_TYPE::Vertex_const_handle) -> bool + { return true; }; + + drawingFunctor.edge_color = [] + (const CGAL_SS_TYPE&, typename CGAL_SS_TYPE::Halfedge_const_handle eh) -> CGAL::IO::Color + { + if (eh->is_bisector()) + { return CGAL::IO::red(); } + + return CGAL::IO::black(); + }; + + drawingFunctor.vertex_color = [] + (const CGAL_SS_TYPE&, typename CGAL_SS_TYPE::Vertex_const_handle vh) -> CGAL::IO::Color + { + if (vh->is_split()) + { return CGAL::IO::Color(10, 10, 180); } // blue, but not flashy + else if (vh->has_infinite_time()) + { return CGAL::IO::orange(); } + + return CGAL::IO::Color(10, 180, 10); // green, but not flashy + }; + + add_to_graphics_scene(ass2, graphics_scene, drawingFunctor); +} + +// Specialization of draw function. +#ifdef CGAL_USE_BASIC_VIEWER + +template +void draw(const CGAL_SS_TYPE &ass2, const GSOptions &gs_options, + const char *title="Straight Skeleton Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ass2, buffer, gs_options); + draw_graphics_scene(buffer, title); +} + +template +void draw(const CGAL_SS_TYPE &ass2, + const char *title="Straight Skeleton Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(ass2, buffer); + draw_graphics_scene(buffer, title); +} + +#endif // CGAL_USE_BASIC_VIEWER + #undef CGAL_SS_TYPE } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_SS2_H diff --git a/Straight_skeleton_2/package_info/Straight_skeleton_2/dependencies b/Straight_skeleton_2/package_info/Straight_skeleton_2/dependencies index ca2e964a173..0fb898c6ec4 100644 --- a/Straight_skeleton_2/package_info/Straight_skeleton_2/dependencies +++ b/Straight_skeleton_2/package_info/Straight_skeleton_2/dependencies @@ -1,13 +1,13 @@ Algebraic_foundations Arithmetic_kernel BGL +Basic_viewer CGAL_Core Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView HalfedgeDS Hash_map Homogeneous_kernel @@ -22,6 +22,10 @@ Number_types Polygon Profiling_tools Property_map +Random_numbers STL_Extension +Spatial_sorting Straight_skeleton_2 Stream_support +TDS_2 +Triangulation_2 diff --git a/Straight_skeleton_extrusion_2/package_info/Straight_skeleton_extrusion_2/dependencies b/Straight_skeleton_extrusion_2/package_info/Straight_skeleton_extrusion_2/dependencies index eafd536cd0a..354e1eb0c02 100644 --- a/Straight_skeleton_extrusion_2/package_info/Straight_skeleton_extrusion_2/dependencies +++ b/Straight_skeleton_extrusion_2/package_info/Straight_skeleton_extrusion_2/dependencies @@ -22,7 +22,6 @@ Polygon Polygon_mesh_processing Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Straight_skeleton_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 - #include - #include typedef CGAL::Simple_cartesian Kernel; @@ -16,7 +14,7 @@ int main(int argc, char* argv[]) Mesh sm; if(!CGAL::IO::read_polygon_mesh(filename, sm)) { - std::cerr << "Invalid input file." << std::endl; + std::cerr << "Invalid input file: " << filename << std::endl; return EXIT_FAILURE; } @@ -28,15 +26,15 @@ int main(int argc, char* argv[]) for(auto v : vertices(sm)) { if(v.idx()%2) - put(vcm, v, CGAL::IO::black()); + { put(vcm, v, CGAL::IO::black()); } else - put(vcm, v, CGAL::IO::blue()); + { put(vcm, v, CGAL::IO::blue()); } } for(auto e : edges(sm)) - put(ecm, e, CGAL::IO::gray()); + { put(ecm, e, CGAL::IO::gray()); } - CGAL_USE(fcm); + put(fcm, *(sm.faces().begin()), CGAL::IO::red()); // Draw! CGAL::draw(sm); diff --git a/Surface_mesh/examples/Surface_mesh/draw_surface_mesh_small_faces.h b/Surface_mesh/examples/Surface_mesh/draw_surface_mesh_small_faces.h deleted file mode 100644 index b8eeac5b008..00000000000 --- a/Surface_mesh/examples/Surface_mesh/draw_surface_mesh_small_faces.h +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2018 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) : Guillaume Damiand - -#ifndef CGAL_DRAW_SURFACE_MESH_SMALL_FACES_H -#define CGAL_DRAW_SURFACE_MESH_SMALL_FACES_H - -#include -#include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include -#include - -#include - -template -class SimpleSurfaceMeshWithSmallFacesViewerQt : public CGAL::Basic_viewer_qt -{ - typedef CGAL::Basic_viewer_qt Base; - typedef typename SM::Point Point; - typedef typename CGAL::Kernel_traits::Kernel Kernel; - typedef typename SM::Vertex_index vertex_descriptor; - typedef typename SM::Face_index face_descriptor; - typedef typename SM::Edge_index edge_descriptor; - typedef typename SM::Halfedge_index halfedge_descriptor; - typedef typename Kernel::FT FT; - -public: - /// Construct the viewer. - /// @param amesh the surface mesh to view - SimpleSurfaceMeshWithSmallFacesViewerQt(QWidget* parent, - SM& amesh) : - // First draw: no vertex; no edge, faces; multi-color; inverse normal - Base(parent, "Surface mesh viewer with small faces", false, false, true, false, false), - sm(amesh), - m_threshold(85), - m_draw_small_faces(true), - m_draw_big_faces(true) - { - // Add custom key description (see keyPressEvent). - setKeyDescription(Qt::Key_I, "Increment threshold for small faces"); - setKeyDescription(Qt::Key_D, "Decrement threshold for small faces"); - setKeyDescription(Qt::Key_S, "Draw small faces only , big faces only, both"); - - if (sm.faces().begin()!=sm.faces().end()) - { - bool exist; - typename SM::template Property_map faces_size; - boost::tie(faces_size, exist)=sm.template property_map("f:size"); - assert(exist); - - m_min_size=faces_size[*(sm.faces().begin())]; - m_max_size=m_min_size; - FT cur_size; - - for (typename SM::Face_range::iterator f=sm.faces().begin(); f!=sm.faces().end(); ++f) - { - cur_size=faces_size[*f]; - if (cur_sizem_max_size) m_max_size=cur_size; - } - } - - compute_elements(); - } - -protected: - void compute_face(face_descriptor fh) - { - // [Face creation] - bool issmall=false; - - // Default color of faces - CGAL::IO::Color c(75,160,255); - - // Compare the size of the face with the % m_threshold - bool exist; - typename SM::template Property_map faces_size; - boost::tie(faces_size, exist)=sm.template property_map("f:size"); - assert(exist); - - // It it is smaller, color the face in red. - if (get(faces_size, fh)::null_face()) - { compute_face(*f); } - } - - for (typename SM::Edge_range::iterator e=sm.edges().begin(); - e!=sm.edges().end(); ++e) - { compute_edge(*e); } - - for (typename SM::Vertex_range::iterator v=sm.vertices().begin(); - v!=sm.vertices().end(); ++v) - { compute_vertex(*v); } - } - - // Call: * compute_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 - virtual void keyPressEvent(QKeyEvent *e) - { - /// [Keypress] - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - if ((e->key()==Qt::Key_I) && (modifiers==Qt::NoButton)) - { - if (m_threshold<100) { ++m_threshold; } - displayMessage(QString("Threshold percent=%1%.").arg(m_threshold)); - compute_elements(); - redraw(); - } - else if ((e->key()==Qt::Key_D) && (modifiers==Qt::NoButton)) - { - if (m_threshold>0) { --m_threshold; } - displayMessage(QString("Threshold percent=%1%.").arg(m_threshold)); - compute_elements(); - redraw(); - } - else if ((e->key()==Qt::Key_S) && (modifiers==Qt::NoButton)) - { - QString msg; - if (m_draw_small_faces) - { - if (m_draw_big_faces) - { - m_draw_big_faces=false; - msg=QString("Draw small faces only."); - } - else - { - m_draw_big_faces=true; m_draw_small_faces=false; - msg=QString("Draw big faces only."); - } - } - else - { - assert(m_draw_big_faces); - m_draw_small_faces=true; - msg=QString("Draw small and big faces."); - } - - displayMessage(msg); - compute_elements(); - redraw(); - } - else - { - // Call the base method to process others/classicals key - Base::keyPressEvent(e); - } - /// [Keypress] - } - -protected: - typename Kernel::Vector_3 get_face_normal(halfedge_descriptor he) - { - typename Kernel::Vector_3 normal=CGAL::NULL_VECTOR; - halfedge_descriptor end=he; - unsigned int nb=0; - do - { - CGAL::internal::newell_single_step_3(sm.point(sm.source(he)), - sm.point(sm.target(he)), normal); - ++nb; - he=sm.next(he); - } - while (he!=end); - assert(nb>0); - return (typename Kernel::Construct_scaled_vector_3()(normal, 1.0/nb)); - } - - typename Kernel::Vector_3 get_vertex_normal(halfedge_descriptor he) - { - typename Kernel::Vector_3 normal=CGAL::NULL_VECTOR; - halfedge_descriptor end=he; - do - { - if (!sm.is_border(he)) - { - typename Kernel::Vector_3 n=get_face_normal(he); - normal=typename Kernel::Construct_sum_of_vectors_3()(normal, n); - } - he=sm.next(sm.opposite(he)); - } - while (he!=end); - - if (!typename Kernel::Equal_3()(normal, CGAL::NULL_VECTOR)) - { normal=(typename Kernel::Construct_scaled_vector_3() - (normal, 1.0/CGAL::sqrt(normal.squared_length()))); } - - return normal; - } - -protected: - SM& sm; - unsigned int m_threshold; - FT m_min_size, m_max_size; - bool m_draw_small_faces; - bool m_draw_big_faces; -}; - -template -void draw_surface_mesh_with_small_faces(CGAL::Surface_mesh& amesh) -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - int argc=1; - const char* argv[2]={"surface_mesh_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimpleSurfaceMeshWithSmallFacesViewerQt> - mainwindow(app.activeWindow(), amesh); - mainwindow.show(); - app.exec(); - } -} - -#else // CGAL_USE_BASIC_VIEWER - -template -void draw_surface_mesh_with_small_faces(CGAL::Surface_mesh&) -{ - std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< #include #include +#include typedef CGAL::Simple_cartesian K; typedef K::Point_3 Point; diff --git a/Surface_mesh/examples/Surface_mesh/sm_draw_small_faces.cpp b/Surface_mesh/examples/Surface_mesh/sm_draw_small_faces.cpp deleted file mode 100644 index 5d6c3b3d54e..00000000000 --- a/Surface_mesh/examples/Surface_mesh/sm_draw_small_faces.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "draw_surface_mesh_small_faces.h" - -typedef CGAL::Simple_cartesian K; -typedef CGAL::Surface_mesh Mesh; -typedef Mesh::Vertex_index vertex_descriptor; -typedef Mesh::Face_index face_descriptor; -typedef K::FT FT; - -int main(int argc, char* argv[]) -{ - const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); - - Mesh sm; - if(!CGAL::IO::read_polygon_mesh(filename, sm)) - { - std::cerr << "Invalid input file." << std::endl; - return EXIT_FAILURE; - } - - CGAL::Polygon_mesh_processing::triangulate_faces(sm); - - Mesh::Property_map faces_size; - bool created; - boost::tie(faces_size, created)=sm.add_property_map("f:size",0.); - assert(created); - - for(face_descriptor fd : sm.faces()) - { faces_size[fd]=CGAL::Polygon_mesh_processing::face_area(fd, sm); } - - draw_surface_mesh_with_small_faces(sm); - - sm.remove_property_map(faces_size); - - return EXIT_SUCCESS; -} diff --git a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h index 785cec65003..fc6ea9d8b9f 100644 --- a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h @@ -353,67 +353,67 @@ namespace CGAL { // get functions for dynamic properties of mutable Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::type -get(dynamic_vertex_property_t, Surface_mesh& sm) +get(dynamic_vertex_property_t, Surface_mesh& sm, const T& default_value = T()) { typedef typename boost::property_map, dynamic_vertex_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_vertex_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Vertex_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Vertex_index, T>(std::string(), default_value).first)); } template typename boost::property_map, dynamic_face_property_t >::type -get(dynamic_face_property_t, Surface_mesh& sm) +get(dynamic_face_property_t, Surface_mesh& sm, const T& default_value = T()) { typedef typename boost::property_map, dynamic_face_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_face_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Face_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Face_index, T>(std::string(), default_value).first)); } template typename boost::property_map, dynamic_edge_property_t >::type -get(dynamic_edge_property_t, Surface_mesh& sm) +get(dynamic_edge_property_t, Surface_mesh& sm, const T& default_value = T()) { typedef typename boost::property_map, dynamic_edge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_edge_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Edge_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Edge_index, T>(std::string(), default_value).first)); } template typename boost::property_map, dynamic_halfedge_property_t >::type -get(dynamic_halfedge_property_t, Surface_mesh& sm) +get(dynamic_halfedge_property_t, Surface_mesh& sm, const T& default_value = T()) { typedef typename boost::property_map, dynamic_halfedge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_halfedge_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Halfedge_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Halfedge_index, T>(std::string(), default_value).first)); } // get functions for dynamic properties of const Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::const_type -get(dynamic_vertex_property_t, const Surface_mesh& sm) +get(dynamic_vertex_property_t, const Surface_mesh& sm, const T& default_value = T()) { - return CGAL::internal::Dynamic_with_index::Vertex_index, T>(num_vertices(sm)); + return CGAL::internal::Dynamic_with_index::Vertex_index, T>(num_vertices(sm), default_value); } template typename boost::property_map, dynamic_face_property_t >::const_type -get(dynamic_face_property_t, const Surface_mesh& sm) +get(dynamic_face_property_t, const Surface_mesh& sm, const T& default_value = T()) { - return CGAL::internal::Dynamic_with_index::Face_index, T>(num_faces(sm)); + return CGAL::internal::Dynamic_with_index::Face_index, T>(num_faces(sm), default_value); } template typename boost::property_map, dynamic_halfedge_property_t >::const_type -get(dynamic_halfedge_property_t, const Surface_mesh& sm) +get(dynamic_halfedge_property_t, const Surface_mesh& sm, const T& default_value = T()) { - return CGAL::internal::Dynamic_with_index::Halfedge_index, T>(num_halfedges(sm)); + return CGAL::internal::Dynamic_with_index::Halfedge_index, T>(num_halfedges(sm), default_value); } template typename boost::property_map, dynamic_edge_property_t >::const_type -get(dynamic_edge_property_t, const Surface_mesh& sm) +get(dynamic_edge_property_t, const Surface_mesh& sm, const T& default_value = T()) { - return CGAL::internal::Dynamic_with_index::Edge_index, T>(num_edges(sm)); + return CGAL::internal::Dynamic_with_index::Edge_index, T>(num_edges(sm), default_value); } // implementation detail: required by Dynamic_property_map_deleter diff --git a/Surface_mesh/include/CGAL/draw_surface_mesh.h b/Surface_mesh/include/CGAL/draw_surface_mesh.h index 72158e05836..776aa5097b2 100644 --- a/Surface_mesh/include/CGAL/draw_surface_mesh.h +++ b/Surface_mesh/include/CGAL/draw_surface_mesh.h @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_SURFACE_MESH_H #define CGAL_DRAW_SURFACE_MESH_H @@ -17,107 +18,179 @@ /*! \ingroup PkgDrawSurfaceMesh -Open a new window and draw `asm`, an instance of the `CGAL::Surface_mesh` class. The 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 SM an instance of the `CGAL::Surface_mesh` class. -\param asm the surface mesh to draw. +opens a new window and draws a surface mesh. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 SM which must be an instanciation of a `CGAL::Surface_mesh<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param sm the surface mesh to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Surface_mesh& sm, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const SM& sm, const GSOptions& gso); + +/*! +\ingroup PkgDrawSurfaceMesh + +A shortcut to `CGAL::draw(sm, Graphics_scene_options{})`. +*/ + template + void draw(const SM& sm); + +/*! +\ingroup PkgDrawSurfaceMesh + +adds the vertices, edges and faces of `sm` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam SM which must be an instanciation of a `CGAL::Surface_mesh<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param sm the surface mesh to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Surface_mesh& sm, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const SM& sm, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawSurfaceMesh + +A shortcut to `CGAL::add_to_graphics_scene(sm, gs, Graphics_scene_options{})`. */ template -void draw(const SM& asm); +void add_to_graphics_scene(const SM& sm, + CGAL::Graphics_scene& gs); #else // DOXYGEN_RUNNING #include -#include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include +#include +#include #include #include -#include +#include namespace CGAL { -// Check if there are any color maps that could be used, random otherwise +// Check if there are any color maps that could be used template -struct Surface_mesh_basic_viewer_color_map - : DefaultColorFunctorFaceGraph +struct Graphics_scene_options_surface_mesh + : public Graphics_scene_options, + typename boost::graph_traits<::CGAL::Surface_mesh>::vertex_descriptor, + typename boost::graph_traits<::CGAL::Surface_mesh>::edge_descriptor, + typename boost::graph_traits<::CGAL::Surface_mesh>::face_descriptor> { - using Base = DefaultColorFunctorFaceGraph; - using SM = ::CGAL::Surface_mesh; using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; using edge_descriptor = typename boost::graph_traits::edge_descriptor; using face_descriptor = typename boost::graph_traits::face_descriptor; - using vertex_colors = typename SM::template Property_map; - using edge_colors = typename SM::template Property_map; - using face_colors = typename SM::template Property_map; - - Surface_mesh_basic_viewer_color_map(const SM& amesh) + Graphics_scene_options_surface_mesh(const SM& amesh) { - bool found = false; - std::tie(vcolors, found) = amesh.template property_map("v:color"); - std::tie(ecolors, found) = amesh.template property_map("e:color"); - std::tie(fcolors, found) = amesh.template property_map("f:color"); - CGAL_USE(found); - } + bool found=false; + std::tie(vcolors, found)= + amesh.template property_map("v:color"); + if(found) + { + this->colored_vertex=[](const SM &, vertex_descriptor)->bool { return true; }; + this->vertex_color=[this](const SM &, vertex_descriptor v)->CGAL::IO::Color + { return get(vcolors, v); }; + } + else + { this->colored_vertex=[](const SM &, vertex_descriptor)->bool { return false; }; } - CGAL::IO::Color operator()(const Surface_mesh& amesh, - const vertex_descriptor v) const - { - return vcolors ? get(vcolors, v) : Base::operator()(amesh, v); - } + std::tie(ecolors, found)= + amesh.template property_map("e:color"); + if(found) + { + this->colored_edge=[](const SM &, edge_descriptor)->bool { return true; }; + this->edge_color=[this](const SM &, edge_descriptor e)->CGAL::IO::Color + { return get(ecolors, e); }; + } + else + { this->colored_edge=[](const SM &, edge_descriptor)->bool { return false; }; } - CGAL::IO::Color operator()(const Surface_mesh& amesh, - const edge_descriptor e) const - { - return ecolors ? get(ecolors, e) : Base::operator()(amesh, e); - } - - CGAL::IO::Color operator()(const Surface_mesh& amesh, - const face_descriptor f) const - { - return fcolors ? get(fcolors, f) : Base::operator()(amesh, f); + std::tie(fcolors, found)= + amesh.template property_map("f:color"); + if(found) + { + this->colored_face=[](const SM &, face_descriptor)->bool { return true; }; + this->face_color=[this](const SM &, face_descriptor f)->CGAL::IO::Color + { return get(fcolors, f); }; + } + else + { this->colored_face=[](const SM &, face_descriptor)->bool { return false; }; } } private: - vertex_colors vcolors; - edge_colors ecolors; - face_colors fcolors; + typename SM::template Property_map vcolors; + typename SM::template Property_map ecolors; + typename SM::template Property_map fcolors; }; -// Specialization of draw function. +template +void add_to_graphics_scene(const Surface_mesh& amesh, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gs_options) +{ add_to_graphics_scene_for_fg(amesh, graphics_scene, gs_options); } + +template +void add_to_graphics_scene(const Surface_mesh& amesh, + CGAL::Graphics_scene &graphics_scene) +{ add_to_graphics_scene_for_fg(amesh, graphics_scene, + Graphics_scene_options_surface_mesh(amesh)); } + +#ifdef CGAL_USE_BASIC_VIEWER + + // Specialization of draw function. template void draw(const Surface_mesh& amesh, - const char* title="Surface_mesh Basic Viewer", - bool nofill=false) + const char* title="Surface_mesh 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) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"surface_mesh_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimpleFaceGraphViewerQt mainwindow(app.activeWindow(), amesh, title, nofill, - Surface_mesh_basic_viewer_color_map(amesh)); - mainwindow.show(); - app.exec(); - } + CGAL::Graphics_scene buffer; + add_to_graphics_scene(amesh, buffer); + draw_graphics_scene(buffer, title); } -} // End namespace CGAL +template +void draw(const Surface_mesh& amesh, + const GSOptions &gs_options, + const char* title="Surface_mesh Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(amesh, buffer, gs_options); + draw_graphics_scene(buffer, title); +} #endif // CGAL_USE_BASIC_VIEWER +} // End namespace CGAL + #endif // DOXYGEN_RUNNING #endif // CGAL_DRAW_SURFACE_MESH_H diff --git a/Surface_mesh/package_info/Surface_mesh/dependencies b/Surface_mesh/package_info/Surface_mesh/dependencies index bb41a0ba625..1e1a85b5e66 100644 --- a/Surface_mesh/package_info/Surface_mesh/dependencies +++ b/Surface_mesh/package_info/Surface_mesh/dependencies @@ -1,19 +1,30 @@ Algebraic_foundations +Arithmetic_kernel BGL +Basic_viewer Cartesian_kernel Circulator Distance_2 Distance_3 -GraphicsView +Filtered_kernel Hash_map +Homogeneous_kernel Installation +Intersections_2 +Intersections_3 Interval_support Kernel_23 +Kernel_d Modular_arithmetic Number_types +Polygon Profiling_tools Property_map Random_numbers STL_Extension +Spatial_sorting Stream_support Surface_mesh +TDS_2 +Triangulation_2 +CGAL_Core diff --git a/Surface_mesh_approximation/package_info/Surface_mesh_approximation/dependencies b/Surface_mesh_approximation/package_info/Surface_mesh_approximation/dependencies index b1ab146c636..aaf95de7e9d 100644 --- a/Surface_mesh_approximation/package_info/Surface_mesh_approximation/dependencies +++ b/Surface_mesh_approximation/package_info/Surface_mesh_approximation/dependencies @@ -14,7 +14,6 @@ Principal_component_analysis Principal_component_analysis_LGPL Profiling_tools Property_map -Random_numbers STL_Extension Solver_interface Stream_support diff --git a/Surface_mesh_deformation/package_info/Surface_mesh_deformation/dependencies b/Surface_mesh_deformation/package_info/Surface_mesh_deformation/dependencies index e4abdd72dbd..2019eca3e76 100644 --- a/Surface_mesh_deformation/package_info/Surface_mesh_deformation/dependencies +++ b/Surface_mesh_deformation/package_info/Surface_mesh_deformation/dependencies @@ -13,7 +13,6 @@ Modular_arithmetic Number_types Profiling_tools Property_map -Random_numbers STL_Extension Solver_interface Stream_support diff --git a/Surface_mesh_parameterization/package_info/Surface_mesh_parameterization/dependencies b/Surface_mesh_parameterization/package_info/Surface_mesh_parameterization/dependencies index 19c7b2f7983..d2906b564b4 100644 --- a/Surface_mesh_parameterization/package_info/Surface_mesh_parameterization/dependencies +++ b/Surface_mesh_parameterization/package_info/Surface_mesh_parameterization/dependencies @@ -2,6 +2,7 @@ Algebraic_foundations Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 @@ -18,7 +19,6 @@ Number_types Polygon_mesh_processing Profiling_tools Property_map -Random_numbers STL_Extension Solver_interface Spatial_sorting diff --git a/Surface_mesh_shortest_path/package_info/Surface_mesh_shortest_path/dependencies b/Surface_mesh_shortest_path/package_info/Surface_mesh_shortest_path/dependencies index 0acb32e8813..5336f29d3b6 100644 --- a/Surface_mesh_shortest_path/package_info/Surface_mesh_shortest_path/dependencies +++ b/Surface_mesh_shortest_path/package_info/Surface_mesh_shortest_path/dependencies @@ -14,7 +14,6 @@ Modular_arithmetic Number_types Profiling_tools Property_map -Random_numbers STL_Extension Spatial_searching Stream_support diff --git a/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies b/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies index 27dd31f34e6..e6a49c552eb 100644 --- a/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies +++ b/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies @@ -14,7 +14,6 @@ Modular_arithmetic Number_types Profiling_tools Property_map -Random_numbers STL_Extension Spatial_searching Stream_support diff --git a/Surface_mesh_skeletonization/package_info/Surface_mesh_skeletonization/dependencies b/Surface_mesh_skeletonization/package_info/Surface_mesh_skeletonization/dependencies index 93a21616366..cea4bd48a90 100644 --- a/Surface_mesh_skeletonization/package_info/Surface_mesh_skeletonization/dependencies +++ b/Surface_mesh_skeletonization/package_info/Surface_mesh_skeletonization/dependencies @@ -2,6 +2,7 @@ AABB_tree Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Surface_mesh_topology/doc/Surface_mesh_topology/CGAL/draw_face_graph_with_paths.h b/Surface_mesh_topology/doc/Surface_mesh_topology/CGAL/draw_face_graph_with_paths.h index df9bde13a6f..b09424dbecd 100644 --- a/Surface_mesh_topology/doc/Surface_mesh_topology/CGAL/draw_face_graph_with_paths.h +++ b/Surface_mesh_topology/doc/Surface_mesh_topology/CGAL/draw_face_graph_with_paths.h @@ -3,28 +3,100 @@ namespace CGAL { /*! \ingroup PkgDrawFaceGraphWithPaths -opens a new window and draws `amesh`, either a 2D linear cell complex or a model of the FaceGraph concept, plus the paths lying on this mesh given in `apaths`. -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 flag `CGAL_USE_BASIC_VIEWER` is defined at compile time. +opens a new window and draws `amesh`, either a 2D linear cell complex or a model of the FaceGraph concept, plus the paths lying on this mesh given in `apaths`. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 Mesh either a 2D linear cell complex or a model of the FaceGraph concept. +\tparam GSOptions a model of `GraphicsSceneOptionsFaceGraphWithPaths` concept. + \param amesh the mesh to draw. \param apaths the paths to draw, which should lie on `amesh`. +\param gso the graphics scene options parameter. */ -template +template void draw(const Mesh& amesh, - const std::vector >& apaths); + const std::vector >& apaths, + const GSOptions& gso); /*! \ingroup PkgDrawFaceGraphWithPaths -opens a new window and draws `amesh`, either a 2D linear cell complex or a model of the FaceGraph concept, plus the paths lying on this mesh given in `apaths`. -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 flag `CGAL_USE_BASIC_VIEWER` is defined at compile time. -\tparam Mesh either a 2D linear cell complex or a model of the FaceGraph concept. -\param amesh the mesh to draw. -\param apaths the paths to draw, which should lie on `amesh`. +A shortcut to `CGAL::draw(amesh, apaths, Graphics_scene_options_face_graph_with_paths{})`. */ template void draw(const Mesh& amesh, - std::initializer_list >& apaths); + const std::vector >& apaths); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +Same function than draw() but taking the paths from a list instead from an std::vector. +*/ +template +void draw(const Mesh& amesh, + std::initializer_list > apaths, + const GSOptions& gso); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +A shortcut to `CGAL::draw(amesh, apaths, Graphics_scene_options_face_graph_with_paths{})`. +*/ +template +void draw(const Mesh& amesh, + std::initializer_list > apaths); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +adds the vertices, edges and faces of `amesh`, either a 2D linear cell complex or a model of the FaceGraph concept, plus the paths lying on this mesh given in `apaths`, into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam Mesh either a 2D linear cell complex or a model of the FaceGraph concept. +\tparam GSOptions a model of `GraphicsSceneOptionsFaceGraphWithPaths` concept. + +\param amesh the mesh to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. +\param apaths the paths to draw, which should lie on `amesh`. +*/ +template +void add_to_graphics_scene(const Mesh& amesh, + CGAL::Graphics_scene& gs, + const GSOptions& gso, + const std::vector>& apaths); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +A shortcut to `CGAL::add_to_graphics_scene(amesh, gs, Graphics_scene_options_face_graph_with_paths{}, apaths)`. +*/ +template +void add_to_graphics_scene(const Mesh& amesh, + CGAL::Graphics_scene& gs, + const std::vector>& apaths); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +Same function than add_to_graphics_scene() but taking the paths from a list instead from an std::vector. +*/ +template +void add_to_graphics_scene(const Mesh& amesh, + CGAL::Graphics_scene& gs, + const GSOptions& gso, + std::initializer_list > apaths); + +/*! +\ingroup PkgDrawFaceGraphWithPaths + +A shortcut to `CGAL::add_to_graphics_scene(amesh, gs, Graphics_scene_options_face_graph_with_paths{}, apaths)`. +*/ +template +void add_to_graphics_scene(const Mesh& amesh, + CGAL::Graphics_scene& gs, + std::initializer_list > apaths); } /* namespace CGAL */ diff --git a/Surface_mesh_topology/examples/Surface_mesh_topology/draw_facewidth.h b/Surface_mesh_topology/examples/Surface_mesh_topology/draw_facewidth.h index 4a489b19ff1..5883d7d32d7 100644 --- a/Surface_mesh_topology/examples/Surface_mesh_topology/draw_facewidth.h +++ b/Surface_mesh_topology/examples/Surface_mesh_topology/draw_facewidth.h @@ -1,51 +1,33 @@ #ifndef DRAW_FACEWIDTH_H #define DRAW_FACEWIDTH_H -#include - #ifdef CGAL_USE_BASIC_VIEWER #include +#include template -struct Facewidth_draw_functor : public CGAL::DefaultDrawingFunctorLCC +struct Facewidth_graphics_scene_options: + public CGAL::Graphics_scene_options { - Facewidth_draw_functor(typename ALCC::size_type amark1, typename ALCC::size_type amark2) : + Facewidth_graphics_scene_options(typename ALCC::size_type amark1, typename ALCC::size_type amark2) : m_vertex_mark(amark1), m_face_mark(amark2) - {} + { + this->colored_vertex=[this](const ALCC& alcc, typename ALCC::Dart_const_handle dh)->bool + { return alcc.is_marked(dh, m_vertex_mark); }; + this->vertex_color=[](const ALCC&, typename ALCC::Dart_const_handle)->CGAL::IO::Color + { return CGAL::IO::Color(0, 255, 0); }; - template - bool colored_vertex(const LCC& alcc, typename LCC::Dart_const_descriptor dh) const - { return alcc.is_marked(dh, m_vertex_mark); } - - template - CGAL::IO::Color vertex_color(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* dh */) const - { return CGAL::IO::Color(0, 255, 0); } - - template - bool colored_edge(const LCC& /*alcc*/, typename LCC::Dart_const_descriptor /*dh*/) const - { return false; } - - template - CGAL::IO::Color edge_color(const LCC& /* alcc*/, - typename LCC::Dart_const_descriptor /* dh */) const - { return CGAL::IO::Color(0, 0, 255); } - - template - bool colored_face(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* dh */) const - {return true;} - - template - CGAL::IO::Color face_color(const LCC& alcc, typename LCC::Dart_const_descriptor dh) const - { return alcc.is_marked(dh, m_face_mark)?CGAL::IO::Color(255, 0, 0) - :CGAL::IO::Color(211, 211, 211); } - - template - bool colored_volume(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* dh */) const - { return false; } + this->colored_face=[](const ALCC&, typename ALCC::Dart_const_handle)->bool + { return true; }; + this->face_color=[this](const ALCC& alcc, typename ALCC::Dart_const_handle dh)->CGAL::IO::Color + { return alcc.is_marked(dh, m_face_mark)?CGAL::IO::Color(255, 0, 0) + :CGAL::IO::Color(211, 211, 211); }; + } typename ALCC::size_type m_vertex_mark, m_face_mark; }; @@ -58,30 +40,21 @@ void draw_facewidth(const LCC& lcc, typename LCC::size_type face_mark = lcc.get_new_mark(); for (std::size_t i=0; i(cycle[i], vertex_mark); } - // Color the face + // Color the faces if (!lcc.is_marked(cycle[i], face_mark)) { lcc.template mark_cell<2>(cycle[i], face_mark); } } - Facewidth_draw_functor df(vertex_mark, face_mark); - CGAL::draw(lcc, "Face width", false, df); + Facewidth_graphics_scene_options df(vertex_mark, face_mark); + CGAL::draw(lcc, df, "Face width"); lcc.free_mark(vertex_mark); lcc.free_mark(face_mark); } -#else // CGAL_USE_BASIC_VIEWER - -template -void draw_facewidth(const LCC&, - const std::vector&) -{ - std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."<=3 && std::string(argv[2])=="-draw") + { draw_facewidth(lcc, cycle); } +#endif } return EXIT_SUCCESS; diff --git a/Surface_mesh_topology/examples/Surface_mesh_topology/open_path_homotopy.cpp b/Surface_mesh_topology/examples/Surface_mesh_topology/open_path_homotopy.cpp index b8340e878a5..28e3c4b439b 100644 --- a/Surface_mesh_topology/examples/Surface_mesh_topology/open_path_homotopy.cpp +++ b/Surface_mesh_topology/examples/Surface_mesh_topology/open_path_homotopy.cpp @@ -63,7 +63,10 @@ int main(int argc, char* argv[]) <<" base point homotopic with path p3 (orange)."< { Draw_functor(LCC_3::size_type am1, LCC_3::size_type am2) : is_root(am1), belong_to_cycle(am2) - {} + { + this->colored_vertex=[this](const LCC_3& alcc, typename LCC_3::Dart_const_handle dh)->bool + { return alcc.is_marked(dh, is_root); }; + this->vertex_color=[](const LCC_3&, typename LCC_3::Dart_const_handle)->CGAL::IO::Color + { return CGAL::IO::Color(0, 255, 0); }; - template - bool colored_vertex(const LCC& alcc, typename LCC::Dart_const_descriptor d) const - { return alcc.is_marked(d, is_root); } + this->colored_edge=[this](const LCC_3& alcc, typename LCC_3::Dart_const_handle dh)->bool + { return alcc.is_marked(dh, belong_to_cycle); }; + this->edge_color=[](const LCC_3&, typename LCC_3::Dart_const_handle)->CGAL::IO::Color + { return CGAL::IO::Color(0, 0, 255); }; - template - CGAL::IO::Color vertex_color(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* d */) const - { return CGAL::IO::Color(0,255,0); } - - template - bool colored_edge(const LCC& alcc, typename LCC::Dart_const_descriptor d) const - { return alcc.is_marked(d, belong_to_cycle); } - - template - CGAL::IO::Color edge_color(const LCC& /* alcc*/, - typename LCC::Dart_const_descriptor /* d */) const - { return CGAL::IO::Color(0, 0, 255); } - - template - bool colored_face(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* d */) const {return true;} - - template - CGAL::IO::Color face_color(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* d */) const - {return CGAL::IO::Color(211, 211, 211);} - - template - bool colored_volume(const LCC& /* alcc */, - typename LCC::Dart_const_descriptor /* d */) const { return false; } + this->colored_face=[](const LCC_3&, typename LCC_3::Dart_const_handle)->bool + { return true; }; + this->face_color=[](const LCC_3&, typename LCC_3::Dart_const_handle)->CGAL::IO::Color + { return CGAL::IO::Color(211, 211, 211); }; + } LCC_3::size_type is_root; LCC_3::size_type belong_to_cycle; @@ -159,7 +147,7 @@ int main(int argc, char* argv[]) if (draw) { Draw_functor df(is_root, belong_to_cycle); - CGAL::draw(lccoriginal, "Unsew edge width repeatdly", false, df); + CGAL::draw(lccoriginal, df, "Unsew edge width repeatdly"); } #endif // CGAL_USE_BASIC_VIEWER diff --git a/Surface_mesh_topology/include/CGAL/draw_face_graph_with_paths.h b/Surface_mesh_topology/include/CGAL/draw_face_graph_with_paths.h index f76c4846d3c..79ba01fedd0 100644 --- a/Surface_mesh_topology/include/CGAL/draw_face_graph_with_paths.h +++ b/Surface_mesh_topology/include/CGAL/draw_face_graph_with_paths.h @@ -8,25 +8,65 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf // #ifndef CGAL_DRAW_FACE_GRAPH_WITH_PATHS_H #define CGAL_DRAW_FACE_GRAPH_WITH_PATHS_H #include +#include +#include + #include #include +#include + #include #include #include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include #include namespace CGAL { +// Specific graphics scene options +template +struct Graphics_scene_options_face_graph_with_paths : + public CGAL::Graphics_scene_options +{ + Graphics_scene_options_face_graph_with_paths() + { + color_of_path=[](std::size_t i)->CGAL::IO::Color + { + CGAL::Random random(static_cast(i)); + return get_random_color(random); + }; + + draw_path=[](std::size_t)->bool + { return true; }; + } + + const CGAL::IO::Color& color_of_marked_edges() const + { return m_color_of_marked_edges; } + void color_of_marked_edges(const CGAL::IO::Color& c) + { m_color_of_marked_edges=c; } + + std::function color_of_path; + std::function draw_path; +protected: + CGAL::IO::Color m_color_of_marked_edges=CGAL::IO::Color(0, 0, 255); +}; + +namespace draw_function_for_lcc +{ + // We need to re-use the namespace draw_function_for_lcc because we want to specialize + // the previous struct LCC_geom_utils +// template +// struct LCC_geom_utils; + // Specialisation for face graph; otherwise use the LCC_geom_utils of LCC. template struct LCC_geom_utils, Local_kernel, 3> @@ -84,401 +124,320 @@ struct LCC_geom_utils, Local_kernel, 3> } }; -// Viewer class for Face_graph with paths. -template -class Face_graph_with_path_viewer : public Basic_viewer_qt +} // namespace draw_function_for_lcc + +namespace draw_function_for_face_graph_with_paths { - typedef Basic_viewer_qt Base; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; +typedef Local_kernel::Point_3 Local_point; +typedef Local_kernel::Vector_3 Local_vector; + +template +const typename CGAL::Get_traits::Point& get_point +(const Mesh &mesh, typename Get_map::type::Dart_const_descriptor dh) +{ return CGAL::Get_traits::get_point(mesh, dh); } + +template +void compute_face(const Mesh& mesh, + const typename Get_map::storage_type& lcc, + typename Get_map::type::Dart_const_descriptor dh, + CGAL::Graphics_scene& graphics_scene, + GSOptions& gso) +{ + if(!gso.are_faces_enabled() || !gso.draw_face(mesh, dh)) + { return; } + + typedef typename Get_map::type LCC; + typedef typename LCC::Dart_const_descriptor Dart_const_descriptor; + + // We fill only closed faces. + Dart_const_descriptor cur=dh; + Dart_const_descriptor min=dh; + do + { + if (!lcc.is_next_exist(cur)) return; // open face=>not filled + if (cur:: + get_vertex_normal(lcc, cur)); + cur=lcc.next(cur); + } + while(cur!=dh); + + graphics_scene.face_end(); +} + +template +void compute_edge(const Mesh &mesh, + const typename Get_map::storage_type& lcc, + typename Get_map::type::Dart_const_descriptor dh, + CGAL::Graphics_scene& graphics_scene, + GSOptions& gso, + bool colored=false, + const CGAL::IO::Color& color=CGAL::IO::black()) +{ + if(!gso.are_edges_enabled() || !gso.draw_edge(mesh, dh)) + { return; } + typedef typename Get_map::type LCC; - typedef typename LCC::Dart_const_descriptor Dart_const_descriptor; - typedef typename LCC::size_type size_type; - typedef typename CGAL::Get_traits::Kernel Kernel; + typedef typename LCC::Dart_const_descriptor Dart_const_descriptor; typedef typename CGAL::Get_traits::Point Point; - typedef typename CGAL::Get_traits::Vector Vector; -public: - /// Construct the viewer. - /// @param alcc the lcc to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; - /// this can be useful for very big object where this time could be long) - Face_graph_with_path_viewer(QWidget* parent, - const Mesh& amesh, - const std::vector - > - *paths=nullptr, - size_type amark=LCC::INVALID_MARK, - const char* title="", bool anofaces=false, - const DrawingFunctorLCC& - drawing_functor=DrawingFunctorLCC()) : - Base(parent, title, true, true, true, false, true), - mesh(amesh), - lcc(amesh), - m_oriented_mark(lcc.get_new_mark()), - m_nofaces(anofaces), - m_drawing_functor(drawing_functor), - m_paths(paths), - m_current_path(m_paths->size()), - m_current_dart(lcc.darts().end()), - m_draw_marked_darts(true), - m_amark(amark==(std::numeric_limits::max)()? - LCC::INVALID_MARK:amark) + Point p1=get_point(mesh, dh); + Dart_const_descriptor d2=lcc.other_extremity(dh); + if (d2!=LCC::null_descriptor) { - lcc.orient(m_oriented_mark); - compute_elements(); - } - - ~Face_graph_with_path_viewer() - { - lcc.free_mark(m_oriented_mark); - } - -protected: - - const Point& get_point(Dart_const_descriptor dh) const - { return CGAL::Get_traits::get_point(mesh, dh); } - - void compute_elements() - { - clear(); - - typename LCC::size_type markfaces = lcc.get_new_mark(); - typename LCC::size_type markedges = lcc.get_new_mark(); - typename LCC::size_type markvertices = lcc.get_new_mark(); - - if (m_current_dart!=lcc.darts().end()) - { // We want to draw only one dart - Dart_const_descriptor selected_dart=m_current_dart; //lcc.dart_descriptor(m_current_dart); - compute_edge(selected_dart, CGAL::IO::Color(255,0,0)); - lcc.template mark_cell<1>(selected_dart, markedges); - compute_vertex(selected_dart); - - if ( !m_nofaces ) - { compute_face(selected_dart); } - - for (typename LCC::Dart_range::const_iterator it=lcc.darts().begin(), - itend=lcc.darts().end(); it!=itend; ++it ) - { - if ( !lcc.is_marked(it, markedges) ) - { - compute_edge(it); - lcc.template mark_cell<1>(it, markedges); - } - } - } + if(colored) + { graphics_scene.add_segment(p1, get_point(mesh, d2), color); } else - { - if (m_current_path==m_paths->size()) - { - for (std::size_t i=0; isize(); ++i) - { compute_path(i, markedges); } - } - else if (m_current_path!=m_paths->size()+1) - { compute_path(m_current_path, markedges); } - - for (typename LCC::Dart_range::const_iterator it=lcc.darts().begin(), - itend=lcc.darts().end(); it!=itend; ++it ) - { - if (!m_nofaces && !lcc.is_marked(it, markfaces) && - !lcc.is_perforated(it) && lcc.is_marked(it, m_oriented_mark)) - { - compute_face(it); - lcc.template mark_cell<2>(it, markfaces); - } - - if ( !lcc.is_marked(it, markedges) ) - { - compute_edge(it); - lcc.template mark_cell<1>(it, markedges); - } - - if ( !lcc.is_marked(it, markvertices) ) - { - compute_vertex(it); - lcc.template mark_cell<0>(it, markvertices); - } - } - } - - lcc.free_mark(markfaces); - lcc.free_mark(markedges); - lcc.free_mark(markvertices); - } - - void compute_face(Dart_const_descriptor dh) - { - // We fill only closed faces. - Dart_const_descriptor cur=dh; - Dart_const_descriptor min=dh; - do - { - if (!lcc.is_next_exist(cur)) return; // open face=>not filled - if (cur:: - get_vertex_normal(lcc, cur)); - cur=lcc.next(cur); - } - while(cur!=dh); - - face_end(); - } - - void compute_edge(Dart_const_descriptor dh) - { - Point p1 = get_point(dh); - Dart_const_descriptor d2 = lcc.other_extremity(dh); - if (d2!=LCC::null_descriptor) - { - if (m_draw_marked_darts && m_amark!=LCC::INVALID_MARK && - (lcc.is_marked(dh, m_amark) || lcc.is_marked(lcc.opposite2(dh), m_amark))) - { add_segment(p1, get_point(d2), CGAL::IO::Color(0, 0, 255)); } - else - { add_segment(p1, get_point(d2)); } - } - } - - void compute_edge(Dart_const_descriptor dh, const CGAL::IO::Color& color) - { - Point p1 = get_point(dh); - Dart_const_descriptor d2 = lcc.other_extremity(dh); - if (d2!=LCC::null_descriptor) - { add_segment(p1, get_point(d2), color); } - } - - void compute_vertex(Dart_const_descriptor dh) - { add_point(get_point(dh)); } - - virtual void init() - { - Base::init(); - setKeyDescription(::Qt::Key_D, "Increase current dart drawing"); - setKeyDescription(::Qt::ControlModifier, ::Qt::Key_D, "Decrease current dart drawing"); - setKeyDescription(::Qt::ShiftModifier, ::Qt::Key_D, "Draw all darts"); - - setKeyDescription(::Qt::Key_X, "Toggles marked darts display"); - - setKeyDescription(::Qt::Key_P, "Increase current path drawing"); - setKeyDescription(::Qt::ControlModifier, ::Qt::Key_P, "Decrease current path drawing"); - setKeyDescription(::Qt::ShiftModifier, ::Qt::Key_P, "Draw all paths"); - } - - virtual void keyPressEvent(QKeyEvent *e) - { - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - - if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::NoButton)) - { - if (m_current_dart==lcc.darts().end()) - { m_current_dart=lcc.darts().begin(); } - else - { ++m_current_dart; } - if (m_current_dart==lcc.darts().end()) - { displayMessage(QString("Draw all darts.")); } - else - { displayMessage(QString("Draw dart=%1.").arg(lcc.darts().index(m_current_dart))); } - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::ControlModifier)) - { - if (m_current_dart==lcc.darts().begin()) - { m_current_dart=lcc.darts().end(); } - else - { --m_current_dart; } - if (m_current_dart==lcc.darts().end()) - { displayMessage(QString("Draw all darts.")); } - else - { displayMessage(QString("Draw dart=%1.").arg(lcc.darts().index(m_current_dart))); } - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_D) && (modifiers==::Qt::ShiftModifier)) - { - m_current_dart=lcc.darts().end(); - displayMessage(QString("Draw all darts.")); - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::NoButton)) - { - m_current_path=(m_current_path+1)%(m_paths->size()+2); - if (m_current_path==m_paths->size()) - { displayMessage(QString("Draw all paths.")); } - else if (m_current_path==m_paths->size()+1) - { displayMessage(QString("Do not draw paths.")); } - else - { displayMessage(QString("Draw path=%1, nb_darts=%2."). - arg(m_current_path). - arg((*m_paths)[m_current_path].length())); } - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::ControlModifier)) - { - m_current_path=(m_current_path==0?m_paths->size()+1:m_current_path-1); - if (m_current_path==m_paths->size()) - { displayMessage(QString("Draw all paths.")); } - else if (m_current_path==m_paths->size()+1) - { displayMessage(QString("Do not draw paths.")); } - else - { displayMessage(QString("Draw path=%1, nb_darts=%2."). - arg(m_current_path). - arg((*m_paths)[m_current_path].length())); } - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_P) && (modifiers==::Qt::ShiftModifier)) - { - m_current_path=m_paths->size(); - displayMessage(QString("Draw all paths.")); - compute_elements(); - redraw(); - } - else if ((e->key()==::Qt::Key_X) && (modifiers==::Qt::NoButton)) - { - m_draw_marked_darts=!m_draw_marked_darts; - - if (m_draw_marked_darts) - { displayMessage(QString("Draw marked darts in blue.")); } - else - { - displayMessage(QString("Do not draw marked darts in different color.")); - } - compute_elements(); - redraw(); - } - else - { Base::keyPressEvent(e); } - } - - void compute_path(std::size_t i, typename LCC::size_type amark) - { - if ((*m_paths)[i].is_empty()) - { return; } - - CGAL::Random random(static_cast(i)); - CGAL::IO::Color color=get_random_color(random); - - add_point(get_point((*m_paths)[i].get_ith_dart(0)), color); - for (std::size_t j=0; j<(*m_paths)[i].length(); ++j) - { - if ( !lcc.is_marked( (*m_paths)[i].get_ith_dart(j), amark) ) - { - compute_edge((*m_paths)[i].get_ith_dart(j), color); - lcc.template mark_cell<1>((*m_paths)[i].get_ith_dart(j), amark); - } - } - } - -protected: - const Mesh& mesh; - const typename Get_map::storage_type lcc; - typename LCC::size_type m_oriented_mark; - bool m_nofaces; - const DrawingFunctorLCC& m_drawing_functor; - const std::vector >* m_paths; - std::size_t m_current_path; - typename LCC::Dart_range::const_iterator m_current_dart; - bool m_draw_marked_darts; - typename LCC::size_type m_amark; // If !=INVALID_MARK, show darts marked with this mark -}; - -template -void draw(const Mesh& alcc, - const std::vector >& paths, - const char* title="Mesh Viewer With Path", - typename Get_map::type::size_type amark= - (std::numeric_limits::type::size_type>::max)(), - bool nofill=false, - const DrawingFunctor& drawing_functor=DrawingFunctor()) -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[1]={"lccviewer"}; - QApplication app(argc,const_cast(argv)); - Face_graph_with_path_viewer mainwindow(app.activeWindow(), - alcc, &paths, amark, - title, nofill, - drawing_functor); - mainwindow.show(); - app.exec(); + { graphics_scene.add_segment(p1, get_point(mesh, d2)); } } } -template -void draw(const Mesh& alcc, +template +void compute_edge(const Mesh &mesh, + const typename Get_map::storage_type& lcc, + typename Get_map::type::Dart_const_descriptor dh, + CGAL::Graphics_scene& graphics_scene, + GSOptions& gso, + typename Get_map::type::size_type amark) +{ + if(!gso.are_edges_enabled() || !gso.draw_edge(mesh, dh)) + { return; } + + if (amark!=Get_map::type::INVALID_MARK && + (lcc.is_marked(dh, amark) || lcc.is_marked(lcc.opposite2(dh), amark))) + { compute_edge(mesh, lcc, dh, graphics_scene, gso, true, gso.color_of_marked_edges()); } + else + { + if(gso.colored_edge(mesh, dh)) + { compute_edge(mesh, lcc, dh, graphics_scene, gso, true, + gso.edge_color(mesh, dh)); } + else + { compute_edge(mesh, lcc, dh, graphics_scene, gso, false); } + } +} + +template +void compute_vertex(const Mesh &mesh, + typename Get_map::type::Dart_const_descriptor dh, + CGAL::Graphics_scene& graphics_scene, + GSOptions& gso) +{ + if(gso.are_vertices_enabled() && gso.draw_vertex(mesh, dh)) + { + if (gso.colored_vertex(mesh, dh)) + { + graphics_scene.add_point(get_point(mesh, dh), + gso.vertex_color(mesh, dh)); + } + else + { graphics_scene.add_point(get_point(mesh, dh)); } + } +} + +template +void compute_path(const Mesh &mesh, + const typename Get_map::storage_type& lcc, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gso, + const std::vector>& paths, + std::size_t i, + typename Get_map::type::size_type amark) +{ + + if (paths[i].is_empty() || !gso.draw_path(i)) + { return; } + + CGAL::IO::Color color=gso.color_of_path(i); + graphics_scene.add_point(get_point(mesh, paths[i].get_ith_dart(0)), color); + for (std::size_t j=0; j(paths[i].get_ith_dart(j), amark); + } + } +} + +template +void compute_elements(const Mesh &mesh, + CGAL::Graphics_scene &graphics_scene, + const GSOptions &gso, + const std::vector>& paths, + typename Get_map::type::size_type mark) +{ + typedef typename Get_map::type LCC; + typedef typename LCC::size_type size_type; + + typename Get_map::storage_type lcc(mesh); + size_type oriented_mark = lcc.get_new_mark(); + std::size_t current_path = paths.size(); + typename LCC::size_type amark=mark==(std::numeric_limits::max)()? + LCC::INVALID_MARK:mark; // If !=INVALID_MARK, show darts marked with this mark + + lcc.orient(oriented_mark); + + size_type markfaces = lcc.get_new_mark(); + size_type markedges = lcc.get_new_mark(); + size_type markvertices = lcc.get_new_mark(); + + if (current_path==paths.size()) + { + for (std::size_t i=0; i(it, markfaces); + } + + if ( !lcc.is_marked(it, markedges) ) + { + compute_edge(mesh, lcc, it, graphics_scene, gso, amark); + lcc.template mark_cell<1>(it, markedges); + } + + if ( !lcc.is_marked(it, markvertices) ) + { + compute_vertex(mesh, it, graphics_scene, gso); + lcc.template mark_cell<0>(it, markvertices); + } + } + + lcc.free_mark(markfaces); + lcc.free_mark(markedges); + lcc.free_mark(markvertices); +} + +} // namespace draw_function_for_face_graph_with_paths + +template +void add_to_graphics_scene(const Mesh& mesh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso, + const std::vector>& paths, + typename Get_map::type::size_type amark= + Get_map::type::INVALID_MARK) +{ + draw_function_for_face_graph_with_paths::compute_elements(mesh, graphics_scene, + gso, paths, amark); +} + +template +void add_to_graphics_scene(const Mesh& mesh, + CGAL::Graphics_scene& graphics_scene, + const std::vector>& paths, + typename Get_map::type::size_type amark= + Get_map::type::INVALID_MARK) +{ + // Default graphics view options. + Graphics_scene_options_face_graph_with_paths::type::Dart_const_descriptor /*vh*/, + typename Get_map::type::Dart_const_descriptor /*eh*/, + typename Get_map::type::Dart_const_descriptor /*fh*/> + gso; + + add_to_graphics_scene(mesh, graphics_scene, gso, paths, amark); +} + +template +void add_to_graphics_scene(const Mesh& mesh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gso, + std::initializer_list> l, + typename Get_map::type::size_type amark= + Get_map::type::INVALID_MARK) +{ + std::vector> paths=l; + add_to_graphics_scene(mesh, graphics_scene, gso, paths, amark); +} + +template +void add_to_graphics_scene(const Mesh& mesh, + CGAL::Graphics_scene& graphics_scene, + std::initializer_list> l, + typename Get_map::type::size_type amark= + Get_map::type::INVALID_MARK) +{ + std::vector> paths=l; + add_to_graphics_scene(mesh, graphics_scene, paths, amark); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +template +void draw(const Mesh& mesh, + const std::vector >& paths, + const GSOptions& gso, + const char* title="Mesh Viewer With Path", + typename Get_map::type::size_type amark= + (std::numeric_limits::type::size_type>::max)()) +{ + CGAL::Graphics_scene graphics_scene; + add_to_graphics_scene(mesh, graphics_scene, paths, gso, amark); + draw_graphics_scene(graphics_scene, title); +} + +template +void draw(const Mesh& mesh, const std::vector >& paths, const char* title="Mesh Viewer With Path", typename Get_map::type::size_type amark= - (std::numeric_limits::type::size_type>::max)(), - bool nofill=false) + (std::numeric_limits::type::size_type>::max)()) { - DefaultDrawingFunctorLCC f; - draw(alcc, paths, title, amark, nofill, f); + CGAL::Graphics_scene graphics_scene; + add_to_graphics_scene(mesh, graphics_scene, paths, amark); + draw_graphics_scene(graphics_scene, title); +} + +template +void draw(const Mesh& mesh, + std::initializer_list> l, + const GSOptions& gso, + const char* title="Mesh Viewer With Path", + typename Get_map::type::size_type amark= + (std::numeric_limits::type::size_type>::max)()) +{ + std::vector> paths=l; + draw(mesh, paths, gso, title, amark); } template -void draw(const Mesh& alcc, +void draw(const Mesh& mesh, std::initializer_list> l, const char* title="Mesh Viewer With Path", typename Get_map::type::size_type amark= - (std::numeric_limits::type::size_type>::max)(), - bool nofill=false) + (std::numeric_limits::type::size_type>::max)()) { - std::vector > paths=l; - draw(alcc, paths, title, amark, nofill); + std::vector> paths=l; + draw(mesh, paths, title, amark); } -} // End namespace CGAL - -#else // CGAL_USE_BASIC_VIEWER - -namespace CGAL -{ - - template - void draw(const Mesh&, - const std::vector >& , - const char* ="", - typename Get_map::type::size_type= - (std::numeric_limits::type::size_type>::max)(), - bool=false) - { - std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< - void draw(const Mesh&, - std::initializer_list>, - const char* ="", - typename Get_map::type::size_type= - (std::numeric_limits::type::size_type>::max)(), - bool=false) - { - std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< +#include +#include /*! \file * * Member-function definitions of the Surface_sweep_2 class-template. */ +#include +#include + namespace CGAL { namespace Surface_sweep_2 { diff --git a/Surface_sweep_2/package_info/Surface_sweep_2/dependencies b/Surface_sweep_2/package_info/Surface_sweep_2/dependencies index 09546686abf..1a04d474a62 100644 --- a/Surface_sweep_2/package_info/Surface_sweep_2/dependencies +++ b/Surface_sweep_2/package_info/Surface_sweep_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/Complex_cells_selector.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/Complex_cells_selector.h new file mode 100644 index 00000000000..d7822d6bea0 --- /dev/null +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/Complex_cells_selector.h @@ -0,0 +1,45 @@ +// Copyright (c) 2024 GeometryFactory (France) and Telecom Paris (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) : Jane Tournois, Noura Faraj, Jean-Marc Thiery, Tamy Boubekeur + +#ifndef CGAL_INTERNAL_C3T3_CELLS_SELECTOR_H +#define CGAL_INTERNAL_C3T3_CELLS_SELECTOR_H + +#include + +#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 9cbff2ba95a..183e53a000d 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; @@ -111,6 +116,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/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index 5912bb67575..1e465f2c3b6 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -57,22 +57,7 @@ public: void after_flip(CellHandle /* c */) {} }; -template -struct All_cells_selected -{ - 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 All_cells_selected&, const key_type& c) - { - using SI = typename Tr::Cell::Subdomain_index; - return c->subdomain_index() != SI(); - } - friend void put(All_cells_selected&, const key_type&, const value_type) - {} //nothing to do : subdomain indices are updated in remeshing}; -}; template #include +#include + #include #include @@ -228,11 +230,11 @@ void tetrahedral_isotropic_remeshing( typedef typename internal_np::Lookup_named_param_def < internal_np::cell_selector_t, NamedParameters, - Tetrahedral_remeshing::internal::All_cells_selected//default + Tetrahedral_remeshing::Complex_cells_selector//default > ::type SelectionFunctor; SelectionFunctor cell_select = choose_parameter(get_parameter(np, internal_np::cell_selector), - Tetrahedral_remeshing::internal::All_cells_selected()); + Tetrahedral_remeshing::Complex_cells_selector()); typedef typename Tr::Vertex_handle Vertex_handle; typedef typename internal_np::Lookup_named_param_def < @@ -485,11 +487,11 @@ void tetrahedral_isotropic_remeshing( typedef typename internal_np::Lookup_named_param_def < internal_np::cell_selector_t, NamedParameters, - Tetrahedral_remeshing::internal::All_cells_selected//default + Tetrahedral_remeshing::Complex_cells_selector//default > ::type SelectionFunctor; SelectionFunctor cell_select = choose_parameter(get_parameter(np, internal_np::cell_selector), - Tetrahedral_remeshing::internal::All_cells_selected()); + Tetrahedral_remeshing::Complex_cells_selector()); typedef typename Tr::Vertex_handle Vertex_handle; typedef typename internal_np::Lookup_named_param_def < diff --git a/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies b/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies index d9c5098d64f..adf468cfd1b 100644 --- a/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies +++ b/Tetrahedral_remeshing/package_info/Tetrahedral_remeshing/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 @@ -19,7 +20,6 @@ Number_types Polygon_mesh_processing Profiling_tools Property_map -Random_numbers SMDS_3 STL_Extension Spatial_sorting diff --git a/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing.cpp b/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing.cpp index fffb560ac88..0d09480d987 100644 --- a/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing.cpp +++ b/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include 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/doc/Triangulation_2/CGAL/draw_constrained_triangulation_2.h b/Triangulation_2/doc/Triangulation_2/CGAL/draw_constrained_triangulation_2.h new file mode 100644 index 00000000000..d714a6c5961 --- /dev/null +++ b/Triangulation_2/doc/Triangulation_2/CGAL/draw_constrained_triangulation_2.h @@ -0,0 +1,56 @@ +namespace CGAL { + + /*! +\ingroup PkgDrawConstrainedTriangulation2 + +opens a new window and draws a constrained triangulation. If the triangulation has constraints they are drawn. The faces inside and outside of the domain, based on the property map, are drawn in different colors. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 CT2 which must be an instanciation of a `CGAL::Constrained_triangulation_2<...>`. +\tparam InDomainPMap a class model of `ReadablePropertyMap` with `CT2::Face_handle` as key type and `bool` as value type. + +\param ct2 the constrained triangulation to draw. +\param ipm the property map defining the faces which are in the domain. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Constrained_triangulation_2& ct2, InDomainPMap ipm); + +\cgalAdvancedEnd +*/ + template + void draw(const CT2& ct2, InDomainPMap ipm); + +/*! +\ingroup PkgDrawConstrainedTriangulation2 + +adds the vertices, edges and faces of `ct2` into the given graphic scene `gs`. If the triangulation has constraints they are drawn. The faces inside and outside of the domain, based on the property map, are drawn in different colors. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam CT2 which must be an instanciation of a `CGAL::Constrained_triangulation_2<...>`. +\tparam InDomainPMap a class model of `ReadablePropertyMap` with `CT2::Face_handle` as key type and `bool` as value type. + +\param ct2 the constrained triangulation to draw. +\param ipm the property map defining the faces which are in the domain. +\param gs the graphic scene to fill. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Constrained_triangulation_2& ct2, InDomainPMap ipm, CGAL::Graphics_scene& gs); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const CT2& ct2, InDomainPMap ipm, + CGAL::Graphics_scene& gs); + +} /* namespace CGAL */ diff --git a/Triangulation_2/doc/Triangulation_2/CGAL/draw_triangulation_2.h b/Triangulation_2/doc/Triangulation_2/CGAL/draw_triangulation_2.h index 6ca50c894cf..a7567db9b0a 100644 --- a/Triangulation_2/doc/Triangulation_2/CGAL/draw_triangulation_2.h +++ b/Triangulation_2/doc/Triangulation_2/CGAL/draw_triangulation_2.h @@ -3,41 +3,72 @@ namespace CGAL { /*! \ingroup PkgDrawTriangulation2 -opens a new window and draws a triangulation. If the triangulation -has constraints they are drawn. If the face type has a member -function `bool is_in_domain()` the faces inside and outside of the -domain are drawn in different colors. +opens a new window and draws a triangulation. Parameters of the drawing are taken from the optional graphics scene options parameter. -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 T2 a triangulation class derived from `Triangulation_2` or `Constrained_triangulation_2` -\param at2 the triangulation to draw. - -*/ -template -void draw(const T2& at2); - - /*! -\ingroup PkgDrawTriangulation2 - -opens a new window and draws a constrained triangulation. If the triangulation -has constraints they are drawn. The faces inside and outside of the -domain, based on the property map, are drawn in different colors. - -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. +A call to this function blocks the execution of the program until the drawing window is closed. 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 CT2 a triangulation class derived from `Constrained_triangulation_2` -\tparam InDomainPMap a class model of `ReadWritePropertyMap` with -`CT::Face_handle` as key type and `bool` as value type. +\tparam T2 which must be an instanciation of a `CGAL::Triangulation_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. -\param act2 the constrained triangulation to draw. -\param ipm the property map defining the faces which are in the domain. +\param at2 the triangulation to draw. +\param gso the graphics scene options parameter. +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Triangulation_2& at2, const GSOptions& gso); + +\cgalAdvancedEnd */ - template - void draw(const CT2& act2, InDomainPMap ipm); + template + void draw(const T2& at2, const GSOptions& gso); + +/*! +\ingroup PkgDrawTriangulation2 + +A shortcut to `CGAL::draw(at2, Graphics_scene_options{})`. +*/ + template + void draw(const T2& at2); + +/*! +\ingroup PkgDrawTriangulation2 + +adds the vertices, edges and faces of `at2` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam T2 which must be an instanciation of a `CGAL::Triangulation_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param at2 the triangulation to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Triangulation_2& at2, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const T2& at2, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawTriangulation2 + +A shortcut to `CGAL::add_to_graphics_scene(at2, gs, Graphics_scene_options{})`. +*/ +template +void add_to_graphics_scene(const T2& at2, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Triangulation_2/doc/Triangulation_2/PackageDescription.txt b/Triangulation_2/doc/Triangulation_2/PackageDescription.txt index 2f10c201757..f84ffe376ba 100644 --- a/Triangulation_2/doc/Triangulation_2/PackageDescription.txt +++ b/Triangulation_2/doc/Triangulation_2/PackageDescription.txt @@ -19,6 +19,12 @@ /// \defgroup PkgDrawTriangulation2 Draw a Triangulation 2 /// \ingroup PkgTriangulation2Ref +/*! +\cgalInclude{CGAL/draw_constrained_triangulation_2.h} +*/ +/// \defgroup PkgDrawConstrainedTriangulation2 Draw a Constrained Triangulation 2 +/// \ingroup PkgTriangulation2Ref + /*! \addtogroup PkgTriangulation2Ref \todo check generated documentation diff --git a/Triangulation_2/doc/Triangulation_2/dependencies b/Triangulation_2/doc/Triangulation_2/dependencies index 22ae69dcfef..db6625f491c 100644 --- a/Triangulation_2/doc/Triangulation_2/dependencies +++ b/Triangulation_2/doc/Triangulation_2/dependencies @@ -10,3 +10,4 @@ Triangulation_3 Spatial_sorting Polygon Polyline_simplification_2 +Basic_viewer diff --git a/Triangulation_2/examples/Triangulation_2/CMakeLists.txt b/Triangulation_2/examples/Triangulation_2/CMakeLists.txt index 47374a85998..8f399fa86da 100644 --- a/Triangulation_2/examples/Triangulation_2/CMakeLists.txt +++ b/Triangulation_2/examples/Triangulation_2/CMakeLists.txt @@ -16,8 +16,9 @@ foreach(cppfile ${cppfiles}) endforeach() if(CGAL_Qt6_FOUND) - target_link_libraries(polygon_triangulation PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(constrained PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(draw_triangulation_2 PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(polygon_triangulation PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(star_conflict_zone PUBLIC CGAL::CGAL_Basic_viewer) else() message(STATUS "NOTICE: Several examples require Qt6 and will not be compiled.") diff --git a/Triangulation_2/examples/Triangulation_2/constrained.cpp b/Triangulation_2/examples/Triangulation_2/constrained.cpp index 9901ec05fd3..3ed1b19a880 100644 --- a/Triangulation_2/examples/Triangulation_2/constrained.cpp +++ b/Triangulation_2/examples/Triangulation_2/constrained.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -27,5 +28,8 @@ main( ) ++count; std::cout << "The number of resulting constrained edges is "; std::cout << count << std::endl; + + CGAL::draw(cdt); + return 0; } diff --git a/Triangulation_2/examples/Triangulation_2/polygon_triangulation.cpp b/Triangulation_2/examples/Triangulation_2/polygon_triangulation.cpp index 405f7eb5516..698ff58b99b 100644 --- a/Triangulation_2/examples/Triangulation_2/polygon_triangulation.cpp +++ b/Triangulation_2/examples/Triangulation_2/polygon_triangulation.cpp @@ -1,8 +1,9 @@ #include #include -#include #include #include +#include +#include "polygon_triangulation_graphics_scene_options.h" #include #include @@ -57,6 +58,7 @@ int main( ) assert(count > 0); assert(count < cdt.number_of_faces()); - CGAL::draw(cdt, in_domain); + Polygon_triangulation_gs_options gso(in_domain); + CGAL::draw(cdt, gso); return 0; } diff --git a/Triangulation_2/examples/Triangulation_2/polygon_triangulation_graphics_scene_options.h b/Triangulation_2/examples/Triangulation_2/polygon_triangulation_graphics_scene_options.h new file mode 100644 index 00000000000..4768211fdfa --- /dev/null +++ b/Triangulation_2/examples/Triangulation_2/polygon_triangulation_graphics_scene_options.h @@ -0,0 +1,54 @@ +// Copyright(c) 2022 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) : Andreas Fabri + +#ifndef CGAL_POLYGON_TRIANGULATION_GRAPHICS_SCENE_OPTIONS_H +#define CGAL_POLYGON_TRIANGULATION_GRAPHICS_SCENE_OPTIONS_H + +#include +#include + +template +struct Polygon_triangulation_gs_options : + public CGAL::Graphics_scene_options +{ + using T2=typename PT::Triangulation; + template + Polygon_triangulation_gs_options(IPM ipm) + { + this->colored_face = + [](const T2&, const typename PT::Finite_faces_iterator) -> bool + { return true; }; + + this->face_color = + [](const T2&, const typename PT::Finite_faces_iterator fh) -> CGAL::IO::Color + { + CGAL::Random random((unsigned int)(std::size_t)(&*fh)); + return get_random_color(random); + }; + + this->draw_face= + [ipm](const T2&, const typename PT::Finite_faces_iterator fh) -> bool + { return get(ipm, fh); }; + + this->draw_edge= + [ipm](const T2& pt, const typename PT::Finite_edges_iterator eh) -> bool + { + typename PT::Face_handle fh1=eh->first; + typename PT::Face_handle fh2=pt.mirror_edge(*eh).first; + return get(ipm, fh1) || get(ipm, fh2); + }; + } +}; + +#endif // CGAL_POLYGON_TRIANGULATION_GRAPHICS_SCENE_OPTIONS_H diff --git a/Triangulation_2/include/CGAL/draw_constrained_triangulation_2.h b/Triangulation_2/include/CGAL/draw_constrained_triangulation_2.h index 322f4837f4e..05d933f92e9 100644 --- a/Triangulation_2/include/CGAL/draw_constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/draw_constrained_triangulation_2.h @@ -13,168 +13,98 @@ #define CGAL_DRAW_CT2_H #include -#include #include #include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include +#include namespace CGAL { -// Viewer class for T2 - template -class SimpleConstrainedTriangulation2ViewerQt : public Basic_viewer_qt +template +struct Graphics_scene_options_constrained_triangulation_2: + public CGAL::Graphics_scene_options { - typedef Basic_viewer_qt Base; - typedef typename T2::Vertex_handle Vertex_const_handle; - typedef typename T2::Finite_edges_iterator Edge_const_handle; - typedef typename T2::Finite_faces_iterator Facet_const_handle; - typedef typename T2::Point Point; + using BASET2=typename CDT::Triangulation; -public: - /// Construct the viewer. - /// @param at2 the t2 to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big object where this time could be long) - SimpleConstrainedTriangulation2ViewerQt(QWidget* parent, const T2& at2, - InDomainPmap ipm, - const char* title="Basic CDT2 Viewer", - bool anofaces=false) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - t2(at2), - ipm(ipm), - m_nofaces(anofaces) + Graphics_scene_options_constrained_triangulation_2(const CDT& cdt) { - compute_elements(); - } + this->colored_edge = + [&cdt](const BASET2&, typename CDT::Finite_edges_iterator eh) -> bool + { return cdt.is_constrained(*eh); }; -protected: - void compute_face(Facet_const_handle fh) + this->edge_color = + [&cdt](const BASET2&, typename CDT::Finite_edges_iterator eh) -> CGAL::IO::Color + { return cdt.is_constrained(*eh)? CGAL::IO::green() : CGAL::IO::black(); }; + }; + + template + Graphics_scene_options_constrained_triangulation_2(const CDT& cdt, InDomainPmap ipm) { - CGAL::IO::Color c = get(ipm, fh)? CGAL::IO::yellow() : CGAL::IO::white(); - face_begin(c); + this->colored_edge = + [&cdt](const BASET2&, typename CDT::Finite_edges_iterator eh) -> bool + { return cdt.is_constrained(*eh); }; - add_point_in_face(fh->vertex(0)->point()); - add_point_in_face(fh->vertex(1)->point()); - add_point_in_face(fh->vertex(2)->point()); + this->edge_color = + [&cdt](const BASET2&, typename CDT::Finite_edges_iterator eh) -> CGAL::IO::Color + { return cdt.is_constrained(*eh)? CGAL::IO::green() : CGAL::IO::black(); }; - face_end(); - } + this->colored_face = + [](const BASET2&, typename CDT::Finite_faces_iterator) -> bool + { return true; }; - void compute_edge(Edge_const_handle eh) - { - CGAL::IO::Color c = t2.is_constrained(*eh)? CGAL::IO::green() : CGAL::IO::black(); - add_segment(eh->first->vertex(eh->first->cw(eh->second))->point(), - eh->first->vertex(eh->first->ccw(eh->second))->point(), - c); - } - - void compute_vertex(Vertex_const_handle vh) - { add_point(vh->point()); } - - void compute_elements() - { - clear(); - - if (!m_nofaces) - { - for (typename T2::Finite_faces_iterator it=t2.finite_faces_begin(); - it!=t2.finite_faces_end(); ++it) - { compute_face(it); } - } - - for (typename T2::Finite_edges_iterator it=t2.finite_edges_begin(); - it!=t2.finite_edges_end(); ++it) - { compute_edge(it); } - - for (typename T2::Finite_vertices_iterator it=t2.finite_vertices_begin(); - it!=t2.finite_vertices_end(); ++it) - { compute_vertex(it); } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); - } - -protected: - const T2& t2; - InDomainPmap ipm; - bool m_nofaces; + this->face_color = + [ipm](const BASET2&, typename CDT::Finite_faces_iterator fh) -> CGAL::IO::Color + { return get(ipm, fh)? CGAL::IO::blue() : CGAL::IO::white(); }; + }; }; // Specialization of draw function. #define CGAL_T2_TYPE CGAL::Constrained_triangulation_2 -template -void draw(const CGAL_T2_TYPE& at2, - InDomainPmap ipm) +template +void add_to_graphics_scene(const CGAL_T2_TYPE& at2, InDomainPmap ipm, + CGAL::Graphics_scene& graphics_scene) { - const char* title="Constrained_triangulation_2 Basic Viewer"; - bool nofill=false; - -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - SimpleConstrainedTriangulation2ViewerQt - mainwindow(app.activeWindow(), at2, ipm, title, nofill); - mainwindow.show(); - app.exec(); - } + Graphics_scene_options_constrained_triangulation_2 gso(at2, ipm); + draw_function_for_t2::compute_elements(at2, graphics_scene, gso); } +template +void add_to_graphics_scene(const CGAL_T2_TYPE& at2, + CGAL::Graphics_scene& graphics_scene) +{ + Graphics_scene_options_constrained_triangulation_2 gso(at2); + draw_function_for_t2::compute_elements(at2, graphics_scene, gso); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +template +void draw(const CGAL_T2_TYPE& at2, InDomainPmap ipm, + const char *title="Constrained Triangulation_2 Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at2, ipm, buffer); + draw_graphics_scene(buffer, title); +} template -void draw(const CGAL_T2_TYPE& at2) +void draw(const CGAL_T2_TYPE& at2, + const char *title="Constrained Triangulation_2 Basic Viewer") { - internal::In_domain in_domain; - draw(at2, in_domain); + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at2, buffer); + draw_graphics_scene(buffer, title); } -#undef CGAL_T2_TYPE - -} // End namespace CGAL - -#else - -namespace CGAL { -// Specialization of draw function. -#define CGAL_T2_TYPE CGAL::Constrained_triangulation_2 - -template -void draw(const CGAL_T2_TYPE& , - InDomainPmap ) -{} -#undef CGAL_T2_TYPE - -} // End namespace CGAL - #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_T2_TYPE + +} // End namespace CGAL + #endif // CGAL_DRAW_CT2_H diff --git a/Triangulation_2/include/CGAL/draw_triangulation_2.h b/Triangulation_2/include/CGAL/draw_triangulation_2.h index 4c194f40dd4..32a42879ae4 100644 --- a/Triangulation_2/include/CGAL/draw_triangulation_2.h +++ b/Triangulation_2/include/CGAL/draw_triangulation_2.h @@ -8,162 +8,168 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_T2_H #define CGAL_DRAW_T2_H #include -#include - -#include - +#include +#include +#include #include +#include + +namespace CGAL { + +namespace draw_function_for_t2 { + +template +void compute_face(const T2& t2, + typename T2::Finite_faces_iterator fh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (!gs_options.draw_face(t2, fh)) + { return; } + + if (gs_options.colored_face(t2, fh)) + { graphics_scene.face_begin(gs_options.face_color(t2, fh)); } + else + { graphics_scene.face_begin(); } + + graphics_scene.add_point_in_face(fh->vertex(0)->point()); + graphics_scene.add_point_in_face(fh->vertex(1)->point()); + graphics_scene.add_point_in_face(fh->vertex(2)->point()); + + graphics_scene.face_end(); +} + +template +void compute_edge(const T2& t2, typename T2::Finite_edges_iterator eh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (!gs_options.draw_edge(t2, eh)) + { return; } + + if (gs_options.colored_edge(t2, eh)) + { + graphics_scene.add_segment + (eh->first->vertex(eh->first->cw(eh->second))->point(), + eh->first->vertex(eh->first->ccw(eh->second))->point(), + gs_options.edge_color(t2, eh)); + } + else + { + graphics_scene.add_segment + (eh->first->vertex(eh->first->cw(eh->second))->point(), + eh->first->vertex(eh->first->ccw(eh->second))->point()); + } +} + +template +void compute_vertex(const T2& t2, typename T2::Vertex_handle vh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (!gs_options.draw_vertex(t2, vh)) + { return; } + + if (gs_options.colored_vertex(t2, vh)) + { graphics_scene.add_point(vh->point(), gs_options.vertex_color(t2, vh)); } + else + { graphics_scene.add_point(vh->point()); } +} + +template +void compute_elements(const T2& t2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (gs_options.are_faces_enabled()) + { + for (typename T2::Finite_faces_iterator it=t2.finite_faces_begin(); + it!=t2.finite_faces_end(); ++it) + { compute_face(t2, it, graphics_scene, gs_options); } + } + + if (gs_options.are_edges_enabled()) + { + for (typename T2::Finite_edges_iterator it=t2.finite_edges_begin(); + it!=t2.finite_edges_end(); ++it) + { compute_edge(t2, it, graphics_scene, gs_options); } + } + + if (gs_options.are_vertices_enabled()) + { + for (typename T2::Finite_vertices_iterator it=t2.finite_vertices_begin(); + it!=t2.finite_vertices_end(); ++it) + { compute_vertex(t2, it, graphics_scene, gs_options); } + } +} + +} // namespace draw_function_for_t2 + +#define CGAL_T2_TYPE CGAL::Triangulation_2 + +template +void add_to_graphics_scene(const CGAL_T2_TYPE& at2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + draw_function_for_t2::compute_elements(at2, graphics_scene, gs_options); +} + +template +void add_to_graphics_scene(const CGAL_T2_TYPE& at2, + CGAL::Graphics_scene& graphics_scene) +{ + Graphics_scene_options + drawingFunctor; + + drawingFunctor.colored_face = + [](const CGAL_T2_TYPE&, const typename CGAL_T2_TYPE::Finite_faces_iterator) -> bool + { return true; }; + + drawingFunctor.face_color = + [](const CGAL_T2_TYPE&, const typename CGAL_T2_TYPE::Finite_faces_iterator fh) -> CGAL::IO::Color + { + CGAL::Random random((unsigned int)(std::size_t)(&*fh)); + return get_random_color(random); + }; + + add_to_graphics_scene(at2, graphics_scene, drawingFunctor); +} #ifdef CGAL_USE_BASIC_VIEWER -#include -#include - -namespace CGAL -{ - -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorT2 -{ - template - static CGAL::IO::Color run(const T2&, - const typename T2::Finite_faces_iterator fh) - { - CGAL::Random random((unsigned int)(std::size_t)(&*fh)); - return get_random_color(random); - } -}; - -// Viewer class for T2 -template -class SimpleTriangulation2ViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename T2::Vertex_handle Vertex_const_handle; - typedef typename T2::Finite_edges_iterator Edge_const_handle; - typedef typename T2::Finite_faces_iterator Facet_const_handle; - typedef typename T2::Point Point; - -public: - /// Construct the viewer. - /// @param at2 the t2 to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big object where this time could be long) - SimpleTriangulation2ViewerQt(QWidget* parent, const T2& at2, - const char* title="Basic T2 Viewer", - bool anofaces=false, - const ColorFunctor& fcolor=ColorFunctor()) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - t2(at2), - m_nofaces(anofaces), - m_fcolor(fcolor) - { - compute_elements(); - } - -protected: - void compute_face(Facet_const_handle fh) - { - CGAL::IO::Color c=m_fcolor.run(t2, fh); - face_begin(c); - - add_point_in_face(fh->vertex(0)->point()); - add_point_in_face(fh->vertex(1)->point()); - add_point_in_face(fh->vertex(2)->point()); - - face_end(); - } - - void compute_edge(Edge_const_handle eh) - { - add_segment(eh->first->vertex(eh->first->cw(eh->second))->point(), - eh->first->vertex(eh->first->ccw(eh->second))->point()); - } - - void compute_vertex(Vertex_const_handle vh) - { add_point(vh->point()); } - - void compute_elements() - { - clear(); - - if (!m_nofaces) - { - for (typename T2::Finite_faces_iterator it=t2.finite_faces_begin(); - it!=t2.finite_faces_end(); ++it) - { compute_face(it); } - } - - for (typename T2::Finite_edges_iterator it=t2.finite_edges_begin(); - it!=t2.finite_edges_end(); ++it) - { compute_edge(it); } - - for (typename T2::Finite_vertices_iterator it=t2.finite_vertices_begin(); - it!=t2.finite_vertices_end(); ++it) - { compute_vertex(it); } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); - } - -protected: - const T2& t2; - bool m_nofaces; - const ColorFunctor& m_fcolor; -}; - // Specialization of draw function. -#define CGAL_T2_TYPE CGAL::Triangulation_2 - -template -void draw(const CGAL_T2_TYPE& at2) +template +void draw(const CGAL_T2_TYPE &at2, const GSOptions &gs_options, + const char *title="Triangulation_2 Basic Viewer") { - const char* title="Triangulation_2 Basic Viewer"; - bool nofill=false; -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite=true; -#else - bool cgal_test_suite=qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t2_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - DefaultColorFunctorT2 fcolor; - SimpleTriangulation2ViewerQt - mainwindow(app.activeWindow(), at2, title, nofill, fcolor); - mainwindow.show(); - app.exec(); - } + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at2, buffer, gs_options); + draw_graphics_scene(buffer, title); } +template +void draw(const CGAL_T2_TYPE& at2, + const char *title="Triangulation_2 Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at2, buffer); + draw_graphics_scene(buffer, title); +} + +#endif // CGAL_USE_BASIC_VIEWER + #undef CGAL_T2_TYPE } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_T2_H diff --git a/Triangulation_2/package_info/Triangulation_2/dependencies b/Triangulation_2/package_info/Triangulation_2/dependencies index c8d90b721ca..c023330d6ae 100644 --- a/Triangulation_2/package_info/Triangulation_2/dependencies +++ b/Triangulation_2/package_info/Triangulation_2/dependencies @@ -1,12 +1,13 @@ Algebraic_foundations Arithmetic_kernel BGL +Basic_viewer +CGAL_Core Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView Hash_map Homogeneous_kernel Installation @@ -20,6 +21,7 @@ Number_types Polygon Profiling_tools Property_map +Random_numbers STL_Extension Spatial_sorting Stream_support diff --git a/Triangulation_3/demo/Triangulation_3/MainWindow.cpp b/Triangulation_3/demo/Triangulation_3/MainWindow.cpp index 030948e7a2c..aad061f5e44 100644 --- a/Triangulation_3/demo/Triangulation_3/MainWindow.cpp +++ b/Triangulation_3/demo/Triangulation_3/MainWindow.cpp @@ -137,6 +137,7 @@ void MainWindow::on_actionLoad_Points_triggered() // update viewer Q_EMIT( sceneChanged() ); + viewer->changed(); } void MainWindow::on_actionSave_Points_triggered() diff --git a/Triangulation_3/doc/Triangulation_3/CGAL/draw_triangulation_3.h b/Triangulation_3/doc/Triangulation_3/CGAL/draw_triangulation_3.h index 82d1caf9124..5c8c0d041c1 100644 --- a/Triangulation_3/doc/Triangulation_3/CGAL/draw_triangulation_3.h +++ b/Triangulation_3/doc/Triangulation_3/CGAL/draw_triangulation_3.h @@ -3,14 +3,72 @@ namespace CGAL { /*! \ingroup PkgDrawTriangulation3 -opens a new window and draws `at3`, a model of the `TriangulationDataStructure_3` concept. 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 T3 a model of the `TriangulationDataStructure_3` concept. -\param at3 the triangulation to draw. +opens a new window and draws a 3D triangulation. Parameters of the drawing are taken from the optional graphics scene options parameter. +A call to this function blocks the execution of the program until the drawing window is closed. 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 T3 which must be an instanciation of a `CGAL::Triangulation_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param at3 the triangulation to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Triangulation_3& at3, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const T3& at3, const GSOptions& gso); + +/*! +\ingroup PkgDrawTriangulation3 + A shortcut to `CGAL::draw(at3, Graphics_scene_options{})`. +*/ + template + void draw(const T3& at3); + +/*! +\ingroup PkgDrawTriangulation3 + +adds the vertices, edges and faces of `at3` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam T3 which must be an instanciation of a `CGAL::Triangulation_3<...>`. +\tparam GSOptions a model of `GraphicsSceneOptions` concept. + +\param at3 the triangulation to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Triangulation_3& at3, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const T3& at3, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawTriangulation3 + +A shortcut to `CGAL::add_to_graphics_scene(at3, gs, Graphics_scene_options{})`. */ template -void draw(const T3& at3); +void add_to_graphics_scene(const T3& at3, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Triangulation_3/include/CGAL/draw_triangulation_3.h b/Triangulation_3/include/CGAL/draw_triangulation_3.h index ddea4548d1b..376b73b8c53 100644 --- a/Triangulation_3/include/CGAL/draw_triangulation_3.h +++ b/Triangulation_3/include/CGAL/draw_triangulation_3.h @@ -8,165 +8,176 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Guillaume Damiand +// Mostafa Ashraf #ifndef CGAL_DRAW_T3_H #define CGAL_DRAW_T3_H #include -#include +#include +#include +#include +#include +#include + +namespace CGAL { + +namespace draw_function_for_t3 +{ + +template +void compute_face(typename T3::Finite_facets_iterator fh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options, const T3 *t3) +{ + if(!gs_options.draw_face(*t3, fh)) + { return; } + + if(gs_options.colored_face(*t3, fh)) + { graphics_scene.face_begin(gs_options.face_color(*t3, fh)); } + else + { graphics_scene.face_begin(); } + + graphics_scene.add_point_in_face(fh->first->vertex((fh->second + 1) % 4)-> + point()); + graphics_scene.add_point_in_face(fh->first->vertex((fh->second + 2) % 4)-> + point()); + graphics_scene.add_point_in_face(fh->first->vertex((fh->second + 3) % 4)-> + point()); + + graphics_scene.face_end(); +} + +template +void compute_edge(typename T3::Finite_edges_iterator eh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options, const T3* t3) +{ + if(!gs_options.draw_edge(*t3, eh)) + { return; } + + if(gs_options.colored_edge(*t3, eh)) + { + graphics_scene.add_segment(eh->first->vertex(eh->second)->point(), + eh->first->vertex(eh->third)->point(), + gs_options.edge_color(*t3, eh)); + } + else + { + graphics_scene.add_segment(eh->first->vertex(eh->second)->point(), + eh->first->vertex(eh->third)->point()); + } +} + +template +void compute_vertex(typename T3::Vertex_handle vh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options, const T3* t3) +{ + if(!gs_options.draw_vertex(*t3, vh)) + { return; } + + if(gs_options.colored_vertex(*t3, vh)) + { + graphics_scene.add_point(vh->point(), gs_options.vertex_color(*t3, vh)); + } + else + { graphics_scene.add_point(vh->point()); } +} + +template +void compute_elements(const T3* t3, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if (gs_options.are_faces_enabled()) + { + for (typename T3::Finite_facets_iterator it=t3->finite_facets_begin(); + it!=t3->finite_facets_end(); ++it) + { compute_face(it, graphics_scene, gs_options, t3); } + } + + if (gs_options.are_edges_enabled()) + { + for (typename T3::Finite_edges_iterator it=t3->finite_edges_begin(); + it!=t3->finite_edges_end(); ++it) + { compute_edge(it, graphics_scene, gs_options, t3); } + } + + if (gs_options.are_vertices_enabled()) + { + for (typename T3::Finite_vertices_iterator it=t3->finite_vertices_begin(); + it!=t3->finite_vertices_end(); ++it) + { compute_vertex(it, graphics_scene, gs_options, t3); } + } +} + +} // namespace draw_function_for_t3 + +#define CGAL_T3_TYPE CGAL::Triangulation_3 + +template +void add_to_graphics_scene(const CGAL_T3_TYPE& at3, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + draw_function_for_t3::compute_elements(&at3, graphics_scene, gs_options); +} + +template +void add_to_graphics_scene(const CGAL_T3_TYPE& at3, + CGAL::Graphics_scene& graphics_scene) +{ + CGAL::Graphics_scene_options + gs_options; + + gs_options.colored_face = + [](const CGAL_T3_TYPE &, const typename CGAL_T3_TYPE::Finite_facets_iterator) -> bool + { return true; }; + + gs_options.face_color = + [](const CGAL_T3_TYPE &at3, const typename CGAL_T3_TYPE::Finite_facets_iterator fh) -> CGAL::IO::Color + { + if (fh==at3.finite_facets_end()) // use to get the mono color + return CGAL::IO::Color(100, 125, 200); // R G B between 0-255 + + CGAL::Random random((unsigned int)((std::size_t)(&*(fh->first)) + + (std::size_t)(fh->second))); + + return get_random_color(random); + }; + + add_to_graphics_scene(at3, graphics_scene, gs_options); +} #ifdef CGAL_USE_BASIC_VIEWER -#include -#include -#include - -namespace CGAL -{ - -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorT3 -{ - template - static CGAL::IO::Color run(const T3&, - const typename T3::Finite_facets_iterator* fh) - { - if (fh==nullptr) // use to get the mono color - return CGAL::IO::Color(100, 125, 200); // R G B between 0-255 - - CGAL::Random random((unsigned int)((std::size_t)(&*((*fh)->first))+ - (std::size_t)((*fh)->second))); - return get_random_color(random); - } -}; - -// Viewer class for T3 -template -class SimpleTriangulation3ViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename T3::Vertex_handle Vertex_const_handle; - typedef typename T3::Finite_edges_iterator Edge_const_handle; - typedef typename T3::Finite_facets_iterator Facet_const_handle; - typedef typename T3::Cell_handle Cell_handle; - typedef typename T3::Point Point; - -public: - /// Construct the viewer. - /// @param at3 the t3 to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this can be - /// useful for very big object where this time could be long) - SimpleTriangulation3ViewerQt(QWidget* parent, - const T3& at3, - const char* title="Basic T3 Viewer", - bool anofaces=false, - const ColorFunctor& fcolor=ColorFunctor()) : - // First draw: vertices; edges, faces; multi-color; no inverse normal - Base(parent, title, true, true, true, false, false), - t3(at3), - m_nofaces(anofaces), - m_fcolor(fcolor) - { - compute_elements(); - } - -protected: - void compute_face(Facet_const_handle fh) - { - CGAL::IO::Color c=m_fcolor.run(t3, &fh); - face_begin(c); - - add_point_in_face(fh->first->vertex((fh->second+1)%4)->point()); - add_point_in_face(fh->first->vertex((fh->second+2)%4)->point()); - add_point_in_face(fh->first->vertex((fh->second+3)%4)->point()); - - face_end(); - } - - void compute_edge(Edge_const_handle eh) - { - add_segment(eh->first->vertex(eh->second)->point(), - eh->first->vertex(eh->third)->point()); - } - - void compute_vertex(Vertex_const_handle vh) - { add_point(vh->point()); } - - void compute_elements() - { - clear(); - - if (!m_nofaces) - { - for (typename T3::Finite_facets_iterator it=t3.finite_facets_begin(); - it!=t3.finite_facets_end(); ++it) - { compute_face(it); } - } - - for (typename T3::Finite_edges_iterator it=t3.finite_edges_begin(); - it!=t3.finite_edges_end(); ++it) - { compute_edge(it); } - - for (typename T3::Finite_vertices_iterator it=t3.finite_vertices_begin(); - it!=t3.finite_vertices_end(); ++it) - { compute_vertex(it); } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * compute_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); - } - -protected: - const T3& t3; - bool m_nofaces; - const ColorFunctor& m_fcolor; -}; - // Specialization of draw function. -#define CGAL_T3_TYPE CGAL::Triangulation_3 - -template -void draw(const CGAL_T3_TYPE& at3, - const char* title="T3 Basic Viewer", - bool nofill=false) +template +void draw(const CGAL_T3_TYPE &at3, const GSOptions &gs_options, + const char *title="T3 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) - { - CGAL::Qt::init_ogl_context(4,3); - int argc=1; - const char* argv[2]={"t3_viewer", nullptr}; - QApplication app(argc,const_cast(argv)); - DefaultColorFunctorT3 fcolor; - SimpleTriangulation3ViewerQt - mainwindow(app.activeWindow(), at3, title, nofill, fcolor); - mainwindow.show(); - app.exec(); - } + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at3, buffer, gs_options); + draw_graphics_scene(buffer, title); } +template +void draw(const CGAL_T3_TYPE &at3, const char *title="T3 Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(at3, buffer); + draw_graphics_scene(buffer, title); +} + +#endif // CGAL_USE_BASIC_VIEWER + #undef CGAL_T3_TYPE } // End namespace CGAL -#endif // CGAL_USE_BASIC_VIEWER - #endif // CGAL_DRAW_T3_H diff --git a/Triangulation_3/package_info/Triangulation_3/dependencies b/Triangulation_3/package_info/Triangulation_3/dependencies index a68982ddaa5..2f40be389e2 100644 --- a/Triangulation_3/package_info/Triangulation_3/dependencies +++ b/Triangulation_3/package_info/Triangulation_3/dependencies @@ -1,12 +1,13 @@ Algebraic_foundations Arithmetic_kernel BGL +Basic_viewer +CGAL_Core Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView Hash_map Homogeneous_kernel Installation @@ -17,11 +18,14 @@ Kernel_23 Kernel_d Modular_arithmetic Number_types +Polygon Profiling_tools Property_map Random_numbers STL_Extension Spatial_sorting Stream_support +TDS_2 TDS_3 +Triangulation_2 Triangulation_3 diff --git a/Triangulation_3/test/Triangulation_3/CMakeLists.txt b/Triangulation_3/test/Triangulation_3/CMakeLists.txt index 4998791123e..85fff794a2f 100644 --- a/Triangulation_3/test/Triangulation_3/CMakeLists.txt +++ b/Triangulation_3/test/Triangulation_3/CMakeLists.txt @@ -23,17 +23,8 @@ create_single_source_cgal_program("test_RT_cell_base_with_weighted_circumcenter_ create_single_source_cgal_program("test_robust_weighted_circumcenter.cpp") create_single_source_cgal_program("test_simplex_3.cpp") create_single_source_cgal_program("test_segment_simplex_traverser_3.cpp" ) -if(cxx_std_17 IN_LIST CMAKE_CXX_COMPILE_FEATURES) - create_single_source_cgal_program("test_simplex_iterator_3.cpp" ) - create_single_source_cgal_program("test_segment_cell_traverser_3.cpp" ) - target_compile_features(test_simplex_iterator_3 PRIVATE cxx_std_17) - target_compile_features(test_segment_simplex_traverser_3 PRIVATE cxx_std_17) -else() - message( - STATUS - "NOTICE: test_simplex_iterator_3.cpp and test_segment_simplex_traverser_3.cpp require C++17 and will not be compiled." - ) -endif() +create_single_source_cgal_program("test_simplex_iterator_3.cpp" ) +create_single_source_cgal_program("test_segment_cell_traverser_3.cpp" ) create_single_source_cgal_program("test_static_filters.cpp") create_single_source_cgal_program("test_triangulation_3.cpp") create_single_source_cgal_program("test_io_triangulation_3.cpp") 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 diff --git a/Voronoi_diagram_2/doc/Voronoi_diagram_2/CGAL/draw_voronoi_diagram_2.h b/Voronoi_diagram_2/doc/Voronoi_diagram_2/CGAL/draw_voronoi_diagram_2.h index 530bcaf5dda..8298b7b1a52 100644 --- a/Voronoi_diagram_2/doc/Voronoi_diagram_2/CGAL/draw_voronoi_diagram_2.h +++ b/Voronoi_diagram_2/doc/Voronoi_diagram_2/CGAL/draw_voronoi_diagram_2.h @@ -3,16 +3,94 @@ namespace CGAL { /*! \ingroup PkgDrawVoronoiDiagram2 -opens a new window and draws `av2`, the `Voronoi_diagram_2` constructed from a Delaunay Graph which is a model of `DelaunayGraph_2` concept. -The class `Voronoi_diagram_2` provides an adaptor to view a triangulated Delaunay graph as their dual subdivision, the -Voronoi diagram. 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 V2 a model of the `AdaptationTraits_2` concept. -\param av2 the voronoi diagram to draw. +The class `Graphics_scene_options_voronoi_diagram_2` defines data and methods used to tune the way that the cells of a `Voronoi_diagram_2` are considered for drawing or to be added into a graphics scene. +This class is a model of `GraphicsSceneOptionsVoronoiDiagram2`. + +\tparam DS a `CGAL::Voronoi_diagram_2`. +\tparam VertexDescriptor a descriptor of vertices of `DS`. +\tparam EdgeDescriptor a descriptor of edges of `DS`. +\tparam FaceDescriptor a descriptor of faces of `DS`. + +\cgalModels{GraphicsSceneOptionsVoronoiDiagram2} */ -template -void draw(const V2& av2); + +template +struct Graphics_scene_options_voronoi_diagram_2: public CGAL::Graphics_scene_options +{}; + +/*! +\ingroup PkgDrawVoronoiDiagram2 + +opens a new window and draws a 2D voronoi diagram. Parameters of the drawing are taken from the optional graphics scene options parameter. + +A call to this function blocks the execution of the program until the drawing window is closed. 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 VD2 which must be an instanciation of a `CGAL::Voronoi_diagram_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsVoronoiDiagram2` concept. + +\param vd2 the voronoi diagram to draw. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::draw(const CGAL::Voronoi_diagram_2& vd2, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void draw(const VD2& vd2, const GSOptions& gso); + +/*! +\ingroup PkgDrawVoronoiDiagram2 + +A shortcut to `CGAL::draw(vd2, Graphics_scene_options_voronoi_diagram_2{})`. +*/ +template +void draw(const VD2& vd2); + +/*! +\ingroup PkgDrawVoronoiDiagram2 + +adds the vertices, edges and faces of `vd2` into the given graphic scene `gs`. Parameters of the cells are taken from the optional graphics scene options parameter `gso`. Note that `gs` is not cleared before being filled (to enable to draw several data structures in the same basic viewer). + +\tparam VD2 which must be an instanciation of a `CGAL::Voronoi_diagram_2<...>`. +\tparam GSOptions a model of `GraphicsSceneOptionsVoronoiDiagram2` concept. + +\param vd2 the voronoi diagram to draw. +\param gs the graphic scene to fill. +\param gso the graphics scene options parameter. + +\cgalAdvancedBegin +The real declaration of this function template is: + + + template + + void CGAL::add_to_graphics_scene(const CGAL::Voronoi_diagram_2& vd2, CGAL::Graphics_scene& gs, const GSOptions& gso); + +\cgalAdvancedEnd +*/ +template +void add_to_graphics_scene(const VD2& vd2, + CGAL::Graphics_scene& gs, + const GSOptions& gso); + +/*! +\ingroup PkgDrawVoronoiDiagram2 + +A shortcut to `CGAL::add_to_graphics_scene(vd2, gs, Graphics_scene_options_voronoi_diagram_2{})`. +*/ +template +void add_to_graphics_scene(const VD2& vd2, + CGAL::Graphics_scene& gs); } /* namespace CGAL */ diff --git a/Voronoi_diagram_2/doc/Voronoi_diagram_2/Concepts/GraphicsSceneOptionsVoronoiDiagram2.h b/Voronoi_diagram_2/doc/Voronoi_diagram_2/Concepts/GraphicsSceneOptionsVoronoiDiagram2.h new file mode 100644 index 00000000000..42c8baee972 --- /dev/null +++ b/Voronoi_diagram_2/doc/Voronoi_diagram_2/Concepts/GraphicsSceneOptionsVoronoiDiagram2.h @@ -0,0 +1,46 @@ +/*! +\ingroup PkgVoronoiDiagram2Concepts + +The concept `GraphicsSceneOptionsVoronoiDiagram2` defines data and methods used to tune the way that the cells of a `Voronoi_diagram_2` are considered for drawing or to be added into a graphics scene. + +\cgalRefines{GraphicsSceneOptions} + +\cgalHasModelsBegin +\cgalHasModelsBare{\link CGAL::Graphics_scene_options_voronoi_diagram_2 `CGAL::Graphics_scene_options_voronoi_diagram_2`\endlink} +\cgalHasModelsEnd + +*/ +class GraphicsSceneOptionsVoronoiDiagram2 +{ +public: + /// returns the color of the dual vertices. + const CGAL::IO::Color& dual_vertex_color() const; + /// sets the color of dual vertices to `c`. + void dual_vertex_color(const CGAL::IO::Color& c); + + /// returns the color of rays. + const CGAL::IO::Color& ray_color() const; + /// sets the color of rays to `c`. + void ray_color(const CGAL::IO::Color& c); + + /// returns the color of the bisectors. + const CGAL::IO::Color& bisector_color() const; + /// sets the color of bisectors to `c`. + void bisector_color(const CGAL::IO::Color& c); + + /// returns `true` if the voronoi vertices must be drawn, `false` otherwise. + /// Returns `false` by default. + bool draw_voronoi_vertices() const; + /// sets the draw of voronoi vertices to `b`. + void draw_voronoi_vertices(bool b); + /// toggles the draw voronoi vertices value. + void toggle_draw_voronoi_vertices(); + + /// returns `true` if the dual vertices must be drawn, `false` otherwise. + /// Returns `false` by default. + bool draw_dual_vertices() const; + /// sets the draw of dual vertices to `b`. + void draw_dual_vertices(); + /// toggles the draw dual vertices value. + void toggle_draw_dual_vertices(); +}; diff --git a/Voronoi_diagram_2/doc/Voronoi_diagram_2/PackageDescription.txt b/Voronoi_diagram_2/doc/Voronoi_diagram_2/PackageDescription.txt index b109a0213d9..58edde2ec5a 100644 --- a/Voronoi_diagram_2/doc/Voronoi_diagram_2/PackageDescription.txt +++ b/Voronoi_diagram_2/doc/Voronoi_diagram_2/PackageDescription.txt @@ -90,6 +90,7 @@ performing this adaptation. \cgalCRPSection{Draw Voronoi Diagram} - \link PkgDrawVoronoiDiagram2 CGAL::draw() \endlink - +- `GraphicsSceneOptionsVoronoiDiagram2` +- `CGAL::Graphics_scene_options_voronoi_2` */ diff --git a/Voronoi_diagram_2/examples/Voronoi_diagram_2/draw_voronoi_diagram_2.cpp b/Voronoi_diagram_2/examples/Voronoi_diagram_2/draw_voronoi_diagram_2.cpp index 2cd8f4a7ef3..54c1e8c9b54 100644 --- a/Voronoi_diagram_2/examples/Voronoi_diagram_2/draw_voronoi_diagram_2.cpp +++ b/Voronoi_diagram_2/examples/Voronoi_diagram_2/draw_voronoi_diagram_2.cpp @@ -23,15 +23,15 @@ int main(int argc, char* argv[]) { VD vd; std::ifstream ifs((argc>1)?argv[1]:"data/data4.dt.cin"); - assert(ifs); + if(ifs) + { + Site_2 t; + while ( ifs >> t ) { vd.insert(t); } + ifs.close(); + assert( vd.is_valid() ); - Site_2 t; - while ( ifs >> t ) { vd.insert(t); } - ifs.close(); - - assert( vd.is_valid() ); - - CGAL::draw(vd); + CGAL::draw(vd); + } return EXIT_SUCCESS; } diff --git a/Voronoi_diagram_2/include/CGAL/draw_voronoi_diagram_2.h b/Voronoi_diagram_2/include/CGAL/draw_voronoi_diagram_2.h index f92e9af630e..534d5aa47c3 100644 --- a/Voronoi_diagram_2/include/CGAL/draw_voronoi_diagram_2.h +++ b/Voronoi_diagram_2/include/CGAL/draw_voronoi_diagram_2.h @@ -8,18 +8,17 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Jasmeet Singh +// Mostafa Ashraf #ifndef CGAL_DRAW_VORONOI_DIAGRAM_2_H #define CGAL_DRAW_VORONOI_DIAGRAM_2_H -#include #include - -#ifdef CGAL_USE_BASIC_VIEWER - -#include -#include +#include +#include +#include #include +#include #include #include #include @@ -28,310 +27,432 @@ namespace CGAL { -// Default color functor; user can change it to have its own face color -struct DefaultColorFunctorV2 +// We need a specific graphics scene option for voronoi2 in order to allow +// to differentiate voronoi and dual vertices, and to manage rays. +template +struct Graphics_scene_options_voronoi_2 : + public CGAL::Graphics_scene_options { - template - static CGAL::IO::Color run(const V2 &, const typename V2::Face_iterator /*fh*/) { - //CGAL::Random random((unsigned int)(std::size_t)(&*fh)); - //return get_random_color(random); - return CGAL::IO::Color(73, 250, 117); - } -}; + Graphics_scene_options_voronoi_2() : m_dual_vertex_color(50, 100, 180), + m_ray_color(100, 0, 0), + m_bisector_color(0, 100, 0), + m_draw_voronoi_vertices(true), + m_draw_dual_vertices(true) + {} -// Viewer for Voronoi diagram -template -class SimpleVoronoiDiagram2ViewerQt : public Basic_viewer_qt -{ - typedef Basic_viewer_qt Base; - typedef typename V2::Vertex_iterator Vertex_const_handle; - typedef typename V2::Delaunay_vertex_handle Delaunay_vertex_const_handle; - typedef typename V2::Delaunay_graph::Finite_vertices_iterator Dual_vertices_iterator; + const CGAL::IO::Color& dual_vertex_color() const + { return m_dual_vertex_color; } + const CGAL::IO::Color& ray_color() const + { return m_ray_color; } + const CGAL::IO::Color& bisector_color() const + { return m_bisector_color; } - typedef typename V2::Halfedge_iterator Halfedge_const_handle; - typedef typename V2::Ccb_halfedge_circulator Ccb_halfedge_circulator; - typedef typename V2::Halfedge_handle Halfedge_handle; + void dual_vertex_color(const CGAL::IO::Color& c) + { m_dual_vertex_color=c; } + void ray_color(const CGAL::IO::Color& c) + { m_ray_color=c; } + void bisector_color(const CGAL::IO::Color& c) + { m_bisector_color=c; } - typedef typename V2::Face_iterator Face_const_handle; + void draw_voronoi_vertices(bool b) { m_draw_voronoi_vertices=b; } + bool draw_voronoi_vertices() const { return m_draw_voronoi_vertices; } + void toggle_draw_voronoi_vertices() { m_draw_voronoi_vertices=!m_draw_voronoi_vertices; } - typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; - -public: - /// Construct the viewer. - /// @param av2 the voronoi diagram to view - /// @param title the title of the window - /// @param anofaces if true, do not draw faces (faces are not computed; this - /// can be useful for very big object where this time could be long) - SimpleVoronoiDiagram2ViewerQt(QWidget *parent, const V2 &av2, - const char *title = "Basic Voronoi Viewer", - bool anofaces = false, - bool draw_voronoi_vertices = true, - bool draw_delaunay_vertices = true, - const ColorFunctor &fcolor = ColorFunctor()) - : // First draw: vertices; half-edges; faces; multi-color; no inverse - // normal - Base(parent, title, true, true, true, false, false, true, true), - v2(av2), m_nofaces(anofaces), - m_draw_voronoi_vertices(draw_voronoi_vertices), - m_draw_dual_vertices(draw_delaunay_vertices), m_fcolor(fcolor) { - // Add custom key description (see keyPressEvent) - setKeyDescription(::Qt::Key_R, "Toggles rays display"); - setKeyDescription(::Qt::Key_D, "Toggles dual vertices display"); - setKeyDescription(::Qt::Key_V, "Toggles voronoi vertices display"); - - compute_elements(); - } + void draw_dual_vertices(bool b) { m_draw_dual_vertices=b; } + bool draw_dual_vertices() const { return m_draw_dual_vertices; } + void toggle_draw_dual_vertices() { m_draw_dual_vertices=!m_draw_dual_vertices; } protected: + CGAL::IO::Color m_dual_vertex_color; + CGAL::IO::Color m_ray_color; + CGAL::IO::Color m_bisector_color; + bool m_draw_voronoi_vertices; + bool m_draw_dual_vertices; +}; - void compute_vertex(Vertex_const_handle vh) { add_point(vh->point()); } +namespace draw_function_for_v2 +{ - void compute_dual_vertex(Dual_vertices_iterator vi) +typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; +typedef Local_kernel::Point_3 Local_point; +typedef Local_kernel::Vector_3 Local_vector; + +template +void compute_vertex(const V2& v2, + typename V2::Vertex_iterator vh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if(!gs_options.draw_vertex(v2, vh)) + { return; } + + if(gs_options.colored_vertex(v2, vh)) + { graphics_scene.add_point(vh->point(), gs_options.vertex_color(v2, vh)); } + else + { graphics_scene.add_point(vh->point()); } +} + +template +void compute_dual_vertex(const V2& /*v2*/, + typename V2::Delaunay_graph::Finite_vertices_iterator vi, + CGAL::Graphics_scene &graphics_scene, + const GSOptions& gs_options) +{ graphics_scene.add_point(vi->point(), gs_options.dual_vertex_color()); } + +template +void add_segments_and_update_bounding_box(const V2& v2, + typename V2::Halfedge_iterator he, + CGAL::Graphics_scene& graphics_scene, + GSOptions& gs_options) +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + typedef typename V2::Delaunay_vertex_handle Delaunay_vertex_const_handle; + + if (he->is_segment()) { - add_point(vi->point(), CGAL::IO::Color(50, 100, 180)); - } - - void add_segments_and_update_bounding_box(Halfedge_handle he) - { - if (he->is_segment()) { - add_segment(he->source()->point(), he->target()->point()); - } else { - Delaunay_vertex_const_handle v1 = he->up(); - Delaunay_vertex_const_handle v2 = he->down(); - - Kernel::Vector_2 direction(v1->point().y() - v2->point().y(), - v2->point().x() - v1->point().x()); - if (he->is_ray()) { - Kernel::Point_2 end_point; - if (he->has_source()) { - end_point = he->source()->point(); - update_bounding_box_for_ray(end_point, direction); - } - } else if (he->is_bisector()) { - Kernel::Point_2 pointOnLine((v1->point().x() + v2->point().x()) / 2, - (v1->point().y() + v2->point().y()) / 2); - Kernel::Vector_2 perpendicularDirection( - v2->point().x() - v1->point().x(), - v2->point().y() - v1->point().y()); - update_bounding_box_for_line(pointOnLine, direction, - perpendicularDirection); + if(gs_options.draw_edge(v2, he)) + { + if(gs_options.colored_edge(v2, he)) + { + graphics_scene.add_segment(he->source()->point(), he->target()->point(), + gs_options.edge_color(v2, he)); + } + else + { + graphics_scene.add_segment(he->source()->point(), he->target()->point()); } } } - - Local_kernel::Point_2 get_second_point(Halfedge_handle ray) - { - Delaunay_vertex_const_handle v1 = ray->up(); - Delaunay_vertex_const_handle v2 = ray->down(); - - // calculate direction of ray and its inverse - Kernel::Vector_2 v(v1->point().y() - v2->point().y(), - v2->point().x() - v1->point().x()); - Local_kernel::Vector_2 inv(1 / v.x(), 1 / v.y()); - - // origin of the ray - Kernel::Point_2 p; - if (ray->has_source()) { - p = ray->source()->point(); - } else { - p = ray->target()->point(); - } - - // get the bounding box of the viewer - Local_kernel::Vector_2 boundsMin(m_bounding_box.xmin(), - m_bounding_box.zmin()); - Local_kernel::Vector_2 boundsMax(m_bounding_box.xmax(), - m_bounding_box.zmax()); - // calculate intersection - double txmax, txmin, tymax, tymin; - - if (inv.x() >= 0) { - txmax = (boundsMax.x() - p.x()) * inv.x(); - txmin = (boundsMin.x() - p.x()) * inv.x(); - } else { - txmax = (boundsMin.x() - p.x()) * inv.x(); - txmin = (boundsMax.x() - p.x()) * inv.x(); - } - - if (inv.y() >= 0) { - tymax = (boundsMax.y() - p.y()) * inv.y(); - tymin = (boundsMin.y() - p.y()) * inv.y(); - } else { - tymax = (boundsMin.y() - p.y()) * inv.y(); - tymin = (boundsMax.y() - p.y()) * inv.y(); - } - - if (tymin > txmin) - txmin = tymin; - if (tymax < txmax) - txmax = tymax; - - Local_kernel::Point_2 p1; - if (v.x() == 0) { - p1 = Local_kernel::Point_2(p.x(), p.y() + tymax * v.y()); - } else if (v.y() == 0) { - p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y()); - } else { - p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y() + tymax * v.y()); - } - return p1; - } - - void compute_rays_and_bisectors(Halfedge_const_handle he) + else { Delaunay_vertex_const_handle v1 = he->up(); Delaunay_vertex_const_handle v2 = he->down(); Kernel::Vector_2 direction(v1->point().y() - v2->point().y(), v2->point().x() - v1->point().x()); - if (he->is_ray()) { - if (he->has_source()) { - // add_ray_segment(he->source()->point(), get_second_point(he)); - add_ray(he->source()->point(), direction, CGAL::IO::Color(100, 0, 0)); + if (he->is_ray()) + { + Kernel::Point_2 end_point; + if (he->has_source()) + { + end_point = he->source()->point(); + + // update_bounding_box_for_ray(end_point, direction); + Local_point lp = graphics_scene.get_local_point(end_point); + Local_vector lv = graphics_scene.get_local_vector(direction); + CGAL::Bbox_3 b = (lp + lv).bbox(); + graphics_scene.update_bounding_box(b); } - } else if (he->is_bisector()) { + } + else if (he->is_bisector()) + { Kernel::Point_2 pointOnLine((v1->point().x() + v2->point().x()) / 2, (v1->point().y() + v2->point().y()) / 2); - add_line(pointOnLine, direction); + Kernel::Vector_2 perpendicularDirection( + v2->point().x() - v1->point().x(), + v2->point().y() - v1->point().y()); + + // update_bounding_box_for_line(pointOnLine, direction, + // perpendicularDirection); + Local_point lp = graphics_scene.get_local_point(pointOnLine); + Local_vector lv = graphics_scene.get_local_vector(direction); + Local_vector lpv = graphics_scene.get_local_vector(perpendicularDirection); + + CGAL::Bbox_3 b = lp.bbox() + (lp + lv).bbox() + (lp + lpv).bbox(); + graphics_scene.update_bounding_box(b); } } - - void compute_face(Face_const_handle fh) - { - CGAL::IO::Color c = m_fcolor.run(v2, fh); - - Ccb_halfedge_circulator ec_start = fh->ccb(); - Ccb_halfedge_circulator ec = ec_start; - - if (!fh->is_unbounded()) { - face_begin(c); - do { - add_point_in_face(ec->source()->point()); - } while (++ec != ec_start); - face_end(); - } - // Test: for unbounded faces - // else { - // do{ - // if( ec->has_source() ){ - // add_point_in_face(ec->source()->point()); - // } - // else{ - // add_point_in_face(get_second_point(ec->twin())); - // } - // } while(++ec != ec_start); - // } - } - - void compute_elements() - { - clear(); - - // Draw the voronoi vertices - if (m_draw_voronoi_vertices) { - for (typename V2::Vertex_iterator it = v2.vertices_begin(); - it != v2.vertices_end(); ++it) { - compute_vertex(it); - } - } - - // Draw the dual vertices - if (m_draw_dual_vertices) { - for (Dual_vertices_iterator it = v2.dual().finite_vertices_begin(); - it != v2.dual().finite_vertices_end(); ++it) { - compute_dual_vertex(it); - } - } - - // Add segments and update bounding box - for (typename V2::Halfedge_iterator it = v2.halfedges_begin(); - it != v2.halfedges_end(); ++it) { - add_segments_and_update_bounding_box(it); - } - - for (typename V2::Halfedge_iterator it = v2.halfedges_begin(); - it != v2.halfedges_end(); ++it) { - compute_rays_and_bisectors(it); - } - - if (!m_nofaces) { - for (typename V2::Face_iterator it = v2.faces_begin(); - it != v2.faces_end(); ++it) { - compute_face(it); - } - } - } - - virtual void keyPressEvent(QKeyEvent *e) - { - /// [Keypress] - const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - if ((e->key() == ::Qt::Key_R) && (modifiers == ::Qt::NoButton)) { - m_draw_rays = !m_draw_rays; - displayMessage( - QString("Draw rays=%1.").arg(m_draw_rays ? "true" : "false")); - update(); - } else if ((e->key() == ::Qt::Key_V) && (modifiers == ::Qt::NoButton)) { - m_draw_voronoi_vertices = !m_draw_voronoi_vertices; - displayMessage( - QString("Voronoi vertices=%1.").arg(m_draw_voronoi_vertices? "true" : "false")); - compute_elements(); - redraw(); - } else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) { - m_draw_dual_vertices = !m_draw_dual_vertices; - displayMessage(QString("Dual vertices=%1.") - .arg(m_draw_dual_vertices ? "true" : "false")); - compute_elements(); - redraw(); - } else { - // Call the base method to process others/classicals key - Base::keyPressEvent(e); - } - /// [Keypress] - } - -protected: - const V2 &v2; - bool m_nofaces; - bool m_draw_voronoi_vertices; - bool m_draw_dual_vertices; - const ColorFunctor &m_fcolor; -}; - -// Specialization of draw function. -#define CGAL_VORONOI_TYPE CGAL::Voronoi_diagram_2 - -template -void draw(const CGAL_VORONOI_TYPE &av2, - const char *title="2D Voronoi Diagram Basic Viewer", - bool nofill = false, - bool draw_voronoi_vertices = true, - bool draw_dual_vertices = true) -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite = true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (!cgal_test_suite) { - CGAL::Qt::init_ogl_context(4,3); - int argc = 1; - const char *argv[2] = {"voronoi_2_viewer", nullptr}; - QApplication app(argc, const_cast(argv)); - DefaultColorFunctorV2 fcolor; - SimpleVoronoiDiagram2ViewerQt - mainwindow(app.activeWindow(), av2, title, nofill, - draw_voronoi_vertices, draw_dual_vertices, fcolor); - mainwindow.show(); - app.exec(); - } } -} // End namespace CGAL +template +Local_kernel::Point_2 get_second_point(typename V2::Halfedge_handle ray, + const CGAL::Bbox_3 & m_bounding_box) +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + typedef typename V2::Delaunay_vertex_handle Delaunay_vertex_const_handle; + + Delaunay_vertex_const_handle v1 = ray->up(); + Delaunay_vertex_const_handle v2 = ray->down(); + + // calculate direction of ray and its inverse + Kernel::Vector_2 v(v1->point().y() - v2->point().y(), + v2->point().x() - v1->point().x()); + Local_kernel::Vector_2 inv(1 / v.x(), 1 / v.y()); + + // origin of the ray + Kernel::Point_2 p; + if (ray->has_source()) + { p = ray->source()->point(); } + else + { p = ray->target()->point(); } + + // get the bounding box of the viewer + Local_kernel::Vector_2 boundsMin(m_bounding_box.xmin(), + m_bounding_box.zmin()); + Local_kernel::Vector_2 boundsMax(m_bounding_box.xmax(), + m_bounding_box.zmax()); + // calculate intersection + double txmax, txmin, tymax, tymin; + + if (inv.x() >= 0) + { + txmax = (boundsMax.x() - p.x()) * inv.x(); + txmin = (boundsMin.x() - p.x()) * inv.x(); + } + else + { + txmax = (boundsMin.x() - p.x()) * inv.x(); + txmin = (boundsMax.x() - p.x()) * inv.x(); + } + + if (inv.y() >= 0) + { + tymax = (boundsMax.y() - p.y()) * inv.y(); + tymin = (boundsMin.y() - p.y()) * inv.y(); + } + else + { + tymax = (boundsMin.y() - p.y()) * inv.y(); + tymin = (boundsMax.y() - p.y()) * inv.y(); + } + + if (tymin > txmin) + txmin = tymin; + if (tymax < txmax) + txmax = tymax; + + Local_kernel::Point_2 p1; + if (v.x() == 0) + { p1 = Local_kernel::Point_2(p.x(), p.y() + tymax * v.y()); } + else if (v.y() == 0) + { p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y()); } + else + { p1 = Local_kernel::Point_2(p.x() + txmax * v.x(), p.y() + tymax * v.y()); } + return p1; +} + +// Halfedge_const_handle +template +void compute_rays_and_bisectors(const V2&, + typename V2::Halfedge_iterator he, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + typedef typename V2::Delaunay_vertex_handle Delaunay_vertex_const_handle; + + Delaunay_vertex_const_handle v1 = he->up(); + Delaunay_vertex_const_handle v2 = he->down(); + + Kernel::Vector_2 direction(v1->point().y() - v2->point().y(), + v2->point().x() - v1->point().x()); + if (he->is_ray()) + { + if (he->has_source()) + { + // add_ray_segment(he->source()->point(), get_second_point(he, graphics_scene.get_bounding_box())); + graphics_scene.add_ray(he->source()->point(), direction, gs_options.ray_color()); + } + } + else if (he->is_bisector()) + { + Kernel::Point_2 pointOnLine((v1->point().x() + v2->point().x()) / 2, + (v1->point().y() + v2->point().y()) / 2); + graphics_scene.add_line(pointOnLine, direction, gs_options.bisector_color()); + } +} + +template +void compute_face(const V2& v2, + typename V2::Face_iterator fh, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& m_gs_options) +{ + if(fh->is_unbounded() || !m_gs_options.draw_face(v2, fh)) + { return; } + + if(m_gs_options.colored_face(v2, fh)) + { graphics_scene.face_begin(m_gs_options.face_color(v2, fh)); } + else { graphics_scene.face_begin(); } + + typename V2::Ccb_halfedge_circulator ec_start=fh->ccb(); + typename V2::Ccb_halfedge_circulator ec=ec_start; + do + { + graphics_scene.add_point_in_face(ec->source()->point()); + } + while (++ec!=ec_start); + graphics_scene.face_end(); + + // Test: for unbounded faces (??) + // else { + // do{ + // if( ec->has_source() ){ + // add_point_in_face(ec->source()->point()); + // } + // else{ + // add_point_in_face(get_second_point(ec->twin(), graphics_scene.get_bounding_box())); + // } + // } while(++ec != ec_start); + // } +} + +template +void compute_elements(const V2& v2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& gs_options) +{ + if(gs_options.are_vertices_enabled()) + { + // Draw the voronoi vertices + if (gs_options.draw_voronoi_vertices()) + { + for (typename V2::Vertex_iterator it=v2.vertices_begin(); + it!=v2.vertices_end(); ++it) + { compute_vertex(v2, it, graphics_scene, gs_options); } + } + + // Draw the dual vertices + if (gs_options.draw_dual_vertices()) + { + for (typename V2::Delaunay_graph::Finite_vertices_iterator + it=v2.dual().finite_vertices_begin(); + it!=v2.dual().finite_vertices_end(); ++it) + { compute_dual_vertex(v2, it, graphics_scene, gs_options); } + } + } + + if(gs_options.are_edges_enabled()) + { + // Add segments and update bounding box + for (typename V2::Halfedge_iterator it=v2.halfedges_begin(); + it!=v2.halfedges_end(); ++it) + { add_segments_and_update_bounding_box(v2, it, + graphics_scene, gs_options); } + } + + for (typename V2::Halfedge_iterator it=v2.halfedges_begin(); + it!=v2.halfedges_end(); ++it) + { compute_rays_and_bisectors(v2, it, graphics_scene, gs_options); } + + if (gs_options.are_faces_enabled()) + { + for (typename V2::Face_iterator it=v2.faces_begin(); it!=v2.faces_end(); ++it) + { compute_face(v2, it, graphics_scene, gs_options); } + } +} + +} // namespace draw_function_for_v2 + +#define CGAL_VORONOI_TYPE CGAL::Voronoi_diagram_2 + +template +void add_to_graphics_scene(const CGAL_VORONOI_TYPE &v2, + CGAL::Graphics_scene& graphics_scene, + const GSOptions& m_gs_options) +{ + draw_function_for_v2::compute_elements(v2, graphics_scene, m_gs_options); +} + +template +void add_to_graphics_scene(const CGAL_VORONOI_TYPE& v2, + CGAL::Graphics_scene& graphics_scene) +{ + // Default graphics view options. + CGAL::Graphics_scene_options_voronoi_2 + gs_options; + + add_to_graphics_scene(v2, graphics_scene, gs_options); +} + +#ifdef CGAL_USE_BASIC_VIEWER + +// Specialization of draw function. +template +void draw(const CGAL_VORONOI_TYPE& av2, + GSOptions& gs_options, + const char *title="2D Voronoi Diagram Basic Viewer") +{ + CGAL::Graphics_scene buffer; + add_to_graphics_scene(av2, buffer, gs_options); + + CGAL::Qt::QApplication_and_basic_viewer app(buffer, title); + if(app) + { + // Here we define the std::function to capture key pressed. + app.basic_viewer().on_key_pressed= + [&av2, &buffer, &gs_options] (QKeyEvent* e, CGAL::Qt::Basic_viewer* basic_viewer) -> bool + { + const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + if ((e->key() == ::Qt::Key_R) && (modifiers == ::Qt::NoButton)) + { + basic_viewer->toggle_draw_rays(); + basic_viewer->displayMessage + (QString("Draw rays=%1.").arg(basic_viewer->draw_rays()?"true":"false")); + + basic_viewer->redraw(); + } + else if ((e->key() == ::Qt::Key_V) && (modifiers == ::Qt::ShiftModifier)) + { + gs_options.toggle_draw_voronoi_vertices(); + basic_viewer->displayMessage + (QString("Voronoi vertices=%1."). + arg(gs_options.draw_voronoi_vertices()?"true":"false")); + + buffer.clear(); + draw_function_for_v2::compute_elements(av2, buffer, gs_options); + basic_viewer->redraw(); + } + else if ((e->key() == ::Qt::Key_D) && (modifiers == ::Qt::NoButton)) + { + gs_options.toggle_draw_dual_vertices(); + basic_viewer->displayMessage(QString("Dual vertices=%1."). + arg(gs_options.draw_dual_vertices()?"true":"false")); + + buffer.clear(); + draw_function_for_v2::compute_elements(av2, buffer, gs_options); + basic_viewer->redraw(); + } + else + { + // Return false will call the base method to process others/classicals key + return false; + } + return true; // the key was captured + }; + + // Here we add shortcut descriptions + app.basic_viewer().setKeyDescription(::Qt::Key_R, "Toggles rays display"); + app.basic_viewer().setKeyDescription(::Qt::Key_D, "Toggles dual vertices display"); + app.basic_viewer().setKeyDescription(::Qt::ShiftModifier, ::Qt::Key_V, "Toggles voronoi vertices display"); + + // Then we run the app + app.run(); + } +} + +template +void draw(const CGAL_VORONOI_TYPE& av2, + const char *title="2D Voronoi Diagram Basic Viewer") +{ + CGAL::Graphics_scene_options_voronoi_2 + gs_options; + draw(av2, gs_options, title); +} #endif // CGAL_USE_BASIC_VIEWER +#undef CGAL_VORONOI_TYPE + +} // End namespace CGAL + #endif // CGAL_DRAW_VORONOI_DIAGRAM_2_H diff --git a/Voronoi_diagram_2/package_info/Voronoi_diagram_2/dependencies b/Voronoi_diagram_2/package_info/Voronoi_diagram_2/dependencies index 47ba473b701..b92af4a0b3b 100644 --- a/Voronoi_diagram_2/package_info/Voronoi_diagram_2/dependencies +++ b/Voronoi_diagram_2/package_info/Voronoi_diagram_2/dependencies @@ -1,15 +1,31 @@ Algebraic_foundations Apollonius_graph_2 +Arithmetic_kernel +BGL +Basic_viewer +Cartesian_kernel Circulator -GraphicsView +Distance_2 +Distance_3 +Filtered_kernel Hash_map +Homogeneous_kernel Installation +Intersections_2 +Intersections_3 Interval_support Kernel_23 +Kernel_d Modular_arithmetic Number_types +Polygon Profiling_tools +Property_map +Random_numbers STL_Extension +Spatial_sorting Stream_support TDS_2 +Triangulation_2 Voronoi_diagram_2 +CGAL_Core diff --git a/Weights/include/CGAL/Weights/cotangent_weights.h b/Weights/include/CGAL/Weights/cotangent_weights.h index dc070c5c493..7bd3f706a4b 100644 --- a/Weights/include/CGAL/Weights/cotangent_weights.h +++ b/Weights/include/CGAL/Weights/cotangent_weights.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/Weights/package_info/Weights/dependencies b/Weights/package_info/Weights/dependencies index 4ff65640789..63d9e7ce133 100644 --- a/Weights/package_info/Weights/dependencies +++ b/Weights/package_info/Weights/dependencies @@ -9,7 +9,6 @@ Number_types Polygon Profiling_tools Property_map -Random_numbers STL_Extension Stream_support Weights