From b3657277d7133d03c6896e5e7448d5e5d58f5938 Mon Sep 17 00:00:00 2001 From: Shepard Liu Date: Wed, 23 Jul 2025 17:08:17 +0800 Subject: [PATCH] made initial bounding box more precise and added margin around the bbox --- .../include/CGAL/Draw_aos/Arr_viewer.h | 46 ++-- .../include/CGAL/Draw_aos/type_utils.h | 22 +- .../include/CGAL/draw_arrangement_2.h | 198 +++++++++++------- 3 files changed, 169 insertions(+), 97 deletions(-) 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 index d0a5dadfacf..a337d9d171d 100644 --- a/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/Arr_viewer.h @@ -103,7 +103,8 @@ private: 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; + 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; @@ -119,15 +120,28 @@ private: } Bbox_2 initial_bbox() const { + const auto& traits = *m_arr.geometry_traits(); Bbox_2 bbox; // Computes a rough bounding box from the vertices. for(const auto& vh : m_arr.vertex_handles()) { - Approx_point pt = m_arr.geometry_traits()->approximate_2_object()(vh->point()); - bbox += pt.bbox(); + bbox += traits.approximate_2_object()(vh->point()).bbox(); } + double approx_error = get_approx_error(bbox); + // Computes a more precise bounding box from the halfedges. + for(const auto& he : m_arr.halfedge_handles()) { + traits.approximate_2_object()( + he->curve(), approx_error, + boost::make_function_output_iterator([&bbox](const Approx_point& pt) { bbox += pt.bbox(); })); + } + // Place margin around the bbox. + double dx = bbox.x_span() * 0.1; + double dy = bbox.y_span() * 0.1; + bbox = Bbox_2(bbox.xmin() - dx, bbox.ymin() - dy, bbox.xmax() + dx, bbox.ymax() + dy); // Make sure the bbox is not degenerate. - if(bbox.x_span() == 0) bbox += Bbox_2(bbox.xmin() - 1, bbox.ymin(), bbox.xmax() + 1, bbox.ymax()); - if(bbox.y_span() == 0) bbox += Bbox_2(bbox.xmin(), bbox.ymin() - 1, bbox.xmax(), bbox.ymax() + 1); + if(bbox.x_span() == 0) + bbox += Bbox_2(bbox.xmin() - 1, bbox.ymin(), bbox.xmax() + 1, bbox.ymax()); + if(bbox.y_span() == 0) + bbox += Bbox_2(bbox.xmin(), bbox.ymin() - 1, bbox.xmax(), bbox.ymax() + 1); return bbox; } @@ -149,7 +163,8 @@ private: 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(); + if(world.w() != 0.0) + world /= world.w(); double x = world.x(); double y = world.y(); xmin = std::min(xmin, x); @@ -193,7 +208,7 @@ private: #endif // add faces - for (const auto& [fh, face_tris] : cache.face_cache()) { + 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_gso.colored_face(m_arr, fh); @@ -202,21 +217,24 @@ private: m_gs.face_begin(m_gso.face_color(m_arr, fh)); else m_gs.face_begin(); - for(const auto idx : t) m_gs.add_point_in_face(points[idx]); + for(const auto idx : t) + m_gs.add_point_in_face(points[idx]); m_gs.face_end(); } } // add edges for(const auto& [he, polyline] : cache.halfedge_cache()) { - if(polyline.size() < 2) continue; + if(polyline.size() < 2) + continue; bool draw_colored_edge = m_gso.colored_edge(m_arr, he); auto color = draw_colored_edge ? m_gso.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(!contains(bbox, mid_pt)) continue; + if(!contains(bbox, mid_pt)) + continue; if(draw_colored_edge) m_gs.add_segment(cur_pt, next_pt, color); else @@ -226,7 +244,8 @@ private: // add vertices for(const auto& [vh, pt] : cache.vertex_cache()) { - if(!contains(bbox, pt)) continue; + if(!contains(bbox, pt)) + continue; if(m_gso.colored_vertex(m_arr, vh)) m_gs.add_point(pt, m_gso.vertex_color(m_arr, vh)); else @@ -234,7 +253,8 @@ private: } // keep scene non-empty to make sure that the Basic_viewer works in 2D mode for planar arrangements. - if(m_gs.empty()) fill_background(bbox); + if(m_gs.empty()) + fill_background(bbox); } /*! @@ -254,7 +274,7 @@ public: , m_pl(arr) {} virtual void draw() override { - if (is_camera_changed()) { + if(is_camera_changed()) { Bbox_2 bbox = view_bbox_from_camera(); #if defined(CGAL_DRAW_AOS_DEBUG) diff --git a/Arrangement_on_surface_2/include/CGAL/Draw_aos/type_utils.h b/Arrangement_on_surface_2/include/CGAL/Draw_aos/type_utils.h index ff1f8fc3717..b6d5b0066b8 100644 --- a/Arrangement_on_surface_2/include/CGAL/Draw_aos/type_utils.h +++ b/Arrangement_on_surface_2/include/CGAL/Draw_aos/type_utils.h @@ -17,17 +17,21 @@ enum class Side_of_boundary { }; template > -struct has_construct_x_monotone_curve_2 : std::false_type {}; +struct has_construct_x_monotone_curve_2 : std::false_type +{}; template -struct has_construct_x_monotone_curve_2> : std::true_type {}; +struct has_construct_x_monotone_curve_2> : std::true_type +{}; 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 template @@ -36,12 +40,13 @@ 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 +struct has_operator_point()(std::declval()))>> + : std::true_type {}; // Convenience variable @@ -51,7 +56,8 @@ 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 +{}; /*! */ 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 03e747fa924..5e482e6f7d3 100644 --- a/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h +++ b/Arrangement_on_surface_2/include/CGAL/draw_arrangement_2.h @@ -45,11 +45,13 @@ 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 template @@ -60,7 +62,8 @@ 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 @@ -77,7 +80,8 @@ 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 @@ -101,7 +105,8 @@ 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*); @@ -119,7 +124,8 @@ inline constexpr bool is_or_derived_from_agas_v = is_or_derived_from_agas::va /// template -class Draw_arr_tool { +class Draw_arr_tool +{ public: using Halfedge_const_handle = typename Arr::Halfedge_const_handle; using Vertex_const_handle = typename Arr::Vertex_const_handle; @@ -133,18 +139,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); } @@ -156,7 +162,8 @@ 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); @@ -180,8 +187,10 @@ public: * * For now we use C++14 features. */ - 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(); + if(m_gso.colored_face(m_aos, circ->face())) + m_gs.face_begin(m_gso.face_color(m_aos, circ->face())); + else + m_gs.face_begin(); const auto* traits = this->m_aos.geometry_traits(); auto ext = find_smallest(circ, *traits); @@ -189,8 +198,10 @@ 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(); + 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); @@ -202,19 +213,22 @@ public: /// template , int> = 0> - void draw_region_impl2(const T& /* traits */, const A& /* approximate */, Halfedge_const_handle curr) - { draw_exact_region(curr); } + void draw_region_impl2(const T& /* traits */, const A& /* approximate */, Halfedge_const_handle curr) { + draw_exact_region(curr); + } /// template , int> = 0> - auto draw_region_impl2(const T& /* traits */, const A& approx, Halfedge_const_handle curr) - { draw_approximate_region(curr, approx); } + auto draw_region_impl2(const T& /* traits */, const A& approx, Halfedge_const_handle curr) { + draw_approximate_region(curr, approx); + } /*! 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); } + void draw_region_impl1(const T& /* traits */, Halfedge_const_handle curr) { + draw_exact_region(curr); + } /// template && !is_or_derived_from_agas_v, int> = 0> @@ -244,10 +258,12 @@ 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. @@ -258,8 +274,10 @@ public: 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. @@ -270,7 +288,8 @@ public: /// Add all faces. template void add_faces(const Traits&) { - for(auto it = m_aos.unbounded_faces_begin(); it != m_aos.unbounded_faces_end(); ++it) add_face(it); + for(auto it = m_aos.unbounded_faces_begin(); it != m_aos.unbounded_faces_end(); ++it) + add_face(it); } /// Compile time dispatching @@ -279,39 +298,47 @@ public: */ 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)); + 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); } + 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) - { draw_approximate_point(p, approx, colored, c); } + 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); } + 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) - { draw_point_impl2(traits, traits.approximate_2_object(), p, colored, c); } + 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. */ @@ -328,8 +355,10 @@ public: 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); + if(colored) + m_gs.add_point(p3, color); + else + m_gs.add_point(p3); } /// Draw a point. @@ -341,8 +370,9 @@ public: /// template Halfedge_const_handle find_smallest(Ccb_halfedge_const_circulator circ, - Arr_geodesic_arc_on_sphere_traits_2 const&) - { return 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. @@ -357,7 +387,8 @@ public: // Find the first halfedge directed from left to right auto curr = circ; do - if(curr->direction() == CGAL::ARR_LEFT_TO_RIGHT) break; + if(curr->direction() == CGAL::ARR_LEFT_TO_RIGHT) + break; while(++curr != circ); Halfedge_const_handle ext = curr; @@ -365,22 +396,25 @@ public: // 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) ext = curr; + if(cmp_y(curr->curve(), ext->curve(), curr->source()->point()) == SMALLER) + ext = curr; } while(++curr != circ); return ext; @@ -392,27 +426,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)) draw_curve(it->curve(), true, m_gso.edge_color(m_aos, it)); - else draw_curve(it->curve(), false, CGAL::IO::Color()); + if(m_gso.are_edges_enabled()) { + for(auto it = m_aos.edges_begin(); it != m_aos.edges_end(); ++it) { + if(it->face() != it->twin()->face()) { + if(m_gso.draw_edge(m_aos, it)) { + if(m_gso.colored_edge(m_aos, it)) + draw_curve(it->curve(), true, m_gso.edge_color(m_aos, it)); + else + draw_curve(it->curve(), false, CGAL::IO::Color()); } } } } // Add all points - if (m_gso.are_vertices_enabled()) { - for (auto it = m_aos.vertices_begin(); it != m_aos.vertices_end(); ++it) { - 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()); + 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()); } } @@ -434,12 +474,15 @@ public: 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); } } @@ -449,20 +492,23 @@ public: const A& /* approximate */, const X_monotone_curve& xcv, bool colored, - const CGAL::IO::Color& c) - { draw_exact_curve(xcv, colored, c); } + 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) - { draw_approximate_curve(xcv, approx, colored, 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); } + 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. */ @@ -493,13 +539,13 @@ public: 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) { + 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) + if(colored) m_gs.add_segment(prev, next, c); else m_gs.add_segment(prev, next);