From 29715e44a44318cd925ae2aa1d1a869725f93286 Mon Sep 17 00:00:00 2001 From: Efi Fogel Date: Sat, 30 Aug 2025 20:47:31 +0300 Subject: [PATCH] Added an alternative divide & conquer for running do_intersect. --- .../Boolean_set_operations_2/Gps_agg_op.h | 61 +++++++- .../Gps_agg_op_surface_sweep_2.h | 145 ++++++++++++++++++ .../CGAL/Boolean_set_operations_2/Gps_merge.h | 20 +++ .../Gps_on_surface_base_2.h | 133 +++++++++++----- 4 files changed, 312 insertions(+), 47 deletions(-) diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op.h index b74d8c69889..e0737433cec 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op.h @@ -99,11 +99,13 @@ public: m_surface_sweep(m_traits, &m_visitor) {} - std::size_t prepare(std::size_t lower, std::size_t upper, std::size_t jump, - std::vector& arr_vec, std::list& curves_list) { + std::pair + prepare(std::size_t lower, std::size_t upper, std::size_t jump, + std::vector& arr_vec, std::list& curves_list) { std::size_t n_inf_pgn = 0; // number of infinite polygons (arrangement // with a contained unbounded face - for (auto i = lower; i <= upper; i += jump) { + std::size_t n_pgn = 0; // number of polygons (arrangements) + for (auto i = lower; i <= upper; i += jump, ++n_pgn) { // The BFS scan (after the loop) starts in the reference face, // so we count the number of polygons that contain the reference face. Arr* arr = (arr_vec[i]).first; @@ -120,16 +122,16 @@ public: curves_list.push_back(Meta_X_monotone_curve_2(he->curve(), cv_data)); } } - return n_inf_pgn; + return std::make_pair(n_inf_pgn, n_pgn); } /*! sweeps the plane without interceptions. */ void sweep_arrangements(std::size_t lower, std::size_t upper, std::size_t jump, std::vector& arr_vec) { - std::size_t n_pgn = upper - lower + 1; // number of polygons (arrangements) + std::size_t n_inf_pgn, n_pgn; std::list curves_list; - auto n_inf_pgn = prepare(lower, upper, jump, arr_vec, curves_list); + std::tie(n_inf_pgn, n_pgn) = prepare(lower, upper, jump, arr_vec, curves_list); m_surface_sweep.sweep(curves_list.begin(), curves_list.end(), lower, upper, jump, arr_vec); m_faces_hash[m_arr->reference_face()] = n_inf_pgn; Bfs_visitor visitor(&m_edges_hash, &m_faces_hash, n_pgn); @@ -143,13 +145,56 @@ public: */ bool sweep_intercept_arrangements(std::size_t lower, std::size_t upper, std::size_t jump, std::vector& arr_vec) { + std::size_t n_inf_pgn, n_pgn; std::list curves_list; - auto n_inf_pgn = prepare(lower, upper, jump, arr_vec, curves_list); + std::tie(n_inf_pgn, n_pgn) = prepare(lower, upper, jump, arr_vec, curves_list); auto res = m_surface_sweep.sweep_intercept(curves_list.begin(), curves_list.end(), lower, upper, jump, arr_vec); if (res) return true; m_faces_hash[m_arr->reference_face()] = n_inf_pgn; - std::size_t n_pgn = upper - lower + 1; // number of polygons (arrangements) + Bfs_visitor visitor(&m_edges_hash, &m_faces_hash, n_pgn); + visitor.visit_ubf(m_arr->faces_begin(), n_inf_pgn); + Bfs_scanner scanner(visitor); + scanner.scan(*m_arr); + visitor.after_scan(*m_arr); + return false; + } + + template + std::size_t prepare2(InputIterator begin, InputIterator end, std::list& curves_list) { + std::size_t n_inf_pgn = 0; // number of infinite polygons (arrangement + // with a contained unbounded face + for (auto it = begin; it != end; ++it) { + // The BFS scan (after the loop) starts in the reference face, + // so we count the number of polygons that contain the reference face. + Arr* arr = it->first; + if (arr->reference_face()->contained()) ++n_inf_pgn; + + for (auto ite = arr->edges_begin(); ite != arr->edges_end(); ++ite) { + // take only relevant edges (which separate between contained and + // non-contained faces. + Halfedge_handle he = ite; + if (he->face()->contained() == he->twin()->face()->contained()) continue; + if ((Arr_halfedge_direction)he->direction() == ARR_RIGHT_TO_LEFT) he = he->twin(); + + Curve_data cv_data(arr, he, 1, 0); + curves_list.push_back(Meta_X_monotone_curve_2(he->curve(), cv_data)); + } + } + return n_inf_pgn; + } + + /*! sweeps the plane without interceptions, but stop when an intersection occurs. + */ + template + bool sweep_intercept_arrangements2(InputIterator begin, InputIterator end) { + std::list curves_list; + auto n_inf_pgn = prepare2(begin, end, curves_list); + auto res = m_surface_sweep.sweep_intercept2(curves_list.begin(), curves_list.end(), begin, end); + if (res) return true; + + m_faces_hash[m_arr->reference_face()] = n_inf_pgn; + std::size_t n_pgn = std::distance(begin, end); // number of polygons (arrangements) Bfs_visitor visitor(&m_edges_hash, &m_faces_hash, n_pgn); visitor.visit_ubf(m_arr->faces_begin(), n_inf_pgn); Bfs_scanner scanner(visitor); diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op_surface_sweep_2.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op_surface_sweep_2.h index ae6cb35b55c..6c91ca5f776 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op_surface_sweep_2.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_agg_op_surface_sweep_2.h @@ -204,6 +204,139 @@ public: } } + template + void pre_process2(CurveInputIterator curves_begin, CurveInputIterator curves_end, + InputIterator begin, InputIterator end) { + CGAL_assertion(this->m_queue->empty() && this->m_statusLine.size() == 0); + + using Vertices_map = Unique_hash_map; + using Compare_xy_2 = typename Gt2::Compare_xy_2; + + // Allocate all of the Subcurve objects as one block. + this->m_num_of_subCurves = std::distance(curves_begin, curves_end); + if (this->m_num_of_subCurves > 0) + this->m_subCurves = + this->m_subCurveAlloc.allocate(this->m_num_of_subCurves); + + + // Initialize the event queue using the vertices vectors. Note that these + // vertices are already sorted, we simply have to merge them + Vertices_map vert_map; + Vertex_handle vh; + Vertex_handle invalid_v; + // std::size_t i = lower; + auto it = begin; + auto n = it->second->size(); + std::size_t j; + EventQueueIter q_iter; + bool first = true; + Attribute event_type; + Event* event; + + for (j = 0; j < n && (vh = (*(it->second))[j]) != invalid_v; j++) { + // Insert the vertices of the first vector one after the other. + event_type = _type_of_vertex(vh); + if (event_type == Event::DEFAULT) continue; + + event = this->_allocate_event(vh->point(), event_type, + ARR_INTERIOR, ARR_INTERIOR); + // \todo When the boolean set operations are extended to support + // unbounded curves, we will need here a special treatment. + + #ifndef CGAL_ARRANGEMENT_ON_SURFACE_2_H + event->set_finite(); + #endif + + if (! first) { + q_iter = this->m_queue->insert_after(q_iter, event); + } + else { + q_iter = this->m_queue->insert(event); + first = false; + } + + vert_map[vh] = event; + } + + Comparison_result res = LARGER; + Compare_xy_2 comp_xy = this->m_traits->compare_xy_2_object(); + EventQueueIter q_end = this->m_queue->end(); + + for (++it; it != end; ++it) { + // Merge the vertices of the other vectors into the existing queue. + q_iter = this->m_queue->begin(); + n = it->second->size(); + + for (j = 0; j < n && (vh = (*(it->second))[j]) != invalid_v; j++) { + event_type = _type_of_vertex(vh); + if (event_type == Event::DEFAULT) continue; + + while ((q_iter != q_end) && + (res = comp_xy(vh->point(), (*q_iter)->point())) == LARGER) + { + ++q_iter; + } + + if (res == SMALLER || q_iter == q_end) { + event = this->_allocate_event(vh->point(), event_type, + ARR_INTERIOR, ARR_INTERIOR); + // \todo When the boolean set operations are extended to support + // unbounded curves, we will need here a special treatment. + +#ifndef CGAL_ARRANGEMENT_ON_SURFACE_2_H + event->set_finite(); +#endif + + this->m_queue->insert_before(q_iter, event); + vert_map[vh] = event; + } + else if (res == EQUAL) { + // In this case q_iter points to an event already associated with + // the vertex, so we just update the map: + vert_map[vh] = *q_iter; + } + } + } + + // Go over all curves (which are associated with halfedges) and associate + // them with the events we have just created. + std::size_t index = 0; + CurveInputIterator iter; + Halfedge_handle he; + Event* e_left; + Event* e_right; + + for (iter = curves_begin; iter != curves_end; ++iter, index++) { + // Get the events associated with the end-vertices of the current + // halfedge. + he = iter->data().halfedge(); + + CGAL_assertion(vert_map.is_defined(he->source())); + CGAL_assertion(vert_map.is_defined(he->target())); + + if ((Arr_halfedge_direction)he->direction() == ARR_LEFT_TO_RIGHT) { + e_left = vert_map[he->source()]; + e_right = vert_map[he->target()]; + } + else { + e_left = vert_map[he->target()]; + e_right = vert_map[he->source()]; + } + + // Create the subcurve object. + using Subcurve_alloc = decltype(this->m_subCurveAlloc); + std::allocator_traits::construct(this->m_subCurveAlloc, + this->m_subCurves + index, + this->m_masterSubcurve); + (this->m_subCurves + index)->init(*iter); + (this->m_subCurves + index)->set_left_event(e_left); + (this->m_subCurves + index)->set_right_event(e_right); + + e_right->add_curve_to_left(this->m_subCurves + index); + this->_add_curve_to_right(e_left, this->m_subCurves + index); + } + } + /*! Perform the sweep. */ template void sweep(CurveInputIterator curves_begin, CurveInputIterator curves_end, @@ -227,6 +360,18 @@ public: return this->m_visitor->found_intersection(); } + /*! Perform the sweep. */ + template + bool sweep_intercept2(CurveInputIterator curves_begin, CurveInputIterator curves_end, + InputIterator begin, InputIterator end) { + this->m_visitor->before_sweep(); + pre_process2(curves_begin, curves_end, begin, end); + this->_sweep(); + this->_complete_sweep(); + this->m_visitor->after_sweep(); + return this->m_visitor->found_intersection(); + } + private: /*! * Check if the given vertex is an endpoint of an edge we are going diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_merge.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_merge.h index 382afd5ba5f..cf5801a57a2 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_merge.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_merge.h @@ -81,6 +81,24 @@ class Base_intercepted_merge { using Arr_entry = std::pair*>; public: + template + bool operator()(InputIterator begin, InputIterator end) { + CGAL_assertion(begin != end); + + const auto* tr = begin->first->geometry_traits(); + Arrangement_2* arr = new Arrangement_2(tr); + std::vector* verts = new std::vector; + + using Agg_op = Gps_agg_op; + Agg_op agg_op(*arr, *verts, *(arr->traits_adaptor())); + auto res = agg_op.sweep_intercept_arrangements2(begin, end); + + begin->first = arr; + begin->second = verts; + + return res; + } + bool operator()(std::size_t i, std::size_t j, std::size_t jump, std::vector& arr_vec) { if (i == j) return false; @@ -94,7 +112,9 @@ public: for (auto count = i; count <= j; count += jump) { delete (arr_vec[count].first); + arr_vec[count].first = nullptr; delete (arr_vec[count].second); + arr_vec[count].second = nullptr; } arr_vec[i].first = arr; diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_on_surface_base_2.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_on_surface_base_2.h index 60530dfd831..1571480afe2 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_on_surface_base_2.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_on_surface_base_2.h @@ -7,14 +7,16 @@ // $Id$ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // -// Author(s) : Baruch Zukerman -// Ophir Setter -// Guy Zucker +// Author(s) : Baruch Zukerman +// Ophir Setter +// Guy Zucker // Efi Fogel #ifndef CGAL_GPS_ON_SURFACE_BASE_2_H #define CGAL_GPS_ON_SURFACE_BASE_2_H +#include + #include #include @@ -114,8 +116,8 @@ private: using Vertex_const_handle = typename Aos_2::Vertex_const_handle; using Halfedge_around_vertex_const_circulator = typename Aos_2::Halfedge_around_vertex_const_circulator; - - using Arr_entry = std::pair *>; + using Vertices = std::vector; + using Arr_entry = std::pair; using Point_location = typename Arrangement_on_surface_2::Topology_traits::Default_point_location_strategy; @@ -127,8 +129,9 @@ protected: CGAL::Arr_traits_adaptor_2 m_traits_adaptor; bool m_traits_owner; - // the underlying arrangement - Aos_2* m_arr; + + Aos_2* m_arr; // the underlying arrangement + Vertices* m_vertices; // sorted vertices public: // constructs default @@ -500,6 +503,16 @@ public: // intersects a range of polygons template inline bool _do_intersect(InputIterator begin, InputIterator end, std::size_t k) { + // m_vertices = new Vertices; + // sort_vertices(*m_arr, *m_vertices); + // Do_intersect_merge do_intersect_merge; + // auto res = do_intersect_divide_and_conquer2(begin, end, k, do_intersect_merge); + // delete m_vertices; + // if (res) return res; + // remove_redundant_edges(); + // _reset_faces(); + // return is_empty(); + std::vector arr_vec(std::distance(begin, end) + 1); arr_vec[0].first = this->m_arr; std::size_t i = 1; @@ -516,7 +529,13 @@ public: // The resulting arrangement is at index 0 this->m_arr = arr_vec[0].first; delete arr_vec[0].second; - if (res) return res; + if (res) { + for (auto i = 1; i < arr_vec.size(); ++i) { + if (arr_vec[i].first) delete arr_vec[i].first; + if (arr_vec[i].second) delete arr_vec[i].second; + } + return res; + } _remove_redundant_edges(arr_vec[0].first); _reset_faces(arr_vec[0].first); @@ -557,7 +576,13 @@ public: // The resulting arrangement is at index 0 this->m_arr = arr_vec[0].first; delete arr_vec[0].second; - if (res) return res; + if (res) { + for (auto i = 1; i < arr_vec.size(); ++i) { + if (arr_vec[i].first) delete arr_vec[i].first; + if (arr_vec[i].second) delete arr_vec[i].second; + } + return res; + } _remove_redundant_edges(arr_vec[0].first); _reset_faces(arr_vec[0].first); @@ -680,6 +705,7 @@ public: // the result arrangement is at index 0 this->m_arr = arr_vec[0].first; delete arr_vec[0].second; + std::cout << "XXXX 1 no. faces: " << m_arr->number_of_faces() << "\n"; } // intersects a range of polygons with holes @@ -702,6 +728,7 @@ public: // the result arrangement is at index 0 this->m_arr = arr_vec[0].first; delete arr_vec[0].second; + std::cout << "XXXX 2 no. edges: " << m_arr->number_of_edges() << "\n"; } template @@ -1223,6 +1250,17 @@ protected: } } + //! extracts and sorts the vertices + void sort_vertices(Aos_2& arr, Vertices& vertices) { + std::size_t j = 0; + vertices.resize(arr.number_of_vertices()); + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) vertices[j++] = vit; + + // Sort the vector. + Less_vertex_handle comp(m_traits->compare_xy_2_object()); + std::sort(vertices.begin(), vertices.end(), comp); + } + //! Divide & conquer template void _divide_and_conquer(std::size_t lower, std::size_t upper, @@ -1261,10 +1299,10 @@ protected: bool do_intersect_divide_and_conquer(std::size_t lower, std::size_t upper, std::vector& arr_vec, std::size_t k, Merge merge_func) { - static int indent = 0; - std::cout << std::setw(indent) << "" << "D&C [" << lower << "," << upper << "," << k << "]\n"; + // static int indent = 0; + // std::cout << std::setw(indent) << "" << "D&C [" << lower << "," << upper << "," << k << "]\n"; if ((upper - lower) < k) { - std::cout << std::setw(indent) << "" << "Merging [" << lower << "," << upper << "," << 1 << "]\n"; + // std::cout << std::setw(indent) << "" << "Merging [" << lower << "," << upper << "," << 1 << "]\n"; _build_sorted_vertices_vectors(lower, upper, arr_vec); return merge_func(lower, upper, 1, arr_vec); } @@ -1272,40 +1310,57 @@ protected: auto sub_size = ((upper - lower + 1) / k); auto curr_lower = lower; - bool res = false; for (std::size_t i = 0; i < k - 1; ++i, curr_lower += sub_size) { - indent += 2; - res = do_intersect_divide_and_conquer(curr_lower, curr_lower + sub_size-1, arr_vec, k, merge_func); - indent -= 2; - if (res) break; - } - if (res) { - // Clean up the entries that have been created - std::cout << std::setw(indent) << "" << "Cleaning [" << lower + sub_size << "," << curr_lower << "," << sub_size << "]\n"; - for (auto count = lower + sub_size; count <= curr_lower; count += sub_size) { - delete (arr_vec[count].first); - delete (arr_vec[count].second); - } - return true; + // indent += 2; + auto res = do_intersect_divide_and_conquer(curr_lower, curr_lower + sub_size-1, arr_vec, k, merge_func); + // indent -= 2; + if (res) return res; } - indent += 2; - res = do_intersect_divide_and_conquer(curr_lower, upper, arr_vec, k, merge_func); - indent -= 2; - if (res) { - // Clean up the entries that have been created - std::cout << std::setw(indent) << "" << "Cleaning [" << lower + sub_size << "," << curr_lower << "," << sub_size << "]\n"; - for (std::size_t count = lower + sub_size; count <= curr_lower; count += sub_size) { - delete (arr_vec[count].first); - delete (arr_vec[count].second); - } - return true; - } + // indent += 2; + auto res = do_intersect_divide_and_conquer(curr_lower, upper, arr_vec, k, merge_func); + // indent -= 2; + if (res) return res; - std::cout << std::setw(indent) << "" << "Merging [" << lower << "," << curr_lower << "," << sub_size << "]\n"; + // std::cout << std::setw(indent) << "" << "Merging [" << lower << "," << curr_lower << "," << sub_size << "]\n"; return merge_func(lower, curr_lower, sub_size, arr_vec); } + template + bool do_intersect_divide_and_conquer2(InputIterator begin, InputIterator end, std::size_t k, Merge merge) { + std::vector arr_entries; + arr_entries.reserve(k); + arr_entries.resize(1); + arr_entries[0].first = m_arr; + arr_entries[0].second = m_vertices; + std::size_t size = std::distance(begin, end); + auto it = begin; + while (it != end) { + std::size_t num = std::min(size+1, k); + arr_entries.resize(num); + for (std::size_t i = 1; i < num; ++i) { + // process pgn + auto* p_arr = new Aos_2(m_traits); + auto* p_vertices = new Vertices; + ValidationPolicy::is_valid(*it, *m_traits); + arr_entries[i].first = p_arr; + arr_entries[i].second = p_vertices; + _insert(*it++, *p_arr); + sort_vertices(*p_arr, *p_vertices); + } + auto res = merge(arr_entries.begin(), arr_entries.end()); + for (std::size_t i = 1; i < num; ++i) { + delete arr_entries[i].first; + delete arr_entries[i].second; + } + arr_entries.resize(1); + size -= (num-1); + } + m_arr = arr_entries[0].first; + m_vertices = arr_entries[0].second; + arr_entries.clear(); + } + // marks all faces as non-visited void _reset_faces() const { _reset_faces(m_arr); }