From 3f90d36604c28fa5b4378cb6b0dfec0d25c8f8aa Mon Sep 17 00:00:00 2001 From: Shepard Liu Date: Wed, 11 Jun 2025 18:54:52 +0800 Subject: [PATCH] feat: draw function and classes for arrangement on surface 2 Now works for segment traits, linear traits and conic traits. --- .clang-format | 3 + .../ArrangementDemoGraphicsView.cpp | 33 +- .../CGAL/Draw_aos/Arr_approximate_point_2.h | 97 + .../CGAL/Draw_aos/Arr_approximation_cache.h | 105 ++ .../Arr_approximation_geometry_traits.h | 26 + .../Arr_bounded_approximate_curve_2.h | 280 +++ .../Draw_aos/Arr_bounded_approximate_face_2.h | 286 +++ .../Arr_bounded_approximate_point_2.h | 55 + .../Draw_aos/Arr_bounded_compute_y_at_x.h | 79 + .../Draw_aos/Arr_bounded_face_triangulator.h | 297 +++ .../CGAL/Draw_aos/Arr_bounded_renderer.h | 216 +++ .../CGAL/Draw_aos/Arr_construct_curve_end.h | 101 + .../CGAL/Draw_aos/Arr_construct_segments.h | 45 + .../include/CGAL/Draw_aos/Arr_graph_conn.h | 93 + .../include/CGAL/Draw_aos/Arr_portals.h | 133 ++ .../CGAL/Draw_aos/Arr_render_context.h | 218 +++ .../include/CGAL/Draw_aos/Arr_viewer.h | 234 +++ .../include/CGAL/Draw_aos/helpers.h | 20 + .../include/CGAL/draw_arrangement_2.h | 338 ++-- .../Arrangement_on_surface_2/CMakeLists.txt | 2 +- .../Arrangement_on_surface_2/approx_y_at_x.h | 11 + .../Arrangement_on_surface_2/cgal_test.cmake | 38 + .../test/Arrangement_on_surface_2/compile.sh | 1 + .../Arrangement_on_surface_2/outer_ccb_0.txt | 4 + .../test/Arrangement_on_surface_2/print_arr.h | 122 ++ ...raw_aos_arr_bounded_approximate_face_2.cpp | 194 ++ .../test_drawing_planar.cpp | 322 ++++ .../test_drawing_spherical.cpp | 99 + .../test/Arrangement_on_surface_2/zone_exp.h | 71 + Basic_viewer/include/CGAL/Basic_viewer.h | 14 +- Basic_viewer/include/CGAL/Qt/Basic_viewer.h | 1656 ++++++++--------- 31 files changed, 4098 insertions(+), 1095 deletions(-) create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximate_point_2.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_cache.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_geometry_traits.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_face_2.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_point_2.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_face_triangulator.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_renderer.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_curve_end.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_segments.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_graph_conn.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_portals.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_render_context.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h create mode 100644 Arrangement_on_surface_2/include/CGAL/Draw_aos/helpers.h create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/approx_y_at_x.h create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/compile.sh create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/outer_ccb_0.txt create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/print_arr.h create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_draw_aos_arr_bounded_approximate_face_2.cpp create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_planar.cpp create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_spherical.cpp create mode 100644 Arrangement_on_surface_2/test/Arrangement_on_surface_2/zone_exp.h diff --git a/.clang-format b/.clang-format index 369aadbb15f..e08dad8d573 100644 --- a/.clang-format +++ b/.clang-format @@ -6,6 +6,7 @@ AllowShortFunctionsOnASingleLine: true BinPackParameters: false BreakConstructorInitializers: BeforeComma BreakBeforeBraces: Custom +AlwaysBreakTemplateDeclarations: Yes BraceWrapping: AfterCaseLabel: false AfterClass: true @@ -32,4 +33,6 @@ PointerAlignment: Left # Control the spaces around conditionals SpacesInConditionalStatement: false SpaceBeforeParens: false +# disable sorting of includes +SortIncludes: false ... diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementDemoGraphicsView.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementDemoGraphicsView.cpp index bc09e4708a5..b8513207e9f 100644 --- a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementDemoGraphicsView.cpp +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementDemoGraphicsView.cpp @@ -12,44 +12,39 @@ #include "ArrangementDemoGraphicsView.h" -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include //! Member function to setup the viewport of the screen /*! \param parent a Qwidget pointer to the class */ -ArrangementDemoGraphicsView::ArrangementDemoGraphicsView( QWidget* parent ) : - QGraphicsView( parent ), - maxScale( 500000 ), - minScale( 0.0002 ) -{ +ArrangementDemoGraphicsView::ArrangementDemoGraphicsView(QWidget* parent) + : QGraphicsView(parent) + , maxScale(500000) + , minScale(0.0002) { this->resetTransform(); this->setResizeAnchor(QGraphicsView::AnchorUnderMouse); this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); - this->setMouseTracking( true ); + this->setMouseTracking(true); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // TODO: Make options menu work this->setRenderHint(QPainter::Antialiasing); } -void ArrangementDemoGraphicsView::paintEvent(QPaintEvent* event) -{ +void ArrangementDemoGraphicsView::paintEvent(QPaintEvent* event) { qreal scale = std::sqrt(std::abs(this->transform().determinant())); - if (scale > this->maxScale) + if(scale > this->maxScale) this->scale(this->maxScale / scale, this->maxScale / scale); - else if (scale < this->minScale) + else if(scale < this->minScale) this->scale(this->minScale / scale, this->minScale / scale); QGraphicsView::paintEvent(event); } -void ArrangementDemoGraphicsView::resetTransform() -{ - this->setTransform({1.0, 0.0, 0.0, -1.0, 0.0, 0.0}); -} +void ArrangementDemoGraphicsView::resetTransform() { this->setTransform({1.0, 0.0, 0.0, -1.0, 0.0, 0.0}); } diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximate_point_2.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximate_point_2.h new file mode 100644 index 00000000000..1fcbbbe0ba7 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximate_point_2.h @@ -0,0 +1,97 @@ +#ifndef CGAL_DRAW_AOS_ARR_APPROXIMATE_POINT_2_H +#define CGAL_DRAW_AOS_ARR_APPROXIMATE_POINT_2_H + +#include "CGAL/Arr_has.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include "CGAL/number_utils.h" + +namespace CGAL { + +namespace internal { + +template +class Arr_approximate_point_2_impl; + +template +class Arr_approximate_point_2_impl +{ + using Approx_kernel = Arr_approximation_geometry_traits::Approximation_kernel; + using Point_2 = typename Geom_traits::Point_2; + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + +public: + Arr_approximate_point_2_impl(const Geom_traits& traits) + : m_approx(traits.approximate_2_object()) {} + + /** + * @brief Approximate a point. + * TODO: make it work for spherical traits. + * + * @param pt + * @return Point_geom + */ + Approx_point operator()(const Point_2& pt) const { return {m_approx(pt, 0), m_approx(pt, 1)}; } + + /** + * @brief Approximate a specific dimension of a point. + * + * @param pt + * @param dim 0 for x, 1 for y, 2 for z if applicable. An exception is thrown if the dimension is invalid. + * @return double + */ + double operator()(const Point_2& pt, int dim) const { return m_approx(pt, dim); } + +private: + typename Geom_traits::Approximate_2 m_approx; +}; + +// Fallback to use CGAL::to_double for traits that do not have an approximate_2_object. +template +class Arr_approximate_point_2_impl +{ + using Approx_kernel = Arr_approximation_geometry_traits::Approximation_kernel; + using Point_2 = typename Geom_traits::Point_2; + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + +public: + // traits object is not used in the fallback implementation, but we keep it for consistency. + Arr_approximate_point_2_impl(const Geom_traits& traits) {} + + /** + * @brief Approximate a point. + * + * @note this functions does not check if the point is within the bounding box. + * @param pt + * @return Point_geom + */ + Approx_point operator()(const Point_2& pt) const { return {CGAL::to_double(pt.x()), CGAL::to_double(pt.y())}; } + + /** + * @brief Approximate a specific dimension of a point. + * + * @param pt + * @param dim 0 for x, 1 for y, 2 for z if applicable. An exception is thrown if the dimension is invalid. + * @return double + */ + double operator()(const Point_2& pt, int dim) const { + if(dim == 0) { + return CGAL::to_double(pt.x()); + } else if(dim == 1) { + return CGAL::to_double(pt.y()); + } else if(dim == 2 && pt.dimension() == 3) { + return CGAL::to_double(pt.z()); + } else { + throw std::invalid_argument("Invalid dimension for approximate_point"); + } + } +}; + +} // namespace internal + +template +using Arr_approximate_point_2 = + internal::Arr_approximate_point_2_impl::value>; + +} // namespace CGAL + +#endif // CGAL_DRAW_AOS_ARR_APPROXIMATE_POINT_2_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_cache.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_cache.h new file mode 100644 index 00000000000..f84f125ab05 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_cache.h @@ -0,0 +1,105 @@ +#ifndef CGAL_DRAW_AOS_ARR_APPROXIMATION_CACHE_H +#define CGAL_DRAW_AOS_ARR_APPROXIMATION_CACHE_H +#include "CGAL/Arr_enums.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include "CGAL/Draw_aos/helpers.h" +#include "CGAL/unordered_flat_map.h" +#include + +namespace CGAL { +class Arr_approximation_cache +{ + using Approx_geom_traits = Arr_approximation_geometry_traits; + +public: + using Vertex_cache_obj = Approx_geom_traits::Point_geom; + using Halfedge_cache_obj = Approx_geom_traits::Polyline_geom; + using Face_cache_obj = Approx_geom_traits::Triangulated_face; + +private: + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Edge_const_handle = Arrangement::Edge_const_iterator; + using Halfedge_const_handle = Arrangement::Halfedge_const_iterator; + using Face_const_handle = Arrangement::Face_const_handle; + using Vertex_cache = unordered_flat_map; + using Halfedge_cache = unordered_flat_map; + using Face_cache = unordered_flat_map; + + using Vertex_cache_const_iterator = typename Vertex_cache::const_iterator; + using Halfedge_cache_const_iterator = typename Halfedge_cache::const_iterator; + using Face_cache_const_iterator = typename Face_cache::const_iterator; + using Vertex_cache_range = boost::iterator_range; + using Halfedge_cache_range = boost::iterator_range; + using Face_cache_range = boost::iterator_range; + +private: + Halfedge_const_handle identify_halfedge(const Halfedge_const_handle& he) const { + return he->direction() == ARR_RIGHT_TO_LEFT ? he : he->twin(); + } + +public: + std::pair try_emplace(const Vertex_const_handle& vh) { + const auto& [it, inserted] = m_vertex_cache.try_emplace(vh, Vertex_cache_obj()); + return {it->second, inserted}; + } + std::pair try_emplace(const Halfedge_const_handle& he) { + const auto& [it, inserted] = m_halfedge_cache.try_emplace(identify_halfedge(he), Halfedge_cache_obj()); + return {it->second, inserted}; + } + std::pair try_emplace(const Face_const_handle& fh) { + const auto& [it, inserted] = m_face_cache.try_emplace(fh, Face_cache_obj()); + return {it->second, inserted}; + } + + std::pair get(const Vertex_const_handle& vh) const { + auto it = m_vertex_cache.find(vh); + if(it != m_vertex_cache.end()) { + return {it->second, true}; + } + return {Vertex_cache_obj(), false}; + } + std::pair get(const Halfedge_const_handle& he) const { + auto it = m_halfedge_cache.find(identify_halfedge(he)); + if(it != m_halfedge_cache.end()) { + return {it->second, true}; + } + return {Halfedge_cache_obj(), false}; + } + std::pair get(const Face_const_handle& fh) const { + auto it = m_face_cache.find(fh); + if(it != m_face_cache.end()) { + return {it->second, true}; + } + return {Face_cache_obj(), false}; + } + + bool has(const Vertex_const_handle& vh) const { return m_vertex_cache.find(vh) != m_vertex_cache.end(); } + bool has(const Halfedge_const_handle& he) const { + return m_halfedge_cache.find(identify_halfedge(he)) != m_halfedge_cache.end(); + } + bool has(const Face_const_handle& fh) const { return m_face_cache.find(fh) != m_face_cache.end(); } + + Vertex_cache_const_iterator vertex_cache_begin() const { return m_vertex_cache.begin(); } + Vertex_cache_const_iterator vertex_cache_end() const { return m_vertex_cache.end(); } + Vertex_cache_range vertex_cache() const { + return boost::make_iterator_range(vertex_cache_begin(), vertex_cache_end()); + } + + Halfedge_cache_const_iterator halfedge_cache_begin() const { return m_halfedge_cache.begin(); } + Halfedge_cache_const_iterator halfedge_cache_end() const { return m_halfedge_cache.end(); } + Halfedge_cache_range halfedge_cache() const { + return boost::make_iterator_range(halfedge_cache_begin(), halfedge_cache_end()); + } + + Face_cache_const_iterator face_cache_begin() const { return m_face_cache.begin(); } + Face_cache_const_iterator face_cache_end() const { return m_face_cache.end(); } + Face_cache_range face_cache() const { return boost::make_iterator_range(face_cache_begin(), face_cache_end()); } + +private: + Vertex_cache m_vertex_cache; + Halfedge_cache m_halfedge_cache; + Face_cache m_face_cache; +}; + +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_APPROXIMATION_CACHE_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_geometry_traits.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_geometry_traits.h new file mode 100644 index 00000000000..24c901f2e90 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_approximation_geometry_traits.h @@ -0,0 +1,26 @@ +#ifndef CGAL_DRAW_AOS_ARR_APPROXIMATION_GEOMETRY_TRAITS_H +#define CGAL_DRAW_AOS_ARR_APPROXIMATION_GEOMETRY_TRAITS_H + +#include "CGAL/Simple_cartesian.h" + +namespace CGAL { +class Arr_approximation_geometry_traits +{ +public: + using Approximation_kernel = Simple_cartesian; + using Approx_point = Approximation_kernel::Point_2; + using FT = double; + using Point_geom = Approx_point; + using Apporx_point_vec = std::vector; + using Polyline_geom = Apporx_point_vec; + using Triangle = std::array; + using Triangle_vec = std::vector; + struct Triangulated_face + { + Apporx_point_vec points; + Triangle_vec triangles; + }; +}; + +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_APPROXIMATION_GEOMETRY_TRAITS_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h new file mode 100644 index 00000000000..83c10aa8d57 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h @@ -0,0 +1,280 @@ +#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_CURVE_2_H +#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_CURVE_2_H + +#include "CGAL/Arr_enums.h" +#include "CGAL/Draw_aos/Arr_bounded_approximate_point_2.h" +#include "CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h" +#include "CGAL/Draw_aos/Arr_construct_curve_end.h" +#include "CGAL/Draw_aos/Arr_construct_segments.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include "CGAL/Draw_aos/helpers.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include "CGAL/basic.h" +#include +#include +#include +#include +#include + +namespace CGAL { + +/** + * @brief Functor to approximate an x-monotone curve within an bounding box. + * The bbox here has closed boundary. + * + * The Approximation is done from xmin to xmax with a given step. For parts outbound the y limits and precedes or + * succeeds a part within, the approximation may be skipped but there will be at least one point outside the bbox for + * indication. + * + * @note Bounded approximation is meaningful only when the curve has at least two points within the bbox (boundary + * points included). + * + * TODO: Possible optimizations: + * - Specialize for traits that models Approximate_2 on curves. + */ +class Arr_bounded_approximate_curve_2 +{ + using FT = Geom_traits::FT; + using Point_2 = Geom_traits::Point_2; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using Halfedge_const_handle = Arrangement::Halfedge_const_iterator; + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + using Point_geom = Arr_approximation_geometry_traits::Point_geom; + using Polyline_geom = Arr_approximation_geometry_traits::Polyline_geom; + using Intersections_vector = std::vector; + + struct Execution_context : public Arr_context_delegator + { + Execution_context(const Arr_bounded_render_context& ctx, + const X_monotone_curve_2& curve, + const Arr_bounded_approximate_point_2& approx_pt, + const Arr_bounded_compute_y_at_x& compute_y_at_x, + const Intersections_vector& top_inters, + const Intersections_vector& bottom_inters, + Polyline_geom& polyline) + : Arr_context_delegator(ctx) + , curve(curve) + , bounded_compute_y_at_x(compute_y_at_x) + , bounded_approx_pt(approx_pt) + , top_inters(top_inters) + , bottom_inters(bottom_inters) + , min_end(Arr_construct_curve_end(ctx.traits)(curve, ARR_MIN_END)) + , max_end(Arr_construct_curve_end(ctx.traits)(curve, ARR_MAX_END)) + , tight_xmin(is_min_end_bounded() ? std::clamp(min_end->x(), FT(ctx.xmin()), FT(ctx.xmax())) : FT(ctx.xmin())) + , tight_xmax(is_max_end_bounded() ? std::clamp(max_end->x(), FT(ctx.xmin()), FT(ctx.xmax())) : FT(ctx.xmax())) + , tight_ymin(is_min_end_bounded() ? std::clamp(min_end->y(), FT(ctx.ymin()), FT(ctx.ymax())) : FT(ctx.ymin())) + , tight_ymax(is_max_end_bounded() ? std::clamp(max_end->y(), FT(ctx.ymin()), FT(ctx.ymax())) : FT(ctx.ymax())) + , out_it(std::back_inserter(polyline)) {} + + bool has_y_intersections() const { return !top_inters.empty() || !bottom_inters.empty(); } + bool is_min_end_bounded() const { return min_end.has_value(); } + bool is_max_end_bounded() const { return max_end.has_value(); } + bool is_bounded_curve() const { return is_min_end_bounded() && is_max_end_bounded(); } + + const X_monotone_curve_2& curve; + const Arr_bounded_compute_y_at_x& bounded_compute_y_at_x; + const Arr_bounded_approximate_point_2& bounded_approx_pt; + const Intersections_vector &top_inters, bottom_inters; + const std::optional min_end, max_end; + const FT tight_xmin, tight_xmax, tight_ymin, tight_ymax; + std::back_insert_iterator out_it; + }; + +private: + static std::vector compute_intersections(const X_monotone_curve_2& cv1, + const X_monotone_curve_2& cv2, + const typename Geom_traits::Intersect_2& intersect_2, + const Arr_construct_curve_end& cst_curve_end) { + using Intersect_point = std::pair; + using Intersect_curve = Geom_traits::X_monotone_curve_2; + using Intersect_type = std::variant; + + std::vector intersections; + auto out_it = std::back_inserter(intersections); + intersect_2(cv1, cv2, boost::make_function_output_iterator([&out_it, &cst_curve_end](const Intersect_type& res) { + if(auto* pt = std::get_if(&res)) { + *out_it++ = pt->first; + return; + } + if(auto* cv = std::get_if(&res)) { + *out_it++ = cst_curve_end(*cv, ARR_MIN_END).value(); + *out_it++ = cst_curve_end(*cv, ARR_MAX_END).value(); + return; + } + CGAL_assertion(false && "Unexpected intersection type"); + })); + return intersections; + } + + static std::optional first_intersection(Execution_context& ctx) { + if(!ctx.top_inters.empty() && !ctx.bottom_inters.empty()) { + return ctx->compare_xy_2(ctx.top_inters.front(), ctx.bottom_inters.front()) == CGAL::SMALLER + ? ctx.top_inters.front() + : ctx.bottom_inters.front(); + } + if(!ctx.top_inters.empty()) { + return ctx.top_inters.front(); + } + if(!ctx.bottom_inters.empty()) { + return ctx.bottom_inters.front(); + } + return std::nullopt; + } + + /** + * @brief approximate strictly x-monotone curve segment that does not cross the y bounds. + * + * @precondition: The segment is either inbound or outbound the bbox in the given range. + * @param start the x-coordinate of the segment start(exclusive) + * @param end the x-coordinate of the segment end(exclusive) + * @param step the step to approximate the curve segment, negative values allowed. + * @returns true if this part of the curve is within the closed bbox + */ + static void approximate_simple_curve_segment(Execution_context& ctx, const FT& start, const FT& end, double step) { + for(FT x = start + step; x < end; x += step) { + auto y = ctx.bounded_compute_y_at_x(ctx.curve, x); + if(!y.has_value()) { + // break as soon as there's no more intersections + break; + } + if(y == ctx->ymin() || y == ctx->ymax()) { + // The segment overlaps with the bbox edge. There's no need to insert a dummy point. + break; + } + *ctx.out_it++ = ctx->approx_pt(Point_2(x, y.value())); + if(y > ctx->ymax() || y < ctx->ymin()) { + // We are outside the bbox. The dummy point is inserted to indicate the curve is outside the bbox. + break; + } + } + }; + + static void approximate_vertical_curve(Execution_context& ctx) { + if(ctx.is_bounded_curve() && ctx.min_end->x() < ctx->xmin() && ctx.min_end->x() > ctx->xmax()) { + // The curve is outside the bbox in x direction, no need to approximate + return; + } + if(!ctx.is_bounded_curve() && !ctx.has_y_intersections()) { + // The curve has unbounded end and has no intersections with the top or bottom edges, + // it must be outbound in x direction. + return; + } + // The vertical curve is now within the x bounds. + + FT tymin = ctx.tight_ymin; + FT tymax = ctx.tight_ymax; + if(tymax == tymin) { + // But the curve is not degenerate. So, either it has only one point within the bbox or it is + // entirely outside the bbox in y direction. + return; + } + // Now we gaurantee that the curve has at least two points within the bbox in y direction. + // We have to obtain the x coordinate of this vertical curve. + FT x = ctx.is_bounded_curve() ? ctx.min_end->x() : first_intersection(ctx)->x(); + *ctx.out_it++ = ctx->approx_pt(Point_2(x, tymin)); + *ctx.out_it++ = ctx->approx_pt(Point_2(x, tymax)); + } + +public: + Arr_bounded_approximate_curve_2(const Arr_bounded_render_context& ctx, + const Arr_bounded_approximate_point_2& point_approx) + : m_bounded_compute_y_at_x(ctx) + , m_approx_pt(point_approx) + , m_ctx(ctx) + , m_top(ctx.cst_horizontal_segment(ctx.ymax(), ctx.xmin(), ctx.xmax())) + , m_bottom(ctx.cst_horizontal_segment(ctx.ymin(), ctx.xmin(), ctx.xmax())) {} + + /** + * @brief Approximate an x-monotone curve from left to right within the bounding box. + * + * @param he non-fictitious halfedge handle + * @return const Polyline_geom& + */ + const Polyline_geom& operator()(const Halfedge_const_handle& he) const { + CGAL_assertion(!he->is_fictitious()); + + auto [polyline, inserted] = m_ctx.cache.try_emplace(he); + if(!inserted) { + return polyline; + } + + if(m_ctx.is_cancelled()) { + return polyline; + } + + const X_monotone_curve_2& curve = he->curve(); + + if(curve.is_degenerate()) { + return polyline; + } + + auto top_inters = compute_intersections(curve, m_top, m_ctx.intersect_2, m_ctx.cst_curve_end); + auto bottom_inters = compute_intersections(curve, m_bottom, m_ctx.intersect_2, m_ctx.cst_curve_end); + Execution_context ctx(m_ctx, curve, m_approx_pt, m_bounded_compute_y_at_x, top_inters, bottom_inters, polyline); + + if(ctx->is_vertical_2(curve)) { + approximate_vertical_curve(ctx); + return polyline; + } + + polyline.reserve(top_inters.size() + bottom_inters.size()); + + FT txmin = ctx.tight_xmin; + FT txmax = ctx.tight_xmax; + FT last_x; + std::optional first_inter = first_intersection(ctx); + + if(auto y_at_txmin = ctx.bounded_compute_y_at_x(curve, txmin); + y_at_txmin.has_value() && y_at_txmin != ctx->ymin() && y_at_txmin != ctx->ymax()) + { + // The tight starting point of the curve is within the bbox and + // it's not on the top or bottom edge. + *ctx.out_it++ = txmin == ctx->xmin() ? ctx->approx_pt_on_boundary(Point_2(txmin, y_at_txmin.value())) + : ctx->approx_pt(Point_2(txmin, y_at_txmin.value())); + FT segment_end = first_inter.has_value() ? first_inter->x() : txmax; + approximate_simple_curve_segment(ctx, txmin, segment_end, ctx->approx_error); + last_x = segment_end; + } else if(first_inter.has_value()) { + last_x = first_inter->x(); + } else { + // We assert that the curve is outbound. + // If the min end is bounded, it's obvious. + // + // If the min end is unbounded, we know that the curve has no intersections with top, bottom or left edge(txmin == + // xmin when the min end is unbounded) and the min end of the curve is outbound (it approaches infinity in one or + // both dimension). + // Assume that the curve does has one point within the bbox. Note that it's a contiguous + // x-monotone curve. So it must cross the top, bottom or left edge to reach the min end from right to left, + // which is a contradiction. + return polyline; + } + + // iterate through the intersections and insert segments in-between. + std::merge(top_inters.begin(), top_inters.end(), bottom_inters.begin(), bottom_inters.end(), + boost::make_function_output_iterator([&last_x, &ctx](const Point_2& inter) { + approximate_simple_curve_segment(ctx, last_x, inter.x(), ctx->approx_error); + *ctx.out_it++ = ctx->approx_pt_on_boundary(inter); + last_x = inter.x(); + }), + [&ctx](const Point_2& pt1, const Point_2& pt2) { return ctx->compare_xy_2(pt1, pt2) == CGAL::SMALLER; }); + + if(auto y_at_txmax = ctx.bounded_compute_y_at_x(curve, txmax); + y_at_txmax.has_value() && y_at_txmax != ctx->ymin() && y_at_txmax != ctx->ymax()) + { + approximate_simple_curve_segment(ctx, last_x, txmax, ctx->approx_error); + *ctx.out_it++ = txmax == ctx->xmax() ? ctx->approx_pt_on_boundary(Point_2(txmax, y_at_txmax.value())) + : ctx->approx_pt(Point_2(txmax, y_at_txmax.value())); + } + + return polyline; + } + +private: + const Arr_bounded_render_context& m_ctx; + const Arr_bounded_approximate_point_2& m_approx_pt; + const Arr_bounded_compute_y_at_x m_bounded_compute_y_at_x; + const X_monotone_curve_2 m_top; + const X_monotone_curve_2 m_bottom; +}; +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_CURVE_2_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_face_2.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_face_2.h new file mode 100644 index 00000000000..d4cc3163c3c --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_face_2.h @@ -0,0 +1,286 @@ +#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_FACE_2_H +#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_FACE_2_H + +#include "CGAL/Arr_enums.h" +#include "CGAL/Bbox_2.h" +#include "CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h" +#include "CGAL/Draw_aos/Arr_bounded_approximate_point_2.h" +#include "CGAL/Draw_aos/Arr_bounded_face_triangulator.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include "CGAL/basic.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { + +namespace internal { + +/** + * @brief Patches corners between two boundary points of the bbox + * counter-clockwisely. + */ +class Patch_boundary +{ + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + + enum class Side_of_boundary { + Top = 0, + Left = 1, + Bottom = 2, + Right = 3, + None = -1, + }; + +private: + Side_of_boundary side_of_boundary(const Approx_point& pt) const { + if(m_bbox.xmin() <= pt.x() && pt.x() < m_bbox.xmax() && pt.y() == m_bbox.ymax()) { + return Side_of_boundary::Top; + } else if(pt.x() == m_bbox.xmin() && m_bbox.ymin() <= pt.y() && pt.y() < m_bbox.ymax()) { + return Side_of_boundary::Left; + } else if(m_bbox.xmin() < pt.x() && pt.x() <= m_bbox.xmax() && pt.y() == m_bbox.ymin()) { + return Side_of_boundary::Bottom; + } else if(pt.x() == m_bbox.xmax() && m_bbox.ymin() < pt.y() && pt.y() <= m_bbox.ymax()) { + return Side_of_boundary::Right; + } else { + return Side_of_boundary::None; + } + } + + Approx_point corner_of_side(Side_of_boundary side) const { + switch(side) { + case Side_of_boundary::Top: + // return the top-left corner + return Approx_point(m_bbox.xmin(), m_bbox.ymax()); + case Side_of_boundary::Left: + // return the bottom-left corner + return Approx_point(m_bbox.xmin(), m_bbox.ymin()); + case Side_of_boundary::Bottom: + // return the bottom-right corner + return Approx_point(m_bbox.xmax(), m_bbox.ymin()); + case Side_of_boundary::Right: + // return the top-right corner + return Approx_point(m_bbox.xmax(), m_bbox.ymax()); + default: + CGAL_assertion(false && "Invalid side of rectangle"); + return Approx_point(); + } + } + + Side_of_boundary prev_side(Side_of_boundary side) const { + CGAL_assertion(side != Side_of_boundary::None); + return static_cast((static_cast(side) + 3) % 4); + } + + Side_of_boundary next_side(Side_of_boundary side) const { + CGAL_assertion(side != Side_of_boundary::None); + return static_cast((static_cast(side) + 1) % 4); + } + + bool is_on_boundary(const Approx_point& pt) const { return side_of_boundary(pt) != Side_of_boundary::None; } + + double distance_on_same_side(const Approx_point& pt1, const Approx_point& pt2) const { + return std::abs(pt1.x() - pt2.x()) + std::abs(pt1.y() - pt2.y()); + } + +public: + Patch_boundary(Bbox_2 bbox) + : m_bbox(bbox) {} + + template + void operator()(const Approx_point& from, const Approx_point& to, OutputIterator out_it) const { + auto from_side = side_of_boundary(from); + auto to_side = side_of_boundary(to); + + if(from_side == Side_of_boundary::None || to_side == Side_of_boundary::None) { + return; + } + + bool from_is_before_to = from_side == to_side && distance_on_same_side(from, corner_of_side(from_side)) <= + distance_on_same_side(to, corner_of_side(to_side)); + // Special case: if both on the same side and the current point is before + // the last point in counter-clockwise boundary of the bbox, We have to go + // around the bbox. Note that this includes when two points overlaps + int num_corners_to_patch = + from_is_before_to ? 4 : (static_cast(to_side) - static_cast(from_side) + 4) % 4; + + for(int i = 0; i < num_corners_to_patch; ++i) { + Side_of_boundary side = static_cast((static_cast(from_side) + i) % 4); + Approx_point corner = corner_of_side(side); + if(corner == from) { + continue; + } + *out_it++ = corner; + } + return; + } + +private: + const Bbox_2 m_bbox; +}; + +} // namespace internal + +/** + * @brief Bounded face approximation for arrangements. + * @note Member functions are not thread-safe. + */ +class Arr_bounded_approximate_face_2 +{ + using Approx_geom_traits = Arr_approximation_geometry_traits; + using Face_const_handle = Arrangement::Face_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Polyline_geom = Approx_geom_traits::Polyline_geom; + using Ccb_halfedge_const_circulator = Arrangement::Ccb_halfedge_const_circulator; + using Approx_point = Approx_geom_traits::Approx_point; + using Patch_boundary = internal::Patch_boundary; + using Triangulated_face = Approx_geom_traits::Triangulated_face; + + struct Left_to_right_tag + {}; + struct Right_to_left_tag + {}; + +private: + class Execution_context : public Arr_context_delegator + { + public: + Execution_context(const Arr_bounded_render_context& ctx, + Arr_bounded_face_triangulator& triangulator, + const Arr_bounded_approximate_point_2& bounded_approx_pt, + const Arr_bounded_approximate_curve_2& bounded_approx_curve, + const Patch_boundary& patch_boundary) + : Arr_context_delegator(ctx) + , triangulator(triangulator) + , patch_boundary(patch_boundary) + , bounded_approx_pt(bounded_approx_pt) + , bounded_approx_curve(bounded_approx_curve) {} + + public: + const Arr_bounded_approximate_point_2& bounded_approx_pt; + const Arr_bounded_approximate_curve_2& bounded_approx_curve; + const Patch_boundary& patch_boundary; + Arr_bounded_face_triangulator& triangulator; + }; + +private: + static void approximate_vertex(Execution_context& ctx, const Vertex_const_handle& vh) { + if(vh->is_at_open_boundary()) { + return; + } + ctx.bounded_approx_pt(vh); + } + + template + static void + approximate_halfedge_of_ccb(Execution_context& ctx, const Halfedge_const_handle& he, OutputIterator& out_it) { + if(he->is_fictitious()) { + return; + } + + const Polyline_geom& polyline = ctx.bounded_approx_curve(he); + if(he->direction() == ARR_LEFT_TO_RIGHT) { + std::copy(polyline.begin(), polyline.end(), out_it); + } else { + std::copy(polyline.rbegin(), polyline.rend(), out_it); + } + } + + template + static void approximate_ccb(Execution_context& ctx, const Ccb_halfedge_const_circulator& start_circ) { + constexpr bool Is_outer_ccb = std::is_same_v; + auto ccb_constraint = ctx.triangulator.make_ccb_constraint(); + auto out_it = ccb_constraint.insert_iterator(); + + std::optional ccb_last_pt, ccb_first_pt; + auto counter_clockwise_start_circ = Is_outer_ccb ? start_circ : Ccb_halfedge_const_circulator(start_circ->twin()); + auto circ = counter_clockwise_start_circ; + do { + bool is_he_first_pt = true; + auto patch_out_it = boost::make_function_output_iterator([&](const Approx_point& pt) { + if(ccb_last_pt == pt || !ctx->contains(pt)) { + return; + } + if(is_he_first_pt && ccb_last_pt.has_value()) { + ctx.patch_boundary(ccb_last_pt.value(), pt, out_it); + } + + *out_it++ = pt; + ccb_last_pt = pt; + if(!ccb_first_pt.has_value()) { + ccb_first_pt = pt; + } + is_he_first_pt = false; + }); + + approximate_halfedge_of_ccb(ctx, circ, patch_out_it); + approximate_vertex(ctx, circ->target()); + } while(++circ != counter_clockwise_start_circ); + + if(Is_outer_ccb && !ccb_first_pt.has_value()) { + *out_it++ = Approx_point(ctx->xmin(), ctx->ymin()); + *out_it++ = Approx_point(ctx->xmax(), ctx->ymin()); + *out_it++ = Approx_point(ctx->xmax(), ctx->ymax()); + *out_it++ = Approx_point(ctx->xmin(), ctx->ymax()); + } + if(ccb_first_pt.has_value() && ccb_first_pt != ccb_last_pt) { + // Close the ccb + ctx.patch_boundary(ccb_last_pt.value(), ccb_first_pt.value(), out_it); + } + } + +public: + Arr_bounded_approximate_face_2(const Arr_bounded_render_context& ctx, + const Arr_bounded_approximate_point_2& point_approx, + const Arr_bounded_approximate_curve_2& curve_approx) + : m_ctx(ctx) + , m_patch_boundary(ctx.bbox()) + , m_point_approx(point_approx) + , m_curve_approx(curve_approx) {} + + const Triangulated_face& operator()(const Face_const_handle& fh) const { + auto [triangulated_face, inserted] = m_ctx.cache.try_emplace(fh); + if(!inserted) { + return triangulated_face; + } + + if(m_ctx.is_cancelled()) { + return triangulated_face; + } + + CGAL_assertion_msg(!fh->is_fictitious(), "Cannot approximate a fictitious face."); + if(!fh->has_outer_ccb()) { + // The face is the unbounded face of bounded arrangements + return triangulated_face; + } + + Arr_bounded_face_triangulator triangulator(m_ctx); + Execution_context ctx(m_ctx, triangulator, m_point_approx, m_curve_approx, m_patch_boundary); + + approximate_ccb(ctx, fh->outer_ccb()); + for(auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb) { + approximate_ccb(ctx, *inner_ccb); + } + for(auto isolated_vh = fh->isolated_vertices_begin(); isolated_vh != fh->isolated_vertices_begin(); ++isolated_vh) { + approximate_vertex(ctx, isolated_vh); + } + + return triangulated_face = (std::move(triangulator)); + } + +private: + const Arr_bounded_render_context& m_ctx; + const Arr_bounded_approximate_point_2& m_point_approx; + const Arr_bounded_approximate_curve_2& m_curve_approx; + const Patch_boundary m_patch_boundary; +}; +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_FACE_2_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_point_2.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_point_2.h new file mode 100644 index 00000000000..c768341f017 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_approximate_point_2.h @@ -0,0 +1,55 @@ + +#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_POINT_2_H +#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_POINT_2_H + +#include "CGAL/Draw_aos/Arr_render_context.h" +#include +#include +#include +#include + +namespace CGAL { +class Arr_bounded_approximate_point_2 +{ + using Approx_kernel = Arr_approximation_geometry_traits::Approximation_kernel; + using Point_2 = Geom_traits::Point_2; + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Point_geom = Arr_approximation_geometry_traits::Point_geom; + +public: + Arr_bounded_approximate_point_2(const Arr_bounded_render_context& ctx) + : m_ctx(ctx) {} + + /** + * @brief Approximate a vertex within the x-bounded range. + * + * The function uses cached values if available. + * @precondition: The vertex must have an associated point. + * + * If the point is outside the bounding box, it returns Point_geom{} and false. + * Otherwise, it returns the approximated point and true. + * @param vh the vertex handle + * @return std::pair + */ + std::pair operator()(const Vertex_const_handle& vh) const { + const auto& pt = vh->point(); + + if(!m_ctx.strictly_contains(pt)) { + return {Point_geom(), false}; + } + + auto [point, inserted] = m_ctx.cache.try_emplace(vh); + if(!inserted) { + return {point, true}; + } + + return {point = m_ctx.approx_pt(pt), true}; + } + +private: + const Arr_bounded_render_context& m_ctx; +}; + +} // namespace CGAL + +#endif // CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_POINT_2_H diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h new file mode 100644 index 00000000000..7e87b76e592 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h @@ -0,0 +1,79 @@ +#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_COMPUTE_Y_AT_X_H +#define CGAL_DRAW_AOS_ARR_BOUNDED_COMPUTE_Y_AT_X_H + +#include "CGAL/Arr_enums.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include "CGAL/basic.h" +#include +#include +#include +#include +#include + +namespace CGAL { +/** + * @brief Functor to compute the y-coordinate at a given x-coordinate for an x-monotone curve within a bounding box. + */ +class Arr_bounded_compute_y_at_x +{ +public: + using Point_2 = Geom_traits::Point_2; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using Intersect_2 = Geom_traits::Intersect_2; + using Construct_min_vertex_2 = Geom_traits::Construct_min_vertex_2; + using FT = Geom_traits::FT; + using Approximate_2 = Geom_traits::Approximate_2; + using Is_vertical_2 = Geom_traits::Is_vertical_2; + + Arr_bounded_compute_y_at_x(const Arr_bounded_render_context& ctx) + : m_ctx(ctx) + // TODO: some traits does not have approximate_2_object. we'll need a specialization for them. + , m_approx(ctx.traits.approximate_2_object()) {} + + /** + * @brief Computes the y-coordinate at a given x-coordinate for an x-monotone curve trimmed + * to the bounding box. + * + * The bounding box here is considered as closed. + * + * @precondition The curve is not verical + * @param curve + * @param x + * @return true if there is an intersection at given x within the bounding box, + * @return false otherwise. + */ + std::optional operator()(const X_monotone_curve_2& curve, const FT& x) const { + CGAL_assertion(!m_ctx.is_vertical_2(curve)); + if(!m_ctx.contains_x(x)) { + return false; + } + + auto min_pt = m_ctx.cst_curve_end(curve, ARR_MIN_END); + auto max_pt = m_ctx.cst_curve_end(curve, ARR_MAX_END); + if(min_pt.has_value() && min_pt->x() == x) { + return min_pt->y(); + } + if(max_pt.has_value() && max_pt->x() == x) { + return max_pt->y(); + } + + using Multiplicity = Geom_traits::Multiplicity; + using Intersect_point = std::pair; + using Intersect_curve = X_monotone_curve_2; + using Intersect_type = std::variant; + + auto vertical_line = m_ctx.cst_vertical_segment(x, m_ctx.ymin(), m_ctx.ymax()); + std::optional y; + auto func_out_iter = boost::make_function_output_iterator( + [&y, this](const Intersect_type& res) { y = std::get(res).first.y(); }); + m_ctx.intersect_2(curve, vertical_line, func_out_iter); + return y; + } + +private: + const Arr_bounded_render_context& m_ctx; + Approximate_2 m_approx; +}; + +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_BOUNDED_COMPUTE_Y_AT_X_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_face_triangulator.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_face_triangulator.h new file mode 100644 index 00000000000..618c71323d6 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_face_triangulator.h @@ -0,0 +1,297 @@ +#ifndef CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H +#define CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H + +#include "CGAL/Constrained_Delaunay_triangulation_2.h" +#include "CGAL/Constrained_triangulation_2.h" +#include "CGAL/Constrained_triangulation_face_base_2.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include "CGAL/Triangulation_vertex_base_with_info_2.h" +#include "CGAL/mark_domain_in_triangulation.h" +#include "CGAL/unordered_flat_map.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { + +/** + * @brief Triangulator a bounded face of an arrangement + * + * @note The face must have an outer CCB. + */ +class Arr_bounded_face_triangulator +{ + using Approx_geom_traits = Arr_approximation_geometry_traits; + using Approx_kernel = Approx_geom_traits::Approximation_kernel; + using Approx_point = Approx_geom_traits::Approx_point; + using Point_vec = Approx_geom_traits::Apporx_point_vec; + using Triangle = Approx_geom_traits::Triangle; + using Triangulated_face = Approx_geom_traits::Triangulated_face; + + struct Point_index + { + constexpr static std::size_t Invalid_index = std::numeric_limits::max(); + std::size_t index{Invalid_index}; + Point_index() = default; + Point_index(std::size_t idx) + : index(idx) {} + bool is_valid() const { return index != Invalid_index; } + operator std::size_t() const { return index; } + }; + using Epick = CGAL::Exact_predicates_inexact_constructions_kernel; + using Vb = CGAL::Triangulation_vertex_base_with_info_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using Tds = CGAL::Triangulation_data_structure_2; + using Ct = Constrained_triangulation_2; + using Point = Epick::Point_2; + using Point_with_info = std::pair; + + std::size_t counter{0}; + + /** + * @brief RAII-style inserter for one CCB in a triangulation. + * Collects points and inserts them as a constraint on destruction. + * Only one instance per Arr_face_triangulator is allowed at a time. + */ + template + class Ccb_constraint + { + constexpr static bool Is_outer_ccb = std::is_same_v; + friend class Arr_bounded_face_triangulator; + + std::ofstream m_ofs; + Ccb_constraint(Arr_bounded_face_triangulator& triangulator) + : m_triangulator(&triangulator) { + m_ccb_start = m_triangulator->m_points.size(); + triangulator.m_has_active_constraint = true; + + if(Is_outer_ccb) { + std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::app); + auto name = "outer_ccb_" + std::to_string((*m_triangulator->m_ctx.counter)++) + ".txt"; + ofs_index << name << std::endl; + m_ofs = std::ofstream("/Users/shep/codes/aos_2_js_helper/" + name, std::ios::out | std::ios::trunc); + } + } + + private: + Point offset_boundary_point(const Point& pt) const { + constexpr double offset = 1; // It doesn't matter how much we offset the point + double x = pt.x(), y = pt.y(); + const auto& ctx = m_triangulator->m_ctx; + + if(x == ctx.xmin()) { + x -= offset; + } + if(x == ctx.xmax()) { + x += offset; + } + if(y == ctx.ymin()) { + y -= offset; + } + if(y == ctx.ymax()) { + y += offset; + } + return Point(x, y); + } + + void insert_ccb() { + auto& ct = m_triangulator->m_ct; + auto& points = m_triangulator->m_points; + + auto begin = points.begin() + m_ccb_start; + auto end = points.end(); + + if constexpr(Is_outer_ccb) { + auto helpers_iter = m_helper_indices.begin(); + auto helpers_end = m_helper_indices.end(); + + auto concrete_pt_filter = [&helpers_iter, helpers_end](std::size_t idx) { + if(helpers_iter == helpers_end) { + return true; + } + if(idx == *helpers_iter) { + ++helpers_iter; + return false; + } + return true; + }; + + auto index_to_point_with_info = [&points](std::size_t idx) { return points[idx]; }; + + auto indexes_begin = boost::make_counting_iterator(m_ccb_start); + auto indexes_end = boost::make_counting_iterator(points.size()); + auto filtered_begin = boost::make_filter_iterator(concrete_pt_filter, indexes_begin, indexes_end); + auto filtered_end = boost::make_filter_iterator(concrete_pt_filter, indexes_end, indexes_end); + auto transformed_begin = boost::make_transform_iterator(filtered_begin, index_to_point_with_info); + auto transformed_end = boost::make_transform_iterator(filtered_end, index_to_point_with_info); + // { + // std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::app); + // auto& ctx = const_cast(m_triangulator->m_ctx); + // ofs_index << "ccb_" << ctx.counter << ".txt" << std::endl; + // std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/ccb_" + std::to_string(ctx.counter++) + ".txt", + // std::ios::out | std::ios::trunc); + // for(auto it = filtered_begin; it != filtered_end; ++it) { + // const auto& pt = it->first; + // ofs << pt.x() << " " << pt.y() << std::endl; + // } + // } + ct.insert_with_info(transformed_begin, transformed_end); + } else { + ct.insert_with_info(begin, end); + } + } + + public: + Ccb_constraint(const Ccb_constraint&) = delete; + Ccb_constraint& operator=(const Ccb_constraint&) = delete; + Ccb_constraint(Ccb_constraint&& other) noexcept + : m_triangulator(other.m_triangulator) { + other.m_triangulator = nullptr; + } + Ccb_constraint& operator=(Ccb_constraint&& other) noexcept { + if(this != &other) { + m_triangulator = other.m_triangulator; + other.m_triangulator = nullptr; + } + return *this; + } + + decltype(auto) insert_iterator() { + return boost::make_function_output_iterator([&, this](const Approx_point& pt) { + auto& points = m_triangulator->m_points; + CGAL_assertion_msg(m_triangulator != nullptr, "Use of destructed or moved Ccb_constraint object."); + CGAL_assertion_msg(m_triangulator->m_ctx.contains(pt), "Outbound point in Ccb_constraint."); + + if constexpr(Is_outer_ccb) { + bool is_on_boundary = m_triangulator->m_ctx.is_on_boundary(pt); + if(is_on_boundary && m_is_last_on_boundary) { + m_helper_indices.push_back(points.size()); + points.emplace_back(offset_boundary_point(points.back().first), Point_index()); + } + m_is_last_on_boundary = is_on_boundary; + m_ofs << pt.x() << " " << pt.y() << std::endl; + } + points.emplace_back(Point(pt.x(), pt.y()), points.size()); + }); + } + + ~Ccb_constraint() { + if(m_triangulator == nullptr) { + return; + } + if(Is_outer_ccb && m_is_last_on_boundary && m_triangulator->m_points.size() != m_ccb_start) { + auto& points = m_triangulator->m_points; + const auto& first_pt = points[m_ccb_start].first; + const auto& last_pt = points.back().first; + bool is_first_on_boundary = m_triangulator->m_ctx.is_on_boundary(first_pt); + if(m_is_last_on_boundary && is_first_on_boundary) { + m_helper_indices.push_back(points.size()); + points.emplace_back(offset_boundary_point(last_pt), Point_index()); + } + } + + insert_ccb(); + m_triangulator->m_has_active_constraint = false; + m_triangulator = nullptr; + } + + private: + Arr_bounded_face_triangulator* m_triangulator; + std::size_t m_ccb_start; + bool m_is_last_on_boundary = false; + std::vector m_helper_indices; // The offseted point indices when inserting outer ccb constraint + }; + +public: + Arr_bounded_face_triangulator(const Arr_bounded_render_context& ctx) + : m_ctx(ctx) {} + Arr_bounded_face_triangulator(const Arr_bounded_face_triangulator& other) = delete; + Arr_bounded_face_triangulator& operator=(const Arr_bounded_face_triangulator& other) = delete; + + operator Triangulated_face() && { + CGAL_assertion(!m_has_active_constraint && "There is an active constraint in the triangulator."); + CGAL_assertion(m_outer_ccb_processed && "Outer CCB has not been processed yet."); + + // insert_constraint() should be called after insert_with_info(), or info will not be set correctly. + auto first_of_pair = [](const Point_with_info& pt_with_info) { return pt_with_info.first; }; + for(std::size_t i = 0; i < m_ccb_start_indices.size(); ++i) { + auto begin = m_points.begin() + m_ccb_start_indices[i]; + auto end = m_points.begin() + (i + 1 < m_ccb_start_indices.size() ? m_ccb_start_indices[i + 1] : m_points.size()); + { + std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::app); + auto& ctx = const_cast(m_ctx); + auto name = "ccb_constraint" + std::to_string((*ctx.counter)++) + ".txt"; + ofs_index << name << std::endl; + std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/" + name, std::ios::out | std::ios::trunc); + for(auto it = begin; it != end; ++it) { + const auto& pt = it->first; + ofs << pt.x() << " " << pt.y() << std::endl; + } + } + m_ct.insert_constraint(boost::make_transform_iterator(begin, first_of_pair), + boost::make_transform_iterator(end, first_of_pair), true); + } + + unordered_flat_map in_domain_map; + boost::associative_property_map in_domain(in_domain_map); + CGAL::mark_domain_in_triangulation(m_ct, in_domain); + + Triangulated_face tf; + tf.triangles.reserve(m_ct.number_of_faces()); + for(auto fit = m_ct.finite_faces_begin(); fit != m_ct.finite_faces_end(); ++fit) { + if(!get(in_domain, fit)) { + continue; + } + Point_index v1 = fit->vertex(0)->info(); + Point_index v2 = fit->vertex(1)->info(); + Point_index v3 = fit->vertex(2)->info(); + if(!v1.is_valid() || !v2.is_valid() || !v3.is_valid()) { + continue; + } + Triangle tri{v1, v2, v3}; + tf.triangles.emplace_back(tri); + } + + auto transform_first_of_pair = [](const Point_with_info& pt_with_info) { + return Approx_point(pt_with_info.first.x(), pt_with_info.first.y()); + }; + tf.points = Point_vec(boost::make_transform_iterator(m_points.begin(), transform_first_of_pair), + boost::make_transform_iterator(m_points.end(), transform_first_of_pair)); + m_ct.clear(); + return tf; + } + + /** + * @brief Get an constraint object for a certain ccb. + * @note Only one Ccb_constraint can be active per triangulator at a certain time point. When it goes out of scope, + * the points collected will be inserted as a constraint into the triangulation. + */ + template + Ccb_constraint make_ccb_constraint() { + if constexpr(std::is_same_v) { + CGAL_assertion_msg(!m_outer_ccb_processed, "Outer CCB has already been processed."); + m_outer_ccb_processed = true; + } + CGAL_assertion_msg(!m_has_active_constraint, "Only one Ccb_constraint can be active per triangulator at a time."); + m_ccb_start_indices.push_back(m_points.size()); + return Ccb_constraint(*this); + } + +private: + const Arr_bounded_render_context& m_ctx; + Ct m_ct; + std::vector m_points; + std::vector m_ccb_start_indices; + bool m_has_active_constraint = false; + bool m_outer_ccb_processed = false; +}; + +} // namespace CGAL + +#endif // CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_renderer.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_renderer.h new file mode 100644 index 00000000000..bda0d15a9da --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_bounded_renderer.h @@ -0,0 +1,216 @@ +#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_RENDERER_H +#define CGAL_DRAW_AOS_ARR_BOUNDED_RENDERER_H + +#include "CGAL/Arr_trapezoid_ric_point_location.h" +#include "CGAL/Bbox_2.h" +#include "CGAL/Draw_aos/Arr_approximation_cache.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include "CGAL/Draw_aos/Arr_bounded_approximate_curve_2.h" +#include "CGAL/Draw_aos/Arr_bounded_approximate_face_2.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include +#include +#include +#include + +namespace CGAL { +/** + * @brief Render arrangement on surface within a bounding box. + * + * @note The class is not thread-safe. + */ +class Arr_bounded_renderer +{ + using Color = IO::Color; + using Point_2 = Geom_traits::Point_2; + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Edge_const_handle = Arrangement::Edge_const_iterator; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + using Face_const_handle = Arrangement::Face_const_handle; + using Vertex_handle = Arrangement::Vertex_handle; + using Halfedge_handle = Arrangement::Halfedge_handle; + using Face_handle = Arrangement::Face_handle; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using Point_Location = Arr_trapezoid_ric_point_location; + using Feature_const = std::variant; + using Feature_const_vector = std::vector; + using Feature_const_vector_inserter = std::back_insert_iterator; + using Arr_approx_geom_traits = Arr_approximation_geometry_traits; + using Point_geom = Arr_approx_geom_traits::Point_geom; + using Polyline_geom = Arr_approx_geom_traits::Polyline_geom; + using Feature_portal_map = Arr_portals::Feature_portals_map; + // QFlags implement this pattern better, but we try not to reply on Qt classes. + template + class Arr_flags + { + public: + Arr_flags(const E& flags) + : m_flags(static_cast(flags)) {} + Arr_flags(std::initializer_list flags) + : m_flags(0) { + for(const auto& flag : flags) { + m_flags |= static_cast(flag); + } + } + + bool is_set(E flag) const { return (m_flags & static_cast(flag)) != 0; } + + private: + int m_flags; + }; + + enum class Feature_type { + Vertex = 1 << 0, + Halfedge = 1 << 1, + Face = 1 << 2, + }; + + struct Execution_context : Arr_context_delegator + { + Execution_context(const Arr_bounded_render_context& ctx) + : Arr_context_delegator(ctx) + , bounded_approx_pt(ctx) + , bounded_approx_curve(ctx, bounded_approx_pt) + , bounded_approx_face(ctx, bounded_approx_pt, bounded_approx_curve) {} + + const Arr_bounded_approximate_point_2 bounded_approx_pt; + const Arr_bounded_approximate_curve_2 bounded_approx_curve; + const Arr_bounded_approximate_face_2 bounded_approx_face; + }; + +private: + template + static void locate_intersecting_features(const Execution_context& ctx, + const X_monotone_curve_2& curve, + OutputIterator out_iter, + Arr_flags feats) { + using Feature = std::variant; + using Feature_vector = std::vector; + + auto func_out_iter = boost::make_function_output_iterator([&out_iter, &feats](const Feature& feature) { + if(auto* vh = std::get_if(&feature)) { + if(!feats.is_set(Feature_type::Vertex) || (*vh)->is_at_open_boundary()) { + return; + } + *out_iter++ = Vertex_const_handle(*vh); + } else if(auto* he = std::get_if(&feature)) { + if(!feats.is_set(Feature_type::Halfedge) || (*he)->is_fictitious()) { + return; + } + *out_iter++ = Halfedge_const_handle(*he); + } else if(auto* fh = std::get_if(&feature)) { + if(!feats.is_set(Feature_type::Face)) { + return; + } + *out_iter++ = Face_const_handle(*fh); + } else { + CGAL_assertion(false && "Unexpected feature type"); + } + }); + + using Zone_visitor = Arr_compute_zone_visitor; + using Zone_2 = Arrangement_zone_2; + + Zone_visitor zone_visitor(func_out_iter); + // use const_cast to fit in the interface but we will not modify the arrangement + Zone_2 zone(const_cast(ctx->arr), &zone_visitor); + zone.init(curve, ctx->point_location); + zone.compute_zone(); + } + + static void discover_faces(Execution_context& ctx, const Face_const_handle& fh) { + if(ctx->cache.has(fh)) { + // The face has already been discovered and approximated. + return; + } + ctx.bounded_approx_face(fh); + + for(auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb) { + auto he = *inner_ccb; + auto inner_face = he->twin()->face(); + + bool is_degenerate = inner_face == fh; + bool within_bounds = ctx->strictly_contains(inner_face->outer_ccb()->source()->point()); + if(is_degenerate || !within_bounds) { + continue; + } + discover_faces(ctx, inner_face); + } + + // We don't need to handle isolated vertices. + // for(...) + + if(!fh->has_outer_ccb()) { + // The unbounded face of bounded arrangements has no outer CCB. + return; + } + // find fully contained adjacent faces + auto outer_ccb = fh->outer_ccb(); + auto circ = outer_ccb; + do { + auto adj_face = circ->twin()->face(); + if(adj_face->is_fictitious() || adj_face->is_unbounded()) { + // Unbounded faces cannot be contained in out bounding box. + continue; + } + + bool within_bounds = ctx->strictly_contains(adj_face->outer_ccb()->source()->point()); + if(!within_bounds) { + continue; + } + // For a face that is not one of the seeding faces, + // the face must be contained iff one of its vertices is contained. + // For a face that is, we'll touch it sooner or later. + discover_faces(ctx, adj_face); + } while(++circ != outer_ccb); + } + +public: + Arr_bounded_renderer(Arr_render_context& ctx, Bbox_2 bbox) + : m_ctx(ctx) + , m_bbox(bbox) {} + + Arr_approximation_cache render() const { + Arr_approximation_cache cache; + + if(m_ctx.is_cancelled()) { + return cache; + } + + Execution_context ctx(Arr_bounded_render_context(m_ctx, m_bbox, cache)); + + auto insert_features = boost::make_function_output_iterator([&ctx](const Feature_const& feature) { + if(auto* vh = std::get_if(&feature)) { + ctx.bounded_approx_pt(*vh); + } else if(auto* he = std::get_if(&feature)) { + ctx.bounded_approx_curve(*he); + } else if(auto* fh = std::get_if(&feature)) { + discover_faces(ctx, *fh); + } else { + CGAL_assertion(false && "Unexpected feature type"); + } + }); + + auto top = ctx->cst_horizontal_segment(ctx->ymax(), ctx->xmin(), ctx->xmax()); + auto bottom = ctx->cst_horizontal_segment(ctx->ymin(), ctx->xmin(), ctx->xmax()); + auto left = ctx->cst_vertical_segment(ctx->xmin(), ctx->ymin(), ctx->ymax()); + auto right = ctx->cst_vertical_segment(ctx->xmax(), ctx->ymin(), ctx->ymax()); + + // top, left are open edges while bottom, right are closed. + locate_intersecting_features(ctx, top, insert_features, Feature_type::Face); + locate_intersecting_features(ctx, left, insert_features, Feature_type::Face); + locate_intersecting_features(ctx, bottom, insert_features, {Feature_type::Face, Feature_type::Halfedge}); + locate_intersecting_features(ctx, right, insert_features, + {Feature_type::Face, Feature_type::Halfedge, Feature_type::Vertex}); + + return cache; + } + +private: + Arr_render_context m_ctx; + const Bbox_2 m_bbox; +}; + +} // namespace CGAL + +#endif // CGAL_DRAW_AOS_ARR_BOUNDED_RENDERER_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_curve_end.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_curve_end.h new file mode 100644 index 00000000000..c27a5e1afc2 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_curve_end.h @@ -0,0 +1,101 @@ +#ifndef CGAL_ARR_CONSTRUCT_CURVE_END_H +#define CGAL_ARR_CONSTRUCT_CURVE_END_H + +#include "CGAL/Arr_enums.h" +#include "CGAL/Arr_has.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include + +namespace CGAL { + +namespace internal { + +template +class Arr_construct_curve_end_base +{ + using Construct_min_vertex_2 = typename Geom_traits::Construct_min_vertex_2; + using Construct_max_vertex_2 = typename Geom_traits::Construct_max_vertex_2; + +protected: + Arr_construct_curve_end_base(const Geom_traits& traits) + : m_cst_min_vertex(traits.construct_min_vertex_2_object()) + , m_cst_max_vertex(traits.construct_max_vertex_2_object()) {} + + Construct_max_vertex_2 m_cst_max_vertex; + Construct_min_vertex_2 m_cst_min_vertex; +}; + +template +class Arr_construct_curve_end_impl; + +template +class Arr_construct_curve_end_impl : public Arr_construct_curve_end_base +{ + using Approx_geom_traits = Arr_approximation_geometry_traits; + using Point_2 = typename Geom_traits::Point_2; + using X_monotone_curve = typename Geom_traits::X_monotone_curve_2; + using Parameter_space_in_x_2 = typename Geom_traits::Parameter_space_in_x_2; + using Parameter_space_in_y_2 = typename Geom_traits::Parameter_space_in_y_2; + +public: + Arr_construct_curve_end_impl(const Geom_traits& traits) + : Arr_construct_curve_end_base(traits) + , m_param_space_in_x(traits.parameter_space_in_x_2_object()) + , m_param_space_in_y(traits.parameter_space_in_y_2_object()) {} + + /** + * @brief Returns an optional type contains the approximated point the given end of the curve is bounded. + * Otherwise it's std::nullopt. + * + * @param curve + * @param ce + * @return std::optional + */ + std::optional operator()(const X_monotone_curve& curve, Arr_curve_end ce) const { + auto ps_x = m_param_space_in_x(curve, ce); + auto ps_y = m_param_space_in_y(curve, ce); + + if(ps_x == ARR_INTERIOR && ps_y == ARR_INTERIOR) { + return std::make_optional(ce == ARR_MIN_END ? this->m_cst_min_vertex(curve) : this->m_cst_max_vertex(curve)); + } + + return std::nullopt; + } + +private: + Parameter_space_in_x_2 m_param_space_in_x; + Parameter_space_in_y_2 m_param_space_in_y; +}; + +template +class Arr_construct_curve_end_impl : public Arr_construct_curve_end_base +{ + using Approx_geom_traits = Arr_approximation_geometry_traits; + using Point_2 = typename Geom_traits::Point_2; + using X_monotone_curve = typename Geom_traits::X_monotone_curve_2; + +public: + Arr_construct_curve_end_impl(const Geom_traits& traits) + : Arr_construct_curve_end_base(traits) {} + + /** + * @brief Construct the min or max vertex of the x-monotone curve based on the curve end enum. + * + * @param curve + * @param ce + * @return std::optional + */ + std::optional operator()(const X_monotone_curve& curve, Arr_curve_end ce) const { + return ce == ARR_MIN_END ? this->m_cst_min_vertex(curve) : this->m_cst_max_vertex(curve); + } +}; + +} // namespace internal + +template +using Arr_construct_curve_end = + internal::Arr_construct_curve_end_impl::value>; + +} // namespace CGAL + +#endif // CGAL_ARR_CONSTRUCT_CURVE_END_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_segments.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_segments.h new file mode 100644 index 00000000000..346d6989038 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_construct_segments.h @@ -0,0 +1,45 @@ +#ifndef CGAL_DRAW_AOS_ARR_CONSTRUCT_SEGMENTS_H +#define CGAL_DRAW_AOS_ARR_CONSTRUCT_SEGMENTS_H +#include "CGAL/Draw_aos/helpers.h" + +namespace CGAL { +class Arr_construct_vertical_segment +{ + using Point_2 = Geom_traits::Point_2; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using FT = Geom_traits::FT; + +public: + Arr_construct_vertical_segment(const Geom_traits& traits) + : m_traits(traits) {} + + X_monotone_curve_2 operator()(const FT& x, const FT& ymin, const FT& ymax) const { + auto cst_x_curve = m_traits.construct_x_monotone_curve_2_object(); + return cst_x_curve(Point_2(x, ymin), Point_2(x, ymax)); + } + +private: + const Geom_traits& m_traits; +}; + +class Arr_construct_horizontal_segment +{ + using Point_2 = Geom_traits::Point_2; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using FT = Geom_traits::FT; + +public: + Arr_construct_horizontal_segment(const Geom_traits& traits) + : m_traits(traits) {} + + X_monotone_curve_2 operator()(FT y, FT xmin, FT xmax) const { + auto cst_x_curve = m_traits.construct_x_monotone_curve_2_object(); + return cst_x_curve(Point_2(xmin, y), Point_2(xmax, y)); + } + +private: + const Geom_traits& m_traits; +}; +} // namespace CGAL + +#endif \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_graph_conn.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_graph_conn.h new file mode 100644 index 00000000000..eb24d908a0b --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_graph_conn.h @@ -0,0 +1,93 @@ + +#ifndef CGAL_DRAW_AOS_ARR_GRAPH_CONN_H +#define CGAL_DRAW_AOS_ARR_GRAPH_CONN_H +#include "CGAL/Union_find.h" +#include "CGAL/unordered_flat_map.h" +#include +namespace CGAL { +/** + * @brief Arr_graph_conn provides fast connectivity queries for arrangement vertices + * based on union-find data structure. + */ +template +class Arr_graph_conn +{ + using Vertex_const_handle = typename Arr::Vertex_const_handle; + using Edge_const_handle = typename Arr::Edge_const_iterator; + using Halfedge_const_handle = typename Arr::Halfedge_const_iterator; + using Union_find_handle = typename Union_find::handle; + +private: + void insert_halfedge(Halfedge_const_handle he) { + const auto& source = he->source(); + const auto& target = he->target(); + + auto [source_it, source_inserted] = m_lookup.try_emplace(source, Union_find_handle()); + auto [target_it, target_inserted] = m_lookup.try_emplace(target, Union_find_handle()); + if(source_inserted) { + source_it->second = m_uf.make_set(source); + } + if(target_inserted) { + target_it->second = m_uf.make_set(target); + } + m_uf.unify_sets(source_it->second, target_it->second); + } + + void insert_isolated_vertex(const Vertex_const_handle& vh) { m_lookup[vh] = m_uf.make_set(vh); } + +public: + Arr_graph_conn(const Arr& arr) { + for(const auto& eh : arr.edge_handles()) { + insert_halfedge(eh); + } + + for(const auto& vh : arr.vertex_handles()) { + if(!vh->is_isolated()) { + continue; + } + insert_isolated_vertex(vh); + } + + // add fictitious edges and open vertices + for(auto fh = arr.unbounded_faces_begin(); fh != arr.unbounded_faces_end(); ++fh) { + if(!fh->has_outer_ccb()) { + continue; + } + auto outer_ccb = fh->outer_ccb(); + auto curr = outer_ccb; + do { + if(!curr->is_fictitious()) { + continue; + } + insert_halfedge(curr); + } while(++curr != outer_ccb); + } + } + + bool is_connected(const Vertex_const_handle& v1, const Vertex_const_handle& v2) const { + auto it1 = m_lookup.find(v1); + auto it2 = m_lookup.find(v2); + // This can't happen + CGAL_assertion(it1 != m_lookup.end() && it2 != m_lookup.end()); + return m_uf.same_set(it1->second, it2->second); + } + + /** + * @brief Returns the representative vertex of the connected component containing the given vertex. + * For each connected component boundary (CCB), the same representative vertex is consistently returned. + * @param vh a vertex handle in the ccb + * @return Vertex_const_handle + */ + Vertex_const_handle ccb_representative_vertex(const Vertex_const_handle& vh) const { + // path compression typically does not mutate the representative member of a set. + auto it = m_lookup.find(vh); + CGAL_assertion(it != m_lookup.end()); + return *m_uf.find(it->second); + } + +private: + Union_find m_uf; + unordered_flat_map m_lookup; +}; +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_GRAPH_CONN_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_portals.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_portals.h new file mode 100644 index 00000000000..b4661de874a --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_portals.h @@ -0,0 +1,133 @@ +#ifndef CGAL_DRAW_AOS_ARR_CREATE_PORTALS_H +#define CGAL_DRAW_AOS_ARR_CREATE_PORTALS_H + +#include "CGAL/Arr_vertical_decomposition_2.h" +#include "CGAL/Draw_aos/Arr_approximate_point_2.h" +#include "CGAL/Draw_aos/Arr_construct_segments.h" +#include "CGAL/Draw_aos/helpers.h" +#include "CGAL/Draw_aos/Arr_graph_conn.h" +#include "CGAL/Object.h" +#include "CGAL/basic.h" +#include "CGAL/unordered_flat_map.h" +#include +#include +#include +#include +#include + +namespace CGAL { +/** + * @brief Portals are virtual vertical segments that connect the outer + * connected component boundary (ccb) of a face with its inner ccbs. + * + * We use vertical decomposition to cast upward rays in a batched manner. + * For each inner ccb, only one "portal" is created at a vertex whose upward ray + * hits another ccb. Eventually, faces of the arrangement become hole-free and can + * be drawn with Graphics_scene. + */ +class Arr_portals +{ + + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + using Face_const_handle = Arrangement::Face_const_handle; + using Point_2 = Geom_traits::Point_2; + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2; + using Feature_const = std::variant; + +public: + // Pair composed of a portal entry point and the portaled vertex on the inner ccb. + using Portal = std::pair, Vertex_const_handle>; + using Portal_vector = std::vector; + // Map from a feature to its portals sorted by the x coordinate of the virtual vertical segments. + using Feature_portals_map = unordered_flat_map; + +private: + // Use this function to locate the intersection point of a vertical ray shooted from a point + // to it's upper x-monotone curve(it can't be vertical, or we should've got its min vertex from vertical + // decomposition). + static Point_2 upper_intersection(const Geom_traits& traits, const Point_2& pt, const X_monotone_curve_2& curve) { + Arr_construct_vertical_segment cst_vertical_segment(traits); + Point_2 intersection_point; + auto intersect = traits.intersect_2_object(); + auto vertical_line = cst_vertical_segment(pt.x(), pt.y(), std::numeric_limits::max()); + bool found_intersection = false; + + using Multiplicity = Geom_traits::Multiplicity; + using Intersect_point = std::pair; + using Intersect_curve = X_monotone_curve_2; + using Intersect_type = std::variant; + + intersect(curve, vertical_line, boost::make_function_output_iterator([&](const Intersect_type& res) { + found_intersection = true; + if(std::holds_alternative(res)) { + intersection_point = std::get(res).first; + return; + } + CGAL_assertion(false && "Unexpected intersection type"); + })); + + return found_intersection ? intersection_point : Point_2(pt.x(), std::numeric_limits::max()); + } + +public: + Feature_portals_map create(const Arrangement& arr) const { + using Object_pair = std::pair; + using Vert_decomp_entry = std::pair; + + Arr_graph_conn conn(arr); + auto visited_ccbs = std::unordered_set(); + Feature_portals_map feature_portals; + auto intersect = arr.traits()->intersect_2_object(); + auto approx_pt = Arr_approximate_point_2(*arr.traits()); + + auto func_out_iter = boost::make_function_output_iterator([&](const Vert_decomp_entry& entry) { + const auto& [vh, obj_pair] = entry; + + const auto& ccb_main_vertex = conn.ccb_representative_vertex(vh); + if(visited_ccbs.find(ccb_main_vertex) != visited_ccbs.end()) { + // This ccb has already been processed + return; + } + + const auto& above_feat = obj_pair.second; + if(Vertex_const_handle above_vh; CGAL::assign(above_vh, above_feat)) { + if(conn.is_connected(above_vh, vh)) { + // This upper vertex is connected to vh, skip it + return; + } + + const auto& [it, _] = feature_portals.try_emplace(above_vh, std::vector{}); + if((above_vh)->is_at_open_boundary()) { + it->second.emplace_back(std::nullopt, vh); + } else { + it->second.emplace_back(approx_pt((above_vh)->point()), vh); + } + } else if(Halfedge_const_handle above_he; CGAL::assign(above_he, above_feat)) { + if(conn.is_connected((above_he)->source(), vh)) { + return; + } + + const auto& [it, _] = feature_portals.try_emplace(above_he, std::vector{}); + if((above_he)->is_fictitious()) { + it->second.emplace_back(std::nullopt, vh); + return; + } + it->second.emplace_back(approx_pt(upper_intersection(*arr.traits(), vh->point(), (above_he)->curve())), vh); + } else if(Face_const_handle above_fh; CGAL::assign(above_fh, above_feat)) { + // We don't create portals for the unbounded face in bounded arrangements. + CGAL_assertion(above_fh->is_unbounded() && !above_fh->has_outer_ccb()); + } else { + return; + } + + visited_ccbs.insert(ccb_main_vertex); + }); + + decompose(arr, func_out_iter); + return feature_portals; + } +}; +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_CREATE_PORTALS_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_render_context.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_render_context.h new file mode 100644 index 00000000000..5639ead835f --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_render_context.h @@ -0,0 +1,218 @@ +#ifndef CGAL_DRAW_AOS_ARR_RENDER_CONTEXT_H +#define CGAL_DRAW_AOS_ARR_RENDER_CONTEXT_H +#include "CGAL/Arr_point_location_result.h" +#include "CGAL/Arr_trapezoid_ric_point_location.h" +#include "CGAL/Bbox_2.h" +#include "CGAL/Draw_aos/Arr_approximate_point_2.h" +#include "CGAL/Draw_aos/Arr_approximation_cache.h" +#include "CGAL/Draw_aos/Arr_construct_curve_end.h" +#include "CGAL/Draw_aos/Arr_construct_segments.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { + +class Arr_cancellable_context_mixin +{ + using Clock = std::chrono::steady_clock; + using Duration = Clock::duration; + using Time_point = std::chrono::time_point; + +protected: + Arr_cancellable_context_mixin() + : m_start_time(Clock::now()) + , m_done(std::make_shared>(false)) {} + +public: + Time_point start_time() const { return m_start_time; } + + Time_point end_time() const { return m_end_time; } + + Duration elapsed_time() const { return Clock::now() - m_start_time; } + + bool is_cancelled() const { return m_done->load(); } + + void cancel() { + m_done->store(true, std::memory_order_relaxed); + m_end_time = Clock::now(); + } + +private: + Time_point m_start_time, m_end_time; + std::shared_ptr> m_done; +}; + +class Arr_bounds_context_mixin +{ + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + +protected: + Arr_bounds_context_mixin(const Bbox_2& bbox) + : m_bbox(bbox) {} + +public: + double xmin() const { return m_bbox.xmin(); } + double xmax() const { return m_bbox.xmax(); } + double ymin() const { return m_bbox.ymin(); } + double ymax() const { return m_bbox.ymax(); } + + const Bbox_2& bbox() const { return m_bbox; } + + template + bool strictly_contains_x(FT x) const { + return xmin() < x && x <= xmax(); + } + + template + bool strictly_contains_y(FT y) const { + return ymin() < y && y <= ymax(); + } + + template + bool strictly_contains(const Point& pt) const { + return strictly_contains_x(pt.x()) && strictly_contains_y(pt.y()); + } + + template + bool contains_x(FT x) const { + return xmin() <= x && x <= xmax(); + } + + template + bool contains_y(FT y) const { + return ymin() <= y && y <= ymax(); + } + + template + bool contains(const Point& pt) const { + return contains_x(pt.x()) && contains_y(pt.y()); + } + + template + bool is_on_boundary(const Point& pt) const { + return (pt.x() == xmin() || pt.x() == xmax()) && contains_y(pt.y()) || + (pt.y() == ymin() || pt.y() == ymax()) && contains_x(pt.x()); + } + +private: + const Bbox_2 m_bbox; +}; + +class Arr_geom_traits_context_mixin +{ +public: + Arr_geom_traits_context_mixin(const Geom_traits& _traits) + : traits(_traits) + , cst_curve_end(_traits) + , cst_horizontal_segment(_traits) + , cst_vertical_segment(_traits) + , intersect_2(_traits.intersect_2_object()) + , compare_xy_2(_traits.compare_xy_2_object()) + , is_vertical_2(_traits.is_vertical_2_object()) + , approx_pt(_traits) {} + + const Geom_traits& traits; + const Arr_construct_curve_end cst_curve_end; + const Arr_construct_vertical_segment cst_vertical_segment; + const Arr_construct_horizontal_segment cst_horizontal_segment; + const Geom_traits::Intersect_2 intersect_2; + const Geom_traits::Compare_xy_2 compare_xy_2; + const Geom_traits::Is_vertical_2 is_vertical_2; + const Arr_approximate_point_2 approx_pt; +}; + +class Arr_render_context : public Arr_cancellable_context_mixin, public Arr_geom_traits_context_mixin +{ + using Point_location = Arr_trapezoid_ric_point_location; + +public: + Arr_render_context(const Arrangement& arr, const Point_location& pl, double approx_error) + : Arr_cancellable_context_mixin() + , Arr_geom_traits_context_mixin(*arr.traits()) + , arr(arr) + , point_location(pl) + , counter(std::make_shared(0)) // TODO: remove this after debugging + , approx_error(approx_error) {} + +public: + const double approx_error; + const Arrangement& arr; + std::shared_ptr counter; // TODO: remove this after debugging + const Point_location& point_location; +}; + +class Arr_bounded_render_context : public Arr_render_context, public Arr_bounds_context_mixin +{ + using Approx_point = Arr_approximation_geometry_traits::Approx_point; + using Point_2 = Geom_traits::Point_2; + constexpr static double ep_base = std::numeric_limits::epsilon(); + +public: + Arr_bounded_render_context(const Arr_render_context& ctx, const Bbox_2& bbox, Arr_approximation_cache& cache) + : Arr_render_context(ctx) + , ep_xmin(std::max(std::abs(ep_base * bbox.xmin()), ep_base)) + , ep_xmax(std::max(std::abs(ep_base * bbox.xmax()), ep_base)) + , ep_ymin(std::max(std::abs(ep_base * bbox.ymin()), ep_base)) + , ep_ymax(std::max(std::abs(ep_base * bbox.ymax()), ep_base)) + , Arr_bounds_context_mixin(bbox) + , cache(cache) { + + // TODO: remove this after debugging + std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::out | std::ios::trunc); + } + + Approx_point approx_pt_on_boundary(const Point_2& pt) const { + double x = this->approx_pt(pt, 0); + double y = this->approx_pt(pt, 1); + + if(std::abs(x - xmin()) < ep_xmin) { + x = xmin(); + } else if(std::abs(x - xmax()) < ep_xmax) { + x = xmax(); + } else if(std::abs(y - ymin()) < ep_ymin) { + y = ymin(); + } else if(std::abs(y - ymax()) < ep_ymax) { + y = ymax(); + } else { + // We shall not call this function if not approximated from a boundary point. + CGAL_assertion(false && "Failed to match point to the boundary"); + } + + return Approx_point(x, y); + } + +public: + Arr_approximation_cache& cache; + +private: + // floating point epsilon at boundary coordinates + double ep_xmin, ep_xmax, ep_ymin, ep_ymax; +}; + +template +class Arr_context_delegator +{ +public: + Arr_context_delegator(const Context& ctx) + : m_ctx(ctx) {} + + const Context* operator->() const { return &m_ctx; } + const Context& operator*() const { return m_ctx; } + + // implicit conversion to the context type + operator const Context&() const { return m_ctx; } + +private: + const Context& m_ctx; +}; + +} // namespace CGAL +#endif // CGAL_DRAW_AOS_ARR_RENDER_CONTEXT_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h new file mode 100644 index 00000000000..f237c2c44fa --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h @@ -0,0 +1,234 @@ + +#ifndef ARR_VIEWER_H +#define ARR_VIEWER_H +#include +#include +#include +#include "CGAL/Arr_trapezoid_ric_point_location.h" +#include "CGAL/Bbox_2.h" +#include "CGAL/Draw_aos/Arr_bounded_renderer.h" +#include "CGAL/Draw_aos/Arr_render_context.h" +#include "CGAL/Graphics_scene.h" +#include "CGAL/Graphics_scene_options.h" +#include "CGAL/Qt/camera.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { + +class Arr_viewer : public Qt::Basic_viewer +{ + using Basic_viewer = Qt::Basic_viewer; + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + using Face_const_handle = Arrangement::Face_const_handle; + using Graphics_scene_options = + Graphics_scene_options; + +private: + // Function to check if the camera's state has changed + bool is_camera_changed() { + QMatrix4x4 proj_mat, mv_mat; + this->camera_->computeProjectionMatrix(); + this->camera_->computeModelViewMatrix(); + this->camera_->getProjectionMatrix(proj_mat.data()); + this->camera_->getModelViewMatrix(mv_mat.data()); + if(proj_mat == m_last_proj_matrix && mv_mat == m_last_modelview_matrix) { + return false; + } + m_last_proj_matrix = proj_mat; + m_last_modelview_matrix = mv_mat; + return true; + } + + Bbox_2 initial_bbox() const { + Bbox_2 bbox; + for(const auto& vh : m_arr.vertex_handles()) { + bbox += vh->point().bbox(); + } + if(bbox.x_span() == 0 || bbox.y_span() == 0) { + // make a default bbox around the degenrate rect + bbox = Bbox_2(bbox.xmin() - 1, bbox.ymin() - 1, bbox.xmax() + 1, bbox.ymax() + 1); + } + return bbox; + } + + /** + * @brief Computes the bounding box of the view from orthogonal camera. + * + * Currently we reverse the entire projection to find out the view bounding box. + * @return Bbox_2 + */ + Bbox_2 view_bbox_from_camera() const { + QMatrix4x4 modelview_matrix, projection_matrix; + camera_->getModelViewMatrix(modelview_matrix.data()); + camera_->getProjectionMatrix(projection_matrix.data()); + QMatrix4x4 inverse_mvp = (projection_matrix * modelview_matrix).inverted(); + + // Define 4 corners of the near plane in NDC (-1 to 1 in x and y) + QVector4D clip_space_corners[] = {QVector4D(-1.0, -1.0, 0.0, 1.0), QVector4D(-1.0, 1.0, 0.0, 1.0), + QVector4D(1.0, -1.0, 0.0, 1.0), QVector4D(1.0, 1.0, 0.0, 1.0)}; + + double xmin = std::numeric_limits::max(); + double xmax = std::numeric_limits::lowest(); + double ymin = std::numeric_limits::max(); + double ymax = std::numeric_limits::lowest(); + + for(const QVector4D& corner : clip_space_corners) { + QVector4D world = inverse_mvp * corner; + if(world.w() != 0.0) + world /= world.w(); + double x = world.x(); + double y = world.y(); + + xmin = std::min(xmin, x); + xmax = std::max(xmax, x); + ymin = std::min(ymin, y); + ymax = std::max(ymax, y); + } + + return Bbox_2(xmin, ymin, xmax, ymax); + } + + double get_approx_error(const Bbox_2& bbox) const { + std::array viewport; + camera_->getViewport(viewport.data()); + double width = static_cast(viewport[2]); + return 0.5; + } + +public: + Arr_viewer(QWidget* parent, + const Arrangement& arr, + Graphics_scene_options options, + const char* title = "Arrangement Viewer") + : Basic_viewer(parent, m_scene, title) + , m_scene_options(options) + , m_pl(arr) + , m_arr(arr) {} + + void rerender(Bbox_2 bbox) { + Arr_render_context ctx(m_arr, m_pl, get_approx_error(bbox)); + Arr_bounded_renderer renderer(ctx, bbox); + const auto& cache = renderer.render(); + + m_scene.clear(); + + int counter = 0; + + // add faces + for(const auto& [fh, face_tris] : cache.face_cache()) { + const auto& points = face_tris.points; + const auto& tris = face_tris.triangles; + bool draw_face = m_scene_options.colored_face(m_arr, fh); + for(const auto& t : tris) { + if(draw_face) { + m_scene.face_begin(m_scene_options.face_color(m_arr, fh)); + } else { + m_scene.face_begin(); + } + for(const auto idx : t) { + m_scene.add_point_in_face(points[idx]); + } + m_scene.face_end(); + } + } + + // add edges + counter = 0; + for(const auto& [he, polyline] : cache.halfedge_cache()) { + if(polyline.size() < 2) { + continue; // skip degenerate edges + } + // ofs_index << "polyline_" << counter << ".txt" << std::endl; + // std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/polyline_" + std::to_string(counter++) + ".txt"); + bool draw_colored_edge = m_scene_options.colored_edge(m_arr, he); + auto color = draw_colored_edge ? m_scene_options.edge_color(m_arr, he) : CGAL::IO::Color(); + for(size_t i = 0; i < polyline.size() - 1; ++i) { + const auto& cur_pt = polyline[i]; + const auto& next_pt = polyline[i + 1]; + auto mid_pt = CGAL::midpoint(cur_pt, next_pt); + if(mid_pt.x() <= bbox.xmin() || mid_pt.x() > bbox.xmax() || mid_pt.y() <= bbox.ymin() || + mid_pt.y() > bbox.ymax()) + { + continue; + } + // ofs << cur_pt << "\n"; + if(draw_colored_edge) { + m_scene.add_segment(cur_pt, next_pt, color); + } else { + m_scene.add_segment(cur_pt, next_pt); + } + } + // ofs << polyline.back() << "\n"; + // ofs << std::endl; + } + // add vertices + for(const auto& [vh, pt] : cache.vertex_cache()) { + // std::cout << pt << std::endl; + if(m_scene_options.colored_vertex(m_arr, vh)) { + m_scene.add_point(pt, m_scene_options.vertex_color(m_arr, vh)); + } else { + m_scene.add_point(pt); + } + } + Basic_viewer::redraw(); + } + + virtual void draw() override { + if(is_camera_changed()) { + Bbox_2 bbox = view_bbox_from_camera(); + // shrink the bbox by 10% for testing + double dx = (bbox.xmax() - bbox.xmin()) * 0.1; + double dy = (bbox.ymax() - bbox.ymin()) * 0.1; + bbox = Bbox_2(bbox.xmin() + dx, bbox.ymin() + dy, bbox.xmax() - dx, bbox.ymax() - dy); + std::cout << "Camera changed, recomputing arrangement bounding box: " << bbox << std::endl; + rerender(bbox); + } + Basic_viewer::draw(); + } + + virtual ~Arr_viewer() {} + +private: + Graphics_scene m_scene; + Graphics_scene_options m_scene_options; + const Arrangement& m_arr; + Arr_trapezoid_ric_point_location m_pl; + QMatrix4x4 m_last_proj_matrix; + QMatrix4x4 m_last_modelview_matrix; +}; + +void draw_viewer(const Arrangement& arr) { + Qt::init_ogl_context(4, 3); + int argc; + QApplication app(argc, nullptr); + Graphics_scene_options + gso; + gso.enable_faces(); + gso.enable_edges(); + gso.enable_vertices(); + gso.face_color = [](const Arrangement&, const Arrangement::Face_const_handle& fh) -> CGAL::IO::Color { + CGAL::Random random((size_t(&*fh))); + return get_random_color(random); + }; + gso.colored_face = [](const Arrangement&, const Arrangement::Face_const_handle&) { return true; }; + gso.vertex_color = [](const Arrangement&, const Arrangement::Vertex_const_handle& vh) -> CGAL::IO::Color { + CGAL::Random random((size_t(&*vh))); + return get_random_color(random); + }; + Arr_viewer viewer(app.activeWindow(), arr, gso, "Arrangement Viewer"); + viewer.show(); + app.exec(); +} + +} // namespace CGAL +#endif // ARR_VIEWER_H \ No newline at end of file diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/helpers.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/helpers.h new file mode 100644 index 00000000000..677ef1726e5 --- /dev/null +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/helpers.h @@ -0,0 +1,20 @@ +#ifndef CGAL_DRAW_AOS_HELPERS_H +#define CGAL_DRAW_AOS_HELPERS_H +#include "CGAL/Arr_linear_traits_2.h" +#include +#include +namespace CGAL { + +using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Geom_traits = Arr_linear_traits_2; +using Geom_traits = CGAL::Arr_linear_traits_2; +using Arrangement = Arrangement_2; + +struct Inner_ccb_tag +{}; +struct Outer_ccb_tag +{}; + +} // namespace CGAL + +#endif // CGAL_DRAW_AOS_HELPERS_H \ No newline at end of file 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 b375f93346e..d9c8c414d1a 100644 --- a/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h +++ b/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h @@ -18,21 +18,31 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include -#include #include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include namespace CGAL { @@ -47,12 +57,12 @@ namespace draw_function_for_arrangement_2 { // Primary templates: detection fails by default // Does the traits have approximate_2_object()? template > -struct has_approximate_2_object : std::false_type {}; +struct has_approximate_2_object : std::false_type +{}; // Specialization: detection succeeds if decltype(T::approximate_2_object()) is valid template -struct has_approximate_2_object().approximate_2_object())>> : - std::true_type +struct has_approximate_2_object().approximate_2_object())>> : std::true_type {}; // Convenience variable @@ -64,50 +74,51 @@ inline constexpr bool has_approximate_2_object_v = has_approximate_2_object:: // Primary templates: detection fails by default // Does a class have operator()(const Point&)? template > -struct has_operator_point : std::false_type {}; +struct has_operator_point : std::false_type +{}; // Specialization: detection succeeds if decltype works out - template - struct has_operator_point()(std::declval()))>> : - std::true_type +template +struct has_operator_point()(std::declval()))>> + : std::true_type {}; // Convenience variable - template - inline constexpr bool has_operator_point_v = has_operator_point::value; +template +inline constexpr bool has_operator_point_v = has_operator_point::value; // ======== // Primary templates: detection fails by default // Does a class have operator()(const X_monotone_curve&)? template > -struct has_operator_xcv : std::false_type {}; +struct has_operator_xcv : std::false_type +{}; // Specialization: detection succeeds if decltype works out -struct Dummy_output {}; +struct Dummy_output +{}; using Concrete_output_iterator = Dummy_output*; template -struct has_operator_xcv()( - std::declval(), - std::declval, - std::declval(), - std::declval() - ) - ) - >> : std::true_type +struct has_operator_xcv()(std::declval(), + std::declval, + std::declval(), + std::declval()))>> : std::true_type {}; // Convenience variable - template - inline constexpr bool has_operator_xcv_v = has_operator_xcv::value; +template +inline constexpr bool has_operator_xcv_v = has_operator_xcv::value; // ======== // Helper: detect whether T is or derives from Arr_geodesic_arc_on_sphere_traits_2<*, *, *> template -struct is_or_derived_from_agas { +struct is_or_derived_from_agas +{ private: template static std::true_type test(const Arr_geodesic_arc_on_sphere_traits_2*); @@ -124,8 +135,9 @@ inline constexpr bool is_or_derived_from_agas_v = is_or_derived_from_agas::va // ======== /// -template -class Draw_arr_tool { +template +class Draw_arr_tool +{ public: using Halfedge_const_handle = typename Arr::Halfedge_const_handle; using Vertex_const_handle = typename Arr::Vertex_const_handle; @@ -139,20 +151,18 @@ public: /*! Construct */ - 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) - {} + 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) {} /// 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); + 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) { + for(Outer_ccb_const_iterator it = face->outer_ccbs_begin(); it != face->outer_ccbs_end(); ++it) { add_ccb(*it); draw_region(*it); } @@ -164,10 +174,11 @@ public: auto curr = circ; do { auto new_face = curr->twin()->face(); - if (m_visited.find(new_face) != m_visited.end()) continue; + if(m_visited.find(new_face) != m_visited.end()) + continue; m_visited[new_face] = true; add_face(new_face); - } while (++curr != circ); + } while(++curr != circ); } /// Draw a region. @@ -188,9 +199,10 @@ public: * * For now we use C++14 features. */ - if (m_gso.colored_face(m_aos, circ->face())) + 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(); + else + m_gs.face_begin(); const auto* traits = this->m_aos.geometry_traits(); auto ext = find_smallest(circ, *traits); @@ -198,10 +210,11 @@ public: do { // Skip halfedges that are "antenas": - while (curr->face() == curr->twin()->face()) curr = curr->twin()->next(); + while(curr->face() == curr->twin()->face()) + curr = curr->twin()->next(); draw_region_impl1(*traits, curr); curr = curr->next(); - } while (curr != ext); + } while(curr != ext); m_gs.face_end(); } @@ -209,9 +222,10 @@ public: /// Compile time dispatching /// - template , int> = 0> - void draw_region_impl2(const T& /* traits */, const A& /* approximate */, Halfedge_const_handle curr) - { draw_exact_region(curr); } + template , int> = 0> + void draw_region_impl2(const T& /* traits */, const A& /* approximate */, Halfedge_const_handle curr) { + draw_exact_region(curr); + } /// template , int> = 0> @@ -221,12 +235,13 @@ public: /*! Draw a region, where the traits does not has approximate_2_object. */ - template && ! is_or_derived_from_agas_v, int> = 0> - void draw_region_impl1(const T& /* traits */, Halfedge_const_handle curr) - { draw_exact_region(curr); } + template && !is_or_derived_from_agas_v, int> = 0> + void draw_region_impl1(const T& /* traits */, Halfedge_const_handle curr) { + draw_exact_region(curr); + } /// - template && ! is_or_derived_from_agas_v, int> = 0> + template && !is_or_derived_from_agas_v, int> = 0> auto draw_region_impl1(const T& traits, Halfedge_const_handle curr) { using Approximate = typename Gt::Approximate_2; draw_region_impl2(traits, traits.approximate_2_object(), curr); @@ -253,35 +268,37 @@ public: 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; + if(polyline.empty()) + return; auto it = polyline.begin(); auto prev = it++; - for (; it != polyline.end(); prev = it++) m_gs.add_point_in_face(*prev); + for(; it != polyline.end(); prev = it++) + m_gs.add_point_in_face(*prev); } /*! Draw an exact curve. */ template - void draw_exact_curve(const XMonotoneCurve& curve, - bool colored, const CGAL::IO::Color& c) { + void draw_exact_curve(const XMonotoneCurve& curve, bool colored, const CGAL::IO::Color& c) { 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)); - if (colored) m_gs.add_segment(ctr_min(curve), ctr_max(curve), c); - else m_gs.add_segment(ctr_min(curve), ctr_max(curve)); + if(colored) + m_gs.add_segment(ctr_min(curve), ctr_max(curve), c); + else + m_gs.add_segment(ctr_min(curve), ctr_max(curve)); } /*! Draw a region in an exact manner. * This fallback simply draws the curve in an exact manner (and even this is not guaranteed). */ - void draw_exact_region(Halfedge_const_handle curr) - { draw_exact_curve(curr->curve(), false, CGAL::IO::Color()); } + void draw_exact_region(Halfedge_const_handle curr) { draw_exact_curve(curr->curve(), false, CGAL::IO::Color()); } /// Add all faces. template void add_faces(const Traits&) { - for (auto it = m_aos.unbounded_faces_begin(); it != m_aos.unbounded_faces_end(); ++it) + for(auto it = m_aos.unbounded_faces_begin(); it != m_aos.unbounded_faces_end(); ++it) add_face(it); } @@ -290,49 +307,52 @@ public: /*! Draw a point using approximate coordinates. */ template - void draw_approximate_point(const Point& p, const Approximate& approx, - bool colored, const CGAL::IO::Color& color) { - if (colored) m_gs.add_point(approx(p), color); - else m_gs.add_point(approx(p)); + void draw_approximate_point(const Point& p, const Approximate& approx, bool colored, const CGAL::IO::Color& color) { + if(colored) + m_gs.add_point(approx(p), color); + else + m_gs.add_point(approx(p)); } /// void draw_exact_point(const Point& p, bool colored, const CGAL::IO::Color& color) { - if (colored) m_gs.add_point(p, color); - else m_gs.add_point(p); + if(colored) + m_gs.add_point(p, color); + else + m_gs.add_point(p); } /// - template , int> = 0> - void draw_point_impl2(const T& /* traits */, const A& /* approximate */, const Point& p, - bool colored, const CGAL::IO::Color& c) - { draw_exact_point(p, colored, c); } + template , int> = 0> + void draw_point_impl2( + const T& /* traits */, const A& /* approximate */, const Point& p, bool colored, const CGAL::IO::Color& c) { + draw_exact_point(p, colored, c); + } /// template , int> = 0> - auto draw_point_impl2(const T& /* traits */, const A& approx, const Point& p, - bool colored, const CGAL::IO::Color& c) { + auto + draw_point_impl2(const T& /* traits */, const A& approx, const Point& p, bool colored, const CGAL::IO::Color& c) { draw_approximate_point(p, approx, colored, c); } /*! Draw a point, where the traits does not has approximate_2_object. */ - template && ! is_or_derived_from_agas_v, int> = 0> - void draw_point_impl1(const T& /* traits */, const Point& p, - bool colored, const CGAL::IO::Color& c) - { draw_exact_point(p, colored, c); } + template && !is_or_derived_from_agas_v, int> = 0> + void draw_point_impl1(const T& /* traits */, const Point& p, bool colored, const CGAL::IO::Color& c) { + draw_exact_point(p, colored, c); + } /*! Draw a point, where the traits does have approximate_2_object. */ - template && ! is_or_derived_from_agas_v, int> = 0> - auto draw_point_impl1(const T& traits, const Point& p, - bool colored, const CGAL::IO::Color& c) { + template && !is_or_derived_from_agas_v, int> = 0> + auto draw_point_impl1(const T& traits, const Point& p, bool colored, const CGAL::IO::Color& c) { draw_point_impl2(traits, traits.approximate_2_object(), p, colored, c); } /*! Draw a geodesic point. */ - template , int> = 0> + template , int> = 0> void draw_point_impl1(const T& traits, const Point& p, bool colored, const CGAL::IO::Color& color) { using Traits = T; using Ak = typename Traits::Approximate_kernel; @@ -343,10 +363,12 @@ public: 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); + 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. @@ -357,10 +379,10 @@ public: /// template - Halfedge_const_handle - find_smallest(Ccb_halfedge_const_circulator circ, - Arr_geodesic_arc_on_sphere_traits_2 const&) - { return circ; } + 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. @@ -374,32 +396,36 @@ public: // 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); + 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; + 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; + if(res == LARGER) + continue; // Store the edge inciden to a point strictly smaller: - if (res == 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) + if(cmp_y(curr->curve(), ext->curve(), curr->source()->point()) == SMALLER) ext = curr; - } while (++curr != circ); + } while(++curr != circ); return ext; } @@ -410,29 +436,33 @@ public: // std::cout << "ratio: " << this->pixel_ratio() << std::endl; m_visited.clear(); - if (m_aos.is_empty()) return; + if(m_aos.is_empty()) + return; - if (m_gso.are_faces_enabled()) add_faces(*(this->m_aos.geometry_traits())); + 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)) + 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()); + 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) { - if (m_gso.colored_vertex(m_aos, it)) + if(m_gso.are_vertices_enabled()) { + for(auto it = m_aos.vertices_begin(); it != m_aos.vertices_end(); ++it) { + if(m_gso.colored_vertex(m_aos, it)) draw_point(it->point(), true, m_gso.vertex_color(m_aos, it)); - else draw_point(it->point(), false, CGAL::IO::Color()); + else + draw_point(it->point(), false, CGAL::IO::Color()); } } @@ -448,45 +478,52 @@ public: template void draw_approximate_curve(const XMonotoneCurve& curve, const Approximate& approx, - bool colored, const CGAL::IO::Color& c) { + bool colored, + const CGAL::IO::Color& c) { // std::cout << "draw_approximate_curve\n"; std::vector polyline; double error(0.01); // TODO? (this->pixel_ratio()); approx(curve, error, std::back_inserter(polyline)); - if (polyline.empty()) return; + if(polyline.empty()) + return; auto it = polyline.begin(); auto prev = it++; - for (; it != polyline.end(); prev = it++) { - if (colored) m_gs.add_segment(*prev, *it, c); - else m_gs.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); } } /// - template , int> = 0> - void draw_curve_impl2(const T& /* traits */, const A& /* approximate */, const X_monotone_curve& xcv, - bool colored, const CGAL::IO::Color& c) - { draw_exact_curve(xcv, colored, c); } + template , int> = 0> + void draw_curve_impl2(const T& /* traits */, + const A& /* approximate */, + const X_monotone_curve& xcv, + bool colored, + const CGAL::IO::Color& c) { + draw_exact_curve(xcv, colored, c); + } /// template , int> = 0> - auto draw_curve_impl2(const T& /* traits */, const A& approx, const X_monotone_curve& xcv, - bool colored, const CGAL::IO::Color& c) { + auto draw_curve_impl2( + const T& /* traits */, const A& approx, const X_monotone_curve& xcv, bool colored, const CGAL::IO::Color& c) { draw_approximate_curve(xcv, approx, colored, c); } /*! Draw a curve, where the traits does not has approximate_2_object. */ - template && ! is_or_derived_from_agas_v, int> = 0> - void draw_curve_impl1(const T& /* traits */, const X_monotone_curve& xcv, - bool colored, const CGAL::IO::Color& c) - { draw_exact_curve(xcv, colored, c); } + template && !is_or_derived_from_agas_v, int> = 0> + void draw_curve_impl1(const T& /* traits */, const X_monotone_curve& xcv, bool colored, const CGAL::IO::Color& c) { + draw_exact_curve(xcv, colored, c); + } /*! Draw a curve, where the traits does have approximate_2_object. */ - template && ! is_or_derived_from_agas_v, int> = 0> - auto draw_curve_impl1(const T& traits, const X_monotone_curve& xcv, - bool colored, const CGAL::IO::Color& c) { + template && !is_or_derived_from_agas_v, int> = 0> + auto draw_curve_impl1(const T& traits, const X_monotone_curve& xcv, bool colored, const CGAL::IO::Color& c) { using Approximate = typename Gt::Approximate_2; draw_curve_impl2(traits, traits.approximate_2_object(), xcv, colored, c); } @@ -494,8 +531,7 @@ public: /*! Draw a geodesic curve */ template , int> = 0> - void draw_curve_impl1(const T& traits, const X_monotone_curve& xcv, - bool colored, const CGAL::IO::Color& c) { + void draw_curve_impl1(const T& traits, const X_monotone_curve& xcv, bool colored, const CGAL::IO::Color& c) { // std::cout << "draw_curve (geodesic)\n"; using Traits = T; using Kernel = typename Traits::Kernel; @@ -511,16 +547,18 @@ public: 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) { + 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) { 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); + 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; } } @@ -570,31 +608,22 @@ protected: /// template -void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, - CGAL::Graphics_scene& graphics_scene, - const GSOptions& gso) { +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(); } /// template -void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, - CGAL::Graphics_scene& graphics_scene) { - CGAL::Graphics_scene_options - gso; +void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, CGAL::Graphics_scene& graphics_scene) { + CGAL::Graphics_scene_options + gso; // colored face? - gso.colored_face=[](const CGAL_ARR_TYPE&, - typename CGAL_ARR_TYPE::Face_const_handle) -> bool - { return true; }; + gso.colored_face = [](const CGAL_ARR_TYPE&, typename CGAL_ARR_TYPE::Face_const_handle) -> bool { return true; }; // face color - gso.face_color=[](const CGAL_ARR_TYPE&, - typename CGAL_ARR_TYPE::Face_const_handle fh) -> CGAL::IO::Color - { + 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); }; @@ -604,18 +633,17 @@ void add_to_graphics_scene(const CGAL_ARR_TYPE& aos, /// Draw an arrangement on surface. template -void draw(const CGAL_ARR_TYPE& aos, const GSOptions& gso, +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); - } /// Draw an arrangement on surface. template -void draw(const CGAL_ARR_TYPE& aos, - const char* title = "2D Arrangement on Surface Basic Viewer") { +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); diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/CMakeLists.txt b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/CMakeLists.txt index 60add4ac51b..a844d943445 100644 --- a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/CMakeLists.txt +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/CMakeLists.txt @@ -6,6 +6,6 @@ project(Arrangement_on_surface_2_Tests) enable_testing() -find_package(CGAL REQUIRED COMPONENTS Core) +find_package(CGAL REQUIRED COMPONENTS Core Qt6) include(${CMAKE_CURRENT_SOURCE_DIR}/cgal_test.cmake) diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/approx_y_at_x.h b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/approx_y_at_x.h new file mode 100644 index 00000000000..ae9d6008460 --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/approx_y_at_x.h @@ -0,0 +1,11 @@ +#ifndef APPROX_Y_AT_X_H +#define APPROX_Y_AT_X_H +#include +#include +#include + +using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +using Segment_traits_2 = CGAL::Arr_segment_traits_2; +using Arr = CGAL::Arrangement_2; +double approx_y_at_x(const Arr::Traits_2& traits, const Arr::X_monotone_curve_2& curve, double x) {} +#endif // APPROX_Y_AT_X_H \ No newline at end of file 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 fef22ea95c5..8f30f676eea 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 @@ -1404,6 +1404,39 @@ function(test_algebraic_traits_core) VERTEX) endfunction() +#---------------------------------------------------------------------# +# Draw_aos tests +#---------------------------------------------------------------------# +function (test_draw_aos_arr_bounded_approximate_face_2) + set(nt ${CGAL_GMPQ_NT}) + set(kernel ${CARTESIAN_KERNEL}) + set(geom_traits ${GEODESIC_ARC_ON_SPHERE_GEOM_TRAITS}) + set(topol_traits ${SPHERICAL_TOPOL_TRAITS}) + set(flags "-DTEST_NT=${nt} -DTEST_KERNEL=${kernel} -DTEST_GEOM_TRAITS=${geom_traits} -DTEST_TOPOL_TRAITS=${topol_traits}") + + compile_test_with_flags(test_draw_aos_arr_bounded_approximate_face_2 draw_aos "${flags}") + # target_link_libraries(test_draw_aos_arr_bounded_approximate_face_2) +endfunction() + +function (test_drawing_planar) + set(nt ${CGAL_GMPQ_NT}) + set(kernel ${CARTESIAN_KERNEL}) + set(geom_traits ${POLYLINE_GEOM_TRAITS}) + set(flags "-DCGAL_USE_BASIC_VIEWER") + compile_test_with_flags(test_drawing_planar drawing "${flags}") + # target_link_libraries(test_drawing_planar PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets) + target_link_libraries(test_drawing_planar PRIVATE CGAL::CGAL_Basic_viewer CGAL::CGAL_Qt6) +endfunction() + +function (test_drawing_spherical) + set(nt ${CGAL_GMPQ_NT}) + set(kernel ${CARTESIAN_KERNEL}) + set(geom_traits ${POLYLINE_GEOM_TRAITS}) + set(flags "-DTEST_NT=${nt} -DTEST_KERNEL=${kernel} -DTEST_GEOM_TRAITS=${geom_traits}") + + compile_test_with_flags(test_drawing_spherical drawing "${flags}") + target_link_libraries(test_drawing_spherical PRIVATE CGAL::CGAL_Basic_viewer CGAL::CGAL_Qt6) +endfunction() configure("") compile_and_run(construction_test_suite_generator) @@ -1486,6 +1519,11 @@ compile_and_run(test_io) compile_and_run(test_sgm) compile_and_run(test_polycurve_intersection) + +test_draw_aos_arr_bounded_approximate_face_2() +test_drawing_planar() +test_drawing_spherical() + if(CGAL_DISABLE_GMP) get_directory_property(LIST_OF_TESTS TESTS) foreach(_test ${LIST_OF_TESTS}) diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/compile.sh b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/compile.sh new file mode 100644 index 00000000000..7b8231bf788 --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/compile.sh @@ -0,0 +1 @@ +/usr/bin/clang++ -DCGAL_DATA_DIR=\\\"/Users/shep/codes/cgal-public-dev/Data/data\\\" -DCGAL_USE_BASIC_VIEWER -DCGAL_USE_CORE=1 -DCGAL_USE_GMPXX=1 -DQT_CORE_LIB -DQT_GUI_LIB -DQT_OPENGLWIDGETS_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -I/Users/shep/codes/cgal-public-dev/AABB_tree/include -I/Users/shep/codes/cgal-public-dev/Advancing_front_surface_reconstruction/include -I/Users/shep/codes/cgal-public-dev/Algebraic_foundations/include -I/Users/shep/codes/cgal-public-dev/Algebraic_kernel_d/include -I/Users/shep/codes/cgal-public-dev/Algebraic_kernel_for_circles/include -I/Users/shep/codes/cgal-public-dev/Algebraic_kernel_for_spheres/include -I/Users/shep/codes/cgal-public-dev/Alpha_shapes_2/include -I/Users/shep/codes/cgal-public-dev/Alpha_shapes_3/include -I/Users/shep/codes/cgal-public-dev/Alpha_wrap_3/include -I/Users/shep/codes/cgal-public-dev/Apollonius_graph_2/include -I/Users/shep/codes/cgal-public-dev/Arithmetic_kernel/include -I/Users/shep/codes/cgal-public-dev/Arrangement_on_surface_2/include -I/Users/shep/codes/cgal-public-dev/BGL/include -I/Users/shep/codes/cgal-public-dev/Barycentric_coordinates_2/include -I/Users/shep/codes/cgal-public-dev/Basic_viewer/include -I/Users/shep/codes/cgal-public-dev/Boolean_set_operations_2/include -I/Users/shep/codes/cgal-public-dev/Bounding_volumes/include -I/Users/shep/codes/cgal-public-dev/Box_intersection_d/include -I/Users/shep/codes/cgal-public-dev/CGAL_Core/include -I/Users/shep/codes/cgal-public-dev/CGAL_ImageIO/include -I/Users/shep/codes/cgal-public-dev/CGAL_ipelets/include -I/Users/shep/codes/cgal-public-dev/Cartesian_kernel/include -I/Users/shep/codes/cgal-public-dev/Circular_kernel_2/include -I/Users/shep/codes/cgal-public-dev/Circular_kernel_3/include -I/Users/shep/codes/cgal-public-dev/Circulator/include -I/Users/shep/codes/cgal-public-dev/Classification/include -I/Users/shep/codes/cgal-public-dev/Combinatorial_map/include -I/Users/shep/codes/cgal-public-dev/Cone_spanners_2/include -I/Users/shep/codes/cgal-public-dev/Convex_decomposition_3/include -I/Users/shep/codes/cgal-public-dev/Convex_hull_2/include -I/Users/shep/codes/cgal-public-dev/Convex_hull_3/include -I/Users/shep/codes/cgal-public-dev/Convex_hull_d/include -I/Users/shep/codes/cgal-public-dev/Distance_2/include -I/Users/shep/codes/cgal-public-dev/Distance_3/include -I/Users/shep/codes/cgal-public-dev/Envelope_2/include -I/Users/shep/codes/cgal-public-dev/Envelope_3/include -I/Users/shep/codes/cgal-public-dev/Filtered_kernel/include -I/Users/shep/codes/cgal-public-dev/Generalized_map/include -I/Users/shep/codes/cgal-public-dev/Generator/include -I/Users/shep/codes/cgal-public-dev/GraphicsView/include -I/Users/shep/codes/cgal-public-dev/HalfedgeDS/include -I/Users/shep/codes/cgal-public-dev/Hash_map/include -I/Users/shep/codes/cgal-public-dev/Heat_method_3/include -I/Users/shep/codes/cgal-public-dev/Homogeneous_kernel/include -I/Users/shep/codes/cgal-public-dev/Hyperbolic_triangulation_2/include -I/Users/shep/codes/cgal-public-dev/Inscribed_areas/include -I/Users/shep/codes/cgal-public-dev/Installation/include -I/Users/shep/codes/cgal-public-dev/Interpolation/include -I/Users/shep/codes/cgal-public-dev/Intersections_2/include -I/Users/shep/codes/cgal-public-dev/Intersections_3/include -I/Users/shep/codes/cgal-public-dev/Interval_skip_list/include -I/Users/shep/codes/cgal-public-dev/Interval_support/include -I/Users/shep/codes/cgal-public-dev/Isosurfacing_3/include -I/Users/shep/codes/cgal-public-dev/Jet_fitting_3/include -I/Users/shep/codes/cgal-public-dev/Kernel_23/include -I/Users/shep/codes/cgal-public-dev/Kernel_d/include -I/Users/shep/codes/cgal-public-dev/Kinetic_space_partition/include -I/Users/shep/codes/cgal-public-dev/Kinetic_surface_reconstruction/include -I/Users/shep/codes/cgal-public-dev/LEDA/include -I/Users/shep/codes/cgal-public-dev/Linear_cell_complex/include -I/Users/shep/codes/cgal-public-dev/Matrix_search/include -I/Users/shep/codes/cgal-public-dev/Mesh_2/include -I/Users/shep/codes/cgal-public-dev/Mesh_3/include -I/Users/shep/codes/cgal-public-dev/Mesher_level/include -I/Users/shep/codes/cgal-public-dev/Minkowski_sum_2/include -I/Users/shep/codes/cgal-public-dev/Minkowski_sum_3/include -I/Users/shep/codes/cgal-public-dev/Modifier/include -I/Users/shep/codes/cgal-public-dev/Modular_arithmetic/include -I/Users/shep/codes/cgal-public-dev/Nef_2/include -I/Users/shep/codes/cgal-public-dev/Nef_3/include -I/Users/shep/codes/cgal-public-dev/Nef_S2/include -I/Users/shep/codes/cgal-public-dev/NewKernel_d/include -I/Users/shep/codes/cgal-public-dev/Number_types/include -I/Users/shep/codes/cgal-public-dev/Optimal_bounding_box/include -I/Users/shep/codes/cgal-public-dev/Optimal_transportation_reconstruction_2/include -I/Users/shep/codes/cgal-public-dev/Optimisation_basic/include -I/Users/shep/codes/cgal-public-dev/Orthtree/include -I/Users/shep/codes/cgal-public-dev/Partition_2/include -I/Users/shep/codes/cgal-public-dev/Periodic_2_triangulation_2/include -I/Users/shep/codes/cgal-public-dev/Periodic_3_mesh_3/include -I/Users/shep/codes/cgal-public-dev/Periodic_3_triangulation_3/include -I/Users/shep/codes/cgal-public-dev/Periodic_4_hyperbolic_triangulation_2/include -I/Users/shep/codes/cgal-public-dev/Point_set_2/include -I/Users/shep/codes/cgal-public-dev/Point_set_3/include -I/Users/shep/codes/cgal-public-dev/Point_set_processing_3/include -I/Users/shep/codes/cgal-public-dev/Poisson_surface_reconstruction_3/include -I/Users/shep/codes/cgal-public-dev/Polygon/include -I/Users/shep/codes/cgal-public-dev/Polygon_mesh_processing/include -I/Users/shep/codes/cgal-public-dev/Polygon_repair/include -I/Users/shep/codes/cgal-public-dev/Polygonal_surface_reconstruction/include -I/Users/shep/codes/cgal-public-dev/Polyhedron/include -I/Users/shep/codes/cgal-public-dev/Polyline_simplification_2/include -I/Users/shep/codes/cgal-public-dev/Polynomial/include -I/Users/shep/codes/cgal-public-dev/Polytope_distance_d/include -I/Users/shep/codes/cgal-public-dev/Principal_component_analysis/include -I/Users/shep/codes/cgal-public-dev/Principal_component_analysis_LGPL/include -I/Users/shep/codes/cgal-public-dev/Profiling_tools/include -I/Users/shep/codes/cgal-public-dev/Property_map/include -I/Users/shep/codes/cgal-public-dev/QP_solver/include -I/Users/shep/codes/cgal-public-dev/Random_numbers/include -I/Users/shep/codes/cgal-public-dev/Ridges_3/include -I/Users/shep/codes/cgal-public-dev/SMDS_3/include -I/Users/shep/codes/cgal-public-dev/STL_Extension/include -I/Users/shep/codes/cgal-public-dev/Scale_space_reconstruction_3/include -I/Users/shep/codes/cgal-public-dev/SearchStructures/include -I/Users/shep/codes/cgal-public-dev/Segment_Delaunay_graph_2/include -I/Users/shep/codes/cgal-public-dev/Segment_Delaunay_graph_Linf_2/include -I/Users/shep/codes/cgal-public-dev/Set_movable_separability_2/include -I/Users/shep/codes/cgal-public-dev/Shape_detection/include -I/Users/shep/codes/cgal-public-dev/Shape_regularization/include -I/Users/shep/codes/cgal-public-dev/Skin_surface_3/include -I/Users/shep/codes/cgal-public-dev/Snap_rounding_2/include -I/Users/shep/codes/cgal-public-dev/Solver_interface/include -I/Users/shep/codes/cgal-public-dev/Spatial_searching/include -I/Users/shep/codes/cgal-public-dev/Spatial_sorting/include -I/Users/shep/codes/cgal-public-dev/Straight_skeleton_2/include -I/Users/shep/codes/cgal-public-dev/Straight_skeleton_extrusion_2/include -I/Users/shep/codes/cgal-public-dev/Stream_lines_2/include -I/Users/shep/codes/cgal-public-dev/Stream_support/include -I/Users/shep/codes/cgal-public-dev/Subdivision_method_3/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_approximation/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_deformation/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_parameterization/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_segmentation/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_shortest_path/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_simplification/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_skeletonization/include -I/Users/shep/codes/cgal-public-dev/Surface_mesh_topology/include -I/Users/shep/codes/cgal-public-dev/Surface_mesher/include -I/Users/shep/codes/cgal-public-dev/Surface_sweep_2/include -I/Users/shep/codes/cgal-public-dev/TDS_2/include -I/Users/shep/codes/cgal-public-dev/TDS_3/include -I/Users/shep/codes/cgal-public-dev/Testsuite/include -I/Users/shep/codes/cgal-public-dev/Tetrahedral_remeshing/include -I/Users/shep/codes/cgal-public-dev/Three/include -I/Users/shep/codes/cgal-public-dev/Triangulation/include -I/Users/shep/codes/cgal-public-dev/Triangulation_2/include -I/Users/shep/codes/cgal-public-dev/Triangulation_3/include -I/Users/shep/codes/cgal-public-dev/Triangulation_on_hyperbolic_surface_2/include -I/Users/shep/codes/cgal-public-dev/Triangulation_on_sphere_2/include -I/Users/shep/codes/cgal-public-dev/Union_find/include -I/Users/shep/codes/cgal-public-dev/Visibility_2/include -I/Users/shep/codes/cgal-public-dev/Voronoi_diagram_2/include -I/Users/shep/codes/cgal-public-dev/Weights/include -I/Users/shep/codes/cgal-public-dev/Arrangement_on_surface_2/test/Arrangement_on_surface_2/build -isystem /opt/homebrew/include -isystem /opt/homebrew/lib/QtOpenGLWidgets.framework/Headers -iframework /opt/homebrew/lib -isystem /opt/homebrew/lib/QtOpenGL.framework/Headers -isystem /opt/homebrew/lib/QtCore.framework/Headers -isystem /opt/homebrew/share/qt/mkspecs/macx-clang -isystem /opt/homebrew/lib/QtGui.framework/Headers -isystem /opt/homebrew/lib/QtWidgets.framework/Headers -ftemplate-backtrace-limit=0 -g -std=gnu++17 -arch arm64 -o CMakeFiles/test_drawing_planar.dir/test_drawing_planar.cpp.o -c /Users/shep/codes/cgal-public-dev/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_planar.cpp diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/outer_ccb_0.txt b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/outer_ccb_0.txt new file mode 100644 index 00000000000..33b89f89ae6 --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/outer_ccb_0.txt @@ -0,0 +1,4 @@ +0.200004 -1.24026 +3.73824 -1.24026 +3.73824 1.94415 +0.200004 1.94415 diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/print_arr.h b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/print_arr.h new file mode 100644 index 00000000000..a2a229d4dca --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/print_arr.h @@ -0,0 +1,122 @@ +#ifndef _PRINT_ARR_H_ +#define _PRINT_ARR_H_ +#include +#include +#include + +//----------------------------------------------------------------------------- +// Print all neighboring vertices to a given arrangement vertex. +// +template +void print_neighboring_vertices(typename Arrangement::Vertex_const_handle v) { + if(v->is_isolated()) { + std::cout << "The vertex (" << v->point() << ") is isolated\n"; + return; + } + + std::cout << "The neighbors of the vertex (" << v->point() << ") are:"; + typename Arrangement::Halfedge_around_vertex_const_circulator first, curr; + first = curr = v->incident_halfedges(); + do + std::cout << " (" << curr->source()->point() << ")"; + while(++curr != first); + std::cout << std::endl; +} + +//----------------------------------------------------------------------------- +// Print all vertices (points) and edges (curves) along a connected component +// boundary. +// +template +void print_ccb(typename Arrangement::Ccb_halfedge_const_circulator circ) { + std::cout << "(" << circ->source()->point() << ")"; + typename Arrangement::Ccb_halfedge_const_circulator curr = circ; + do { + typename Arrangement::Halfedge_const_handle e = curr; + std::cout << " [" << e->curve() << "] " + << "(" << e->target()->point() << ")"; + } while(++curr != circ); + std::cout << std::endl; +} + +//----------------------------------------------------------------------------- +// Print the boundary description of an arrangement face. +// +template +void print_face(typename Arrangement::Face_const_handle f) { + // Print the outer boundary. + if(f->is_unbounded()) { + std::cout << "Unbounded face.\n"; + } else { + std::cout << "Outer boundary: "; + print_ccb(f->outer_ccb()); + } + + // Print the boundary of each of the holes. + size_t index = 1; + for(auto hole = f->holes_begin(); hole != f->holes_end(); ++hole, ++index) { + std::cout << " Hole #" << index << ": "; + // The following statement pacifies msvc. + typename Arrangement::Ccb_halfedge_const_circulator circ = *hole; + print_ccb(circ); + } + + // Print the isolated vertices. + index = 1; + for(auto iv = f->isolated_vertices_begin(); iv != f->isolated_vertices_end(); ++iv, ++index) { + std::cout << " Isolated vertex #" << index << ": " + << "(" << iv->point() << ")" << std::endl; + } +} + +//----------------------------------------------------------------------------- +// Print the given arrangement. +// +template +void print_arrangement(const Arrangement& arr) { + CGAL_precondition(arr.is_valid()); + + // Print the arrangement vertices. + std::cout << arr.number_of_vertices() << " vertices:\n"; + for(auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) { + std::cout << "(" << vit->point() << ")"; + if(vit->is_isolated()) + std::cout << " - Isolated.\n"; + else + std::cout << " - degree " << vit->degree() << std::endl; + } + + // Print the arrangement edges. + std::cout << arr.number_of_edges() << " edges:\n"; + for(auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit) + std::cout << "[" << eit->curve() << "]\n"; + + // Print the arrangement faces. + std::cout << arr.number_of_faces() << " faces:\n"; + for(auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) + print_face(fit); +} + +//----------------------------------------------------------------------------- +// Print the size of the given arrangement. +// +template +void print_arrangement_size(const Arrangement& arr) { + std::cout << "The arrangement size:\n" + << " |V| = " << arr.number_of_vertices() << ", |E| = " << arr.number_of_edges() + << ", |F| = " << arr.number_of_faces() << std::endl; +} + +//----------------------------------------------------------------------------- +// Print the size of the given unbounded arrangement. +// +template +void print_unbounded_arrangement_size(const Arrangement& arr) { + std::cout << "The arrangement size:\n" + << " |V| = " << arr.number_of_vertices() << " (plus " << arr.number_of_vertices_at_infinity() + << " at infinity)" + << ", |E| = " << arr.number_of_edges() << ", |F| = " << arr.number_of_faces() << " (" + << arr.number_of_unbounded_faces() << " unbounded)\n\n"; +} + +#endif diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_draw_aos_arr_bounded_approximate_face_2.cpp b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_draw_aos_arr_bounded_approximate_face_2.cpp new file mode 100644 index 00000000000..8f54f158724 --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_draw_aos_arr_bounded_approximate_face_2.cpp @@ -0,0 +1,194 @@ +#include "CGAL/Bbox_2.h" +#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h" +#include +#include +#include +#include + +using Approx_point = CGAL::Arr_approximation_geometry_traits::Approx_point; + +struct Test_case +{ + std::vector points_to_insert; + std::vector expected_points; + CGAL::Bbox_2 bbox; + + Test_case(std::vector points, std::vector expected, CGAL::Bbox_2 b) + : points_to_insert(std::move(points)) + , expected_points(std::move(expected)) + , bbox(b) {} +}; + +void test_geom_fill_corners_output_iterator(const Test_case& test_case) { + std::vector points{}; + auto inserter = std::back_inserter(points); + CGAL::Geom_fill_corners_output_iterator> geom_inserter( + inserter, CGAL::Bbox_2(test_case.bbox)); + + for(const auto& pt : test_case.points_to_insert) { + geom_inserter++ = pt; + } + + if(points != test_case.expected_points) { + std::cerr << "Test failed for bbox: " << test_case.bbox << std::endl; + std::cerr << "Points inserted: \n"; + for(const auto& pt : test_case.points_to_insert) { + std::cerr << pt << "\n"; + } + std::cerr << "\nExpected: \n"; + for(const auto& pt : test_case.expected_points) { + std::cerr << pt << "\n"; + } + std::cerr << "\nGot:\n"; + for(const auto& pt : points) { + std::cerr << pt << "\n"; + } + std::cerr << std::endl; + assert(false); + } +} + +int main() { + CGAL::Bbox_2 bbox(0, 0, 10, 10); + std::vector cases{ + /** + * -------------------- + * | | + * | | + * | | + * | | + * | | + * | | + * 1------------------- + */ + Test_case({Approx_point(0, 0)}, {Approx_point(0, 0)}, bbox), + + /** + * 2------------------- + * | | + * | | + * | | + * | | + * | | + * | | + * 1------------------- + * + * Expected: + * + * 4------------------3 + * | | + * | | + * | | + * | | + * | | + * | | + * 1------------------2 + */ + Test_case({Approx_point(0, 0), Approx_point(0, 10)}, + {Approx_point(0, 0), Approx_point(10, 0), Approx_point(10, 10), Approx_point(0, 10)}, bbox), + /** + * -------------------- + * | | + * | | + * | | + * | | + * | | + * | | + * 1------------------2 + * + * Expected: + * + * 2------------------- + * | | + * | | + * | | + * | | + * | | + * | | + * 1------------------2 + */ + Test_case({Approx_point(0, 0), Approx_point(10, 0)}, {Approx_point(0, 0), Approx_point(10, 0)}, bbox), + /** + * -------------------- + * | | + * | | + * | | + * | | + * | | + * | | + * 1---------2--------- + * + * Expected: + * + * -------------------- + * | | + * | | + * 2 | + * | | + * | | + * | | + * 1------------------- + */ + Test_case({Approx_point(0, 0), Approx_point(5, 0)}, {Approx_point(0, 0), Approx_point(5, 0)}, bbox), + /** + * -------------------- + * | | + * | | + * | 1 + * | | + * | 2 + * | | + * -------------------- + * + * Expected: + * + * 3------------------2 + * | | + * | | + * | 1 + * | | + * | 6 + * | | + * 4------------------5 + */ + Test_case({Approx_point(10, 5), Approx_point(10, 3)}, + {Approx_point(10, 5), Approx_point(10, 10), Approx_point(0, 10), Approx_point(0, 0), + Approx_point(10, 0), Approx_point(10, 3)}, + bbox), + /** + * -------------------1 + * | | + * | | + * | | + * | | + * | 2 + * | | + * -------------------| + * + * Expected: + * + * 2------------------1 + * | | + * | | + * | | + * | | + * | 5 + * | | + * 3------------------4 + */ + Test_case({Approx_point(10, 10), Approx_point(10, 3)}, + { + Approx_point(10, 10), + Approx_point(0, 10), + Approx_point(0, 0), + Approx_point(10, 0), + Approx_point(10, 3), + }, + bbox), + }; + + for(size_t i = 0; i < cases.size(); ++i) { + const auto& test_case = cases[i]; + test_geom_fill_corners_output_iterator(test_case); + } +} \ No newline at end of file diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_planar.cpp b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_planar.cpp new file mode 100644 index 00000000000..1570982f54b --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_planar.cpp @@ -0,0 +1,322 @@ + +#include "CGAL/Arr_algebraic_segment_traits_2.h" +#include "CGAL/Arr_circle_segment_traits_2.h" +#include "CGAL/Arr_conic_traits_2.h" +#include "CGAL/Arr_default_dcel.h" +#include "CGAL/Arr_enums.h" +#include "CGAL/Arr_linear_traits_2.h" +#include "CGAL/Arr_rational_function_traits_2.h" +#include "CGAL/Arr_segment_traits_2.h" +#include "CGAL/Arr_spherical_topology_traits_2.h" +#include "CGAL/Arrangement_2.h" +#include "CGAL/Arrangement_on_surface_2.h" +#include "CGAL/CORE_algebraic_number_traits.h" +#include "CGAL/Draw_aos/Arr_viewer.h" +#include +#include +#include +#include "CGAL/Exact_predicates_exact_constructions_kernel.h" +#include "CGAL/Exact_predicates_inexact_constructions_kernel.h" +#include + +// void draw_segments_arr_1() { +// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Segment_traits = CGAL::Arr_segment_traits_2; +// using Point_2 = Segment_traits::Point_2; +// using Arrangement = CGAL::Arrangement_2; +// // Make a square +// Arrangement arr; +// auto traits = arr.traits(); +// auto cst_x_curve = traits->construct_x_monotone_curve_2_object(); +// auto square = {cst_x_curve({0, 0}, {5, 0}), cst_x_curve({5, 0}, {5, 5}), cst_x_curve({5, 5}, {0, 5}), +// cst_x_curve({0, 5}, {0, 0})}; +// insert(arr, square.begin(), square.end()); +// auto hole_triangle = { +// cst_x_curve({1, 1}, {2, 1}), +// cst_x_curve({2, 1}, {2, 2}), +// cst_x_curve({2, 2}, {1, 1}), +// }; +// insert(arr, hole_triangle.begin(), hole_triangle.end()); +// // CGAL::draw_viewer(arr); +// } + +// void draw_segments_arr_2() { +// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Segment_traits = CGAL::Arr_segment_traits_2; +// using Point_2 = Segment_traits::Point_2; +// using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2; +// using Arrangement = CGAL::Arrangement_2; +// Arrangement arr; +// auto traits = arr.traits(); +// auto cst_x_curve = traits->construct_x_monotone_curve_2_object(); +// // make a hexagon centered at the origin with radius 10 +// double radius = 10.0; +// std::array hexagon_points = {{ +// {radius * cos(0 * CGAL_PI / 3), radius * sin(0 * CGAL_PI / 3)}, // 0° +// {radius * cos(1 * CGAL_PI / 3), radius * sin(1 * CGAL_PI / 3)}, // 60° +// {radius * cos(2 * CGAL_PI / 3), radius * sin(2 * CGAL_PI / 3)}, // 120° +// {radius * cos(3 * CGAL_PI / 3), radius * sin(3 * CGAL_PI / 3)}, // 180° +// {radius * cos(4 * CGAL_PI / 3), radius * sin(4 * CGAL_PI / 3)}, // 240° +// {radius * cos(5 * CGAL_PI / 3), radius * sin(5 * CGAL_PI / 3)}, // 300° +// }}; +// std::array hexagon; +// for(size_t i = 0; i < hexagon_points.size(); ++i) { +// size_t next_i = (i + 1) % hexagon_points.size(); +// hexagon[i] = cst_x_curve(hexagon_points[i], hexagon_points[next_i]); +// } +// // rect hole +// auto hole_rectangle = {cst_x_curve({-2, -2}, {2, -2}), cst_x_curve({2, -2}, {2, 2}), cst_x_curve({2, 2}, {-2, 2}), +// cst_x_curve({-2, 2}, {-2, -2})}; +// // iso vertex inside rect hole +// auto iso_vertex_inside_hole = Point_2{0.5, 0.5}; +// // degenerate segment below the rect hole +// auto degenerate_segment = cst_x_curve({0, -3}, {1, -3}); + +// CGAL::insert_point(arr, iso_vertex_inside_hole); +// CGAL::insert(arr, hexagon.begin(), hexagon.end()); +// CGAL::insert(arr, hole_rectangle.begin(), hole_rectangle.end()); +// CGAL::insert(arr, degenerate_segment); +// CGAL::draw_viewer(arr); +// } + +// void draw_segments_arr_3() { +// // generate random segments and draw them +// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Segment_traits = CGAL::Arr_segment_traits_2; +// using Point_2 = Segment_traits::Point_2; +// using Arrangement = CGAL::Arrangement_2; +// using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2; +// using Random = CGAL::Random; + +// Arrangement arr; +// auto traits = arr.traits(); +// auto cst_x_curve = traits->construct_x_monotone_curve_2_object(); +// Random random; +// std::vector segments; +// std::cout << "Generating random segments..." << std::endl; +// for(int i = 0; i < 100; ++i) { +// // generate random points +// Point_2 p1(random.get_double(-100, 100), random.get_double(-100, 100)); +// Point_2 p2(random.get_double(-100, 100), random.get_double(-100, 100)); +// // create a segment +// X_monotone_curve_2 seg = cst_x_curve(p1, p2); +// segments.push_back(seg); +// } + +// std::cout << "Inserting segments into the arrangement..." << std::endl; +// // insert segments into the arrangement +// CGAL::insert(arr, segments.begin(), segments.end()); + +// // draw the arrangement +// // CGAL::draw_viewer(arr); +// } + +void draw_linear_arr_1() { + using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; + using Traits = CGAL::Arr_linear_traits_2; + using Point_2 = Traits::Point_2; + using Line_2 = Traits::Line_2; + using Ray_2 = Traits::Ray_2; + using Curve_2 = Traits::Curve_2; + using Arrangement = CGAL::Arrangement_2; + using Face_const_handle = Arrangement::Face_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_iterator; + using X_monotone_curve_2 = Traits::X_monotone_curve_2; + + Arrangement arr; + auto x_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(1, 0))); + auto y_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(0, 1))); + CGAL::insert(arr, x_axis); + CGAL::insert(arr, y_axis); + + CGAL::draw_viewer(arr); +} + +void draw_linear_arr_2() { + using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; + using Traits = CGAL::Arr_linear_traits_2; + using Point_2 = Traits::Point_2; + using Line_2 = Traits::Line_2; + using Segment_2 = Traits::Segment_2; + using Ray_2 = Traits::Ray_2; + using Curve_2 = Traits::Curve_2; + using Arrangement = CGAL::Arrangement_2; + using Face_const_handle = Arrangement::Face_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_iterator; + using X_monotone_curve_2 = Traits::X_monotone_curve_2; + + Arrangement arr; + auto& traits = *arr.traits(); + // Insert a n*n grid, each cell is a square of size 5 + int n = 1; + // for(int i = 0; i < n; ++i) { + // Point_2 p1(i * 5, 0); + // Point_2 p2(i * 5, 1); + // CGAL::insert(arr, Curve_2(Line_2(p1, p2))); + // } + // for(int i = 0; i < n; ++i) { + // Point_2 p1(0, i * 5); + // Point_2 p2(1, i * 5); + // CGAL::insert(arr, Curve_2(Line_2(p1, p2))); + // } + // Generate a inner square(2*2) for all cells + // And an inner triangle for each square + for(int i = 0; i < n; ++i) { + for(int j = 0; j < n; ++j) { + Point_2 p1(i * 5 + 1, j * 5 + 1); + Point_2 p2(i * 5 + 4, j * 5 + 4); + CGAL::insert(arr, Curve_2(Segment_2(p1, Point_2(p2.x(), p1.y())))); + CGAL::insert(arr, Curve_2(Segment_2(Point_2(p1.x(), p2.y()), p2))); + CGAL::insert(arr, Curve_2(Segment_2(p1, Point_2(p1.x(), p2.y())))); + CGAL::insert(arr, Curve_2(Segment_2(Point_2(p2.x(), p1.y()), p2))); + + // Insert a triangle inside the square + Point_2 tri_p1(i * 5 + 2, j * 5 + 2); + Point_2 tri_p2(i * 5 + 3, j * 5 + 2); + Point_2 tri_p3(i * 5 + 2.5, j * 5 + 3); + CGAL::insert(arr, Curve_2(Segment_2(tri_p1, tri_p2))); + CGAL::insert(arr, Curve_2(Segment_2(tri_p2, tri_p3))); + CGAL::insert(arr, Curve_2(Segment_2(tri_p3, tri_p1))); + + // Connect the triangle to the square + Point_2 top(i * 5 + 2.5, j * 5 + 4); + CGAL::insert(arr, Curve_2(Segment_2(tri_p1, top))); + } + } + + auto arr2 = arr; + + CGAL::draw_viewer(arr); +} + +// // supports segments +// void draw_circle_segs_arr() { +// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Traits = CGAL::Arr_circle_segment_traits_2; +// using Point_2 = Traits::Point_2; +// using Curve_2 = Traits::Curve_2; +// using Arrangement = CGAL::Arrangement_2; + +// auto traits = Traits(); +// Arrangement arr; +// auto cv1 = Curve_2(Exact_kernel::Circle_2({0, 0}, 10)); +// CGAL::insert(arr, cv1); +// CGAL::draw(arr); +// } + +// void draw_conic_arcs_arr() { +// using Nt_traits = CGAL::CORE_algebraic_number_traits; +// using Rational = Nt_traits::Rational; +// using Rat_kernel = CGAL::Cartesian; +// using Rat_point = Rat_kernel::Point_2; +// using Rat_segment = Rat_kernel::Segment_2; +// using Rat_circle = Rat_kernel::Circle_2; +// using Algebraic = Nt_traits::Algebraic; +// using Alg_kernel = CGAL::Cartesian; +// using Traits = CGAL::Arr_conic_traits_2; +// using Point = Traits::Point_2; +// using Conic_arc = Traits::Curve_2; +// using X_monotone_conic_arc = Traits::X_monotone_curve_2; +// using Arrangement = CGAL::Arrangement_2; + +// Arrangement arr; +// auto traits = Traits(); +// auto cst_x_curve = traits.construct_curve_2_object(); + +// auto vert_seg = cst_x_curve(Rat_segment(Rat_point(0, 0), Rat_point(1, 0))); +// auto hor_seg = cst_x_curve(Rat_segment(Rat_point(0, 0), Rat_point(0, 1))); + +// CGAL::insert(arr, vert_seg); +// CGAL::insert(arr, hor_seg); + +// CGAL::draw(arr); +// } + +// void draw_algebraic_arr() { +// #if CGAL_USE_GMP && CGAL_USE_MPFI +// #include +// using Integer = CGAL::Gmpz; +// #elif CGAL_USE_CORE +// #include +// using Integer = CORE::BigInt; +// #else +// #include +// using Integer = LEDA::integer; +// #endif +// using Traits = CGAL::Arr_algebraic_segment_traits_2; +// using Arrangement = CGAL::Arrangement_2; +// using Polynomial = Traits::Polynomial_2; +// using X_monotone_curve_2 = Traits::X_monotone_curve_2; +// using Parameter_space_in_x_2 = Traits::Parameter_space_in_x_2; + +// Arrangement arr; +// auto traits = arr.traits(); +// X_monotone_curve_2 cv; +// auto param_space_in_x = traits->parameter_space_in_x_2_object(); +// auto ctr_cv = traits->construct_curve_2_object(); +// Polynomial x = CGAL::shift(Polynomial(1), 1, 0); +// Polynomial y = CGAL::shift(Polynomial(1), 1, 1); +// auto cst_x_curve = traits->construct_x_monotone_segment_2_object(); +// auto curve = ctr_cv(CGAL::ipower(x, 4) + CGAL::ipower(y, 3) - 1); +// CGAL::insert(arr, curve); +// // CGAL::draw(arr); +// } + +// void draw_rational_arr() { +// using AK1 = CGAL::Algebraic_kernel_d_1; +// using Traits = CGAL::Arr_rational_function_traits_2; +// using Arrangement = CGAL::Arrangement_2; +// using Polynomial = Traits::Polynomial_1; +// using Alg_real = Traits::Algebraic_real_1; +// using Bound = Traits::Bound; + +// auto traits = Traits(); +// auto approx = traits.approximate_2_object(); +// auto cst_x_curve = traits.construct_x_monotone_curve_2_object(); +// Arrangement arr; +// Polynomial x = CGAL::shift(Polynomial(1), 1); +// Polynomial P1 = CGAL::ipower(x, 4) - 6 * x * x + 8; +// Alg_real l(Bound(-2.1)), r(Bound(2.1)); +// auto cv1 = cst_x_curve(P1, l, r); +// CGAL::insert(arr, cv1); +// // CGAL::draw(arr); +// } + +// void draw_spherical_arr() { +// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; +// using Direction_3 = Exact_kernel::Direction_3; +// using Geodesic_traits = CGAL::Arr_geodesic_arc_on_sphere_traits_2; +// using Spherical_topo_traits = CGAL::Arr_spherical_topology_traits_2; +// using Arrangement = CGAL::Arrangement_on_surface_2; +// using Point_2 = Geodesic_traits::Point_2; + +// Arrangement arr; +// auto traits = arr.geometry_traits(); +// auto cst_pt = traits->construct_point_2_object(); +// auto cst_param = traits->parameter_space_in_x_2_object(); + +// Point_2 p1 = cst_pt(Direction_3(1, 0, 0)); +// } + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point = Kernel::Point_2; +// void write_polyline(std::string filename, const std::vector& points) { +// std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt"); +// ofs_index << filename << std::endl; +// std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/" + filename); +// for(const auto& pt : points) { +// ofs << pt << "\n"; +// } +// ofs << std::endl; +// } + +int main() { + // draw_segments_arr_2(); + draw_linear_arr_2(); + // test_zone(); + // draw_conic_arcs_arr(); + // draw_algebraic_arr(); + // draw_rational_arr(); + // draw_circle_segs_arr(); + return 0; +} \ No newline at end of file diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_spherical.cpp b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_spherical.cpp new file mode 100644 index 00000000000..536f64e3b0b --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/test_drawing_spherical.cpp @@ -0,0 +1,99 @@ +#include "CGAL/Constrained_triangulation_2.h" +#include "CGAL/Graphics_scene.h" +#include "CGAL/Triangulation_data_structure_2.h" +#include "CGAL/Triangulation_vertex_base_with_info_2.h" +#include +#include +#include +#include +#include +#include "CGAL/Random.h" +#include "CGAL/IO/Color.h" +#include "CGAL/draw_triangulation_2.h" +#include "CGAL/mark_domain_in_triangulation.h" +#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 = [ipm](const T2&, const typename PT::Finite_faces_iterator fh) -> CGAL::IO::Color { + if(!get(ipm, fh)) { + std::cout << "Face not in domain, returning black color." << std::endl; + return CGAL::IO::Color(0, 0, 0); + } + CGAL::Random random((unsigned int)(std::size_t)(&*fh)); + return CGAL::get_random_color(random); + }; + + this->draw_face = [ipm](const T2&, const typename PT::Finite_faces_iterator fh) -> bool { return true; }; + + 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); + }; + } +}; + +int main() { + using K = CGAL::Exact_predicates_inexact_constructions_kernel; + using Vb = CGAL::Triangulation_vertex_base_with_info_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using Tds = CGAL::Triangulation_data_structure_2; + using Cdt = CGAL::Constrained_triangulation_2; + using Point = K::Point_2; + + Cdt cdt; + std::vector outer = {Point(0, 0), Point(5, 0), Point(5, 5), Point(0, 5), Point(0, 0)}; + std::vector inner = {Point(0, 0), Point(0.5, 0), Point(0.5, 0.5), Point(0.5, 0), + Point(1, 0), Point(1, 1), Point(0, 1)}; + std::vector outer_constraint = {Point(0, 0), Point(-1, -1), Point(5, 0), Point(6, -1), + Point(5, 5), Point(6, 6), Point(0, 5), Point(-1, 6)}; + + auto add_info = [](const Point& p) { return std::make_pair(p, 1); }; + + cdt.insert_with_info>(boost::make_transform_iterator(outer.begin(), add_info), + boost::make_transform_iterator(outer.end(), add_info)); + // cdt.insert_with_info>(boost::make_transform_iterator(inner.begin(), add_info), + // boost::make_transform_iterator(inner.end(), add_info)); + cdt.insert_constraint(outer_constraint.begin(), outer_constraint.end(), true); + // cdt.insert_constraint(inner.begin(), inner.end(), true); + using In_domain_map = CGAL::unordered_flat_map; + In_domain_map in_domain_map; + boost::associative_property_map in_domain(in_domain_map); + + CGAL::mark_domain_in_triangulation(cdt, in_domain); + + std::cout << "Number of faces in triangulation: " << cdt.number_of_faces() << std::endl; + + CGAL::Graphics_scene_options + gso; + gso.face_color = [&](const Cdt::Triangulation&, const typename Cdt::Finite_faces_iterator fh) -> CGAL::IO::Color { + if(!in_domain_map[fh]) { + return CGAL::IO::Color(255, 255, 255); // black for faces not in domain + } + std::array vertices; + for(int i = 0; i < 3; ++i) { + vertices[i] = fh->vertex(i)->info(); + } + if(std::any_of(vertices.begin(), vertices.end(), [](auto idx) { return idx == 0; })) { + return CGAL::IO::Color(255, 255, 255); + } + CGAL::Random rand((std::size_t)(&*fh)); + return CGAL::get_random_color(rand); + }; + gso.colored_face = [&](const Cdt::Triangulation&, const typename Cdt::Finite_faces_iterator) -> bool { + return true; // always color faces + }; + + CGAL::draw(cdt, gso, "Polygon Triangulation Viewer"); +} \ No newline at end of file diff --git a/Arrangement_on_surface_2/test/Arrangement_on_surface_2/zone_exp.h b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/zone_exp.h new file mode 100644 index 00000000000..e0702188a81 --- /dev/null +++ b/Arrangement_on_surface_2/test/Arrangement_on_surface_2/zone_exp.h @@ -0,0 +1,71 @@ +#ifndef ZONE_EXP_H +#define ZONE_EXP_H +#include "CGAL/Arr_trapezoid_ric_point_location.h" +#include +#include +#include +#include +#include +#include +#include +#include + +void test_zone() { + using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel; + using Segment_traits = CGAL::Arr_segment_traits_2; + using Point_2 = Segment_traits::Point_2; + using Arrangement = CGAL::Arrangement_2; + using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2; + using Traits = CGAL::Arr_linear_traits_2; + using Face_const_handle = Arrangement::Face_const_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + using Vertex_const_handle = Arrangement::Vertex_const_handle; + using Face_handle = Arrangement::Face_handle; + using Halfedge_handle = Arrangement::Halfedge_handle; + using Vertex_handle = Arrangement::Vertex_handle; + using Halfedge_const_handle = Arrangement::Halfedge_const_handle; + + Arrangement arr; + auto traits = arr.traits(); + + auto cst_x_curve = traits->construct_x_monotone_curve_2_object(); + auto tri = { + cst_x_curve({-1, 0}, {1, 0}), + cst_x_curve({1, 0}, {0, 3}), + cst_x_curve({0, 3}, {-1, 0}), + }; + CGAL::insert(arr, tri.begin(), tri.end()); + + // point location + auto pl = CGAL::Arr_trapezoid_ric_point_location(arr); + + // test zone construction + using Feature = std::variant; + using Feature_vector = std::vector; + using Feature_vector_out_iter = std::back_insert_iterator; + using Zone_visitor = CGAL::Arr_compute_zone_visitor; + + Feature_vector zone_features; + Feature_vector_out_iter out_iter(zone_features); + Zone_visitor zone_visitor(out_iter); + auto cv = cst_x_curve({-1, 0}, {1, 0}); + CGAL::Arrangement_zone_2 zone(arr, &zone_visitor); + + zone.init(cv, pl); + zone.compute_zone(); + + for(const auto& feature : zone_features) { + if(auto* hf = std::get_if(&feature)) { + std::cout << "Halfedge: " << (*hf)->curve() << std::endl; + Halfedge_const_handle che(*hf); + std::cout << " const curve" << che->curve() << std::endl; + } else if(auto* vh = std::get_if(&feature)) { + std::cout << "Vertex: " << (*vh)->point() << std::endl; + } else if(auto* fh = std::get_if(&feature)) { + std::cout << "Face: " << std::endl; + } + } + + CGAL::draw(arr); +} +#endif // ZONE_EXP_H \ No newline at end of file diff --git a/Basic_viewer/include/CGAL/Basic_viewer.h b/Basic_viewer/include/CGAL/Basic_viewer.h index 7a3354691c2..ed8ca7620db 100644 --- a/Basic_viewer/include/CGAL/Basic_viewer.h +++ b/Basic_viewer/include/CGAL/Basic_viewer.h @@ -13,8 +13,8 @@ #ifndef CGAL_BASIC_VIEWER_H #define CGAL_BASIC_VIEWER_H -#include #include +#include // compatibility #if defined(CGAL_USE_BASIC_VIEWER) && !defined(CGAL_USE_BASIC_VIEWER_QT) @@ -30,14 +30,10 @@ // #elif defined(CGAL_USE_BASIC_VIEWER_GLFW) // #include #else -namespace CGAL -{ - inline - void draw_graphics_scene(const Graphics_scene&, - const char* ="CGAL Basic Viewer") - { - std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."< +#include #include #include -#include #include -#include +#include #ifdef __GNUC__ -#if __GNUC__ >= 9 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-copy" +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-copy" #endif #endif #include #include -#include #include -#include +#include #include #include +#include #ifdef __GNUC__ #if __GNUC__ >= 9 -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif #endif -#include -#include #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 @@ -64,49 +66,48 @@ namespace Qt { class Basic_viewer : public CGAL::QGLViewer { public: - using BufferType=float; + using BufferType = float; typedef CGAL::Exact_predicates_inexact_constructions_kernel Local_kernel; - typedef Local_kernel::Point_3 Local_point; + typedef Local_kernel::Point_3 Local_point; typedef Local_kernel::Vector_3 Local_vector; - using GS=Graphics_scene; + using GS = Graphics_scene; // Constructor/Destructor 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_default_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), - m_scene(buf), - m_draw_vertices(draw_vertices), - m_draw_edges(draw_edges), - m_draw_rays(draw_rays), - m_draw_lines(draw_lines), - m_draw_faces(draw_faces), - m_draw_text(draw_text), - m_draw_normals(false), - m_draw_cylinder_edge(false), - m_draw_sphere_vertex(false), - m_draw_mesh_triangles(false), - m_flat_shading(true), - m_use_default_color(use_default_color), - m_use_default_color_normal(false), - m_display_face_normal(false), - m_inverse_normal(inverse_normal), - m_no_2D_mode(no_2D_mode), - m_geometry_feature_enabled(true), - m_prev_scene_empty(true), - m_default_color_normal(220, 60, 20), - m_ambient_color(0.6f, 0.5f, 0.5f, 0.5f), - m_are_buffers_initialized(false) - { + const char* title = "", + bool draw_vertices = false, + bool draw_edges = true, + bool draw_faces = true, + bool use_default_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) + , m_scene(buf) + , m_draw_vertices(draw_vertices) + , m_draw_edges(draw_edges) + , m_draw_rays(draw_rays) + , m_draw_lines(draw_lines) + , m_draw_faces(draw_faces) + , m_draw_text(draw_text) + , m_draw_normals(false) + , m_draw_cylinder_edge(false) + , m_draw_sphere_vertex(false) + , m_draw_mesh_triangles(false) + , m_flat_shading(true) + , m_use_default_color(use_default_color) + , m_use_default_color_normal(false) + , m_display_face_normal(false) + , m_inverse_normal(inverse_normal) + , m_no_2D_mode(no_2D_mode) + , m_geometry_feature_enabled(true) + , m_prev_scene_empty(true) + , m_default_color_normal(220, 60, 20) + , m_ambient_color(0.6f, 0.5f, 0.5f, 0.5f) + , 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); @@ -154,7 +155,7 @@ public: setMouseBinding(::Qt::Key_C, ::Qt::ControlModifier, ::Qt::MiddleButton, qglviewer::FRAME, qglviewer::ZOOM); setWheelBinding(::Qt::Key_C, ::Qt::ControlModifier, qglviewer::FRAME, qglviewer::ZOOM); - if (title[0]==0) + if(title[0] == 0) setWindowTitle("CGAL Basic Viewer"); else setWindowTitle(title); @@ -162,13 +163,12 @@ public: resize(CGAL_BASIC_VIEWER_INIT_SIZE_X, CGAL_BASIC_VIEWER_INIT_SIZE_Y); } - ~Basic_viewer() - { + ~Basic_viewer() { makeCurrent(); - for (unsigned int i=0; iinverseTransformOf - (CGAL::qglviewer::Vec(0.f, 0.f, 1.f)); - const CGAL::qglviewer::Vec& pos=m_frame_plane->position(); - return Local_kernel::Plane_3(n[0], n[1], n[2], -n*pos); + Local_kernel::Plane_3 clipping_plane() const { + CGAL::qglviewer::Vec n = m_frame_plane->inverseTransformOf(CGAL::qglviewer::Vec(0.f, 0.f, 1.f)); + const CGAL::qglviewer::Vec& pos = m_frame_plane->position(); + return Local_kernel::Plane_3(n[0], n[1], n[2], -n * pos); } - bool clipping_plane_enabled() const - { return (m_use_clipping_plane!=CLIPPING_PLANE_OFF); } + bool clipping_plane_enabled() const { return (m_use_clipping_plane != CLIPPING_PLANE_OFF); } - const Graphics_scene& graphics_scene() const - { return m_scene; } + const Graphics_scene& graphics_scene() const { return m_scene; } /*****************/ - virtual void redraw() - { + virtual void redraw() { initialize_buffers(); update(); - if(m_prev_scene_empty) - { initialize_vertices_and_edges_size(); } - m_prev_scene_empty=(m_scene.empty()); + if(m_prev_scene_empty) { + initialize_vertices_and_edges_size(); + } + m_prev_scene_empty = (m_scene.empty()); } - void reverse_all_normals() - { - m_inverse_normal=!m_inverse_normal; + void reverse_all_normals() { + m_inverse_normal = !m_inverse_normal; m_scene.reverse_all_normals(); } // Returns true if the data structure lies on a plane - bool is_two_dimensional() const - { return !m_no_2D_mode && m_scene.is_two_dimensional(); } + bool is_two_dimensional() const { return !m_no_2D_mode && m_scene.is_two_dimensional(); } - virtual void draw() - { + 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]; } + 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(); } + QVector4D plane_point = clipping_mMatrix * QVector4D(0, 0, 0, 1); + if(!m_are_buffers_initialized) { + initialize_buffers(); + } QVector3D color; attrib_buffers(this); - if(m_draw_vertices) - { - if (m_draw_sphere_vertex && m_geometry_feature_enabled) - { + if(m_draw_vertices) { + if(m_draw_sphere_vertex && m_geometry_feature_enabled) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_sphere.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto vertex_color = m_scene.get_default_color_point(); - color = QVector3D((double)vertex_color.red()/(double)255, - (double)vertex_color.green()/(double)255, - (double)vertex_color.blue()/(double)255); + color = QVector3D((double)vertex_color.red() / (double)255, (double)vertex_color.green() / (double)255, + (double)vertex_color.blue() / (double)255); rendering_program_sphere.setUniformValue("u_DefaultColor", color); rendering_program_sphere.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_sphere.setUniformValue("u_UseDefaultColor", static_cast(0)); } - rendering_program_sphere.setUniformValue("u_Radius", static_cast(sceneRadius()*m_size_vertices*0.002)); - rendering_program_sphere.setUniformValue("u_ClipPlane", clipPlane); + rendering_program_sphere.setUniformValue("u_Radius", + static_cast(sceneRadius() * m_size_vertices * 0.002)); + rendering_program_sphere.setUniformValue("u_ClipPlane", clipPlane); rendering_program_sphere.setUniformValue("u_PointPlane", plane_point); rendering_program_sphere.setUniformValue("u_RenderingMode", rendering_mode); @@ -351,41 +305,32 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } rendering_program_sphere.release(); - } - else - { + } else { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto vertex_color = m_scene.get_default_color_point(); - color = QVector3D((double)vertex_color.red()/(double)255, - (double)vertex_color.green()/(double)255, - (double)vertex_color.blue()/(double)255); + color = QVector3D((double)vertex_color.red() / (double)255, (double)vertex_color.green() / (double)255, + (double)vertex_color.blue() / (double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } - rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_vertices)); + rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_vertices)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); @@ -397,17 +342,14 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } @@ -415,28 +357,23 @@ public: } } - if(m_draw_edges && !m_draw_mesh_triangles) - { - if (m_draw_cylinder_edge && m_geometry_feature_enabled) - { + if(m_draw_edges && !m_draw_mesh_triangles) { + if(m_draw_cylinder_edge && m_geometry_feature_enabled) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_cylinder.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto edge_color = m_scene.get_default_color_segment(); - color = QVector3D((double)edge_color.red()/(double)255, - (double)edge_color.green()/(double)255, - (double)edge_color.blue()/(double)255); + color = QVector3D((double)edge_color.red() / (double)255, (double)edge_color.green() / (double)255, + (double)edge_color.blue() / (double)255); rendering_program_cylinder.setUniformValue("u_DefaultColor", color); rendering_program_cylinder.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_cylinder.setUniformValue("u_UseDefaultColor", static_cast(0)); } - rendering_program_cylinder.setUniformValue("u_Radius", static_cast(sceneRadius()*m_size_edges*0.001)); - rendering_program_cylinder.setUniformValue("u_ClipPlane", clipPlane); + rendering_program_cylinder.setUniformValue("u_Radius", + static_cast(sceneRadius() * m_size_edges * 0.001)); + rendering_program_cylinder.setUniformValue("u_ClipPlane", clipPlane); rendering_program_cylinder.setUniformValue("u_PointPlane", plane_point); rendering_program_cylinder.setUniformValue("u_RenderingMode", rendering_mode); @@ -445,44 +382,31 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } rendering_program_cylinder.release(); - } - else - { + } else { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { - - QVector2D viewport = { - CGAL_BASIC_VIEWER_INIT_SIZE_X, - CGAL_BASIC_VIEWER_INIT_SIZE_Y - }; + QVector2D viewport = {CGAL_BASIC_VIEWER_INIT_SIZE_X, CGAL_BASIC_VIEWER_INIT_SIZE_Y}; rendering_program_line.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto edge_color = m_scene.get_default_color_segment(); - color = QVector3D((double)edge_color.red()/(double)255, - (double)edge_color.green()/(double)255, - (double)edge_color.blue()/(double)255); + color = QVector3D((double)edge_color.red() / (double)255, (double)edge_color.green() / (double)255, + (double)edge_color.blue() / (double)255); rendering_program_line.setUniformValue("u_DefaultColor", color); rendering_program_line.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_line.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_line.setUniformValue("u_PointSize", static_cast(m_size_edges)); @@ -498,17 +422,14 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } @@ -516,25 +437,20 @@ public: } } - if(m_draw_rays) - { + if(m_draw_rays) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto ray_color = m_scene.get_default_color_ray(); - color = QVector3D((double)ray_color.red()/(double)255, - (double)ray_color.green()/(double)255, - (double)ray_color.blue()/(double)255); + color = QVector3D((double)ray_color.red() / (double)255, (double)ray_color.green() / (double)255, + (double)ray_color.blue() / (double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } - rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_rays)); + rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_rays)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); @@ -546,42 +462,34 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } rendering_program_p_l.release(); } - if(m_draw_lines) - { + if(m_draw_lines) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_p_l.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto line_color = m_scene.get_default_color_line(); - color = QVector3D((double)line_color.red()/(double)255, - (double)line_color.green()/(double)255, - (double)line_color.blue()/(double)255); + color = QVector3D((double)line_color.red() / (double)255, (double)line_color.green() / (double)255, + (double)line_color.blue() / (double)255); rendering_program_p_l.setUniformValue("u_DefaultColor", color); rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_p_l.setUniformValue("u_UseDefaultColor", static_cast(0)); } - rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_lines)); + rendering_program_p_l.setUniformValue("u_PointSize", GLfloat(m_size_lines)); rendering_program_p_l.setUniformValue("u_IsOrthographic", GLint(is_two_dimensional())); rendering_program_p_l.setUniformValue("u_ClipPlane", clipPlane); @@ -593,17 +501,14 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } @@ -613,37 +518,31 @@ public: // Fix Z-fighting by drawing faces at a depth GLfloat offset_factor; GLfloat offset_units; - if (is_two_dimensional()) { + 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) - { + if(m_draw_faces) { // 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) - { + auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(2.0, 2.0); glDepthFunc(GL_LESS); rendering_program_face.bind(); - if (m_use_default_color) - { + if(m_use_default_color) { auto face_color = m_scene.get_default_color_face(); - color = QVector3D((double)face_color.red()/(double)255, - (double)face_color.green()/(double)255, - (double)face_color.blue()/(double)255); + color = QVector3D((double)face_color.red() / (double)255, (double)face_color.green() / (double)255, + (double)face_color.blue() / (double)255); rendering_program_face.setUniformValue("u_DefaultColor", color); rendering_program_face.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_face.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_face.setUniformValue("u_RenderingMode", rendering_mode); @@ -657,26 +556,27 @@ public: }; auto renderer_clipping_plane = [this](bool clipping_plane_rendering) { - if (!isOpenGL_4_3()) return; - if (!clipping_plane_rendering) return; + 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))); + 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_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) - { + 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. @@ -684,7 +584,7 @@ public: renderer(DRAW_SOLID_HALF); // 2. draw transparent layer second with back face culling to avoid messy triangles - glDepthMask(false); //disable z-testing + glDepthMask(false); // disable z-testing glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); @@ -693,63 +593,53 @@ public: 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 + 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) + } 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 - { + } else { // 1. draw solid FOR ALL renderer(DRAW_SOLID_ALL); } - if (is_two_dimensional()) + if(is_two_dimensional()) glPolygonOffset(offset_factor, offset_units); rendering_program_face.release(); } - if (m_draw_normals) - { + if(m_draw_normals) { auto renderer = [this, &color, &clipPlane, &plane_point](float rendering_mode) { rendering_program_normal.bind(); - if (m_use_default_color_normal) - { - color = QVector3D((double)m_default_color_normal.red()/(double)255, - (double)m_default_color_normal.green()/(double)255, - (double)m_default_color_normal.blue()/(double)255); + if(m_use_default_color_normal) { + color = QVector3D((double)m_default_color_normal.red() / (double)255, + (double)m_default_color_normal.green() / (double)255, + (double)m_default_color_normal.blue() / (double)255); rendering_program_normal.setUniformValue("u_DefaultColor", color); rendering_program_normal.setUniformValue("u_UseDefaultColor", static_cast(1)); - } - else - { + } else { rendering_program_normal.setUniformValue("u_UseDefaultColor", static_cast(0)); } rendering_program_normal.setUniformValue("u_Factor", static_cast(m_height_factor_normals)); rendering_program_normal.setUniformValue("u_SceneRadius", static_cast(sceneRadius())); - if (m_display_face_normal) - { + if(m_display_face_normal) { rendering_program_normal.setUniformValue("u_DisplayFaceNormal", static_cast(1)); - } - else - { + } else { rendering_program_normal.setUniformValue("u_DisplayFaceNormal", static_cast(0)); } - rendering_program_normal.setUniformValue("u_ClipPlane", clipPlane); + rendering_program_normal.setUniformValue("u_ClipPlane", clipPlane); rendering_program_normal.setUniformValue("u_PointPlane", plane_point); rendering_program_normal.setUniformValue("u_RenderingMode", rendering_mode); @@ -759,28 +649,23 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } rendering_program_normal.release(); } - if (m_draw_mesh_triangles) - { + if(m_draw_mesh_triangles) { - auto renderer = [this, &clipPlane, &plane_point](float rendering_mode) - { + auto renderer = [this, &clipPlane, &plane_point](float rendering_mode) { rendering_program_triangle.bind(); rendering_program_triangle.setUniformValue("u_RenderingMode", rendering_mode); rendering_program_triangle.setUniformValue("u_ClipPlane", clipPlane); @@ -791,36 +676,28 @@ public: }; enum { - DRAW_ALL = -1, // draw all + 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) - { + if(m_use_clipping_plane == CLIPPING_PLANE_SOLID_HALF_ONLY) { renderer(DRAW_INSIDE_ONLY); - } - else - { + } else { renderer(DRAW_ALL); } rendering_program_triangle.release(); } - if (m_draw_text) - { + 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())); + CGAL::qglviewer::Vec screenPos = camera()->projectedCoordinatesOf(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())); + drawText((int)screenPos[0], (int)screenPos[1], QString(std::get<1>(m_texts_vec[i]).c_str())); } glEnable(GL_LIGHTING); } @@ -834,8 +711,7 @@ public: } protected: - void compile_shaders() - { + void compile_shaders() { rendering_program_face.removeAllShaders(); rendering_program_p_l.removeAllShaders(); rendering_program_line.removeAllShaders(); @@ -846,16 +722,16 @@ protected: rendering_program_triangle.removeAllShaders(); // Create the buffers - for (unsigned int i=0; icompileSourceCode(source_)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(source_)) { + std::cerr << "Compiling vertex source FAILED" << std::endl; + } - source_ = isOpenGL_4_3() - ? FRAGMENT_SOURCE_P_L - : FRAGMENT_SOURCE_P_L_COMP; + source_ = isOpenGL_4_3() ? FRAGMENT_SOURCE_P_L : FRAGMENT_SOURCE_P_L_COMP; - QOpenGLShader *fragment_shader_p_l= new QOpenGLShader(QOpenGLShader::Fragment); - if(!fragment_shader_p_l->compileSourceCode(source_)) - { std::cerr<<"Compiling fragment source FAILED"<compileSourceCode(source_)) { + std::cerr << "Compiling fragment source FAILED" << std::endl; + } - if(!rendering_program_p_l.addShader(vertex_shader_p_l)) - { std::cerr<<"adding vertex shader FAILED"<compileSourceCode(source_)) - { std::cerr<<"Compiling vertex source FAILED"<compileSourceCode(source_)) { + std::cerr << "Compiling vertex source FAILED" << std::endl; + } - source_ = isOpenGL_4_3() - ? FRAGMENT_SOURCE_COLOR - : FRAGMENT_SOURCE_COLOR_COMP; + source_ = isOpenGL_4_3() ? FRAGMENT_SOURCE_COLOR : FRAGMENT_SOURCE_COLOR_COMP; - QOpenGLShader *fragment_shader_face= new QOpenGLShader(QOpenGLShader::Fragment); - if(!fragment_shader_face->compileSourceCode(source_)) - { std::cerr<<"Compiling fragment source FAILED"<compileSourceCode(source_)) { + std::cerr << "Compiling fragment source FAILED" << std::endl; + } - if(!rendering_program_face.addShader(vertex_shader_face)) - { std::cerr<<"adding vertex shader FAILED"<compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for clipping plane FAILED" << std::endl; } + QOpenGLShader* vertex_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_clipping_plane->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for clipping plane FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_CLIPPING_PLANE; - QOpenGLShader *fragment_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_clipping_plane->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for clipping plane FAILED" << std::endl; } - - if (!rendering_program_clipping_plane.addShader(vertex_shader_clipping_plane)) - { std::cerr << "Adding vertex shader for clipping plane FAILED" << std::endl;} - if (!rendering_program_clipping_plane.addShader(fragment_shader_clipping_plane)) - { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } - if (!rendering_program_clipping_plane.link()) - { std::cerr << "Linking Program for clipping plane FAILED" << std::endl; } + QOpenGLShader* fragment_shader_clipping_plane = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_clipping_plane->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for clipping plane FAILED" << std::endl; + } + if(!rendering_program_clipping_plane.addShader(vertex_shader_clipping_plane)) { + std::cerr << "Adding vertex shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_clipping_plane.addShader(fragment_shader_clipping_plane)) { + std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_clipping_plane.link()) { + std::cerr << "Linking Program for clipping plane FAILED" << std::endl; + } } // source_ = isOpenGL_4_3() @@ -960,169 +841,193 @@ protected: // { std::cerr << "Linking Program for clipping plane FAILED" << std::endl; } // Sphere shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { source_ = VERTEX_SOURCE_SHAPE; - QOpenGLShader *vertex_shader_sphere = new QOpenGLShader(QOpenGLShader::Vertex); - if (!vertex_shader_sphere->compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for sphere FAILED" << std::endl; } + QOpenGLShader* vertex_shader_sphere = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_sphere->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for sphere FAILED" << std::endl; + } source_ = GEOMETRY_SOURCE_SPHERE; - QOpenGLShader *geometry_shader_sphere = new QOpenGLShader(QOpenGLShader::Geometry); - if (!geometry_shader_sphere->compileSourceCode(source_)) - { std::cerr << "Compiling geometry source for sphere FAILED" << std::endl; } + QOpenGLShader* geometry_shader_sphere = new QOpenGLShader(QOpenGLShader::Geometry); + if(!geometry_shader_sphere->compileSourceCode(source_)) { + std::cerr << "Compiling geometry source for sphere FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_P_L; - QOpenGLShader *fragment_shader_sphere = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_sphere->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for sphere FAILED" << std::endl; } + QOpenGLShader* fragment_shader_sphere = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_sphere->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for sphere FAILED" << std::endl; + } - - if (!rendering_program_sphere.addShader(vertex_shader_sphere)) - { std::cerr << "Adding vertex shader for sphere FAILED" << std::endl;} - if (!rendering_program_sphere.addShader(geometry_shader_sphere)) - { std::cerr << "Adding geometry shader for sphere FAILED" << std::endl;} - if (!rendering_program_sphere.addShader(fragment_shader_sphere)) - { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } - if (!rendering_program_sphere.link()) - { std::cerr << "Linking Program for sphere FAILED" << std::endl; } + if(!rendering_program_sphere.addShader(vertex_shader_sphere)) { + std::cerr << "Adding vertex shader for sphere FAILED" << std::endl; + } + if(!rendering_program_sphere.addShader(geometry_shader_sphere)) { + std::cerr << "Adding geometry shader for sphere FAILED" << std::endl; + } + if(!rendering_program_sphere.addShader(fragment_shader_sphere)) { + std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_sphere.link()) { + std::cerr << "Linking Program for sphere FAILED" << std::endl; + } } // Cylinder shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { // clipping plane shader source_ = VERTEX_SOURCE_SHAPE; - QOpenGLShader *vertex_shader_cylinder = new QOpenGLShader(QOpenGLShader::Vertex); - if (!vertex_shader_cylinder->compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for cylinder FAILED" << std::endl; } + QOpenGLShader* vertex_shader_cylinder = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_cylinder->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for cylinder FAILED" << std::endl; + } source_ = GEOMETRY_SOURCE_CYLINDER; - QOpenGLShader *geometry_shader_cylinder = new QOpenGLShader(QOpenGLShader::Geometry); - if (!geometry_shader_cylinder->compileSourceCode(source_)) - { std::cerr << "Compiling geometry source for cylinder FAILED" << std::endl; } + QOpenGLShader* geometry_shader_cylinder = new QOpenGLShader(QOpenGLShader::Geometry); + if(!geometry_shader_cylinder->compileSourceCode(source_)) { + std::cerr << "Compiling geometry source for cylinder FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_P_L; - QOpenGLShader *fragment_shader_cylinder = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_cylinder->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for cylinder FAILED" << std::endl; } + QOpenGLShader* fragment_shader_cylinder = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_cylinder->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for cylinder FAILED" << std::endl; + } - - if (!rendering_program_cylinder.addShader(vertex_shader_cylinder)) - { std::cerr << "Adding vertex shader for cylinder FAILED" << std::endl;} - if (!rendering_program_cylinder.addShader(geometry_shader_cylinder)) - { std::cerr << "Adding geometry shader for cylinder FAILED" << std::endl;} - if (!rendering_program_cylinder.addShader(fragment_shader_cylinder)) - { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } - if (!rendering_program_cylinder.link()) - { std::cerr << "Linking Program for cylinder FAILED" << std::endl; } + if(!rendering_program_cylinder.addShader(vertex_shader_cylinder)) { + std::cerr << "Adding vertex shader for cylinder FAILED" << std::endl; + } + if(!rendering_program_cylinder.addShader(geometry_shader_cylinder)) { + std::cerr << "Adding geometry shader for cylinder FAILED" << std::endl; + } + if(!rendering_program_cylinder.addShader(fragment_shader_cylinder)) { + std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_cylinder.link()) { + std::cerr << "Linking Program for cylinder FAILED" << std::endl; + } } // Normal shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { source_ = VERTEX_SOURCE_NORMAL; - QOpenGLShader *vertex_shader_normal = new QOpenGLShader(QOpenGLShader::Vertex); - if (!vertex_shader_normal->compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for normal FAILED" << std::endl; } + QOpenGLShader* vertex_shader_normal = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_normal->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for normal FAILED" << std::endl; + } source_ = GEOMETRY_SOURCE_NORMAL; - QOpenGLShader *geometry_shader_normal = new QOpenGLShader(QOpenGLShader::Geometry); - if (!geometry_shader_normal->compileSourceCode(source_)) - { std::cerr << "Compiling geometry source for normal FAILED" << std::endl; } + QOpenGLShader* geometry_shader_normal = new QOpenGLShader(QOpenGLShader::Geometry); + if(!geometry_shader_normal->compileSourceCode(source_)) { + std::cerr << "Compiling geometry source for normal FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_P_L; - QOpenGLShader *fragment_shader_normal = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_normal->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for normal FAILED" << std::endl; } + QOpenGLShader* fragment_shader_normal = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_normal->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for normal FAILED" << std::endl; + } - - if (!rendering_program_normal.addShader(vertex_shader_normal)) - { std::cerr << "Adding vertex shader for normal FAILED" << std::endl;} - if (!rendering_program_normal.addShader(geometry_shader_normal)) - { std::cerr << "Adding geometry shader for normal FAILED" << std::endl;} - if (!rendering_program_normal.addShader(fragment_shader_normal)) - { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } - if (!rendering_program_normal.link()) - { std::cerr << "Linking Program for normal FAILED" << std::endl; } + if(!rendering_program_normal.addShader(vertex_shader_normal)) { + std::cerr << "Adding vertex shader for normal FAILED" << std::endl; + } + if(!rendering_program_normal.addShader(geometry_shader_normal)) { + std::cerr << "Adding geometry shader for normal FAILED" << std::endl; + } + if(!rendering_program_normal.addShader(fragment_shader_normal)) { + std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_normal.link()) { + std::cerr << "Linking Program for normal FAILED" << std::endl; + } } // Normal shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { source_ = VERTEX_SOURCE_TRIANGLE; - QOpenGLShader *vertex_shader_triangle = new QOpenGLShader(QOpenGLShader::Vertex); - if (!vertex_shader_triangle->compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for triangle FAILED" << std::endl; } + QOpenGLShader* vertex_shader_triangle = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_triangle->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for triangle FAILED" << std::endl; + } source_ = GEOMETRY_SOURCE_TRIANGLE; - QOpenGLShader *geometry_shader_triangle = new QOpenGLShader(QOpenGLShader::Geometry); - if (!geometry_shader_triangle->compileSourceCode(source_)) - { std::cerr << "Compiling geometry source for triangle FAILED" << std::endl; } + QOpenGLShader* geometry_shader_triangle = new QOpenGLShader(QOpenGLShader::Geometry); + if(!geometry_shader_triangle->compileSourceCode(source_)) { + std::cerr << "Compiling geometry source for triangle FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_P_L; - QOpenGLShader *fragment_shader_triangle = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_triangle->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for triangle FAILED" << std::endl; } + QOpenGLShader* fragment_shader_triangle = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_triangle->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for triangle FAILED" << std::endl; + } - - if (!rendering_program_triangle.addShader(vertex_shader_triangle)) - { std::cerr << "Adding vertex shader for triangle FAILED" << std::endl;} - if (!rendering_program_triangle.addShader(geometry_shader_triangle)) - { std::cerr << "Adding geometry shader for triangle FAILED" << std::endl;} - if (!rendering_program_triangle.addShader(fragment_shader_triangle)) - { std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; } - if (!rendering_program_triangle.link()) - { std::cerr << "Linking Program for triangle FAILED" << std::endl; } + if(!rendering_program_triangle.addShader(vertex_shader_triangle)) { + std::cerr << "Adding vertex shader for triangle FAILED" << std::endl; + } + if(!rendering_program_triangle.addShader(geometry_shader_triangle)) { + std::cerr << "Adding geometry shader for triangle FAILED" << std::endl; + } + if(!rendering_program_triangle.addShader(fragment_shader_triangle)) { + std::cerr << "Adding fragment shader for clipping plane FAILED" << std::endl; + } + if(!rendering_program_triangle.link()) { + std::cerr << "Linking Program for triangle FAILED" << std::endl; + } } // Line shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { source_ = VERTEX_SOURCE_LINE_WIDTH; - QOpenGLShader *vertex_shader_line = new QOpenGLShader(QOpenGLShader::Vertex); - if (!vertex_shader_line->compileSourceCode(source_)) - { std::cerr << "Compiling vertex source for line FAILED" << std::endl; } + QOpenGLShader* vertex_shader_line = new QOpenGLShader(QOpenGLShader::Vertex); + if(!vertex_shader_line->compileSourceCode(source_)) { + std::cerr << "Compiling vertex source for line FAILED" << std::endl; + } source_ = GEOMETRY_SOURCE_LINE_WIDTH; - QOpenGLShader *geometry_shader_line = new QOpenGLShader(QOpenGLShader::Geometry); - if (!geometry_shader_line->compileSourceCode(source_)) - { std::cerr << "Compiling geometry source for line FAILED" << std::endl; } + QOpenGLShader* geometry_shader_line = new QOpenGLShader(QOpenGLShader::Geometry); + if(!geometry_shader_line->compileSourceCode(source_)) { + std::cerr << "Compiling geometry source for line FAILED" << std::endl; + } source_ = FRAGMENT_SOURCE_P_L; - QOpenGLShader *fragment_shader_line = new QOpenGLShader(QOpenGLShader::Fragment); - if (!fragment_shader_line->compileSourceCode(source_)) - { std::cerr << "Compiling fragment source for line FAILED" << std::endl; } + QOpenGLShader* fragment_shader_line = new QOpenGLShader(QOpenGLShader::Fragment); + if(!fragment_shader_line->compileSourceCode(source_)) { + std::cerr << "Compiling fragment source for line FAILED" << std::endl; + } - - if (!rendering_program_line.addShader(vertex_shader_line)) - { std::cerr << "Adding vertex shader for line FAILED" << std::endl;} - if (!rendering_program_line.addShader(geometry_shader_line)) - { std::cerr << "Adding geometry shader for line FAILED" << std::endl;} - if (!rendering_program_line.addShader(fragment_shader_line)) - { std::cerr << "Adding fragment shader for line FAILED" << std::endl; } - if (!rendering_program_line.link()) - { std::cerr << "Linking Program for line FAILED" << std::endl; } + if(!rendering_program_line.addShader(vertex_shader_line)) { + std::cerr << "Adding vertex shader for line FAILED" << std::endl; + } + if(!rendering_program_line.addShader(geometry_shader_line)) { + std::cerr << "Adding geometry shader for line FAILED" << std::endl; + } + if(!rendering_program_line.addShader(fragment_shader_line)) { + std::cerr << "Adding fragment shader for line FAILED" << std::endl; + } + if(!rendering_program_line.link()) { + std::cerr << "Linking Program for line FAILED" << std::endl; + } } } - void initialize_buffers() - { + void initialize_buffers() { set_camera_mode(); rendering_program_p_l.bind(); @@ -1134,18 +1039,18 @@ protected: positions = m_scene.get_array_of_index(GS::POS_POINTS); colors = m_scene.get_array_of_index(GS::COLOR_POINTS); - CGAL_assertion(bufn(positions.size()*sizeof(float))); + buffers[bufn].allocate(positions.data(), static_cast(positions.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); - rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(colors.size()*sizeof(float))); + buffers[bufn].allocate(colors.data(), static_cast(colors.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); - rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Color", GL_FLOAT, 0, 3); // 2) SEGMENT SHADER @@ -1154,18 +1059,18 @@ protected: colors = m_scene.get_array_of_index(GS::COLOR_SEGMENTS); ++bufn; - CGAL_assertion(bufn(positions.size()*sizeof(float))); + buffers[bufn].allocate(positions.data(), static_cast(positions.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); - rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(colors.size()*sizeof(float))); + buffers[bufn].allocate(colors.data(), static_cast(colors.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); - rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Color", GL_FLOAT, 0, 3); // 3) RAYS SHADER @@ -1174,18 +1079,18 @@ protected: colors = m_scene.get_array_of_index(GS::COLOR_RAYS); ++bufn; - CGAL_assertion(bufn(positions.size()*sizeof(float))); + buffers[bufn].allocate(positions.data(), static_cast(positions.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); - rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(colors.size()*sizeof(float))); + buffers[bufn].allocate(colors.data(), static_cast(colors.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); - rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Color", GL_FLOAT, 0, 3); // 4) LINES SHADER @@ -1194,52 +1099,49 @@ protected: colors = m_scene.get_array_of_index(GS::COLOR_LINES); ++bufn; - CGAL_assertion(bufn(positions.size()*sizeof(float))); + buffers[bufn].allocate(positions.data(), static_cast(positions.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Pos"); - rendering_program_p_l.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(colors.size()*sizeof(float))); + buffers[bufn].allocate(colors.data(), static_cast(colors.size() * sizeof(float))); rendering_program_p_l.enableAttributeArray("a_Color"); - rendering_program_p_l.setAttributeBuffer("a_Color",GL_FLOAT,0,3); + rendering_program_p_l.setAttributeBuffer("a_Color", GL_FLOAT, 0, 3); // 5) FACE SHADER vao[VAO_FACES].bind(); positions = m_scene.get_array_of_index(GS::POS_FACES); - normals = m_scene.get_array_of_index( - m_flat_shading ? GS::FLAT_NORMAL_FACES : GS::SMOOTH_NORMAL_FACES - ); + normals = m_scene.get_array_of_index(m_flat_shading ? GS::FLAT_NORMAL_FACES : GS::SMOOTH_NORMAL_FACES); colors = m_scene.get_array_of_index(GS::COLOR_FACES); ++bufn; - CGAL_assertion(bufn(positions.size()*sizeof(float))); + buffers[bufn].allocate(positions.data(), static_cast(positions.size() * sizeof(float))); rendering_program_face.enableAttributeArray("a_Pos"); - rendering_program_face.setAttributeBuffer("a_Pos",GL_FLOAT,0,3); + rendering_program_face.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(normals.size()*sizeof(float))); + buffers[bufn].allocate(normals.data(), static_cast(normals.size() * sizeof(float))); rendering_program_face.enableAttributeArray("a_Normal"); - rendering_program_face.setAttributeBuffer("a_Normal",GL_FLOAT,0,3); + rendering_program_face.setAttributeBuffer("a_Normal", GL_FLOAT, 0, 3); ++bufn; - CGAL_assertion(bufn(colors.size()*sizeof(float))); + buffers[bufn].allocate(colors.data(), static_cast(colors.size() * sizeof(float))); rendering_program_face.enableAttributeArray("a_Color"); - rendering_program_face.setAttributeBuffer("a_Color",GL_FLOAT,0,3); + rendering_program_face.setAttributeBuffer("a_Color", GL_FLOAT, 0, 3); // 6) clipping plane shader - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { generate_clipping_plane(); rendering_program_clipping_plane.bind(); @@ -1249,7 +1151,7 @@ protected: CGAL_assertion(bufn < NB_GL_BUFFERS); buffers[bufn].bind(); buffers[bufn].allocate(m_array_for_clipping_plane.data(), - static_cast(m_array_for_clipping_plane.size()*sizeof(BufferType))); + static_cast(m_array_for_clipping_plane.size() * sizeof(BufferType))); rendering_program_clipping_plane.enableAttributeArray("a_Pos"); rendering_program_clipping_plane.setAttributeBuffer("a_Pos", GL_FLOAT, 0, 3); @@ -1261,45 +1163,34 @@ protected: m_are_buffers_initialized = true; } - void attrib_buffers(CGAL::QGLViewer* viewer) - { + void attrib_buffers(CGAL::QGLViewer* viewer) { QMatrix4x4 mvpMatrix; QMatrix4x4 mvMatrix; double mat[16]; viewer->camera()->getModelViewProjectionMatrix(mat); - for(unsigned int i=0; i < 16; i++) - { + for(unsigned int i = 0; i < 16; i++) { mvpMatrix.data()[i] = (float)mat[i]; } viewer->camera()->getModelViewMatrix(mat); - for(unsigned int i=0; i < 16; i++) - { + for(unsigned 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 diffuse(0.9f, 0.9f, 0.9f, 0.9f); - QVector4D specular( 0.0f, - 0.0f, - 0.0f, - 1.0f ); + QVector4D specular(0.0f, 0.0f, 0.0f, 1.0f); CGAL::Bbox_3 bb; - if (bb==m_scene.bounding_box()) // Case of "empty" bounding box + if(bb == m_scene.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 + bb = Local_point(CGAL::ORIGIN).bbox(); + bb = bb + Local_point(1, 1, 1).bbox(); // To avoid a warning from Qglviewer + } else { + bb = m_scene.bounding_box(); } - else - { bb=m_scene.bounding_box(); } - QVector4D position((bb.xmax()-bb.xmin())/2, - (bb.ymax()-bb.ymin())/2, - bb.zmax(), 0.0); - GLfloat shininess = 1.0f; + QVector4D position((bb.xmax() - bb.xmin()) / 2, (bb.ymax() - bb.ymin()) / 2, bb.zmax(), 0.0); + GLfloat shininess = 1.0f; rendering_program_face.bind(); int mvpLocation = rendering_program_face.uniformLocation("u_Mvp"); @@ -1337,12 +1228,12 @@ protected: rendering_program_sphere.setUniformValue(mvpLocation, mvpMatrix); rendering_program_sphere.release(); - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { QMatrix4x4 clipping_mMatrix; clipping_mMatrix.setToIdentity(); - for(int i=0; i< 16 ; i++) - { clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; } + for(int i = 0; i < 16; i++) { + clipping_mMatrix.data()[i] = m_frame_plane->matrix()[i]; + } rendering_program_clipping_plane.bind(); int vpLocation = rendering_program_clipping_plane.uniformLocation("u_Vp"); @@ -1352,15 +1243,13 @@ protected: rendering_program_clipping_plane.release(); } - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { rendering_program_normal.bind(); QMatrix4x4 projection; double mat[16]; viewer->camera()->getProjectionMatrix(mat); - for(unsigned int i=0; i < 16; i++) - { + for(unsigned int i = 0; i < 16; i++) { projection.data()[i] = (float)mat[i]; } @@ -1371,8 +1260,7 @@ protected: rendering_program_normal.release(); } - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { rendering_program_triangle.bind(); int mvpLocation = rendering_program_triangle.uniformLocation("u_Mvp"); @@ -1380,8 +1268,7 @@ protected: rendering_program_triangle.release(); } - if (isOpenGL_4_3()) - { + if(isOpenGL_4_3()) { rendering_program_line.bind(); int mvpLocation = rendering_program_line.uniformLocation("u_Mvp"); @@ -1390,41 +1277,40 @@ protected: } } - void set_camera_mode() - { - if (is_two_dimensional()) - { + void set_camera_mode() { + if(is_two_dimensional()) { camera()->setType(CGAL::qglviewer::Camera::ORTHOGRAPHIC); // Camera Constraint: constraint.setRotationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::AXIS); constraint.setTranslationConstraintType(CGAL::qglviewer::AxisPlaneConstraint::FREE); - double cx=0., cy=0., cz=0.; - if (m_scene.has_zero_x()) { cx=1.; } - else if (m_scene.has_zero_y()) { cy=1.; } - else { cz=1.; } + double cx = 0., cy = 0., cz = 0.; + if(m_scene.has_zero_x()) { + cx = 1.; + } else if(m_scene.has_zero_y()) { + cy = 1.; + } else { + cz = 1.; + } - camera()->setViewDirection(CGAL::qglviewer::Vec(-cx,-cy,-cz)); + camera()->setViewDirection(CGAL::qglviewer::Vec(-cx, -cy, -cz)); constraint.setRotationConstraintDirection(CGAL::qglviewer::Vec(cx, cy, cz)); camera()->frame()->setConstraint(&constraint); - } - else - { + } else { camera()->setType(CGAL::qglviewer::Camera::PERSPECTIVE); camera()->frame()->setConstraint(nullptr); } } - virtual void init() - { + virtual void init() { set_camera_mode(); initializeOpenGLFunctions(); // 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); + glPolygonOffset(1.f, 1.f); + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); glDisable(GL_BLEND); glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_LINE_SMOOTH); @@ -1435,21 +1321,17 @@ protected: compile_shaders(); CGAL::Bbox_3 bb; - if (bb==m_scene.bounding_box()) // Case of "empty" bounding box + if(bb == m_scene.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 + bb = Local_point(CGAL::ORIGIN).bbox(); + bb = bb + Local_point(1, 1, 1).bbox(); // To avoid a warning from Qglviewer + } else { + bb = m_scene.bounding_box(); } - else - { bb=m_scene.bounding_box(); } - this->camera()->setSceneBoundingBox(CGAL::qglviewer::Vec(bb.xmin(), - bb.ymin(), - bb.zmin()), - CGAL::qglviewer::Vec(bb.xmax(), - bb.ymax(), - bb.zmax())); + this->camera()->setSceneBoundingBox(CGAL::qglviewer::Vec(bb.xmin(), bb.ymin(), bb.zmin()), + CGAL::qglviewer::Vec(bb.xmax(), bb.ymax(), bb.zmax())); - m_frame_plane=new CGAL::qglviewer::ManipulatedFrame; + m_frame_plane = new CGAL::qglviewer::ManipulatedFrame; // Check for geometry shader availability int max_geometry_output_vertices = 0; @@ -1457,54 +1339,49 @@ protected: int max_geometry_output_components = 0; glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &max_geometry_output_components); - if (max_geometry_output_vertices < 128 || max_geometry_output_components < 1024) - { + if(max_geometry_output_vertices < 128 || max_geometry_output_components < 1024) { std::cout << "Cylinder edge and sphere vertex feature disabled! (max_geometry_output_vertices=" - << max_geometry_output_vertices << ", max_geometry_output_components=" - << max_geometry_output_components << ")\n"; + << max_geometry_output_vertices << ", max_geometry_output_components=" << max_geometry_output_components + << ")\n"; m_geometry_feature_enabled = false; } /// This code cannot be done in the constructor, because the Graphics_scene /// is not yet created (cf. for example LCC demo). - if (m_inverse_normal) - { reverse_all_normals(); } + if(m_inverse_normal) { + reverse_all_normals(); + } initialize_vertices_and_edges_size(); this->showEntireScene(); } - void initialize_vertices_and_edges_size() - { - if(!m_scene.empty()) - { - auto& bbox=m_scene.bounding_box(); - double d=CGAL::sqrt(CGAL::squared_distance - (Local_point(bbox.xmin(), bbox.ymin(), bbox.zmin()), - Local_point(bbox.xmax(), bbox.ymax(), bbox.zmax()))); + void initialize_vertices_and_edges_size() { + if(!m_scene.empty()) { + auto& bbox = m_scene.bounding_box(); + double d = CGAL::sqrt(CGAL::squared_distance(Local_point(bbox.xmin(), bbox.ymin(), bbox.zmin()), + Local_point(bbox.xmax(), bbox.ymax(), bbox.zmax()))); // std::cout<<"Length of the diagonal: "<modifiers()==::Qt::ShiftModifier) && (e->button()==::Qt::LeftButton)) - { - if (manipulatedFrame()) - { + virtual void mouseDoubleClickEvent(QMouseEvent* e) { + if((e->modifiers() == ::Qt::ShiftModifier) && (e->button() == ::Qt::LeftButton)) { + if(manipulatedFrame()) { camera()->frame()->alignWithFrame(manipulatedFrame(), true); } } else { @@ -1544,307 +1418,287 @@ protected: } } - virtual void keyPressEvent(QKeyEvent *e) - { - if(!on_key_pressed || !on_key_pressed(e, this)) - { + virtual void keyPressEvent(QKeyEvent* e) { + if(!on_key_pressed || !on_key_pressed(e, this)) { const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - if ((e->key()==::Qt::Key_C) && (modifiers==::Qt::NoButton)) - { - if (!isOpenGL_4_3()) return; - if (!is_two_dimensional()) - { + if((e->key() == ::Qt::Key_C) && (modifiers == ::Qt::NoButton)) { + if(!isOpenGL_4_3()) + return; + if(!is_two_dimensional()) { // 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); } + if(m_use_clipping_plane == CLIPPING_PLANE_OFF) { + setManipulatedFrame(nullptr); + } else { + setManipulatedFrame(m_frame_plane); + } - 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; + 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)) + else if((e->key() == ::Qt::Key_C) && (modifiers == ::Qt::AltModifier)) { - if (!isOpenGL_4_3()) return; - if (m_use_clipping_plane!=CLIPPING_PLANE_OFF) - { + 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")); + 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")); + } 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_default_color=!m_use_default_color; - displayMessage(QString("Mono color=%1.").arg(m_use_default_color?"true":"false")); + } else if((e->key() == ::Qt::Key_M) && (modifiers == ::Qt::NoButton)) { + m_use_default_color = !m_use_default_color; + displayMessage(QString("Mono color=%1.").arg(m_use_default_color ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::NoButton)) - { + } else if((e->key() == ::Qt::Key_N) && (modifiers == ::Qt::NoButton)) { reverse_all_normals(); - displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal?"true":"false")); + displayMessage(QString("Inverse normal=%1.").arg(m_inverse_normal ? "true" : "false")); redraw(); - } - else if ((e->key()==::Qt::Key_S) && (modifiers==::Qt::NoButton)) - { - m_flat_shading=!m_flat_shading; - if (m_flat_shading) + } else if((e->key() == ::Qt::Key_S) && (modifiers == ::Qt::NoButton)) { + m_flat_shading = !m_flat_shading; + if(m_flat_shading) 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")); + } 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_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; } - else { cz=-cz; } - double cx=0., cy=0., cz=0.; - if (has_zero_x()) { cx=(cur.x<0?-1.:1); } - else if (has_zero_y()) { cy=(cur.y<0?-1.:1); } - else { cz=(cur.z<0?-1.:1); }*/ + /* 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; } + else { cz=-cz; } + double cx=0., cy=0., cz=0.; + if (has_zero_x()) { cx=(cur.x<0?-1.:1); } + else if (has_zero_y()) { cy=(cur.y<0?-1.:1); } + else { cz=(cur.z<0?-1.:1); }*/ 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(); + // 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(); } - } - 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")); + } 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")); + } 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 + } else if((e->key() == ::Qt::Key_Plus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl { - 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_Minus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl + } else if((e->key() == ::Qt::Key_Minus) && (!modifiers.testFlag(::Qt::ControlModifier))) // No ctrl { - if (m_size_edges>.5) m_size_edges-=.5; + 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_vertices+=.5; + } else if((e->key() == ::Qt::Key_Plus) && (modifiers.testFlag(::Qt::ControlModifier))) { + m_size_vertices += .5; displayMessage(QString("Size of points=%1.").arg(m_size_vertices)); update(); - } - else if ((e->key()==::Qt::Key_Minus) && (modifiers.testFlag(::Qt::ControlModifier))) - { - if (m_size_vertices>.5) m_size_vertices-=.5; + } else if((e->key() == ::Qt::Key_Minus) && (modifiers.testFlag(::Qt::ControlModifier))) { + if(m_size_vertices > .5) + m_size_vertices -= .5; displayMessage(QString("Size of points=%1.").arg(m_size_vertices)); 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())); + } 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())); + } 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())); + } 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())); + } 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())); + } 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())); + } 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())); + } 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())); + } 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.")); } + } 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 if ((e->key()==::Qt::Key_V) && (modifiers==::Qt::ControlModifier)) - { + } else if((e->key() == ::Qt::Key_V) && (modifiers == ::Qt::ControlModifier)) { m_draw_sphere_vertex = !m_draw_sphere_vertex; - displayMessage(QString("Draw sphere vertex=%1.").arg(m_draw_sphere_vertex?"true":"false")); + displayMessage(QString("Draw sphere vertex=%1.").arg(m_draw_sphere_vertex ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_E) && (modifiers==::Qt::ControlModifier)) - { + } else if((e->key() == ::Qt::Key_E) && (modifiers == ::Qt::ControlModifier)) { m_draw_cylinder_edge = !m_draw_cylinder_edge; - displayMessage(QString("Draw cylinder edge=%1.").arg(m_draw_cylinder_edge?"true":"false")); + displayMessage(QString("Draw cylinder edge=%1.").arg(m_draw_cylinder_edge ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::ControlModifier)) - { + } else if((e->key() == ::Qt::Key_N) && (modifiers == ::Qt::ControlModifier)) { m_draw_normals = !m_draw_normals; - displayMessage(QString("Draw normals=%1.").arg(m_draw_normals?"true":"false")); + displayMessage(QString("Draw normals=%1.").arg(m_draw_normals ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_T) && (modifiers==::Qt::ControlModifier)) - { + } else if((e->key() == ::Qt::Key_T) && (modifiers == ::Qt::ControlModifier)) { m_draw_mesh_triangles = !m_draw_mesh_triangles; - displayMessage(QString("Draw triangles=%1.").arg(m_draw_mesh_triangles?"true":"false")); + displayMessage(QString("Draw triangles=%1.").arg(m_draw_mesh_triangles ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_M) && (modifiers==::Qt::ControlModifier)) - { + } else if((e->key() == ::Qt::Key_M) && (modifiers == ::Qt::ControlModifier)) { m_use_default_color_normal = !m_use_default_color_normal; - displayMessage(QString("Normal mono color=%1.").arg(m_use_default_color_normal?"true":"false")); + displayMessage(QString("Normal mono color=%1.").arg(m_use_default_color_normal ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_N) && (modifiers==::Qt::ShiftModifier)) - { + } else if((e->key() == ::Qt::Key_N) && (modifiers == ::Qt::ShiftModifier)) { m_display_face_normal = !m_display_face_normal; - displayMessage(QString("Display face normal=%1.").arg(m_display_face_normal?"true":"false")); + displayMessage(QString("Display face normal=%1.").arg(m_display_face_normal ? "true" : "false")); update(); - } - else if ((e->key()==::Qt::Key_F2)) - { + } else if((e->key() == ::Qt::Key_F2)) { capture_screenshot(QString("./screenshot.png")); displayMessage(QString("Screenshot saved in ./screenshot")); - } - else - { CGAL::QGLViewer::keyPressEvent(e); } // By default call QGLViewer key press + } else { + CGAL::QGLViewer::keyPressEvent(e); + } // By default call QGLViewer key press } } - virtual QString helpString() const - { return helpString("CGAL Basic Viewer"); } + virtual QString helpString() const { return helpString("CGAL Basic Viewer"); } - virtual QString helpString(const char* title) const - { - QString text(QString("

")+QString(title)+QString("

")); + virtual QString helpString(const char* title) const { + QString text(QString("

") + QString(title) + QString("

")); 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. "; + "the three mouse buttons. "; text += "Left and middle buttons pressed together rotate around the " - "camera view direction axis

"; + "camera view direction axis

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

"; + "and restored at next start.

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

"; + "shortcut list.

"; text += "Double clicks automates single click actions: A left button " - "double click aligns the closer axis with the camera (if close enough). "; + "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.

"; + "the right button re-centers the scene.

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

"; + "details.

"; text += "Press Escape to exit the viewer."; return text; } - void capture_screenshot(const QString& file_path) - { - QScreen *screen; + void capture_screenshot(const QString& file_path) { + QScreen* screen; screen = QApplication::primaryScreen(); // auto geom = screen->geometry(); @@ -1853,7 +1707,7 @@ protected: } public: - std::function on_key_pressed; + std::function on_key_pressed; protected: const Graphics_scene& m_scene; @@ -1885,19 +1739,19 @@ protected: CLIPPING_PLANE_END_INDEX }; - int m_use_clipping_plane=CLIPPING_PLANE_OFF; - CGAL::qglviewer::ManipulatedFrame* m_frame_plane=nullptr; + 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_vertices=1.; - double m_size_edges=1.; - double m_size_rays=1.; - double m_size_lines=1.; - double m_size_normals=.2; - double m_height_factor_normals=.02; + double m_size_vertices = 1.; + double m_size_edges = 1.; + double m_size_rays = 1.; + double m_size_lines = 1.; + double m_size_normals = .2; + double m_height_factor_normals = .02; CGAL::IO::Color m_default_color_normal; QVector4D m_ambient_color; @@ -1907,22 +1761,13 @@ protected: // CGAL::qglviewer::LocalConstraint constraint; CGAL::qglviewer::WorldConstraint constraint; - 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 + 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 QOpenGLBuffer buffers[NB_GL_BUFFERS]; // +1 for the buffer of clipping plane // The following enum gives the indices of the different vao. - enum - { - VAO_POINTS=0, - VAO_SEGMENTS, - VAO_RAYS, - VAO_LINES, - VAO_FACES, - VAO_CLIPPING_PLANE, - NB_VAO_BUFFERS - }; + enum { VAO_POINTS = 0, VAO_SEGMENTS, VAO_RAYS, VAO_LINES, VAO_FACES, VAO_CLIPPING_PLANE, NB_VAO_BUFFERS }; QOpenGLVertexArrayObject vao[NB_VAO_BUFFERS]; QOpenGLShaderProgram rendering_program_face; @@ -1935,24 +1780,22 @@ protected: QOpenGLShaderProgram rendering_program_triangle; // variables for clipping plane - 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 ; + 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; - }; //------------------------------------------------------------------------------ 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; + 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; @@ -1960,44 +1803,39 @@ public: bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif - if (cgal_test_suite) - { return; } + 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); + 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() - { + ~QApplication_and_basic_viewer() { delete[] m_argv[0]; delete m_basic_viewer; delete m_application; } - operator bool() const - { return m_application!=nullptr; } + operator bool() const { return m_application != nullptr; } - void run() - { - if (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); + 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]; + char* m_argv[2]; int m_argc; }; @@ -2007,23 +1845,19 @@ protected: // 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 (Qt)") -{ +inline void draw_graphics_scene(const Graphics_scene& graphics_scene, const char* title = "CGAL Basic Viewer (Qt)") { #if defined(CGAL_TEST_SUITE) bool cgal_test_suite = true; #else bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); #endif - if (!cgal_test_suite) - { + 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)); + const char* argv[2] = {title, nullptr}; + QApplication app(argc, const_cast(argv)); Basic_viewer basic_viewer(app.activeWindow(), graphics_scene, title); basic_viewer.show();