diff --git a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/CMakeLists.txt b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/CMakeLists.txt index 213a2566931..3d61e7b6c81 100644 --- a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/CMakeLists.txt +++ b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/CMakeLists.txt @@ -21,4 +21,7 @@ if(CGAL_Qt5_FOUND) target_link_libraries(parabolas PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(ellipses PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(hyperbolas PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(polylines PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(circles PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(circular_arcs PUBLIC CGAL::CGAL_Basic_viewer) endif() diff --git a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circles.cpp b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circles.cpp index 014cae1d161..f12cfb5af63 100644 --- a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circles.cpp +++ b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circles.cpp @@ -1,6 +1,8 @@ //! \file examples/Arrangement_on_surface_2/circles.cpp // Constructing an arrangement of circles using the circle-segment traits. +#include + #include "arr_circular.h" int main() { @@ -27,5 +29,6 @@ int main() { std::cout << "The vertex with maximal degree in the arrangement is: " << "v_max = (" << v_max->point() << ") " << "with degree " << v_max->degree() << "." << std::endl; + CGAL::draw(arr); return 0; } diff --git a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circular_arcs.cpp b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circular_arcs.cpp index 926162f1997..11043fb5555 100644 --- a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circular_arcs.cpp +++ b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/circular_arcs.cpp @@ -1,6 +1,8 @@ //! \file examples/Arrangement_on_surface_2/circular_arc.cpp // Constructing an arrangement of various circular arcs and line segments. +#include + #include "arr_circular.h" #include "arr_print.h" @@ -56,5 +58,6 @@ int main() { Arrangement arr; insert(arr, curves.begin(), curves.end()); print_arrangement_size(arr); + CGAL::draw(arr); return 0; } diff --git a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/polylines.cpp b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/polylines.cpp index b81c2ad65fc..51fa18d82c8 100644 --- a/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/polylines.cpp +++ b/Arrangement_on_surface_2/examples/Arrangement_on_surface_2/polylines.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "arr_polylines.h" #include "arr_print.h" @@ -44,5 +46,6 @@ int main() { insert(arr, pi2); insert(arr, pi3); print_arrangement_size(arr); // print the arrangement size + CGAL::draw(arr); return 0; } diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_circle_segment_traits_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_circle_segment_traits_2.h index 746ef587cac..919b3ec0548 100644 --- a/Arrangement_on_surface_2/include/CGAL/Arr_circle_segment_traits_2.h +++ b/Arrangement_on_surface_2/include/CGAL/Arr_circle_segment_traits_2.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -378,6 +379,183 @@ public: } //@} + /// \name Functor definitions for approximations. Used by the landmarks + // point-location strategy and the drawing procedure. + //@{ + typedef double Approximate_number_type; + typedef CGAL::Cartesian Approximate_kernel; + typedef Approximate_kernel::Point_2 Approximate_point_2; + + class Approximate_2 { + protected: + using Traits = Arr_circle_segment_traits_2; + + /*! The traits (in case it has state) */ + const Traits& m_traits; + + /*! Constructor + * \param traits the traits. + */ + Approximate_2(const Traits& traits) : m_traits(traits) {} + + friend class Arr_circle_segment_traits_2; + + public: + /*! Obtain an approximation of a point coordinate. + * \param p the exact point. + * \param i the coordinate index (either 0 or 1). + * \pre i is either 0 or 1. + * \return An approximation of p's x-coordinate (if i == 0), or an + * approximation of p's y-coordinate (if i == 1). + */ + Approximate_number_type operator()(const Point_2& p, int i) const { + CGAL_precondition((i == 0) || (i == 1)); + return (i == 0) ? (CGAL::to_double(p.x())) : (CGAL::to_double(p.y())); + } + + /*! Obtain an approximation of a point. + */ + Approximate_point_2 operator()(const Point_2& p) const + { return Approximate_point_2(operator()(p, 0), operator()(p, 1)); } + + /*! Obtain an approximation of an \f$x\f$-monotone curve. + */ + template + OutputIterator operator()(const X_monotone_curve_2& xcv, double error, + OutputIterator oi, bool l2r = true) const { + if (xcv.is_linear()) return approximate_segment(xcv, oi, l2r); + return approximate_arc(xcv, error, oi, l2r);; + } + + private: + /*! Handle segments. + */ + template + OutputIterator approximate_segment(const X_monotone_curve_2& xcv, + OutputIterator oi, + bool l2r = true) const { + // std::cout << "SEGMENT\n"; + auto min_vertex = m_traits.construct_min_vertex_2_object(); + auto max_vertex = m_traits.construct_max_vertex_2_object(); + const auto& src = (l2r) ? min_vertex(xcv) : max_vertex(xcv); + const auto& trg = (l2r) ? max_vertex(xcv) : min_vertex(xcv); + auto xs = CGAL::to_double(src.x()); + auto ys = CGAL::to_double(src.y()); + auto xt = CGAL::to_double(trg.x()); + auto yt = CGAL::to_double(trg.y()); + *oi++ = Approximate_point_2(xs, ys); + *oi++ = Approximate_point_2(xt, yt); + return oi; + } + + template + OutputIterator add_points(double x1, double y1, double t1, + double x2, double y2, double t2, + double error, OutputIterator oi, + Op op, Transform transform) const { + auto tm = (t1 + t2)*0.5; + + // Compute the canocal point where the error is maximal. + double xm, ym; + op(tm, xm, ym); + + auto dx = x2 - x1; + auto dy = y2 - y1; + + // Compute the error; abort if it is below the threshold + auto l = std::sqrt(dx*dx + dy*dy); + auto e = std::abs((xm*dy - ym*dx + x2*y1 - x1*y2) / l); + if (e < error) return oi; + + double x, y; + transform(xm, ym, x, y); + add_points(x1, y1, t1, xm, ym, tm, error, oi, op, transform); + *oi++ = Approximate_point_2(x, y); + add_points(xm, ym, tm, x2, y2, t2, error, oi, op, transform); + return oi; + } + + /*! Compute the circular point given the parameter t and the transform + * data, that is, the center (translation) and the sin and cos of the + * rotation angle. + */ + void circular_point(double r, double t, double& x, double& y) const { + x = r * std::cos(t); + y = r * std::sin(t); + } + + /*! Transform a point. In particular, rotate the canonical point + * (`xc`,`yc`) by an angle, the sine and cosine of which are `sint` and + * `cost`, respectively, and translate by (`cx`,`cy`). + */ + void transform_point(double xc, double yc, double cx, double cy, + double& x, double& y) const { + x = xc + cx; + y = yc + cy; + } + + /*! Handle circular arcs. + */ + template + OutputIterator approximate_arc(const X_monotone_curve_2& xcv, + double error, OutputIterator oi, + bool l2r = true) const { + auto min_vertex = m_traits.construct_min_vertex_2_object(); + auto max_vertex = m_traits.construct_max_vertex_2_object(); + const auto& src = (l2r) ? min_vertex(xcv) : max_vertex(xcv); + const auto& trg = (l2r) ? max_vertex(xcv) : min_vertex(xcv); + auto xs = CGAL::to_double(src.x()); + auto ys = CGAL::to_double(src.y()); + auto xt = CGAL::to_double(trg.x()); + auto yt = CGAL::to_double(trg.y()); + + const typename Kernel::Circle_2& circ = xcv.supporting_circle(); + auto r_sqr = circ.squared_radius(); + auto r = std::sqrt(CGAL::to_double(r_sqr)); + + // Obtain the center: + auto cx = CGAL::to_double(circ.center().x()); + auto cy = CGAL::to_double(circ.center().y()); + + // Inverse transform the source and target + auto xs_t = xs - cx; + auto ys_t = ys - cy; + auto xt_t = xt - cx; + auto yt_t = yt - cy; + + // Compute the parameters ts and tt such that + // source == (x(ts),y(ts)), and + // target == (x(tt),y(tt)) + auto ts = std::atan2(r*ys_t, r*xs_t); + if (ts < 0) ts += 2*M_PI; + auto tt = std::atan2(r*yt_t, r*xt_t); + if (tt < 0) tt += 2*M_PI; + auto orient(xcv.orientation()); + if (xcv.source() != src) orient = CGAL::opposite(orient); + if (orient == COUNTERCLOCKWISE) { + if (tt < ts) tt += 2*M_PI; + } + else { + if (ts < tt) ts += 2*M_PI; + } + + *oi++ = Approximate_point_2(xs, ys); + add_points(xs_t, ys_t, ts, xt_t, yt_t, tt, error, oi, + [&](double tm, double& xm, double& ym) { + circular_point(r, tm, xm, ym); + }, + [&](double xc, double& yc, double& x, double& y) { + transform_point(xc, yc, cx, cy, x, y); + }); + *oi++ = Approximate_point_2(xt, yt); + return oi; + } + }; + + /*! Obtain an Approximate_2 functor object. */ + Approximate_2 approximate_2_object() const { return Approximate_2(*this); } + //@} + /// \name Intersections, subdivisions, and mergings //@{ 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 2d243b01f35..019685ee0a1 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 @@ -1674,9 +1674,9 @@ public: double a; double ts, tt; double cx, cy; - m_traits.approximate_parabola(r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, - xs_t, ys_t, xt_t, yt_t, a, ts, tt, cx, cy, - xcv); + m_traits.approximate_parabola(xcv, + r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, + xs_t, ys_t, xt_t, yt_t, a, ts, tt, cx, cy); auto ds = parabolic_arc_length(xs_t, 2.0*std::abs(ys_t)); auto dt = parabolic_arc_length(xt_t, 2.0*std::abs(yt_t)); @@ -1694,9 +1694,9 @@ public: double a, b; double cx, cy; double ts, tt; - m_traits.approximate_ellipse(r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, + m_traits.approximate_ellipse(xcv, r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, xs_t, ys_t, ts, xt_t, yt_t, tt, - a, b, cx, cy, xcv); + a, b, cx, cy); namespace bm = boost::math; auto ratio = b/a; @@ -1746,31 +1746,29 @@ public: /*! Obtain an approximation of a point. */ Approximate_point_2 operator()(const Point_2& p) const - { return std::make_pair(operator()(p, 0), operator()(p, 1)); } + { return Approximate_point_2(operator()(p, 0), operator()(p, 1)); } /*! Obtain an approximation of an \f$x\f$-monotone curve. */ template - OutputIterator operator()(OutputIterator oi, double error, - const X_monotone_curve_2& xcv, - bool l2r = true) const { + OutputIterator operator()(const X_monotone_curve_2& xcv, double error, + OutputIterator oi, bool l2r = true) const { if (xcv.orientation() == COLLINEAR) - return approximate_segment(oi, xcv, l2r); + return approximate_segment(xcv, oi, l2r); CGAL::Sign sign_conic = CGAL::sign(4*xcv.r()*xcv.s() - xcv.t()*xcv.t()); if (sign_conic == POSITIVE) - return approximate_ellipse(oi, error, xcv, l2r); + return approximate_ellipse(xcv, error, oi, l2r); if (sign_conic == NEGATIVE) - return approximate_hyperbola(oi, error, xcv, l2r); - return approximate_parabola(oi, error, xcv, l2r); + return approximate_hyperbola(xcv, error, oi, l2r); + return approximate_parabola(xcv, error, oi, l2r); } private: /*! Handle segments. */ template - OutputIterator approximate_segment(OutputIterator oi, - const X_monotone_curve_2& xcv, - bool l2r) const { + OutputIterator approximate_segment(const X_monotone_curve_2& xcv, + OutputIterator oi, bool l2r) const { // std::cout << "SEGMENT\n"; auto min_vertex = m_traits.construct_min_vertex_2_object(); auto max_vertex = m_traits.construct_max_vertex_2_object(); @@ -1865,9 +1863,9 @@ public: * which approximates the arc. */ template - OutputIterator approximate_ellipse(OutputIterator oi, double error, - const X_monotone_curve_2& xcv, - bool l2r) const { + OutputIterator approximate_ellipse(const X_monotone_curve_2& xcv, + double error, OutputIterator oi, + bool l2r = true) const { // std::cout << "ELLIPSE\n"; auto min_vertex = m_traits.construct_min_vertex_2_object(); auto max_vertex = m_traits.construct_max_vertex_2_object(); @@ -1887,9 +1885,9 @@ public: double a, b; double cx, cy; double ts, tt; - m_traits.approximate_ellipse(r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, + m_traits.approximate_ellipse(xcv, r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, xs_t, ys_t, ts, xt_t, yt_t, tt, - a, b, cx, cy, xcv, l2r); + a, b, cx, cy, l2r); // std::cout << "a, b: " << a << "," << b << std::endl; *oi++ = Approximate_point_2(xs, ys); @@ -1954,7 +1952,7 @@ public: return oi; } - /*! Compute the hyperbolic point given the parameter t and the transform + /*! Compute the elliptic point given the parameter t and the transform * data, that is, the center (translation) and the sin and cos of the * rotation angle. */ @@ -1969,8 +1967,9 @@ public: * https://www.vcalc.com/wiki/vCalc/Parabola+-+arc+length */ template - OutputIterator approximate_parabola(OutputIterator oi, double error, - const X_monotone_curve_2& xcv, bool l2r) + OutputIterator approximate_parabola(const X_monotone_curve_2& xcv, + double error, OutputIterator oi, + bool l2r = true) const { // std::cout << "PARABOLA\n"; auto min_vertex = m_traits.construct_min_vertex_2_object(); @@ -1991,9 +1990,10 @@ public: double a; double ts, tt; double cx, cy; - m_traits.approximate_parabola(r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, + m_traits.approximate_parabola(xcv, + r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, xs_t, ys_t, xt_t, yt_t, a, ts, tt, cx, cy, - xcv, l2r); + l2r); // std::cout << "sint, cost: " << sint << "," << cost << std::endl; // std::cout << "a: " << a << std::endl; // std::cout << "xs' = " << xs_t << "," << ys_t << std::endl; @@ -2074,9 +2074,9 @@ public: /*! Handle hyperbolas. */ template - OutputIterator approximate_hyperbola(OutputIterator oi, double error, - const X_monotone_curve_2& xcv, - bool l2r) const { + OutputIterator approximate_hyperbola(const X_monotone_curve_2& xcv, + double error, OutputIterator oi, + bool l2r = true) const { // std::cout << "HYPERBOLA\n"; auto min_vertex = m_traits.construct_min_vertex_2_object(); auto max_vertex = m_traits.construct_max_vertex_2_object(); @@ -2093,9 +2093,10 @@ public: double a, b; double cx, cy; double ts, tt; - m_traits.approximate_hyperbola(r_m, t_m, s_m, u_m, v_m, w_m, cost, sint, + m_traits.approximate_hyperbola(xcv, r_m, t_m, s_m, u_m, v_m, w_m, + cost, sint, xs_t, ys_t, ts, xt_t, yt_t, tt, - a, b, cx, cy, xcv, l2r); + a, b, cx, cy, l2r); // std::cout << "a, b: " << a << "," << b << std::endl; // std::cout << "ts, tt: " << ts << "," << tt << std::endl; @@ -4027,14 +4028,15 @@ public: * The arc-length closed form can be found here: * https://www.vcalc.com/wiki/vCalc/Parabola+-+arc+length */ - void approximate_parabola(double& r_m, double& t_m, double& s_m, + void approximate_parabola(const X_monotone_curve_2& xcv, + double& r_m, double& t_m, double& s_m, double& u_m, double& v_m, double& w_m, double& cost, double& sint, double& xs_t, double& ys_t, double& xt_t, double& yt_t, double& a, double& ts, double& tt, double& cx, double& cy, - const X_monotone_curve_2& xcv, bool l2r = true) + bool l2r = true) const { auto min_vertex = construct_min_vertex_2_object(); auto max_vertex = construct_max_vertex_2_object(); @@ -4095,13 +4097,14 @@ public: /*! Handle ellipses. */ - void approximate_ellipse(double& r_m, double& t_m, double& s_m, + void approximate_ellipse(const X_monotone_curve_2& xcv, + double& r_m, double& t_m, double& s_m, double& u_m, double& v_m, double& w_m, double& cost, double& sint, double& xs_t, double& ys_t, double& ts, double& xt_t, double& yt_t, double& tt, double& a, double& b, double& cx, double& cy, - const X_monotone_curve_2& xcv, bool l2r = true) + bool l2r = true) const { auto min_vertex = construct_min_vertex_2_object(); auto max_vertex = construct_max_vertex_2_object(); @@ -4172,13 +4175,14 @@ public: /*! Handle hyperbolas. */ - void approximate_hyperbola(double& r_m, double& t_m, double& s_m, + void approximate_hyperbola(const X_monotone_curve_2& xcv, + double& r_m, double& t_m, double& s_m, double& u_m, double& v_m, double& w_m, double& cost, double& sint, double& xs_t, double& ys_t, double& ts, double& xt_t, double& yt_t, double& tt, double& a, double& b, double& cx, double& cy, - const X_monotone_curve_2& xcv, bool l2r = true) + bool l2r = true) const { auto min_vertex = construct_min_vertex_2_object(); auto max_vertex = construct_max_vertex_2_object(); 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 d9e8f7c8189..e757c247976 100644 --- a/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h +++ b/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h @@ -21,17 +21,19 @@ #include #include +#include +#include + #include #ifdef CGAL_USE_BASIC_VIEWER #include #include -#include namespace CGAL { -// Viewer class for Polygon_2 +// Viewer class for`< Polygon_2 template class Arr_2_basic_viewer_qt : public Basic_viewer_qt { typedef GeometryTraits_2 Gt; @@ -45,9 +47,9 @@ class Arr_2_basic_viewer_qt : public Basic_viewer_qt { typedef typename Arr::Ccb_halfedge_const_circulator Ccb_halfedge_const_circulator; - // typedef double Approximate_number_type; - // typedef CGAL::Cartesian Approximate_kernel; - // typedef Approximate_kernel::Point_2 Approximate_point_2; + template + using approximate_2_object_t = + decltype(std::declval().approximate_2_object()); public: /// Construct the viewer. @@ -86,12 +88,33 @@ public: //! Compute the bounding box CGAL::Bbox_2 bounding_box() { + namespace bh = boost::hana; + auto has_approximate_2_object = + bh::is_valid([](auto&& x) -> decltype(x.approximate_2_object()){}); + // At this point we assume that the arrangement is not open, and thus the // bounding box is defined by the vertices. //! The bounding box CGAL::Bbox_2 bbox; - for (auto it = m_arr.vertices_begin(); it != m_arr.vertices_end(); ++it) - bbox += it->point().bbox(); + for (auto it = m_arr.vertices_begin(); it != m_arr.vertices_end(); ++it) { + bh::if_(has_approximate_2_object(Gt{}), + [&](auto& t) { + const auto* traits = this->m_arr.geometry_traits(); + auto approx = traits->approximate_2_object(); + auto has_operator = + bh::is_valid([](auto&& x) -> decltype(x.operator()(Point{}, int{})){}); + bh::if_(has_operator(approx), + [&](auto& a) { + auto x = approx(it->point(), 0); + auto y = approx(it->point(), 1); + bbox += CGAL::Bbox_2(x, y, x, y); + }, + [&](auto& a) { bbox += it->point().bbox(); } + )(approx); + }, + [&](auto& t) { bbox += it->point().bbox(); } + )(*this); + } return bbox; } @@ -167,36 +190,175 @@ protected: return ext; } - //! - virtual void draw_region(Ccb_halfedge_const_circulator circ) { - CGAL::IO::Color color(m_uni(m_rng), m_uni(m_rng), m_uni(m_rng)); - this->face_begin(color); - - auto ext = find_smallest(circ); - auto curr = ext; - do { - // Skip halfedges that are "antenas": - while (curr->face() == curr->twin()->face()) - curr = curr->twin()->next(); - - this->add_point_in_face(curr->source()->point()); - draw_curve(curr->curve()); - curr = curr->next(); - } while (curr != ext); - - this->face_end(); + //! Draw a region using aproximate coordinates. + // Call this member function only of the geometry traits is equipped to + // provide aproximate coordinates. + template + void draw_approximate_region(Halfedge_const_handle curr, + const Approximate& approx) { + 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); + } } - //! - virtual void draw_curve(const X_monotone_curve& curve) { + //! Draw an exact curve. + template + void draw_exact_curve(const XMonotoneCurve& curve) { const auto* traits = this->m_arr.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()); + } + + //! Utility struct + template + struct can_call_operator_curve { + template + constexpr auto operator()(F&& f) -> + decltype(f.template operator()(X_monotone_curve{}, double{}, T{}, bool{})) {} + }; + + //! Draw a region. + void draw_region(Ccb_halfedge_const_circulator circ) { + /* 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 and boost.hana instead. + */ + namespace bh = boost::hana; + auto has_approximate_2_object = + bh::is_valid([](auto&& x) -> decltype(x.approximate_2_object()){}); + + CGAL::IO::Color color(m_uni(m_rng), m_uni(m_rng), m_uni(m_rng)); + this->face_begin(color); + + const auto* traits = this->m_arr.geometry_traits(); + auto ext = find_smallest(circ); + auto curr = ext; + do { + // Skip halfedges that are "antenas": + while (curr->face() == curr->twin()->face()) curr = curr->twin()->next(); + + bh::if_(has_approximate_2_object(Gt{}), + [&](auto& x) { + auto approx = traits->approximate_2_object(); + auto has_operator = bh::is_valid(can_call_operator_curve{}); + bh::if_(has_operator(approx), + [&](auto& x) { x.draw_approximate_region(curr, approx); }, + [&](auto& x) { x.draw_exact_region(curr); } + )(*this); + }, + [&](auto& x) { x.draw_exact_region(curr); } + )(*this); + curr = curr->next(); + } while (curr != ext); + + this->face_end(); + } + + //! Draw a curve using aproximate coordinates. + // Call this member function only of the geometry traits is equipped to + // provide aproximate coordinates. + template + void draw_approximate_curve(const XMonotoneCurve& curve, + const Approximate& approx) { + std::vector polyline; + double error(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); + } + + //! 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 and boost.hana instead. + */ +#if 0 + if constexpr (std::experimental::is_detected_v) { + const auto* traits = this->m_arr.geometry_traits(); + auto approx = traits->approximate_2_object(); + draw_approximate_curve(curve, approx); + return; + } + draw_exact_curve(curve); +#else + namespace bh = boost::hana; + auto has_approximate_2_object = + bh::is_valid([](auto&& x) -> decltype(x.approximate_2_object()){}); + const auto* traits = this->m_arr.geometry_traits(); + bh::if_(has_approximate_2_object(Gt{}), + [&](auto& x) { + auto approx = traits->approximate_2_object(); + auto has_operator = bh::is_valid(can_call_operator_curve{}); + bh::if_(has_operator(approx), + [&](auto& x) { x.draw_approximate_curve(curve, approx); }, + [&](auto& x) { x.draw_exact_curve(curve); } + )(*this); + }, + [&](auto& x) { x.draw_exact_curve(curve); } + )(*this); +#endif + } + //! - virtual void draw_point(const Point& p) { this->add_point(p); } + void draw_point(const Point& p) { + namespace bh = boost::hana; + auto has_approximate_2_object = + bh::is_valid([](auto&& x) -> decltype(x.approximate_2_object()){}); + bh::if_(has_approximate_2_object(Gt{}), + [&](auto& x) { + const auto* traits = x.m_arr.geometry_traits(); + auto approx = traits->approximate_2_object(); + auto has_operator = + bh::is_valid([](auto&& x) -> decltype(x.operator()(Point{})){}); + bh::if_(has_operator(approx), + [&](auto& x) { /* x.add_point(approx(p)) */; }, + [&](auto& x) { x.add_point(p); } + )(*this); + }, + [&](auto& x) { x.add_point(p); } + )(*this); + } //! void add_ccb(Ccb_halfedge_const_circulator circ) { @@ -280,81 +442,6 @@ public: {} }; -//! -template -class Arr_2_viewer_qt, - Dcel> : - public Arr_2_basic_viewer_qt, Dcel> { -public: - typedef Arr_conic_traits_2 Gt; - typedef CGAL::Arrangement_2 Arr; - typedef Arr_2_basic_viewer_qt Base; - typedef typename Arr::Point_2 Point; - typedef typename Arr::X_monotone_curve_2 X_monotone_curve; - typedef typename Arr::Halfedge_const_handle Halfedge_const_handle; - typedef typename Arr::Face_const_handle Face_const_handle; - typedef typename Arr::Ccb_halfedge_const_circulator - Ccb_halfedge_const_circulator; - - /// Construct the viewer. - /// @param arr the arrangement to view - /// @param title the title of the window - Arr_2_viewer_qt(QWidget* parent, const Arr& arr, - const char* title = "2D Arrangement Basic Viewer") : - Base(parent, arr, title) - {} - - //! - virtual void draw_region(Ccb_halfedge_const_circulator circ) { - auto& uni = this->m_uni; - auto& rng = this->m_rng; - CGAL::IO::Color color(uni(rng), uni(rng), uni(rng)); - this->face_begin(color); - - const auto* traits = this->m_arr.geometry_traits(); - auto approx = traits->approximate_2_object(); - - // Find the lexicographically smallest halfedge: - auto ext = this->find_smallest(circ); - - // Iterate, starting from the lexicographically smallest vertex: - auto curr = ext; - do { - // Skip halfedges that are "antenas": - while (curr->face() == curr->twin()->face()) - curr = curr->twin()->next(); - - std::vector polyline; - double error(this->pixel_ratio()); - bool l2r = curr->direction() == ARR_LEFT_TO_RIGHT; - approx(std::back_inserter(polyline), error, curr->curve(), l2r); - auto it = polyline.begin(); - auto prev = it++; - for (; it != polyline.end(); prev = it++) { - this->add_segment(*prev, *it); - this->add_point_in_face(*prev); - } - curr = curr->next(); - } while (curr != ext); - - this->face_end(); - } - - //! - virtual void draw_curve(const X_monotone_curve& curve) { - const auto* traits = this->m_arr.geometry_traits(); - auto approx = traits->approximate_2_object(); - std::vector polyline; - double error(this->pixel_ratio()); - approx(std::back_inserter(polyline), error, curve); - auto it = polyline.begin(); - auto prev = it++; - for (; it != polyline.end(); prev = it++) this->add_segment(*prev, *it); - } -}; - //! template void draw(const Arrangement_2& arr,