diff --git a/AABB_tree/benchmark/AABB_tree/CMakeLists.txt b/AABB_tree/benchmark/AABB_tree/CMakeLists.txt index 21e4c4565ef3..d23d008eaaa7 100644 --- a/AABB_tree/benchmark/AABB_tree/CMakeLists.txt +++ b/AABB_tree/benchmark/AABB_tree/CMakeLists.txt @@ -6,8 +6,10 @@ project(AABB_traits_benchmark) find_package(CGAL REQUIRED) -create_single_source_cgal_program("test.cpp") +create_single_source_cgal_program("old_bench_AABB_tree.cpp") create_single_source_cgal_program("tree_construction.cpp") +create_single_source_cgal_program("knot_generation.cpp") +create_single_source_cgal_program("tree_queries.cpp") # google benchmark find_package(benchmark QUIET) @@ -17,3 +19,11 @@ if(benchmark_FOUND) else() message(STATUS "NOTICE: The benchmark 'tree_creation.cpp' requires the Google benchmark library, and will not be compiled.") endif() + +find_package(TBB QUIET) +include(CGAL_TBB_support) +if(TARGET CGAL::TBB_support) + target_link_libraries(tree_construction PRIVATE CGAL::TBB_support) +else() + message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.") +endif() diff --git a/AABB_tree/benchmark/AABB_tree/knot_generation.cpp b/AABB_tree/benchmark/AABB_tree/knot_generation.cpp new file mode 100644 index 000000000000..aa19db304e44 --- /dev/null +++ b/AABB_tree/benchmark/AABB_tree/knot_generation.cpp @@ -0,0 +1,122 @@ + +#include +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Vector = Kernel::Vector_3; +using Triangle = std::array; + +int main(int argc, char* argv[]) +{ + const std::string filename = argc > 1 ? argv[1] : "knot.off"; + + const int p = argc > 2 ? std::atoi(argv[2]) : 1; // Nb of major turns + const int q = argc > 3 ? std::atoi(argv[3]) : 6; // Nb of knot turns + + const double R = argc > 4 ? std::atof(argv[4]) : 2.0; // major radius + const double r = argc > 5 ? std::atof(argv[5]) : 0.9; // knot radius + const double tube_radius = argc > 6 ? std::atof(argv[6]) : 0.4; + + const int tubular_segments = argc > 7 ? std::atoi(argv[7]) : 120; + const int radial_segments = argc > 8 ? std::atoi(argv[8]) : 60; + + const double exc = argc > 9 ? std::atof(argv[9]) : 1; // excentricity + + std::vector vertices; + std::vector faces; + + // Generate knot polyline + auto knot = [&](double t){ + double cq = std::cos(q * t); + double sq = std::sin(q * t); + double factor = R + r * cq; + return Point(factor * std::cos(p * t), + factor * std::sin(p * t), + r * sq); + }; + auto knot_derivative = [&](double t){ + double cq = std::cos(q * t); + double sq = std::sin(q * t); + double cp = std::cos(p * t); + double sp = std::sin(p * t); + + double factor = R + r*cq; + double dfactor = -r*q*sq; + + return Vector(dfactor*cp - factor*p*sp, + dfactor*sp + factor*p*cp, + r*q*cq); + }; + + const double dt = 2.0 * M_PI / tubular_segments; + const double dr = 2.0 * M_PI / radial_segments; + + // A normal to the knot axis + Vector N(1,0,0); + for(int i=0; isq){ + min = sq; + offset = j; + } + } + for(int j=0; j #include #include +#include #include #include @@ -157,17 +158,16 @@ int main(int argc, const char** argv) std::string path = (argc>2)?argv[2]: CGAL::data_file_path("meshes/handle.off"); std::cout<< k<<" steps in "<(end - start).count() << " ms." << std::endl; - start = std::chrono::steady_clock::now(); + << t.time() << " ms." << std::endl; + t.stop(); t.reset(); t.start(); test_no_collision(k, path,nb_inter, nb_no_inter, nb_include); - end = std::chrono::steady_clock::now(); std::cout<<"With transform_traits: "<(end - start).count() << " ms." << std::endl; + << t.time() << " ms." << std::endl; return 0; } diff --git a/AABB_tree/benchmark/AABB_tree/tree_construction.cpp b/AABB_tree/benchmark/AABB_tree/tree_construction.cpp index 3ce2948b0506..2ab8d413a4f3 100644 --- a/AABB_tree/benchmark/AABB_tree/tree_construction.cpp +++ b/AABB_tree/benchmark/AABB_tree/tree_construction.cpp @@ -1,12 +1,14 @@ #include #include #include -#include +#include #include #include #include +#include #include +#include #include #include @@ -61,30 +63,52 @@ struct Compute_bbox { BBM bbm; }; -template +template void run(std::string input) { typedef typename K::Point_3 Point_3; typedef CGAL::Surface_mesh Mesh; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; Mesh tm; - std::ifstream(input) >> tm; + CGAL::IO::read_polygon_mesh(input, tm); { Tree tree(faces(tm).begin(), faces(tm).end(), tm); - CGAL::Timer time; + CGAL::Real_timer time; time.start(); - tree.build(); - time.stop(); + tree.template build(); std::cout << " build() time: " << time.time() << "\n"; + tree.template accelerate_distance_queries(); + std::cout << " build() + build kd-tree time: " << time.time() << "\n"; + } + + { + typedef CGAL::dynamic_face_property_t Face_bbox_tag; + typedef typename boost::property_map::type BboxMap; + typedef CGAL::AABB_traits_3 BTraits; + typedef CGAL::AABB_tree BTree; + + auto bb = get( Face_bbox_tag(), tm); + for(auto fd : faces(tm)) + put(bb, fd, CGAL::Polygon_mesh_processing::face_bbox(fd, tm)); + + BTraits traits(bb); + BTree tree(traits); + tree.insert(faces(tm).begin(), faces(tm).end(), tm); + CGAL::Real_timer time; + time.start(); + tree.template build(); + std::cout << " build() with reference bbox time: " << time.time() << "\n"; + tree.template accelerate_distance_queries(); + std::cout << " build() with reference bbox + build kd-tree time: " << time.time() << "\n"; } { Tree tree(faces(tm).begin(), faces(tm).end(), tm); - CGAL::Timer time; + CGAL::Real_timer time; time.start(); typedef CGAL::Pointer_property_map::type BBM; @@ -108,17 +132,26 @@ void run(std::string input) Compute_bbox compute_bbox(bbm); Split_primitives split_primitives(rpm); - tree.custom_build(compute_bbox, split_primitives); - time.stop(); + tree.template custom_build(compute_bbox, split_primitives); std::cout << " custom_build() time: " << time.time() << "\n"; + tree.template accelerate_distance_queries(); + std::cout << " custom_build() + build kd-tree time: " << time.time() << "\n"; } } int main(int, char** argv) { + std::cout << "Build with Cartesian\n"; + run>(argv[1]); std::cout << "Build with Epick\n"; - run(argv[1]); + run(argv[1]); std::cout << "Build with Epeck\n"; - run(argv[1]); + run(argv[1]); + std::cout << "Build with Cartesian (Sequential) \n"; + run>(argv[1]); + std::cout << "Build with Epick (Sequential) \n"; + run(argv[1]); + std::cout << "Build with Epeck (Sequential) \n"; + run(argv[1]); return EXIT_SUCCESS; } diff --git a/AABB_tree/benchmark/AABB_tree/tree_creation.cpp b/AABB_tree/benchmark/AABB_tree/tree_creation.cpp index c5bd5396d513..fe074cb5e3cf 100644 --- a/AABB_tree/benchmark/AABB_tree/tree_creation.cpp +++ b/AABB_tree/benchmark/AABB_tree/tree_creation.cpp @@ -55,7 +55,7 @@ BENCHMARK(BM_Intersections); int main(int argc, char** argv) { std::string default_file = CGAL::data_file_path("meshes/handle.off"); - std::strint filename = argc > 2? argv[2] : default_file; + std::string filename = argc > 2? argv[2] : default_file; { std::ifstream input(filename); diff --git a/AABB_tree/benchmark/AABB_tree/tree_queries.cpp b/AABB_tree/benchmark/AABB_tree/tree_queries.cpp new file mode 100644 index 000000000000..b27e44b714f3 --- /dev/null +++ b/AABB_tree/benchmark/AABB_tree/tree_queries.cpp @@ -0,0 +1,228 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +using K = CGAL::Simple_cartesian; + +using Point = K::Point_3; +using Vector = K::Vector_3; +using Segment = K::Segment_3; +using Ray = K::Ray_3; +using Line = K::Line_3; +using Plane = K::Plane_3; + +using Mesh = CGAL::Surface_mesh; + +using Primitive = CGAL::AABB_face_graph_triangle_primitive; +using Traits = CGAL::AABB_traits_3; +using Tree = CGAL::AABB_tree; + +CGAL::Random rng; + +template +typename Kernel::Point_3 random_point(const CGAL::Bbox_3& bb){ + return typename Kernel::Point_3(rng.get_double(bb.xmin(), bb.xmax()), + rng.get_double(bb.ymin(), bb.ymax()), + rng.get_double(bb.zmin(), bb.zmax())); +} + +Vector random_vector(){ + Vector v; + do{ + v = Vector(rng.get_double(-1., 1.), + rng.get_double(-1., 1.), + rng.get_double(-1., 1.)); + } while(v.squared_length() < 1e-12); + return v; +} + +template +double benchmark(QueryGenerator gen, + Function f, + std::size_t nb_queries = 500000) +{ + CGAL::Real_timer timer; + timer.start(); + for(std::size_t i=0; i ids; \ + tree.all_intersected_primitives(q, std::back_inserter(ids)); \ + }, N)); \ + \ + print("all_intersections", \ + benchmark(Generator, \ + [&](const QueryType& q) \ + { \ + using Intersection = \ + typename Tree::template Intersection_and_primitive_id::Type; \ + \ + std::vector out; \ + tree.all_intersections(q, std::back_inserter(out)); \ + }, N)); \ + } + + BENCHMARK(Segment, [&](){ return Segment(random_point(bb), random_point(bb)); }); + BENCHMARK(Ray, [&](){ return Ray(random_point(bb), random_vector()); }); + BENCHMARK(Line, [&](){ return Line(random_point(bb), random_point(bb)); }); + BENCHMARK(Plane, [&](){ return Plane(random_point(bb), random_vector()); }); +#undef BENCHMARK +} + +template +void benchmark_kernel(const std::string &filename, const std::string &kernel_name) +{ + using Segment = typename Kernel::Segment_3; + + using Mesh = CGAL::Surface_mesh; + using Primitive = CGAL::AABB_face_graph_triangle_primitive; + using Traits = CGAL::AABB_traits_3; + using Tree = CGAL::AABB_tree; + const int N = 100000; + + Mesh mesh; + CGAL::IO::read_polygon_mesh(filename, mesh); + Tree tree(faces(mesh).first, faces(mesh).second, mesh); + tree.build(); + CGAL::Bbox_3 bb = PMP::bbox(mesh); + + CGAL::Real_timer t; + t.start(); + for(std::size_t i=0;i(bb), random_point(bb)); + using Intersection = typename Tree::template Intersection_and_primitive_id::Type; + std::vector out; + tree.all_intersections(s, std::back_inserter(out)); + } + t.stop(); + + std::cout << std::setw(40) + << kernel_name + << N/t.time() + << '\n'; +} + +void benchmark_distances(const Mesh& mesh, std::size_t N = 300000) +{ + Tree tree(faces(mesh).first, faces(mesh).second, mesh); + tree.build(); + tree.accelerate_distance_queries(); + + const CGAL::Bbox_3 bb = PMP::bbox(mesh); + + std::vector queries; + queries.reserve(N); + for(std::size_t i=0; i>(filename, "Simple_cartesian"); + // benchmark_kernel>(filename, "Simple_cartesian"); + // benchmark_kernel>(filename, "Cartesian"); + // benchmark_kernel>(filename, "Cartesian>"); + // benchmark_kernel(filename, "Epick"); + benchmark_distances(mesh); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/AABB_tree/doc/AABB_tree/PackageDescription.txt b/AABB_tree/doc/AABB_tree/PackageDescription.txt index e16cfc867d40..3f6361165814 100644 --- a/AABB_tree/doc/AABB_tree/PackageDescription.txt +++ b/AABB_tree/doc/AABB_tree/PackageDescription.txt @@ -50,4 +50,8 @@ - `CGAL::AABB_primitive` - `CGAL::AABB_halfedge_graph_segment_primitive` - `CGAL::AABB_face_graph_triangle_primitive` + +\cgalCRPSection{Functions} +- `CGAL::AABB_trees::do_intersect()` +- `CGAL::AABB_trees::all_pairs_of_intersecting_primitives()` */ diff --git a/AABB_tree/doc/AABB_tree/aabb_tree.txt b/AABB_tree/doc/AABB_tree/aabb_tree.txt index fbdc0b890c32..90ecc33fda5f 100644 --- a/AABB_tree/doc/AABB_tree/aabb_tree.txt +++ b/AABB_tree/doc/AABB_tree/aabb_tree.txt @@ -23,10 +23,9 @@ triangles, or plane objects (planes, triangles) against sets of segments. An example of a distance query consists of finding the closest point from a point query to a set of triangles. -Note that this component is not suited to the problem of finding all -intersecting pairs of objects. We refer to the component -\ref chapterBoxIntersection "Intersecting Sequences of dD Iso-oriented Boxes" -which can find all intersecting pairs of iso-oriented boxes. +Similarly to the package \ref PkgBoxIntersectionD, +this component might also be used for finding all intersecting pairs of objects, +and can even achieve a better performance when running on several threads. The AABB tree data structure takes as input an iterator range of geometric data, which is then converted into primitives. From these @@ -109,6 +108,18 @@ For example if one is using `CGAL::AABB_traits` with a Kernel from \cgal, having degenerate triangles or segments in the AABB-tree will result in an undefined behavior or a crash. +\section two_aabb_tree_interface Operations between Two AABB Trees + +Given two AABB trees, the function `CGAL::AABB_trees::do_intersect()` determines whether any +primitive of the first tree intersects a primitive of the second tree. +To enumerate all intersecting pairs of primitives, use `CGAL::AABB_trees::all_pairs_of_intersecting_primitives()`. + +These functions provide an alternative to `CGAL::box_intersection_d()`. In practice, `CGAL::AABB_trees::all_pairs_of_intersecting_primitives()` generally provides better performance, +particularly when using a parallel execution tag (see \ref aabb_tree_perf). + +Note that compared to `CGAL::box_intersection_d()`, the tree of primitives is actually constructed. It becomes even more interesting in term of runtime +if several calls to the aforementioned intersection functions are done with the same tree. + \section aabb_tree_examples Examples \subsection aabb_tree_examples_1 Tree of Triangles, for Intersection and Distance Queries @@ -200,22 +211,29 @@ to custom points. In \ref AABB_tree/AABB_custom_triangle_soup_example.cpp "AABB_ triangles are stored into a single array as to form a triangle soup. The primitive internally uses a `boost::iterator_adaptor` as to provide the three functions `AABBPrimitive::id()`, `AABBPrimitive::datum()`, -and `AABBPrimitive::reference_point()`) required by the primitive concept. In +and `AABBPrimitive::reference_point()` required by the primitive concept. In \ref AABB_tree/AABB_custom_indexed_triangle_set_example.cpp "AABB_custom_indexed_triangle_set_example.cpp" the input is an indexed triangle set stored through two arrays: one array of points and one array of indices which refer to the point array. Here also the primitive internally uses a `boost::iterator_adaptor`. +\subsection aabb_tree_examples_9 Intersection of Two Trees + +In the following example, we compute all the intersections between two triangle soups both representing a tetrahedron. The triangle soups +are stored as a vector of `Triangle_3`. We first compute if the tetrahedra do intersect and then we +compute the indices of the intersecting triangles. + +\cgalExample{AABB_tree/AABB_intersection_example.cpp} + \section aabb_tree_perf Performances We provide some performance numbers for the case where the AABB tree contains a set of polyhedron triangle facets. We measure the tree construction time, the memory occupancy and the number of queries per second for a variety of intersection and distance queries. The machine -used is a PC running Windows XP64 with an Intel CPU Core2 Extreme -clocked at 3.06 GHz with 4GB of RAM. By default, the kernel used is -`Simple_cartesian` (the fastest in our experiments). The -program has been compiled with Visual C++ 2005 compiler with the O2 +used is a PC running Linux Mint with an Intel CPU Core Ultra 7 155H with 16 cores and 32GB of RAM. +By default, the kernel used is `Simple_cartesian` (the fastest in our experiments). The +program has been compiled with the compiler gcc 13.3 2023 with the `O3` option which maximizes speed. \subsection aabb_tree_perf_cons Construction @@ -223,22 +241,20 @@ option which maximizes speed. The surface triangle mesh chosen for benchmarking the tree construction is the knot model (14,400 triangles) depicted by \cgalFigureRef{figAABB-tree-bench}. We measure the tree construction time (both -AABB tree alone and AABB tree with internal KD-tree) for this model as -well as for three denser versions subdivided through the Loop -subdivision scheme which multiplies the number of triangles by four. +AABB tree alone and AABB tree with internal KD-tree) for four denser versions of the knot model. -| Number of triangles | Construction (in ms) | Construction with internal KD-tree (in ms) | -| ----: | ----: | ----: | - 14,400 | 156 | 157 | - 57,600 | 328 | 328 | - 230,400 | 1,141 | 1,437 | - 921,600 | 4,813 | 5,953 | +| Number of triangles | Construction (in ms) (Parallel) | (Sequential) | Construction with internal KD-tree (in ms) (Parallel) | (Sequential) | +| ----: | ----: | ----: | ----: | ----: | + 230,400 | 20 | 36 | 48 | 66 | + 921,600 | 72 | 187 | 155 | 306 | + 3,686,400 | 292 | 816 | 729 | 1426 | + 14,745,600 | 1,425 | 4,196 | 3,028 | 7,016 | \subsection aabb_tree_perf_mem Memory When using the polyhedron triangle facet primitive (defined in -`AABB_face_graph_triangle_primitive.h`) the AABB tree occupies +`CGAL/AABB_face_graph_triangle_primitive.h`) the AABB tree occupies approximately 61 bytes per primitive (without constructing the internal KD-tree). It increases to approximately 150 bytes per primitive when constructing the internal KD-tree with one reference @@ -269,7 +285,7 @@ takes an iterator range as input. \subsection aabb_tree_perf_inter Intersections The following table measures the number of intersection queries per -second on the 14,400 triangle version of the knot mesh model for ray, +second on one core on the 14,400 triangle version of the knot mesh model for ray, line, segment and plane queries. Each ray query is generated by choosing a random source point within the mesh bounding box and a random vector. A line or segment query is generated by choosing two @@ -282,45 +298,38 @@ the intersection functions which enumerate all intersections. | Function | Segment | Ray | Line | Plane | | :---- | ----: | ----: | -: | -: | -| AABB_tree::do_intersect() | 187,868 | 185,649 | 206,096 | 377,969 | -| AABB_tree::any_intersected_primitive() | 190,684 | 190,027 | 208,941 | 360,337 | -| AABB_tree::any_intersection() | 147,468 | 143,230 | 148,235 | 229,336 | -| AABB_tree::number_of_intersected_primitives() | 64,389 | 52,943 | 54,559 | 7,906 | -| AABB_tree::all_intersected_primitives() | 65,553 | 54,838 | 53,183 | 5,693 | -| AABB_tree::all_intersections() | 46,507 | 38,471 | 36,374 | 2,644 | - +| AABB_tree::do_intersect() | 513,505 | 788,246 | 773,947 | 1,863,721 | +| AABB_tree::any_intersected_primitive() | 521,651 | 852,929 | 831,919 | 2,125,582 | +| AABB_tree::any_intersection() | 504,836 | 835,164 | 821,369 | 1,730,017 | +| AABB_tree::number_of_intersected_primitives() | 186,102 | 397,069 | 210,052 | 49,941 | +| AABB_tree::all_intersected_primitives() | 178,769 | 397,458 | 192,482 | 49,324 | +| AABB_tree::all_intersections() | 178,536 | 352,541 | 193,700 | 21,745 | Curve of \cgalFigureRef{figAABB-tree-bench} plots the number of queries per second (here the `AABB_tree::all_intersections()` function with random segment queries) against the number of input triangles for the knot triangle surface mesh. - -\cgalFigureBegin{figAABB-tree-bench,bench.png} -Number of queries per second against number of triangles for the knot model with 14K (shown), 57K, 230K and 921K triangles. We call the `all_intersections()` function with segment queries randomly chosen within the bounding box. +\cgalFigureBegin{figAABB-tree-bench,bench.png, knot.png} +Number of queries per second against number of triangles for the knot model with 14K (shown) to 15M triangles. We call the `all_intersections()` function with segment queries randomly chosen within the bounding box. \cgalFigureEnd The following table measures the number of `AABB_tree::all_intersections()` queries per second against several kernels. We use the 14,400 triangle -version of the knot mesh model for random segment queries. Note how -the `Simple_cartesian` kernel is substantially faster than the -`Cartesian` kernel. +version of the knot mesh model for random segment queries. | Kernel | Queries/s (all_intersections() with segment queries)| | :---- | ----: | -|`Simple_cartesian` | 46,507 | -|`Simple_cartesian` | 43,187 | -|`Cartesian` | 5,335 | -|`Cartesian` | 5,522 | -|`Exact_predicates_inexact_constructions_kernel` | 18,411 | - - +|`Simple_cartesian` | 201,618 | +|`Simple_cartesian` | 193,414 | +|`Cartesian` | 164,784 | +|`Cartesian` | 159,686 | +|`Exact_predicates_inexact_constructions_kernel` | 119,590 | \subsection aabb_tree_perf_dist Distances The surface triangle mesh chosen for benchmarking distances is again -the knot model in four increasing resolutions obtained through Loop -subdivision. In the following table we first measure the tree +the knot model in six increasing resolutions. In the following table we first measure the tree construction time (which includes the construction of the internal KD-tree data structure used to accelerate the distance queries by up to one order of magnitude in our experiments). We then measure the @@ -329,12 +338,33 @@ number of queries per second for the three types distance queries `AABB_tree::closest_point_and_primitive()`) from point queries randomly chosen inside the bounding box. -| Nb triangles | Construction (ms) | Closest_point() | Squared_distance() | Closest_point_and_primitive() | -| ----: | ----: | ----: | ----: | -: | -| 14,400 | 157.000 | 45,132 | 45,626 | 45,770 | -| 57,600 | 328.000 | 21,589 | 21,312 | 21,137 | -| 230,400 | 1.437 | 11,063 | 10,962 | 11,086 | -| 921,600 | 5.953 | 5,636 | 5,722 | 5,703 | +| Nb triangles | Closest_point() | Squared_distance() | Closest_point_and_primitive() | +| ----: | ----: | ----: | -: | +| 14,400 | 196,043 | 196,356 | 196,708 | +| 57,600 | 123,388 | 123,844 | 123,567 | +| 230,400 | 54,480 | 53,235 | 54,329 | +| 921,600 | 25,761 | 26,890 | 25,386 | +| 3,686,400 | 13,696 | 13,389 | 13,440 | +| 14,745,600 | 7,158 | 6,913| 7,010 | + +\subsection aabb_two_trees_perf Intersection of Two AABB Trees + +We test the intersection of two AABB trees using 3 models: two common models of mesh processing (Iphigenia and Nefertiti), and twisted knot to maximize the number of intersection. For each model, +we test their intersection with a translated version of themselves (Note: We do not exploit the fact that it is the same model, the tree is built twice). +We compare here `CGAL::AABB_trees::all_pairs_of_intersecting_primitives()` and `CGAL::box_intersection_d()`. + + + +\cgalFigureBegin{figAABB-tree-model,iphigenia.png, nefertiti.png, twisted_knot.png} +The three input models used to illustrate AABB trees intersection performance. Iphigenia (left), Nefertiti (center), Twisted knot (right). +\cgalFigureEnd + +| Models | Nb Triangles | Nb Intersections | **AABB** | Tree (ms) | | | **Box** d || **AABB** | Tree Seq. | (ms) | | **Box** d Seq. | +| ----: | ----: | ----: | ----: | ----: | ----: | ----: | ----: || ----: | ----: | ----: | ----: | ----: | +| | | | Build | Do intersect | All pairs | All pairs + Build | || Build | Do intersect | All pairs | All pairs + Build | | +| Iphigenia × Iphigenia Tr. | 703,512 × 703,512 | 47,677 | 91 | <0.1 | 37 | 128 | 424 || 318 | <0.1 | 169 | 488 | 868 | +| Nefertiti × Nefertiti Tr. | 2,018,232 × 2,018,232 | 14,246 | 261 | <0.1 | 10 | 271 | 1,006 ||1,087 | <0.1 | 46 | 1,134 | 2,348 | +| Twisted knot × Twisted knot Rot. | 600,000 × 600,000 | 240,144 | 154 | <0.1 | 57 | 211 | 850 || 541 | <0.1 | 440 | 981 | 2,993 | \subsection aabb_tree_perf_summary Summary @@ -450,6 +480,8 @@ thread-safe queries, introduction of shared data stored in the traits for lighter primitive types, ... In 2024, the package was made compatible with 2D and 3D primitives by Andreas Fabri, Sébastien Loriot, and Sven Oesau. +In 2026, the package was extended to support the intersection of two AABB trees. +This work also introduced performance optimizations and parallelization, contributed by Leo Valque. */ diff --git a/AABB_tree/doc/AABB_tree/examples.txt b/AABB_tree/doc/AABB_tree/examples.txt index d986357796cd..7add888c9321 100644 --- a/AABB_tree/doc/AABB_tree/examples.txt +++ b/AABB_tree/doc/AABB_tree/examples.txt @@ -16,4 +16,5 @@ \example AABB_tree/AABB_triangle_3_example.cpp \example AABB_tree/AABB_halfedge_graph_edge_example.cpp \example AABB_tree/AABB_face_graph_triangle_example.cpp +\example AABB_tree/AABB_intersection_example.cpp */ diff --git a/AABB_tree/doc/AABB_tree/fig/bench.png b/AABB_tree/doc/AABB_tree/fig/bench.png index b64ca29deef7..5d2fa041d20e 100644 Binary files a/AABB_tree/doc/AABB_tree/fig/bench.png and b/AABB_tree/doc/AABB_tree/fig/bench.png differ diff --git a/AABB_tree/doc/AABB_tree/fig/iphigenia.png b/AABB_tree/doc/AABB_tree/fig/iphigenia.png new file mode 100644 index 000000000000..589524a1ca84 Binary files /dev/null and b/AABB_tree/doc/AABB_tree/fig/iphigenia.png differ diff --git a/AABB_tree/doc/AABB_tree/fig/knot.png b/AABB_tree/doc/AABB_tree/fig/knot.png new file mode 100644 index 000000000000..215a63ff7f4b Binary files /dev/null and b/AABB_tree/doc/AABB_tree/fig/knot.png differ diff --git a/AABB_tree/doc/AABB_tree/fig/nefertiti.png b/AABB_tree/doc/AABB_tree/fig/nefertiti.png new file mode 100644 index 000000000000..430d7362df8f Binary files /dev/null and b/AABB_tree/doc/AABB_tree/fig/nefertiti.png differ diff --git a/AABB_tree/doc/AABB_tree/fig/twisted_knot.png b/AABB_tree/doc/AABB_tree/fig/twisted_knot.png new file mode 100644 index 000000000000..541dd108eb4b Binary files /dev/null and b/AABB_tree/doc/AABB_tree/fig/twisted_knot.png differ diff --git a/AABB_tree/examples/AABB_tree/AABB_intersection_example.cpp b/AABB_tree/examples/AABB_tree/AABB_intersection_example.cpp new file mode 100644 index 000000000000..b31b3a2fdcd8 --- /dev/null +++ b/AABB_tree/examples/AABB_tree/AABB_intersection_example.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include +#include +#include + +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + +typedef K::Point_3 Point; +typedef K::Triangle_3 Triangle; + +typedef std::vector::iterator Iterator; +typedef CGAL::AABB_triangle_primitive_3 Primitive; +typedef CGAL::AABB_traits_3 AABB_triangle_traits; +typedef CGAL::AABB_tree Tree; + +int main() +{ + Point a(1.0, 0.0, 0.0); + Point b(0.0, 1.0, 0.0); + Point c(0.0, 0.0, 1.0); + Point d(0.0, 0.0, 0.0); + + Point e(1.2, 0.2, 0.2); + Point f(0.2, 1.2, 0.2); + Point g(0.2, 0.2, 1.2); + Point h(0.2, 0.2, 0.2); + + std::vector tetra1; + tetra1.push_back(Triangle(a,b,c)); + tetra1.push_back(Triangle(a,b,d)); + tetra1.push_back(Triangle(a,d,c)); + tetra1.push_back(Triangle(b,c,d)); + + std::vector tetra2; + tetra2.push_back(Triangle(e,f,g)); + tetra2.push_back(Triangle(e,f,h)); + tetra2.push_back(Triangle(e,h,g)); + tetra2.push_back(Triangle(f,g,h)); + + // constructs AABB tree + Tree tree1(tetra1.begin(),tetra1.end()); + Tree tree2(tetra2.begin(),tetra2.end()); + + // do intersect + std::cout << "Tetrahedra do intersect: " << CGAL::AABB_trees::do_intersect(tree1, tree2) << std::endl; + + std::vector< std::pair > intersections; + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(intersections)); + + for(auto [id1, id2]: intersections) + std::cout << "ids: " << std::distance(tetra1.begin(), id1) << " " << std::distance(tetra2.begin(), id2) << std::endl; + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_3.h b/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_3.h new file mode 100644 index 000000000000..f2b7616f1a22 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_3.h @@ -0,0 +1,166 @@ +// Copyright (c) 2026 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Leo Valque +// + + +#ifndef CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_3_H_ +#define CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_3_H_ + +#include + +#include +#include +#include + +namespace CGAL { + +namespace internal { + +template +struct Triangle_3_from_triangle_soup_property_map +{ + using key_type = std::size_t; + using value_type = typename GeomTraits::Triangle_3; + using reference = value_type; + + using category = boost::readable_property_map_tag; + using Self = Triangle_3_from_triangle_soup_property_map; + + Triangle_3_from_triangle_soup_property_map(){} + template , int> = 0> + Triangle_3_from_triangle_soup_property_map(const PointRange &pts_, const FaceRange &triangles_) : pts(&pts_), triangles(&triangles_), pmap(std::make_optional(PointMap())){} + Triangle_3_from_triangle_soup_property_map(const PointRange &pts_, const FaceRange &triangles_, PointMap pmap) : pts(&pts_), triangles(&triangles_), pmap(std::make_optional(pmap)) {} + + inline friend value_type + get(const Self &s, const key_type &i) + { + return GeomTraits().construct_triangle_3_object()(get(*s.pmap, (*s.pts)[(*s.triangles)[i][0]]), + get(*s.pmap, (*s.pts)[(*s.triangles)[i][1]]), + get(*s.pmap, (*s.pts)[(*s.triangles)[i][2]])); + } + + const PointRange *pts; + const FaceRange *triangles; + std::optional pmap; +}; + +template +struct Reference_point_from_triangle_soup_property_map +{ + using key_type = std::size_t; + using value_type = typename GeomTraits::Point_3; + using reference = value_type; + + using category = boost::readable_property_map_tag; + using Self = Reference_point_from_triangle_soup_property_map; + + Reference_point_from_triangle_soup_property_map(){} + template , int> = 0> + Reference_point_from_triangle_soup_property_map(const PointRange &pts_, const FaceRange &triangles_) : pts(&pts_), triangles(&triangles_), pmap(std::make_optional(PointMap())){} + Reference_point_from_triangle_soup_property_map(const PointRange &pts_, const FaceRange &triangles_, PointMap pmap) : pts(&pts_), triangles(&triangles_), pmap(std::make_optional(pmap)) {} + + inline friend value_type + get(const Self &s, const key_type &i) + { + return get(*s.pmap, (*s.pts)[(*s.triangles)[i][0]]); + } + + const PointRange *pts; + const FaceRange *triangles; + std::optional pmap; +}; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as an identifier an index into a range of triplets of indices as its `value_type`. + * The range of triplets and the range of points must not be invalidated + * while the AABB tree holding this primitive is in use. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_3` and `Triangle_3`. + * It also provides the functor `Construct_triangle_3` that has an operator taking three `Point_3` as + * parameters and returns a `Triangle_3` + * \tparam PointRange is a model of `RandomAccessRange`. Its value type needs to be compatible to `PointMap` or `Point_3` in the default case. + * \tparam FaceRange is a model of `RandomAccessRange`. Its value type needs to a `RandomAccessRange` of size 3 with value_type begin `std::size_t`. + * \tparam CacheDatum is either `CGAL::Tag_true` or `CGAL::Tag_false`. In the former case, + * the datum is stored in the primitive, while in the latter it is + * constructed on the fly to reduce the memory footprint. + * The default is `CGAL::Tag_false`, that is the datum is not stored. + * \tparam PointMap is a model of `ReadablePropertyMap` with its key type being the value type of `PointRange` and the value type being a `Point_3`. + * The default is \link Identity_property_map `CGAL::Identity_property_map`\endlink. + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_2` + * \sa `AABB_triangle_primitive_2` + * \sa `AABB_triangle_primitive_3` + */ +template < class GeomTraits, + class PointRange, + class FaceRange, + class CacheDatum = Tag_false, + class PointMap = Identity_property_map > +class AABB_indexed_triangle_primitive_3 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< std::size_t, + internal::Triangle_3_from_triangle_soup_property_map, + internal::Reference_point_from_triangle_soup_property_map, + Tag_true, + CacheDatum > +#endif +{ + using Base = AABB_primitive< std::size_t, + internal::Triangle_3_from_triangle_soup_property_map, + internal::Reference_point_from_triangle_soup_property_map, + Tag_true, + CacheDatum >; + using Triangle_property_map = internal::Triangle_3_from_triangle_soup_property_map; + using Point_property_map = internal::Reference_point_from_triangle_soup_property_map; + using Face_iterator = typename FaceRange::iterator; + using Face_const_iterator = typename FaceRange::const_iterator; +public: + ///constructor from an iterator + template , int> = 0> + AABB_indexed_triangle_primitive_3(Face_const_iterator it, const PointRange&, const FaceRange& triangles) : Base(std::distance(triangles.begin(), it)){} + template, int> = 0> + AABB_indexed_triangle_primitive_3(Face_iterator it, const PointRange&, const FaceRange& triangles) : Base(std::size_t(std::distance(triangles.begin(), Face_const_iterator(it)))){} + template, int> = 0> + AABB_indexed_triangle_primitive_3(IndexIterator it, const PointRange&, const FaceRange&) : Base(it){} + template , int> = 0> + AABB_indexed_triangle_primitive_3(std::size_t i, const PointRange&, const FaceRange&) : Base(i){} + + AABB_indexed_triangle_primitive_3(Face_const_iterator it, const PointRange&, const FaceRange& triangles, PointMap) : Base(std::distance(triangles.begin(), it)){} + AABB_indexed_triangle_primitive_3(Face_iterator it, const PointRange&, const FaceRange& triangles, PointMap) : Base(std::size_t(std::distance(triangles.begin(), Face_const_iterator(it)))){} + template + AABB_indexed_triangle_primitive_3(IndexIterator it, const PointRange&, const FaceRange&, PointMap) : Base(it){} + AABB_indexed_triangle_primitive_3(std::size_t i, const PointRange&, const FaceRange&, PointMap) : Base(i){} + + /// \internal + static typename Base::Shared_data construct_shared_data(const PointRange &pts, const FaceRange &triangles, PointMap pmap) { + return std::make_pair( + Triangle_property_map(pts, triangles, pmap), + Point_property_map(pts, triangles, pmap)); + } + template , int> = 0> + static typename Base::Shared_data construct_shared_data(const PointRange &pts, const FaceRange &triangles) { + return std::make_pair( + Triangle_property_map(pts, triangles), + Point_property_map(pts, triangles)); + } +}; + +} // end namespace CGAL + +#endif // CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_3_H_ diff --git a/AABB_tree/include/CGAL/AABB_segment_primitive_2.h b/AABB_tree/include/CGAL/AABB_segment_primitive_2.h index 71e76f72cdee..17a46cb7db74 100644 --- a/AABB_tree/include/CGAL/AABB_segment_primitive_2.h +++ b/AABB_tree/include/CGAL/AABB_segment_primitive_2.h @@ -30,12 +30,11 @@ namespace internal { //classical typedefs typedef Iterator key_type; typedef typename GeomTraits::Point_2 value_type; - // typedef decltype( - // std::declval()( - // std::declval())) reference; - typedef decltype( - typename GeomTraits::Construct_source_2()( - *std::declval())) reference; + using reference = std::conditional_t< + std::is_reference_v())>, + const typename GeomTraits::Point_2&, + typename GeomTraits::Point_2 + >; typedef boost::readable_property_map_tag category; typedef Source_of_segment_2_iterator_property_map Self; diff --git a/AABB_tree/include/CGAL/AABB_tree.h b/AABB_tree/include/CGAL/AABB_tree.h index 6382bef9976f..688f069f972d 100644 --- a/AABB_tree/include/CGAL/AABB_tree.h +++ b/AABB_tree/include/CGAL/AABB_tree.h @@ -32,6 +32,10 @@ #include #endif +#ifdef CGAL_LINKED_WITH_TBB +#include +#endif + /// \file AABB_tree.h namespace CGAL { @@ -139,23 +143,35 @@ namespace CGAL { /// after one or more calls to `insert()`. /// This procedure is called implicitly at the first call to a query member function. /// An explicit call to `build()` must be made to ensure that the next call to - /// a query function will not trigger the construction of the data structure. + /// a query function will not trigger the construction of the data structure and/or + /// allow the tree to be built in parallel if supported. + /// `ConcurrencyTag` enables sequential versus parallel algorithm. Possible values are Sequential_tag, Parallel_tag, and Parallel_if_available_tag. /// A call to \link AABBTraits::set_shared_data `AABBTraits::set_shared_data(t...)`\endlink - // is made using the internally stored traits. + /// is made using the internally stored traits. /// This procedure has a complexity of \cgalBigO{n log(n)}, where \f$n\f$ is the number of /// primitives of the tree. - template + template void build(T&& ...); #ifndef DOXYGEN_RUNNING + template void build(); /// triggers the (re)construction of the tree similarly to a call to `build()` /// but the traits functors `Compute_bbox` and `Split_primitives` are ignored /// and `compute_bbox` and `split_primitives` are used instead. - template + template void custom_build(const ComputeBbox& compute_bbox, const SplitPrimitives& split_primitives); #endif + /// @private + template + void partial_build(const size_t cutoff); + + /// @private + template + void custom_partial_build(const size_t cutoff, + const ComputeBbox& compute_bbox, + const SplitPrimitives& split_primitives); ///@} /// \name Operations @@ -163,7 +179,7 @@ namespace CGAL { /// is equivalent to calling `clear()`, \link insert(InputIterator, InputIterator, T&&...) `insert(first,last,t...)`\endlink, // and `build()` - template + template void rebuild(ConstPrimitiveIterator first, ConstPrimitiveIterator beyond,T&& ...); /// adds a sequence of primitives to the set of primitives of the AABB tree. @@ -228,8 +244,9 @@ namespace CGAL { set_primitive_data_impl(CGAL::Boolean_tag::value>(),std::forward(t)...); } + template bool build_kd_tree(); - template + template bool build_kd_tree(ConstPointIterator first, ConstPointIterator beyond); public: @@ -439,6 +456,7 @@ namespace CGAL { /// constructs the internal search tree from /// a point set taken on the internal primitives /// returns `true` iff successful memory allocation + template bool accelerate_distance_queries(); /// turns off the usage of the internal search tree and clears it if it was already constructed. void do_not_accelerate_distance_queries(); @@ -451,11 +469,11 @@ namespace CGAL { /// is needed to update the search tree. /// \tparam ConstPointIterator is an iterator with /// value type `Point_and_primitive_id`. - template + template bool accelerate_distance_queries(ConstPointIterator first, ConstPointIterator beyond) { m_use_default_search_tree = false; - return build_kd_tree(first,beyond); + return build_kd_tree(first,beyond); } /// returns the minimum squared distance between the query point @@ -569,14 +587,36 @@ namespace CGAL { * * [first,beyond[ is the range of primitives to be added to the tree. */ - template + template void expand(Node& node, + std::size_t node_index, ConstPrimitiveIterator first, ConstPrimitiveIterator beyond, const std::size_t range, const ComputeBbox& compute_bbox, const SplitPrimitives& split_primitives); + /// @private + template + void partial_expand(Node& node, + std::size_t node_index, + ConstPrimitiveIterator first, + ConstPrimitiveIterator beyond, + const std::size_t range, + const std::size_t cutoff, + const ComputeBbox& compute_bbox, + const SplitPrimitives& split_primitives); + + public: + /// @private + std::pair::iterator, typename std::vector::iterator> + partial_node_to_primitives_iterator(const Node& node){ + const Primitive* begin = std::addressof(node.left_data()); + const Primitive* end = std::addressof(node.right_data()); + return std::make_pair(m_primitives.begin() + (begin - m_primitives.data()), + m_primitives.begin() + (end - m_primitives.data())); + } + public: // returns a point which must be on one primitive Point_and_primitive_id any_reference_point_and_id() const @@ -629,9 +669,9 @@ namespace CGAL { Primitives m_primitives; // tree nodes. first node is the root node std::vector m_nodes; - #ifdef CGAL_HAS_THREADS +#ifdef CGAL_HAS_THREADS mutable CGAL_MUTEX build_mutex; // mutex used to protect const calls inducing build() and build_kd_tree() - #endif +#endif public: const Node* root_node() const { CGAL_assertion(size() > 1); @@ -651,11 +691,6 @@ namespace CGAL { return std::addressof(m_nodes[0]); } - Node& new_node() - { - m_nodes.emplace_back(); - return m_nodes.back(); - } private: const Primitive& singleton_data() const { CGAL_assertion(size() == 1); @@ -748,7 +783,7 @@ namespace CGAL { // Clears tree and insert a set of primitives template - template + template void AABB_tree::rebuild(ConstPrimitiveIterator first, ConstPrimitiveIterator beyond, T&& ... t) @@ -759,11 +794,11 @@ namespace CGAL { // inserts primitives insert(first, beyond,std::forward(t)...); - build(); + build(); } template - template + template void AABB_tree::build(T&& ... t) { set_shared_data(std::forward(t)...); @@ -783,16 +818,22 @@ namespace CGAL { #endif } + template - template + template void AABB_tree::expand(Node& node, + std::size_t node_index, ConstPrimitiveIterator first, ConstPrimitiveIterator beyond, const std::size_t range, const ComputeBbox& compute_bbox, const SplitPrimitives& split_primitives) { + // TODO refined this hardcode value +#ifdef CGAL_LINKED_WITH_TBB + const std::size_t cutoff_parallel_call = 30000; // min size for parallel call +#endif node.set_bbox(compute_bbox(first, beyond)); // sort primitives along longest axis aabb @@ -804,29 +845,49 @@ namespace CGAL { node.set_children(*first, *(first+1)); break; case 3: - node.set_children(*first, new_node()); - expand(node.right_child(), first+1, beyond, 2, compute_bbox, split_primitives); + node.set_children(*first, m_nodes[node_index+1]); + expand(node.right_child(), node_index+1, first+1, beyond, 2, compute_bbox, split_primitives); break; default: const std::size_t new_range = range/2; - node.set_children(new_node(), new_node()); - expand(node.left_child(), first, first + new_range, new_range, compute_bbox, split_primitives); - expand(node.right_child(), first + new_range, beyond, range - new_range, compute_bbox, split_primitives); + node.set_children(m_nodes[node_index+1], m_nodes[node_index+new_range]); +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(ConcurrencyTag::is_parallel) + { + if(range > cutoff_parallel_call){ + oneapi::tbb::task_group tg; + tg.run([&]{ + expand(node.left_child(), node_index+1, first, first + new_range, new_range, compute_bbox, split_primitives); } + ); + expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, compute_bbox, split_primitives); + tg.wait(); + } else { + expand(node.left_child(), node_index+1, first, first + new_range, new_range, compute_bbox, split_primitives); + expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, compute_bbox, split_primitives); + } + } + else +#endif + { + expand(node.left_child(), node_index+1, first, first + new_range, new_range, compute_bbox, split_primitives); + expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, compute_bbox, split_primitives); + } } } // Build the data structure, after calls to insert(..) template + template void AABB_tree::build() { - custom_build(m_traits.compute_bbox_object(), - m_traits.split_primitives_object()); + custom_build(m_traits.compute_bbox_object(), + m_traits.split_primitives_object()); } #ifndef DOXYGEN_RUNNING // Build the data structure, after calls to insert(..) template - template + template void AABB_tree::custom_build( const ComputeBbox& compute_bbox, const SplitPrimitives& split_primitives) @@ -836,14 +897,15 @@ namespace CGAL { if(m_primitives.size() > 1) { // allocates tree nodes - m_nodes.reserve(m_primitives.size()-1); + m_nodes.resize(m_primitives.size()-1); // constructs the tree - expand(new_node(), - m_primitives.begin(), m_primitives.end(), - m_primitives.size(), - compute_bbox, - split_primitives); + expand(m_nodes[0], + 0, + m_primitives.begin(), m_primitives.end(), + m_primitives.size(), + compute_bbox, + split_primitives); } #ifdef CGAL_HAS_THREADS m_atomic_need_build.store(false, std::memory_order_release); // in case build() is triggered by a call to root_node() @@ -855,6 +917,7 @@ namespace CGAL { // constructs the search KD tree from given points // to accelerate the distance queries template + template bool AABB_tree::build_kd_tree() { // iterate over primitives to get reference points on them @@ -864,18 +927,18 @@ namespace CGAL { points.push_back( Point_and_primitive_id( Helper::get_reference_point(p, m_traits), p.id() ) ); // clears current KD tree - return build_kd_tree(points.begin(), points.end()); + return build_kd_tree(points.begin(), points.end()); } // constructs the search KD tree from given points // to accelerate the distance queries template - template + template bool AABB_tree::build_kd_tree(ConstPointIterator first, ConstPointIterator beyond) { clear_search_tree(); - m_p_search_tree = std::make_unique(first, beyond); + m_p_search_tree = std::make_unique(first, beyond, ConcurrencyTag()); #ifdef CGAL_HAS_THREADS m_atomic_search_tree_constructed.store(true, std::memory_order_release); // in case build_kd_tree() is triggered by a call to best_hint() #else @@ -884,6 +947,97 @@ namespace CGAL { return true; } + template + template + void AABB_tree::partial_build(const std::size_t cutoff) + { + custom_partial_build(cutoff, + m_traits.compute_bbox_object(), + m_traits.split_primitives_object()); + } + + // Build the data structure, after calls to insert(..) + template + template + void AABB_tree::custom_partial_build( + const std::size_t cutoff, + const ComputeBbox& compute_bbox, + const SplitPrimitives& split_primitives) + { + clear_nodes(); + + if(m_primitives.size() > 1) { + + // allocates tree nodes + m_nodes.resize(m_primitives.size()-1); + + // constructs the tree + partial_expand(m_nodes[0], + 0, + m_primitives.begin(), m_primitives.end(), + m_primitives.size(), + cutoff, + compute_bbox, + split_primitives); + } +#ifdef CGAL_HAS_THREADS + m_atomic_need_build.store(false, std::memory_order_release); // in case build() is triggered by a call to root_node() +#else + m_need_build = false; +#endif + } + + template + template + void + AABB_tree::partial_expand(Node& node, + std::size_t node_index, + ConstPrimitiveIterator first, + ConstPrimitiveIterator beyond, + const std::size_t range, + const std::size_t cutoff, + const ComputeBbox& compute_bbox, + const SplitPrimitives& split_primitives) + { +#ifdef CGAL_LINKED_WITH_TBB + const std::size_t cutoff_parallel_call = 30000; // min size for parallel call +#endif + node.set_bbox(compute_bbox(first, beyond)); + + if(range < cutoff) + { + node.set_children(*first, *beyond); + } + else + { + // sort primitives along longest axis aabb + split_primitives(first, beyond, node.bbox()); + const std::size_t new_range = range/2; + node.set_children(m_nodes[node_index+1], m_nodes[node_index+new_range]); +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(ConcurrencyTag::is_parallel) + { + if(range > cutoff_parallel_call){ + oneapi::tbb::task_group tg; + tg.run([&]{ + partial_expand(node.left_child(), node_index+1, first, first + new_range, new_range, cutoff, compute_bbox, split_primitives); } + ); + partial_expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, cutoff, compute_bbox, split_primitives); + tg.wait(); + } else { + partial_expand(node.left_child(), node_index+1, first, first + new_range, new_range, cutoff, compute_bbox, split_primitives); + partial_expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, cutoff, compute_bbox, split_primitives); + } + } + else +#endif + { + partial_expand(node.left_child(), node_index+1, first, first + new_range, new_range, cutoff, compute_bbox, split_primitives); + partial_expand(node.right_child(), node_index+new_range, first + new_range, beyond, range - new_range, cutoff, compute_bbox, split_primitives); + } + } + } + template void AABB_tree::do_not_accelerate_distance_queries() { @@ -893,11 +1047,12 @@ namespace CGAL { // constructs the search KD tree from internal primitives template + template bool AABB_tree::accelerate_distance_queries() { m_use_default_search_tree = true; if(m_primitives.empty()) return true; - return build_kd_tree(); + return build_kd_tree(); } template diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_search_tree.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_search_tree.h index f44130eda28b..4c963792ee11 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_search_tree.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_search_tree.h @@ -47,8 +47,8 @@ struct AABB_search_tree } public: - template - AABB_search_tree(ConstPointIterator begin, ConstPointIterator beyond) + template + AABB_search_tree(ConstPointIterator begin, ConstPointIterator beyond, ConcurrencyTag=ConcurrencyTag()) : m_tree{} { std::vector points; @@ -58,7 +58,14 @@ struct AABB_search_tree ++begin; } m_tree.insert(points.begin(), points.end()); - m_tree.build(); + if constexpr(std::is_same_v) + build(); + } + + template + void build() + { + m_tree.template build(); } template diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traversal_traits.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traversal_traits.h index 11fe903df932..171142bf30a4 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traversal_traits.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traversal_traits.h @@ -15,7 +15,7 @@ #include - +#include #include #include @@ -104,7 +104,7 @@ class First_intersection_traits /** * @class Listing_intersection_traits */ -template +template class Listing_intersection_traits { typedef typename AABBTraits::FT FT; @@ -117,7 +117,7 @@ class Listing_intersection_traits typedef ::CGAL::AABB_node Node; public: - Listing_intersection_traits(Output_iterator out_it, const AABBTraits& traits) + Listing_intersection_traits(OutputIterator out_it, const AABBTraits& traits) : m_out_it(out_it), m_traits(traits) {} constexpr bool go_further() const { return true; } @@ -139,7 +139,7 @@ class Listing_intersection_traits } private: - Output_iterator m_out_it; + OutputIterator m_out_it; const AABBTraits& m_traits; }; @@ -147,7 +147,7 @@ class Listing_intersection_traits /** * @class Listing_primitive_traits */ -template +template class Listing_primitive_traits { typedef typename AABBTraits::FT FT; @@ -160,7 +160,7 @@ class Listing_primitive_traits typedef ::CGAL::AABB_node Node; public: - Listing_primitive_traits(Output_iterator out_it, const AABBTraits& traits) + Listing_primitive_traits(OutputIterator out_it, const AABBTraits& traits) : m_out_it(out_it), m_traits(traits) {} constexpr bool go_further() const { return true; } @@ -179,7 +179,47 @@ class Listing_primitive_traits } private: - Output_iterator m_out_it; + OutputIterator m_out_it; + const AABBTraits& m_traits; +}; + +/** + * @class Listing_distinct_primitive_traits + * used by `all_pairs_of_intersecting_primitives()` to avoid reporting `(i, i)` and twice `(i, j)`. + */ +template +class Listing_distinct_primitive_traits +{ + typedef typename AABBTraits::FT FT; + typedef typename AABBTraits::Point Point; + typedef typename AABBTraits::Primitive Primitive; + typedef typename AABBTraits::Bounding_box Bounding_box; + typedef typename AABBTraits::Primitive::Id Primitive_id; + typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; + typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; + typedef ::CGAL::AABB_node Node; + +public: + Listing_distinct_primitive_traits(OutputIterator out_it, const AABBTraits& traits) + : m_out_it(out_it), m_traits(traits) {} + + constexpr bool go_further() const { return true; } + + void intersection(const Primitive& query, const Primitive& primitive) + { + if( query.id()::get_datum(query, m_traits), primitive) ) + { + *m_out_it++ = primitive.id(); + } + } + + bool do_intersect(const Primitive& query, const Node& node) const + { + return m_traits.do_intersect_object()(internal::Primitive_helper::get_datum(query, m_traits), node.bbox()); + } + +private: + OutputIterator m_out_it; const AABBTraits& m_traits; }; @@ -270,6 +310,63 @@ class Do_intersect_traits const AABBTraits& m_traits; }; +/** + * It is more efficient to apply the inverse transformation to the query and then use the original traits, +* but computing an inverse is less numerically stable than applying the transformation to the primitives. + * @class Do_intersect_traits_with_transformation + */ +template +class Do_intersect_traits_with_transformation +{ + typedef typename AABBTraits::FT FT; + typedef typename AABBTraits::Point Point; + typedef typename AABBTraits::Primitive Primitive; + typedef typename AABBTraits::Bounding_box Bounding_box; + typedef typename AABBTraits::Primitive::Id Primitive_id; + typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; + typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; + typedef ::CGAL::AABB_node Node; + + typedef Aff_transformation_3 Transformation_3; + +public: + Do_intersect_traits_with_transformation(const AABBTraits& traits) + : m_is_found(false), m_traits(traits), m_transfo(CGAL::IDENTITY), m_has_rotation(false) + {} + + Do_intersect_traits_with_transformation(const AABBTraits& traits, const Transformation_3& transfo) + : m_is_found(false), m_traits(traits), m_transfo(transfo), m_has_rotation(transfo.has_rotation()) + {} + + bool go_further() const { return !m_is_found; } + + void intersection(const Query& query, const Primitive& primitive) + { + auto datum_transformed = internal::Primitive_helper::get_datum(primitive, m_traits).transform(m_transfo); + if( CGAL::do_intersect(query, datum_transformed)) + m_is_found = true; + } + + bool do_intersect(const Query& query, const Node& node) const + { + return m_traits.do_intersect_object()(query, compute_transformed_bbox(m_transfo, node.bbox(), m_has_rotation)); + } + + bool is_intersection_found() const { return m_is_found; } + + const Transformation_3& transformation() const { return m_transfo; } + void set_transformation(const Transformation_3& transfo) + { + m_transfo = transfo; + m_has_rotation = m_transfo.has_rotation(); + } + +private: + bool m_is_found; + const AABBTraits& m_traits; + Transformation_3 m_transfo; + bool m_has_rotation; +}; /** * @class Projection_traits diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal.h new file mode 100644 index 000000000000..b024e15fa766 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal.h @@ -0,0 +1,219 @@ +// Copyright (c) 2026 Geometry Factory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Léo Valque + +#ifndef CGAL_AABB_TWO_TREES_TRAVERSAL_H +#define CGAL_AABB_TWO_TREES_TRAVERSAL_H + +#include + +#include +#include + +namespace CGAL { + +namespace internal { namespace AABB_tree { + +template +void two_trees_traversal(const ::CGAL::AABB_node& node_A, + const ::CGAL::AABB_node& node_B, + const std::size_t nb_primitives_A, + const std::size_t nb_primitives_B, + TwoTreeTraversalTraits& traversal_traits) +{ +#if CGAL_LINKED_WITH_TBB + const std::size_t cutoff_parallel_call = 100000; +#endif + auto recursive_call = [](const auto &node_A, const auto &node_B, const std::size_t nb_primitives_A, const std::size_t nb_primitives_B, auto &traversal_traits){ + if(traversal_traits.prefer_A_for_next_step(node_A, node_B, nb_primitives_A, nb_primitives_B)) + two_trees_traversal< in_order, ConcurrencyTag>(node_A, node_B, nb_primitives_A, nb_primitives_B, traversal_traits); + else + two_trees_traversal(node_B, node_A, nb_primitives_B, nb_primitives_A, traversal_traits); + }; + switch(nb_primitives_A) + { + case 2: + { + if constexpr(in_order){ + traversal_traits.intersection(node_A.left_data(), node_B, nb_primitives_B); + traversal_traits.intersection(node_A.right_data(), node_B, nb_primitives_B); + } else { + traversal_traits.intersection(node_B, nb_primitives_B, node_A.left_data()); + traversal_traits.intersection(node_B, nb_primitives_B, node_A.right_data()); + } + break; + } + case 3: + { + if constexpr(in_order) + traversal_traits.intersection(node_A.left_data(), node_B, nb_primitives_B); + else + traversal_traits.intersection(node_B, nb_primitives_B, node_A.left_data()); + + bool do_intersect_right; + if constexpr(in_order) + do_intersect_right = traversal_traits.do_intersect(node_A.right_child(), node_B); + else + do_intersect_right = traversal_traits.do_intersect(node_B, node_A.right_child()); + if( do_intersect_right ) + two_trees_traversal(node_B, node_A.right_child(), nb_primitives_B, 2, traversal_traits); + break; + } + default: + { + bool do_intersect_left, do_intersect_right; + if constexpr(in_order){ + do_intersect_left = traversal_traits.do_intersect(node_A.left_child(), node_B); + do_intersect_right = traversal_traits.do_intersect(node_A.right_child(), node_B); + } else { + do_intersect_left = traversal_traits.do_intersect(node_B, node_A.left_child()); + do_intersect_right = traversal_traits.do_intersect(node_B, node_A.right_child()); + } +#if CGAL_LINKED_WITH_TBB + if constexpr(ConcurrencyTag::is_parallel) + { + if(do_intersect_left && do_intersect_right && nb_primitives_A > cutoff_parallel_call && nb_primitives_B > cutoff_parallel_call) + { + oneapi::tbb::task_group tg; + tg.run([&]{ + recursive_call(node_A.left_child(), node_B, nb_primitives_A/2, nb_primitives_B, traversal_traits);} + ); + recursive_call(node_A.right_child(), node_B, nb_primitives_A - nb_primitives_A/2, nb_primitives_B, traversal_traits); + tg.wait(); + } + else + { + if( do_intersect_left ) + recursive_call(node_A.left_child(), node_B, nb_primitives_A/2, nb_primitives_B, traversal_traits); + if( traversal_traits.go_further() && do_intersect_right ) + recursive_call(node_A.right_child(), node_B, nb_primitives_A - nb_primitives_A/2, nb_primitives_B, traversal_traits); + } + } + else +#endif + { + if( do_intersect_left ) + recursive_call(node_A.left_child(), node_B, nb_primitives_A/2, nb_primitives_B, traversal_traits); + if( traversal_traits.go_further() && do_intersect_right ) + recursive_call(node_A.right_child(), node_B, nb_primitives_A - nb_primitives_A/2, nb_primitives_B, traversal_traits); + } + }} // switch end +} + +template +void two_trees_traversal(const Tree_A& tree_A, + const Tree_B& tree_B, + TwoTreeTraversalTraits &traits) +{ + CGAL_precondition(tree_A.size() != 0 && tree_B.size() != 0); + two_trees_traversal(*tree_A.root_node(), *tree_B.root_node(), tree_A.size(), tree_B.size(), traits); +} + +namespace experimental{ + +template +void two_trees_partial_traversal(const ::CGAL::AABB_node& node_A, + const ::CGAL::AABB_node& node_B, + const std::size_t nb_primitives_A, + const std::size_t nb_primitives_B, + const std::size_t cutoff, + TwoTreeTraversalTraits& traversal_traits) +{ +#if CGAL_LINKED_WITH_TBB + const std::size_t cutoff_parallel_call = 100000; +#endif + auto recursive_call = [&](const auto &node_A, const auto &node_B, const std::size_t nb_primitives_A, const std::size_t nb_primitives_B, auto &traversal_traits){ + if(traversal_traits.prefer_A_for_next_step(node_A, node_B, nb_primitives_A, nb_primitives_B)) + two_trees_partial_traversal< in_order, ConcurrencyTag>(node_A, node_B, nb_primitives_A, nb_primitives_B, cutoff, traversal_traits); + else + two_trees_partial_traversal(node_B, node_A, nb_primitives_B, nb_primitives_A, cutoff, traversal_traits); + }; + if(nb_primitives_A < cutoff && nb_primitives_B < cutoff) + { + if constexpr(in_order) + traversal_traits.intersection(node_A, node_B); + else + traversal_traits.intersection(node_B, node_A); + } + else if(nb_primitives_A < cutoff && nb_primitives_B < cutoff) + { + two_trees_partial_traversal(node_B, node_A, nb_primitives_B, nb_primitives_A, cutoff, traversal_traits); + } + else + { + bool do_intersect_left, do_intersect_right; + if constexpr(in_order){ + do_intersect_left = traversal_traits.do_intersect(node_A.left_child(), node_B); + do_intersect_right = traversal_traits.do_intersect(node_A.right_child(), node_B); + } else { + do_intersect_left = traversal_traits.do_intersect(node_B, node_A.left_child()); + do_intersect_right = traversal_traits.do_intersect(node_B, node_A.right_child()); + } +#if CGAL_LINKED_WITH_TBB + if constexpr(ConcurrencyTag::is_parallel) + { + if(do_intersect_left && do_intersect_right && nb_primitives_A > cutoff_parallel_call && nb_primitives_B > cutoff_parallel_call) + { + oneapi::tbb::task_group tg; + tg.run([&]{ + recursive_call(node_B, node_A.left_child(), nb_primitives_B, nb_primitives_A/2, traversal_traits); + }); + recursive_call(node_B, node_A.right_child(), nb_primitives_B, nb_primitives_A-nb_primitives_A/2, traversal_traits); + tg.wait(); + } + else + { + if( do_intersect_left ) + recursive_call(node_B, node_A.left_child(), nb_primitives_B, nb_primitives_A/2, traversal_traits); + if( traversal_traits.go_further() && do_intersect_right ) + recursive_call(node_B, node_A.right_child(), nb_primitives_B, nb_primitives_A-nb_primitives_A/2, traversal_traits); + } + } + else +#endif + { + if( do_intersect_left ) + recursive_call(node_B, node_A.left_child(), nb_primitives_B, nb_primitives_A/2, traversal_traits); + if( traversal_traits.go_further() && do_intersect_right ) + recursive_call(node_B, node_A.right_child(), nb_primitives_B, nb_primitives_A-nb_primitives_A/2, traversal_traits); + } + } +} + +template +void two_trees_partial_traversal(const Tree_A& tree_A, + const Tree_B& tree_B, + const std::size_t cutoff, + TwoTreeTraversalTraits &traits) +{ + CGAL_precondition(tree_A.size() != 0 && tree_B.size() != 0); + two_trees_partial_traversal(*tree_A.root_node(), *tree_B.root_node(), tree_A.size(), tree_B.size(), cutoff, traits); +} + +} // end of namespace experimental + +}}} // end of namespace CGAL::internal::AABB_tree + +#endif // CGAL_AABB_TRAVERSAL_TRAITS_H diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal_traits.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal_traits.h new file mode 100644 index 000000000000..4eec42d5c818 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_two_trees_traversal_traits.h @@ -0,0 +1,398 @@ +// Copyright (c) 2026 Geometry Factory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Léo Valque + +#ifndef CGAL_AABB_TWO_TREES_TRAVERSAL_TRAITS_H +#define CGAL_AABB_TWO_TREES_TRAVERSAL_TRAITS_H + +#include + +#include +#include +#include +#include +#include + +namespace CGAL { + +namespace internal { namespace AABB_tree { + +template +class Wrap_output_iterator +{ + Value first; + OutputIterator out; +public: + Wrap_output_iterator(Value first_, OutputIterator out_): first(first_), out(out_){} + + Wrap_output_iterator& operator=(Value second){ + if constexpr(in_order) + out = std::make_pair(first, second); + else + out = std::make_pair(second, first); + return *this; + } + Wrap_output_iterator& operator*(){ return *this; } + Wrap_output_iterator& operator++(){ ++out; return *this; } + Wrap_output_iterator operator++(int){ auto tmp = *this; ++out; return tmp; } + Wrap_output_iterator& operator+(int d){ out += d; return *this; } +}; + +template +class Two_trees_listing_intersecting_primitives_traits +{ + typedef typename AABBTraits1::Primitive Primitive1; + typedef typename AABBTraits2::Primitive Primitive2; + typedef ::CGAL::AABB_node Node1; + typedef ::CGAL::AABB_node Node2; + +public: + Two_trees_listing_intersecting_primitives_traits(const AABBTraits1& traits1, const AABBTraits2& traits2, OutputIterator out_) + : m_traits1(traits1), m_traits2(traits2), out(out_) + {} + + bool go_further() const { + return true; + } + + // If true, the next step of traversal continue on A, if false, the next step of traversal is on B + template + bool prefer_A_for_next_step(const Node_A& node_a, const Node_B& node_b, const std::size_t&, const std::size_t&) const { + return node_a.bbox().squared_diagonal_length() > node_b.bbox().squared_diagonal_length(); + } + + void intersection(const Primitive1& primitive1, const Node2& node2, std::size_t nb_primitives_2) + { + using Wrap_iterator = Wrap_output_iterator; + Wrap_iterator wrap_out(primitive1.id(), out); + Listing_primitive_traits traits(wrap_out, m_traits2); + node2.traversal( internal::Primitive_helper::get_datum(primitive1, m_traits1), traits, nb_primitives_2); + } + + void intersection(const Node1& node1, std::size_t nb_primitives_1, const Primitive2& primitive2) + { + using Wrap_iterator= Wrap_output_iterator; + Wrap_iterator wrap_out(primitive2.id(), out); + Listing_primitive_traits traits(wrap_out, m_traits1); + node1.traversal( internal::Primitive_helper::get_datum(primitive2, m_traits2), traits, nb_primitives_1); + } + + bool do_intersect(const Node1& node1, const Node2& node2) const + { + return do_overlap(node1.bbox(), node2.bbox()); + } + +private: + const AABBTraits1& m_traits1; + const AABBTraits2& m_traits2; + OutputIterator out; +}; + +template +class Two_trees_listing_intersecting_primitives_traits_with_transformation +{ + typedef typename AABBTraits1::Primitive Primitive1; + typedef typename AABBTraits2::Primitive Primitive2; + typedef ::CGAL::AABB_node Node1; + typedef ::CGAL::AABB_node Node2; + +public: + Two_trees_listing_intersecting_primitives_traits_with_transformation(const AABBTraits1& traits1, const AABBTraits2& traits2, + OutputIterator out_, + const AffTransformation &tr1, const AffTransformation &tr2) + : m_traits1(traits1), m_traits2(traits2), + out(out_), + m_tr1(tr1), m_tr2(tr2), + m_tr1_inverse(tr1.inverse()), m_tr2_inverse(tr2.inverse()), + m_tr1_has_rotation(tr1.has_rotation()), m_tr2_has_rotation(tr2.has_rotation()) + {} + + bool go_further() const { + return true; + } + + // If true, the next step of traversal continue on A, if false, the next step of traversal is on B + template + bool prefer_A_for_next_step(const Node_A& node_a, const Node_B& node_b, const std::size_t&, const std::size_t&) const { + return node_a.bbox().squared_diagonal_length() > node_b.bbox().squared_diagonal_length(); + } + + void intersection(const Primitive1& primitive1, const Node2& node2, std::size_t nb_primitives_2) + { + using Wrap_iterator = Wrap_output_iterator; + Wrap_iterator wrap_out(primitive1.id(), out); + Listing_primitive_traits traits(wrap_out, m_traits2); + auto datum = (internal::Primitive_helper::get_datum(primitive1, m_traits1).transform(m_tr1)).transform(m_tr2_inverse); + node2.traversal(datum, traits, nb_primitives_2); + } + + void intersection(const Node1& node1, std::size_t nb_primitives_1, const Primitive2& primitive2) + { + using Wrap_iterator= Wrap_output_iterator; + Wrap_iterator wrap_out(primitive2.id(), out); + Listing_primitive_traits traits(wrap_out, m_traits1); + auto datum = (internal::Primitive_helper::get_datum(primitive2, m_traits2).transform(m_tr2)).transform(m_tr1_inverse); + node1.traversal(datum, traits, nb_primitives_1); + } + + bool do_intersect(const Node1& node1, const Node2& node2) const + { + // TODO write a specific do_intersect function between two OBBs + return do_overlap(compute_transformed_bbox(m_tr1, node1.bbox(), m_tr1_has_rotation), compute_transformed_bbox(m_tr2, node2.bbox(), m_tr2_has_rotation)); + } + +private: + const AABBTraits1& m_traits1; + const AABBTraits2& m_traits2; + OutputIterator out; + const AffTransformation& m_tr1; + const AffTransformation& m_tr2; + AffTransformation m_tr1_inverse; + AffTransformation m_tr2_inverse; + bool m_tr1_has_rotation, m_tr2_has_rotation; +}; + +template +class Listing_self_intersecting_primitives_traits +{ + typedef typename AABBTraits::Primitive Primitive; + typedef ::CGAL::AABB_node Node; + +public: + Listing_self_intersecting_primitives_traits(const AABBTraits& traits, OutputIterator out_) + : m_traits(traits), out(out_) + {} + + bool go_further() const { + return true; + } + + // If true, the next step of traversal continue on A, if false, the next step of traversal is on B + template + bool prefer_A_for_next_step(const Node_A& node_a, const Node_B& node_b, const std::size_t&, const std::size_t&) const { + return node_a.bbox().squared_diagonal_length() > node_b.bbox().squared_diagonal_length(); + } + + void intersection(const Primitive& primitive1, const Node& node2, std::size_t nb_primitives_2) + { + // TODO Since we are in a symmetric case, maybe we can ignore this call + using Wrap_iterator = Wrap_output_iterator; + Wrap_iterator wrap_out(primitive1.id(), out); + Listing_distinct_primitive_traits traits(wrap_out, m_traits); + node2.traversal( primitive1, traits, nb_primitives_2); + } + + void intersection(const Node& node1, std::size_t nb_primitives_1, const Primitive& primitive2) + { + using Wrap_iterator= Wrap_output_iterator; + Wrap_iterator wrap_out(primitive2.id(), out); + Listing_distinct_primitive_traits traits(wrap_out, m_traits); + node1.traversal( primitive2, traits, nb_primitives_1); + } + + bool do_intersect(const Node& node1, const Node& node2) const + { + return do_overlap(node1.bbox(), node2.bbox()); + } + +private: + const AABBTraits& m_traits; + OutputIterator out; +}; + +template +class Two_trees_do_intersect_traits +{ + typedef typename AABBTraits1::Primitive Primitive1; + typedef typename AABBTraits2::Primitive Primitive2; + typedef ::CGAL::AABB_node Node1; + typedef ::CGAL::AABB_node Node2; + +public: + Two_trees_do_intersect_traits(const AABBTraits1& traits1, const AABBTraits2& traits2) + : m_traits1(traits1), m_traits2(traits2), m_is_found(false) + {} + + bool go_further() const { + return !m_is_found; + } + + // If true, the next step of traversal continues on A, if false, the next step of traversal is on B + template + bool prefer_A_for_next_step(const Node_A& node_a, const Node_B& node_b, const std::size_t&, const std::size_t&) const { + return node_a.bbox().squared_diagonal_length() > node_b.bbox().squared_diagonal_length(); + } + + void intersection(const Primitive1& primitive1, const Node2& node2, std::size_t nb_primitives_2) + { + Do_intersect_traits traits(m_traits2); + node2.traversal( internal::Primitive_helper::get_datum(primitive1, m_traits1), traits, nb_primitives_2); + if(traits.is_intersection_found()) + m_is_found = true; + } + + void intersection(const Node1& node1, std::size_t nb_primitives_1, const Primitive2& primitive2) + { + Do_intersect_traits traits(m_traits1); + node1.traversal( internal::Primitive_helper::get_datum(primitive2, m_traits2), traits, nb_primitives_1); + if(traits.is_intersection_found()) + m_is_found = true; + } + + bool do_intersect(const Node1& node1, const Node2& node2) const + { + return do_overlap(node1.bbox(), node2.bbox()); + } + + bool is_intersection_found() const { return m_is_found; } + +private: + const AABBTraits1& m_traits1; + const AABBTraits2& m_traits2; + bool m_is_found; +}; + +template +class Two_trees_do_intersect_traits_with_transformation +{ + typedef typename AABBTraits1::Primitive Primitive1; + typedef typename AABBTraits2::Primitive Primitive2; + typedef ::CGAL::AABB_node Node1; + typedef ::CGAL::AABB_node Node2; + +public: + Two_trees_do_intersect_traits_with_transformation(const AABBTraits1& traits1, const AABBTraits2& traits2, const AffTransformation &tr1, const AffTransformation &tr2) + : m_traits1(traits1), m_traits2(traits2), + m_tr1(tr1), m_tr2(tr2), + m_tr1_has_rotation(tr1.has_rotation()), m_tr2_has_rotation(tr2.has_rotation()), + m_is_found(false) + { + if constexpr(Use_inverse_transformation::value) + { + m_tr1_inverse = tr1.inverse(); + m_tr2_inverse = tr2.inverse(); + } + } + + bool go_further() const { + return !m_is_found; + } + + // If true, the next step of traversal continues on A, if false, the next step of traversal is on B + template + bool prefer_A_for_next_step(const Node_A& node_a, const Node_B& node_b, const std::size_t&, const std::size_t&) const { + return node_a.bbox().squared_diagonal_length() > node_b.bbox().squared_diagonal_length(); + } + + void intersection(const Primitive1& primitive1, const Node2& node2, std::size_t nb_primitives_2) + { + // Use inverse transformation is faster but less numerically stable. + if constexpr(Use_inverse_transformation::value) + { + Do_intersect_traits traits(m_traits2); + auto datum = (internal::Primitive_helper::get_datum(primitive1, m_traits1).transform(m_tr1)).transform(m_tr2_inverse); + node2.traversal( datum, traits, nb_primitives_2); + + if(traits.is_intersection_found()) + m_is_found = true; + } + else + { + Do_intersect_traits_with_transformation traits(m_traits2, m_tr2); + auto datum = (internal::Primitive_helper::get_datum(primitive1, m_traits1).transform(m_tr1)); + node2.traversal( datum, traits, nb_primitives_2); + + if(traits.is_intersection_found()) + m_is_found = true; + } + } + + void intersection(const Node1& node1, std::size_t nb_primitives_1, const Primitive2& primitive2) + { + if constexpr(Use_inverse_transformation::value) + { + Do_intersect_traits traits(m_traits1); + auto datum = (internal::Primitive_helper::get_datum(primitive2, m_traits2).transform(m_tr2)).transform(m_tr1_inverse); + node1.traversal( datum, traits, nb_primitives_1); + + if(traits.is_intersection_found()) + m_is_found = true; + } + else + { + Do_intersect_traits_with_transformation traits(m_traits1, m_tr1); + auto datum = (internal::Primitive_helper::get_datum(primitive2, m_traits2).transform(m_tr2)); + node1.traversal( datum, traits, nb_primitives_1); + + if(traits.is_intersection_found()) + m_is_found = true; + } + } + + bool do_intersect(const Node1& node1, const Node2& node2) const + { + return do_overlap(compute_transformed_bbox(m_tr1, node1.bbox(), m_tr1_has_rotation), compute_transformed_bbox(m_tr2, node2.bbox(), m_tr2_has_rotation)); + } + + bool is_intersection_found() const { return m_is_found; } + +private: + const AABBTraits1& m_traits1; + const AABBTraits2& m_traits2; + const AffTransformation& m_tr1; + const AffTransformation& m_tr2; + AffTransformation m_tr1_inverse; + AffTransformation m_tr2_inverse; + bool m_tr1_has_rotation, m_tr2_has_rotation; + bool m_is_found; +}; + +namespace experimental{ + +template +class Two_trees_intersecting_nodes_traits +{ + typedef typename AABBTraits1::Primitive Primitive1; + typedef typename AABBTraits2::Primitive Primitive2; + typedef ::CGAL::AABB_node Node1; + typedef ::CGAL::AABB_node Node2; + +public: + Two_trees_intersecting_nodes_traits(const AABBTraits1& traits1, const AABBTraits2& traits2, OutputIterator out_) + : m_traits1(traits1), m_traits2(traits2), out(out_) + {} + + bool go_further() const { + return true; + } + + void intersection(const Node1& node1, const Node2& node2) + { + *out++ = std::make_pair(&node1, &node2); + } + + bool do_intersect(const Node1& node1, const Node2& node2) const + { + return do_overlap(node1.bbox(), node2.bbox()); + } + +private: + const AABBTraits1& m_traits1; + const AABBTraits2& m_traits2; + OutputIterator out; +}; + +} + + +}}} // end namespace CGAL::internal::AABB_tree + +#endif // CGAL_AABB_TRAVERSAL_TRAITS_H diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/Primitive_helper.h b/AABB_tree/include/CGAL/AABB_tree/internal/Primitive_helper.h index b8bfa8a199c8..4ae0b042ca8a 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/Primitive_helper.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/Primitive_helper.h @@ -14,7 +14,11 @@ #include - +#include +#include +#include +#include +#include #include #include @@ -61,6 +65,271 @@ struct Primitive_helper{ static Reference_point_type get_reference_point(const typename AABBTraits::Primitive& p,const AABBTraits&) {return p.reference_point();} }; +#include +#include + +#include + +template +bool do_intersect_transformed_BB(const CGAL::Bbox_3& b1, + const CGAL::Bbox_3& b2, + const CGAL::Aff_transformation_3& t1, + const CGAL::Aff_transformation_3& t2) +{ + typedef Simple_cartesian AK; + typedef Cartesian_converter C2F; + C2F c2f; + + AK::Aff_transformation_3 a_t1 = c2f(t1); + AK::FT xtrm1[6] = {c2f((b1.min)(0)), c2f((b1.max)(0)), + c2f((b1.min)(1)), c2f((b1.max)(1)), + c2f((b1.min)(2)), c2f((b1.max)(2)) }; + + AK::Aff_transformation_3 a_t2 = c2f(t2); + AK::FT xtrm2[6] = {c2f((b2.min)(0)), c2f((b2.max)(0)), + c2f((b2.min)(1)), c2f((b2.max)(1)), + c2f((b2.min)(2)), c2f((b2.max)(2)) }; + + AK::Point_3 ps[4]; + ps[0] = a_t1( AK::Point_3(xtrm1[0], xtrm1[2], xtrm1[4]) ); + ps[1] = a_t1( AK::Point_3(xtrm1[1], xtrm1[3], xtrm1[5]) ); + ps[2] = a_t2( AK::Point_3(xtrm2[0], xtrm2[2], xtrm2[4]) ); + ps[3] = a_t2( AK::Point_3(xtrm2[1], xtrm2[3], xtrm2[5]) ); + + return do_overlap(bbox_3(ps, ps+2), bbox_3(ps+2, ps+4)); +} + +// Tests if two oriented bounding boxes (OBBs) intersect using the Separating Axis Theorem (SAT). +// Tests separation along the 6 principal axes (3 from each OBB). +// Note: Does not test the 9 cross-product axes for efficiency; thus false positives may be returned. +template +bool do_intersect_OBB(const Bbox_3& b1, + const Bbox_3& b2, + const CGAL::Aff_transformation_3& t1, + const CGAL::Aff_transformation_3& t2) +{ + using AK = Simple_cartesian; + using C2F = Cartesian_converter; + C2F c2f; + + using FT = AK::FT; + using Point = AK::Point_3; + using Vector = AK::Vector_3; + + AK::Aff_transformation_3 a_t1 = c2f(t1); + AK::Aff_transformation_3 a_t2 = c2f(t2); + + // The center of each box. + Point p1 = a_t1.transform( Point((FT(b1.xmin())+FT(b1.xmax()))/2, (FT(b1.ymin())+FT(b1.ymax()))/2, (FT(b1.zmin())+FT(b1.zmax()))/2) ); + Point p2 = a_t2.transform( Point((FT(b2.xmin())+FT(b2.xmax()))/2, (FT(b2.ymin())+FT(b2.ymax()))/2, (FT(b2.zmin())+FT(b2.zmax()))/2) ); + + // Half width vectors of each box. + const Vector A[3] = { + a_t1.transform(Vector(b1.x_span()/2, 0, 0)), + a_t1.transform(Vector(0, b1.y_span()/2, 0)), + a_t1.transform(Vector(0, 0, b1.z_span()/2)) + }; + + const Vector B[3] = { + a_t2.transform(Vector(b2.x_span()/2, 0, 0)), + a_t2.transform(Vector(0, b2.y_span()/2, 0)), + a_t2.transform(Vector(0, 0, b2.z_span()/2)) + }; + + const Vector dir = p2 - p1; + + // Test separation on the 3 axes of A. + for(int i=0; i<3; ++i) + { + // Project the center and half-width along each axis and compare the distance between centers with the sum of half-width projections. + const Vector& axis = A[i]; + const FT dist = CGAL::abs(dir * axis); + + const FT ra = axis.squared_length(); + const FT rb = CGAL::abs(B[0]*axis) + CGAL::abs(B[1]*axis) + CGAL::abs(B[2]*axis); + if(dist > ra + rb) return false; + } + + // Test separation on the 3 axes of B. + for(int i = 0; i < 3; ++i) + { + const Vector& axis = B[i]; + const FT dist = CGAL::abs(dir * axis); + + const FT rb = axis.squared_length(); + const FT ra = CGAL::abs(A[0]*axis) + CGAL::abs(A[1]*axis) + CGAL::abs(A[2]*axis); + if(dist > ra + rb) return false; + } + + // No separating axis among the 6 face normals. + return true; +} + +template +bool do_intersect_transformed_BB(const CGAL::Bbox_2& b1, + const CGAL::Bbox_2& b2, + const CGAL::Aff_transformation_2& t1, + const CGAL::Aff_transformation_2& t2) +{ + typedef Simple_cartesian AK; + typedef Cartesian_converter C2F; + C2F c2f; + + AK::Aff_transformation_2 a_t1 = c2f(t1); + AK::FT xtrm1[4] = {c2f((b1.min)(0)), c2f((b1.max)(0)), + c2f((b1.min)(1)), c2f((b1.max)(1)) }; + + AK::Aff_transformation_2 a_t2 = c2f(t2); + AK::FT xtrm2[4] = {c2f((b2.min)(0)), c2f((b2.max)(0)), + c2f((b2.min)(1)), c2f((b2.max)(1)) }; + + AK::Point_2 ps[4]; + ps[0] = a_t1( AK::Point_2(xtrm1[0], xtrm1[2]) ); + ps[1] = a_t1( AK::Point_2(xtrm1[1], xtrm1[3]) ); + ps[2] = a_t2( AK::Point_2(xtrm2[0], xtrm2[2]) ); + ps[3] = a_t2( AK::Point_2(xtrm2[1], xtrm2[3]) ); + + return do_overlap(bbox_2(ps, ps+2), bbox_2(ps+2, ps+4)); +} + +// Tests if two oriented bounding boxes (OBBs) intersect using the Separating Axis Theorem (SAT). +// Tests separation along the 4 principal axes (2 from each OBB). +template +bool do_intersect_OBB(const Bbox_2& b1, + const Bbox_2& b2, + const CGAL::Aff_transformation_2& t1, + const CGAL::Aff_transformation_2& t2) +{ + using AK = Simple_cartesian; + using C2F = Cartesian_converter; + C2F c2f; + + using FT = AK::FT; + using Point = AK::Point_2; + using Vector = AK::Vector_2; + + AK::Aff_transformation_2 a_t1 = c2f(t1); + AK::Aff_transformation_2 a_t2 = c2f(t2); + + // The center of each box. + Point p1 = a_t1.transform( Point((FT(b1.xmin())+FT(b1.xmax()))/2, (FT(b1.ymin())+FT(b1.ymax()))/2) ); + Point p2 = a_t2.transform( Point((FT(b2.xmin())+FT(b2.xmax()))/2, (FT(b2.ymin())+FT(b2.ymax()))/2) ); + + // Half width vectors of each box. + const Vector A[2] = { + a_t1.transform(Vector(b1.x_span()/2, 0)), + a_t1.transform(Vector(0, b1.y_span()/2)) + }; + + const Vector B[2] = { + a_t2.transform(Vector(b2.x_span()/2, 0)), + a_t2.transform(Vector(0, b2.y_span()/2)) + }; + + const Vector dir = p2 - p1; + + // Test separation on the 2 axes of A. + for(int i=0; i<2; ++i) + { + // Project the center and half-width along each axis and compare the distance between centers with the sum of half-width projections. + const Vector& axis = A[i]; + const FT dist = CGAL::abs(dir * axis); + + const FT ra = axis.squared_length(); + const FT rb = CGAL::abs(B[0]*axis) + CGAL::abs(B[1]*axis); + if(dist > ra + rb) return false; + } + + // Test separation on the 2 axes of B. + for(int i = 0; i < 2; ++i) + { + const Vector& axis = B[i]; + const FT dist = CGAL::abs(dir * axis); + + const FT rb = axis.squared_length(); + const FT ra = CGAL::abs(A[0]*axis) + CGAL::abs(A[1]*axis); + if(dist > ra + rb) return false; + } + + // No separating axis among the 4 face normals. + return true; +} + +template +Bbox_3 compute_transformed_bbox(const CGAL::Aff_transformation_3& at, const Bbox_3& bbox, bool has_rotation) +{ + typedef Simple_cartesian AK; + typedef Cartesian_converter C2F; + C2F c2f; + + AK::Aff_transformation_3 a_at = c2f(at); + AK::FT xtrm[6] = { c2f((bbox.min)(0)), c2f((bbox.max)(0)), + c2f((bbox.min)(1)), c2f((bbox.max)(1)), + c2f((bbox.min)(2)), c2f((bbox.max)(2)) }; + + if(!has_rotation){ + AK::Point_3 ps[2]; + ps[0] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[4]) ); + ps[1] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[5]) ); + + return bbox_3(ps, ps+2); + } + + AK::Point_3 ps[8]; + ps[0] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[4]) ); + ps[1] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[5]) ); + ps[2] = a_at( AK::Point_3(xtrm[0], xtrm[3], xtrm[4]) ); + ps[3] = a_at( AK::Point_3(xtrm[0], xtrm[3], xtrm[5]) ); + + ps[4] = a_at( AK::Point_3(xtrm[1], xtrm[2], xtrm[4]) ); + ps[5] = a_at( AK::Point_3(xtrm[1], xtrm[2], xtrm[5]) ); + ps[6] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[4]) ); + ps[7] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[5]) ); + + return bbox_3(ps, ps+8); +} + +template +Bbox_3 compute_transformed_bbox(const CGAL::Aff_transformation_3& at, const Bbox_3& bbox) +{ + return compute_transformed_bbox(at, bbox, at.has_rotation()); +} + +template +Bbox_2 compute_transformed_bbox(const CGAL::Aff_transformation_2& at, const Bbox_2& bbox, bool has_rotation) +{ + typedef Simple_cartesian> AK; + typedef Cartesian_converter C2F; + C2F c2f; + + + AK::Aff_transformation_2 a_at = c2f(at); + AK::FT xtrm[4] = { c2f((bbox.min)(0)), c2f((bbox.max)(0)), + c2f((bbox.min)(1)), c2f((bbox.max)(1)) }; + + if(!has_rotation){ + AK::Point_2 ps[2]; + ps[0] = a_at( AK::Point_2(xtrm[0], xtrm[2]) ); + ps[1] = a_at( AK::Point_2(xtrm[1], xtrm[3]) ); + + return bbox_2(ps, ps+2); + } + + AK::Point_2 ps[4]; + ps[0] = a_at( AK::Point_2(xtrm[0], xtrm[2]) ); + ps[1] = a_at( AK::Point_2(xtrm[0], xtrm[3]) ); + ps[2] = a_at( AK::Point_2(xtrm[1], xtrm[2]) ); + ps[3] = a_at( AK::Point_2(xtrm[1], xtrm[3]) ); + + return bbox_2(ps, ps+4); +} + +template +Bbox_2 compute_transformed_bbox(const CGAL::Aff_transformation_2& at, const Bbox_2& bbox) +{ + return compute_transformed_bbox(at, bbox, at.has_rotation()); +} + } } //namespace CGAL::internal #endif //CGAL_INTERNAL_AABB_TREE_PRIMITIVE_HELPER diff --git a/AABB_tree/include/CGAL/AABB_trees/intersection.h b/AABB_tree/include/CGAL/AABB_trees/intersection.h new file mode 100644 index 000000000000..cee02613d126 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_trees/intersection.h @@ -0,0 +1,215 @@ +// Copyright (c) 2026 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Léo Valque + +#ifndef CGAL_AABB_TREES_INTERSECTIONS_H +#define CGAL_AABB_TREES_INTERSECTIONS_H + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef CGAL_LINKED_WITH_TBB +#include +#endif + +/// \file AABB_trees/intersection.h + +namespace CGAL{ +namespace AABB_trees { + /// \ingroup PkgAABBTreeRef + /// + /// \brief tests if at least two primitives each from an AABB tree intersect. + /// + /// \cgalNamedParamsBegin + /// \cgalParamNBegin{concurrency_tag} + /// \cgalParamDescription{a tag indicating if the task should be done using one or several threads.} + /// \cgalParamType{Either `CGAL::Sequential_tag`, or `CGAL::Parallel_tag`, or `CGAL::Parallel_if_available_tag`} + /// \cgalParamDefault{`CGAL::Sequential_tag`} + /// \cgalParamExtra{`np1` only} + /// \cgalParamNEnd + /// \cgalParamNBegin{transformation} + /// \cgalParamDescription{An affine transformation apply to `tree1` (`tree2`)} + /// \cgalParamType{`CGAL::Aff_transformation_3` where `Kernel` is deduced from `AABBTree1::AABB_traits::Point`, using `Kernel_traits`} + /// \cgalParamDefault{An identity transformation} + /// \cgalParamNEnd + /// \cgalParamNBegin{use_inverse_transformation} + /// \cgalParamDescription{If true, the inverse of the transformations are used to accelerate the queries. + /// \cgalParamType{`CGAL::Tag_true` or `CGAL::Tag_false`} + /// \cgalParamDefault{`CGAL::Tag_true`} + /// \cgalParamExtra{The result may be less accurate than using the original transformations.} + /// \cgalParamExtra{`np1` only} + /// \cgalParamNEnd + /// \cgalNamedParamsEnd + /// + /// \warning The `Do_intersect` functors of the `AABBTraits` of both AABB trees should accept the Datum type of the other tree as the Query type. + /// + /// \return `true` if at least one primitive of `tree1` intersects + /// a primitive of `tree2`, and `false` otherwise. + template< typename AABBTree1, + typename AABBTree2, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> + bool do_intersect(const AABBTree1 &tree1, + const AABBTree2 &tree2, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) + { + using parameters::get_parameter; + using parameters::choose_parameter; + using parameters::is_default_parameter; + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + + // Early exit if one of the trees is empty + if(tree1.empty() || tree2.empty()) + return false; + + using Inverse_tag = typename internal_np::Lookup_named_param_def < + internal_np::use_inverse_transformation_t, + NamedParameters1, + Tag_true + > ::type; + + if constexpr(is_default_parameter::value && + is_default_parameter::value) + { + CGAL::internal::AABB_tree::Two_trees_do_intersect_traits traversal_traits(tree1.traits(), tree2.traits()); + CGAL::internal::AABB_tree::two_trees_traversal(tree1, tree2, traversal_traits); + return traversal_traits.is_intersection_found(); + } + else + { + using Kernel = typename Kernel_traits::Kernel; + // Get the dimension of the AABBTraits through the bbox and get the appropriate Aff_transformation + using Aff_tr = std::conditional_t< std::is_same_v, + Aff_transformation_2, + Aff_transformation_3>; + Aff_tr tr1 = choose_parameter(get_parameter(np1, internal_np::transformation), Aff_tr(Identity_transformation())); + Aff_tr tr2 = choose_parameter(get_parameter(np2, internal_np::transformation), Aff_tr(Identity_transformation())); + CGAL::internal::AABB_tree::Two_trees_do_intersect_traits_with_transformation + traversal_traits(tree1.traits(), tree2.traits(), tr1, tr2); + CGAL::internal::AABB_tree::two_trees_traversal(tree1, tree2, traversal_traits); + return traversal_traits.is_intersection_found(); + } + } + + /// \ingroup PkgAABBTreeRef + /// + /// \brief computes all pairs of intersecting primitive from two AABB trees. + /// + /// Both trees are traversed and all pairs of primitives that intersect are collected. + /// Each output element is a pair `(id1, id2)` where: + /// - `id1` is the ID of a primitive from `tree1` + /// - `id2` is the ID of a primitive from `tree2` + /// + /// \tparam AABBTree1 Type of the first AABB tree. + /// \tparam AABBTree2 Type of the second AABB tree. + /// \tparam OutputIterator Output iterator storing std::pair. + /// \tparam NamedParameters1 a sequence of \ref bgl_namedparameters "Named Parameters" + /// \tparam NamedParameters2 a sequence of \ref bgl_namedparameters "Named Parameters" + /// + /// \cgalNamedParamsBegin + /// \cgalParamNBegin{concurrency_tag} + /// \cgalParamDescription{a tag indicating if the task should be done using one or several threads.} + /// \cgalParamType{Either `CGAL::Sequential_tag`, or `CGAL::Parallel_tag`, or `CGAL::Parallel_if_available_tag`} + /// \cgalParamDefault{`CGAL::Sequential_tag`} + /// \cgalParamExtra{`np1` only} + /// \cgalParamNEnd + /// \cgalParamNBegin{transformation} + /// \cgalParamDescription{An affine transformation apply to `tree1` (`tree2`)} + /// \cgalParamType{`CGAL::Aff_transformation_3` where `Kernel` is the kernel associated with `AABBTree1::AABB_traits::Point` (`AABBTree2::AABB_traits::Point`)} + /// \cgalParamDefault{An identity transformation} + /// \cgalParamNEnd + /// \cgalNamedParamsEnd + /// + /// \see do_intersect() + template< typename AABBTree1, + typename AABBTree2, + typename OutputIterator, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> + void all_pairs_of_intersecting_primitives(const AABBTree1 &tree1, + const AABBTree2 &tree2, + OutputIterator out, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) + { + using parameters::get_parameter; + using parameters::choose_parameter; + using parameters::is_default_parameter; + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + + // Early exit if one of the trees is empty + if(tree1.empty() || tree2.empty()) + return; + + if constexpr(is_default_parameter::value && + is_default_parameter::value) + { + CGAL::internal::AABB_tree::Two_trees_listing_intersecting_primitives_traits traversal_traits(tree1.traits(), tree2.traits(), out); + CGAL::internal::AABB_tree::two_trees_traversal(tree1, tree2, traversal_traits); + } + else + { + using Kernel = typename Kernel_traits::Kernel; + // Get the dimension of the AABBTraits through the bbox and get the appropriate Aff_transformation + using Aff_tr = std::conditional_t< std::is_same_v, + Aff_transformation_2, + Aff_transformation_3>; + Aff_tr tr1 = choose_parameter(get_parameter(np1, internal_np::transformation), Aff_tr(Identity_transformation())); + Aff_tr tr2 = choose_parameter(get_parameter(np2, internal_np::transformation), Aff_tr(Identity_transformation())); + CGAL::internal::AABB_tree::Two_trees_listing_intersecting_primitives_traits_with_transformation + traversal_traits(tree1.traits(), tree2.traits(), out, tr1, tr2); + CGAL::internal::AABB_tree::two_trees_traversal(tree1, tree2, traversal_traits); + } + } + + /// \ingroup PkgAABBTreeRef + /// + /// \brief computes all pairs of primitives from a single AABB tree that are intersecting. + /// + /// \note Whether two objects that share a common subfeature (e.g., two triangles sharing an edge) are considered to intersect depends on the primitive type used. + /// + /// Intersections of a primitive with itself are not reported, and each intersecting + /// pair of distinct primitives is reported only once. + template< typename Concurrency_tag = Sequential_tag, + typename AABBTree, + typename OutputIterator> + void all_pairs_of_intersecting_primitives(const AABBTree &tree, + OutputIterator out) + { + CGAL::internal::AABB_tree::Listing_self_intersecting_primitives_traits traversal_traits(tree.traits(), out); + CGAL::internal::AABB_tree::two_trees_traversal(tree, tree, traversal_traits); + } + +}} // end namespace CGAL::AABB_trees + +#endif diff --git a/AABB_tree/test/AABB_tree/CMakeLists.txt b/AABB_tree/test/AABB_tree/CMakeLists.txt index 1e13ff0ba265..750fc7bf2ddf 100644 --- a/AABB_tree/test/AABB_tree/CMakeLists.txt +++ b/AABB_tree/test/AABB_tree/CMakeLists.txt @@ -14,3 +14,11 @@ file( foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() + +find_package(TBB QUIET) +include(CGAL_TBB_support) +if(TARGET CGAL::TBB_support) + target_link_libraries(aabb_test_two_trees_intersection PRIVATE CGAL::TBB_support) +else() + message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.") +endif() \ No newline at end of file diff --git a/AABB_tree/test/AABB_tree/aabb_intersection_test.cpp b/AABB_tree/test/AABB_tree/aabb_intersection_test.cpp new file mode 100644 index 000000000000..8fddeac3f9ad --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_intersection_test.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; + +void test(const std::string fname1, const std::string fname2, std::size_t nb_inter) +{ + typedef CGAL::Surface_mesh Surface_mesh; + typedef CGAL::AABB_face_graph_triangle_primitive Primitive; + typedef CGAL::AABB_traits_3 Traits; + typedef CGAL::AABB_tree Tree; + + Surface_mesh sm1, sm2; + CGAL::IO::read_polygon_mesh(fname1, sm1); + CGAL::IO::read_polygon_mesh(fname2, sm2); + + Tree tree1(faces(sm1).begin(), faces(sm1).end(), sm1); + Tree tree2(faces(sm2).begin(), faces(sm2).end(), sm2); + + std::vector< std::pair > inter; + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter)); + assert( CGAL::AABB_trees::do_intersect(tree1, tree2) == (nb_inter!=0) ); + assert( inter.size() == nb_inter ); +} + +int main() +{ + test(CGAL::data_file_path("meshes/cube.off"), CGAL::data_file_path("meshes/cylinder.off"), 121); + test(CGAL::data_file_path("meshes/cube.off"), CGAL::data_file_path("meshes/femur.off"), 0); + test(CGAL::data_file_path("meshes/cube.off"), CGAL::data_file_path("meshes/pinion_small.off"), 0); + test(CGAL::data_file_path("meshes/cylinder.off"), CGAL::data_file_path("meshes/femur.off"), 0); + test(CGAL::data_file_path("meshes/cylinder.off"), CGAL::data_file_path("meshes/pinion_small.off"), 0); + test(CGAL::data_file_path("meshes/femur.off"), CGAL::data_file_path("meshes/pinion_small.off"), 905); + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_triangle_soup.cpp b/AABB_tree/test/AABB_tree/aabb_test_triangle_soup.cpp new file mode 100644 index 000000000000..198f39fa4535 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_triangle_soup.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using K = CGAL::Simple_cartesian; +using P = K::Point_3; +using L = K::Line_3; +using T = K::Triangle_3; +using R = K::Ray_3; + +using PointRange = std::vector

; +using FaceRange = std::vector >; +using Primitive = CGAL::AABB_indexed_triangle_primitive_3; +using AABB_triangle_traits = CGAL::AABB_traits_3; +using Tree = CGAL::AABB_tree; +using Point_and_primitive_id = Tree::Point_and_primitive_id; + +int main() +{ + P a(0.0, 0.0, 0.0); + P b(0.0, 1.0, 0.0); + P c(1.0, 0.0, 0.0); + P d(1.0, 1.0, 0.0); + P e(2.0, 0.0, 0.0); + P f(2.0, 1.0, 0.0); + + PointRange points = { a, b, c, d, e, f }; + + FaceRange triangles; + triangles.push_back({ 0, 2, 1 }); + triangles.push_back({ 1, 2, 3 }); + triangles.push_back({ 3, 2, 4 }); + triangles.push_back({ 3, 4, 5 }); + + // constructs AABB tree + std::vector indices(triangles.size(), 0); + std::iota(indices.begin(), indices.end(), 0); + Tree tree(indices.begin(), indices.end(), points, triangles); + + // point sampling + Point_and_primitive_id id; + id = tree.closest_point_and_primitive(P(0.5, 0.4, 0)); + assert(id.second == 0); + id = tree.closest_point_and_primitive(P(0.5, 0.6, 0)); + assert(id.second == 1); + id = tree.closest_point_and_primitive(P(1.5, 0.4, 0)); + assert(id.second == 2); + id = tree.closest_point_and_primitive(P(1.5, 0.6, 0)); + assert(id.second == 3); + id = tree.closest_point_and_primitive(P(3.0, 0.5, 0)); + assert(id.second == 3); + + R ray(P(5.5, 0.5, 0), P(1.5, 0.4, 0)); + auto intersection = tree.first_intersection(ray); + + assert(intersection.has_value()); + assert(intersection->second == 3); + + std::vector

pts1, pts2; + std::vector > trs1, trs2; + if(!CGAL::IO::read_polygon_soup(CGAL::data_file_path("meshes/knot1.off"), pts1, trs1)){ + std::cout << "error reading knot1" << std::endl; + exit(1); + } + if(!CGAL::IO::read_polygon_soup(CGAL::data_file_path("meshes/lion.off"), pts2, trs2)){ + std::cout << "error reading lion" << std::endl; + exit(1); + } + + Tree tree1(trs1.begin(), trs1.end(), pts1, trs1); + Tree tree2(trs2.begin(), trs2.end(), pts2, trs2); + tree1.build(); + tree2.build(); + + std::vector< std::pair > inter; + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter)); + assert(inter.size() == 1191); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_two_trees_intersection.cpp b/AABB_tree/test/AABB_tree/aabb_test_two_trees_intersection.cpp new file mode 100644 index 000000000000..a7d8e766823c --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_two_trees_intersection.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +using Epick = CGAL::Exact_predicates_inexact_constructions_kernel; +using Epeck = CGAL::Exact_predicates_exact_constructions_kernel; +using SCD = CGAL::Simple_cartesian; + +template +void test() +{ + using P = typename K::Point_3; + using V = typename K::Vector_3; + using M = CGAL::Surface_mesh

; + using fd = typename boost::graph_traits::face_descriptor; + using Primitive = CGAL::AABB_face_graph_triangle_primitive; + using Traits = CGAL::AABB_traits_3; + using Tree = CGAL::AABB_tree; + using Aff_tr = CGAL::Aff_transformation_3; + + M m1, m2; + if(!CGAL::IO::read_polygon_mesh(CGAL::data_file_path("meshes/knot1.off"), m1)){ + std::cout << "error reading knot1" << std::endl; + exit(1); + } + if(!CGAL::IO::read_polygon_mesh(CGAL::data_file_path("meshes/lion.off"), m2)){ + std::cout << "error reading lion" << std::endl; + exit(1); + } + + Tree tree1(faces(m1).first, faces(m1).second, m1); + Tree tree2(faces(m2).first, faces(m2).second, m2); + tree1.build(); + tree2.build(); + + std::vector< std::pair > inter; + assert(CGAL::AABB_trees::do_intersect(tree1, tree2)); + + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter)); + assert(inter.size() == 1191); + inter.clear(); + + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter), CGAL::parameters::transformation(Aff_tr(CGAL::Translation(), V(1,0,0)))); + assert(inter.size() == 0); + inter.clear(); + + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter), CGAL::parameters::transformation(Aff_tr(0, 1, 0, 1, 0, 0, 0, 0, 1, 1))); + assert(inter.size() == 1289); +} + +#ifdef CGAL_LINKED_WITH_TBB +template +void test_parallel() +{ + using P = typename K::Point_3; + using V = typename K::Vector_3; + using M = CGAL::Surface_mesh

; + using fd = typename boost::graph_traits::face_descriptor; + using Primitive = CGAL::AABB_face_graph_triangle_primitive; + using Traits = CGAL::AABB_traits_3; + using Tree = CGAL::AABB_tree; + using Aff_tr = CGAL::Aff_transformation_3; + + M m1, m2; + if(!CGAL::IO::read_polygon_mesh(CGAL::data_file_path("meshes/knot1.off"), m1)){ + std::cout << "error reading knot1" << std::endl; + exit(1); + } + if(!CGAL::IO::read_polygon_mesh(CGAL::data_file_path("meshes/lion.off"), m2)){ + std::cout << "error reading lion" << std::endl; + exit(1); + } + + Tree tree1(faces(m1).first, faces(m1).second, m1); + Tree tree2(faces(m2).first, faces(m2).second, m2); + tree1.template build(); + tree2.template build(); + + tbb::concurrent_vector< std::pair > inter; + assert(CGAL::AABB_trees::do_intersect(tree1, tree2, CGAL::parameters::concurrency_tag(CGAL::Parallel_tag()))); + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag())); + assert(inter.size() == 1191); + inter.clear(); + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag()).transformation(Aff_tr(CGAL::Translation(), V(0.5,0,0)))); + assert(inter.size() == 280); + inter.clear(); + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, std::back_inserter(inter), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag()).transformation(Aff_tr(0, 1, 0, 1, 0, 0, 0, 0, 1, 1))); + assert(inter.size() == 1289); +} +#endif + +int main(){ + test(); + test(); + test(); +#ifdef CGAL_LINKED_WITH_TBB + test_parallel(); + test_parallel(); + test_parallel(); +#endif + return 0; +} diff --git a/AABB_tree/test/AABB_tree/address_of_local.cpp b/AABB_tree/test/AABB_tree/address_of_local.cpp new file mode 100644 index 000000000000..3e800674bacb --- /dev/null +++ b/AABB_tree/test/AABB_tree/address_of_local.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::Segment_2 Segment_2; +typedef K::Point_2 Point_2; + +typedef CGAL::Polygon_2 Polygon_2; +typedef Polygon_2::Edge_const_iterator Edge_const_iterator; +typedef CGAL::internal::Source_of_segment_2_iterator_property_map Sosi_polygon_map; + +typedef std::vector::const_iterator Seg_const_iterator; +typedef CGAL::internal::Source_of_segment_2_iterator_property_map Sosi_segment_map; + +int main() +{ + Polygon_2 poly; + poly.push_back(Point_2(0,0)); + poly.push_back(Point_2(1,0)); + poly.push_back(Point_2(1,1)); + + Edge_const_iterator it = poly.edges_begin(); + + Sosi_polygon_map sosi_polygon_map; + + std::vector segs; + segs.push_back(Segment_2(Point_2(0,0), Point_2(1,0))); + segs.push_back(Segment_2(Point_2(1,0), Point_2(1,1))); + segs.push_back(Segment_2(Point_2(1,1), Point_2(1,0))); + + Sosi_segment_map sosi_segment_map; + + const Point_2& p = get(sosi_polygon_map, it); + const Point_2& q = get(sosi_segment_map, segs.begin()); + assert(p == poly[0]); + assert(q == segs[0].source()); + return 0; +} diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_2.h b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_2.h index 16395549ab34..d73b8e8407cd 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_2.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_2.h @@ -169,6 +169,7 @@ class Aff_transformationC2 bool is_scaling() const { return this->Ptr()->is_scaling(); } bool is_reflection() const { return this->Ptr()->is_reflection(); } bool is_rotation() const { return this->Ptr()->is_rotation(); } + bool has_rotation() const { return this->Ptr()->has_rotation(); } FT cartesian(int i, int j) const { return this->Ptr()->cartesian(i,j); } FT homogeneous(int i, int j) const { return cartesian(i,j); } diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_3.h b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_3.h index d696e1917a30..e4596fb552da 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_3.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_3.h @@ -117,6 +117,12 @@ class Aff_transformationC3 m31, m32, m33, m34)); } + // General form: with translation + Aff_transformationC3(const Aff_transformation_repC3 &b) + { + initialize_with(b); + } + Point_3 transform(const Point_3 &p) const { return this->Ptr()->transform(p); } @@ -156,6 +162,7 @@ class Aff_transformationC3 bool is_translation() const { return this->Ptr()->is_translation(); } bool is_scaling() const { return this->Ptr()->is_scaling(); } + bool has_rotation() const { return this->Ptr()->has_rotation(); } FT cartesian(int i, int j) const { return this->Ptr()->cartesian(i,j); } diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_2.h b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_2.h index afce7dc25edf..260bfc9039db 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_2.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_2.h @@ -64,6 +64,7 @@ class Aff_transformation_rep_baseC2 virtual bool is_scaling() const { return false; } virtual bool is_rotation() const { return false; } virtual bool is_reflection() const { return false; } + virtual bool has_rotation() const { return false; } virtual FT cartesian(int i, int j) const = 0; virtual std::ostream &print(std::ostream &os) const = 0; @@ -123,6 +124,11 @@ friend class Reflection_repC2; t21 * dir.dx() + t22 * dir.dy()); } + virtual bool has_rotation() const + { + return !(is_zero(t12) && is_zero(t21)); + } + // Note that Aff_transformation is not defined yet, // so the following 6 functions have to be implemented later... Aff_transformation_2 inverse() const; diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_3.h b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_3.h index 2e441e194d3b..5c1166d5e18b 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_3.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Aff_transformation_rep_3.h @@ -59,6 +59,8 @@ class Aff_transformation_rep_baseC3 virtual bool is_translation() const { return false; } virtual bool is_scaling() const { return false; } + virtual bool has_rotation() const { return false; } + virtual FT cartesian(int i, int j) const = 0; virtual std::ostream &print(std::ostream &os) const = 0; }; @@ -135,7 +137,7 @@ class Aff_transformation_repC3 virtual Plane_3 transform(const Plane_3& p) const { - if (is_even()) + if (is_even()) return Plane_3(transform(p.point()), transpose().inverse().transform(p.orthogonal_direction())); else @@ -143,6 +145,10 @@ class Aff_transformation_repC3 - transpose().inverse().transform(p.orthogonal_direction())); } + virtual bool has_rotation() const + { + return !(is_zero(t11) && is_zero(t12) && is_zero(t13) && is_zero(t21) && is_zero(t23) && is_zero(t31) && is_zero(t32)); + } // Note that Aff_transformation is not defined yet, // so the following 6 functions have to be implemented @@ -157,8 +163,8 @@ class Aff_transformation_repC3 virtual bool is_even() const { return sign_of_determinant(t11, t12, t13, - t21, t22, t23, - t31, t32, t33) == POSITIVE; + t21, t22, t23, + t31, t32, t33) == POSITIVE; } diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Reflection_rep_2.h b/Cartesian_kernel/include/CGAL/Cartesian/Reflection_rep_2.h index 7a89746ffba5..022e3efa7814 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Reflection_rep_2.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Reflection_rep_2.h @@ -152,6 +152,11 @@ typedef typename CGAL::Line_2 Line_2; return true; } + bool has_rotation() const + { + return true; + } + FT cartesian(int i, int j) const { switch (i) diff --git a/Cartesian_kernel/include/CGAL/Cartesian/Rotation_rep_2.h b/Cartesian_kernel/include/CGAL/Cartesian/Rotation_rep_2.h index 2ea097c733ba..924a989f62f7 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/Rotation_rep_2.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/Rotation_rep_2.h @@ -150,6 +150,11 @@ friend class Reflection_repC2; return true; } + bool has_rotation() const + { + return true; + } + FT cartesian(int i, int j) const { switch (i) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/large_cube_coplanar.off b/Data/data/meshes/large_cube_coplanar.off similarity index 100% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/data/large_cube_coplanar.off rename to Data/data/meshes/large_cube_coplanar.off diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/small_spheres.off b/Data/data/meshes/small_spheres.off similarity index 100% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/data/small_spheres.off rename to Data/data/meshes/small_spheres.off diff --git a/Homogeneous_kernel/include/CGAL/Homogeneous/Aff_transformationH3.h b/Homogeneous_kernel/include/CGAL/Homogeneous/Aff_transformationH3.h index fa0ba377b4d3..35cc78ae3072 100644 --- a/Homogeneous_kernel/include/CGAL/Homogeneous/Aff_transformationH3.h +++ b/Homogeneous_kernel/include/CGAL/Homogeneous/Aff_transformationH3.h @@ -447,12 +447,13 @@ class Aff_transformationH3 // Scaling Aff_transformationH3(const Scaling&, const RT& num, const RT& den); - // General form + // General form with translation Aff_transformationH3( const RT& m00, const RT& m01, const RT& m02, const RT& m03, const RT& m10, const RT& m11, const RT& m12, const RT& m13, const RT& m20, const RT& m21, const RT& m22, const RT& m23, const RT& m33); + // General form without translation Aff_transformationH3( const RT& m00, const RT& m01, const RT& m02, const RT& m10, const RT& m11, const RT& m12, diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 22255b8075ad..d56ed1fc281b 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -4,6 +4,13 @@ Release date: December 2026 +### [2D and 3D Fast Intersection and Distance Computation (AABB Tree)](https://doc.cgal.org/6.3/Manual/packages.html#PkgAABBTree) +- `CGAL::AABB_tree::build()` now accepts an optional `Concurrency_tag` template parameter (`CGAL::Sequential_tag` by default). + When `CGAL::Parallel_tag` is specified, the tree construction is performed in parallel. +- Added the functions `CGAL::AABB_trees::do_intersect()` and `CGAL::AABB_trees::all_pairs_of_intersecting_primitives()`. + These functions respectively determine whether two AABB trees intersect and compute all pairs of intersecting primitives + between two AABB trees. + ### [2D Arrangements](https://doc.cgal.org/6.3/Manual/packages.html#PkgArrangementOnSurface2) - **Breaking change**: Enhanced the metadata traits-class decorators `Arr_counting_traits_2` and `Arr_tracing_traits_2`. Each is (still) parameterized with another traits class being decorated, but it does not inherit from it. In addition one can get and set a smart pointer to the class being decorated. diff --git a/Kernel_23/include/CGAL/Bbox_2.h b/Kernel_23/include/CGAL/Bbox_2.h index 0222cf947782..f80cd0ba7ad7 100644 --- a/Kernel_23/include/CGAL/Bbox_2.h +++ b/Kernel_23/include/CGAL/Bbox_2.h @@ -61,8 +61,9 @@ class Bbox_2 inline double ymin() const; inline double xmax() const; inline double ymax() const; - inline double x_span() const; - inline double y_span() const; + inline double x_span() const; + inline double y_span() const; + inline double squared_diagonal_length() const; inline double max BOOST_PREVENT_MACRO_SUBSTITUTION (int i) const; inline double min BOOST_PREVENT_MACRO_SUBSTITUTION (int i) const; @@ -70,6 +71,8 @@ class Bbox_2 inline Bbox_2 operator+(const Bbox_2 &b) const; inline Bbox_2& operator+=(const Bbox_2 &b); + inline int largest_span_index() const; + inline void dilate(int dist); inline void scale(double factor); }; @@ -102,6 +105,14 @@ inline double Bbox_2::y_span() const { return ymax() - ymin(); } +inline double Bbox_2::squared_diagonal_length() const { + return x_span()*x_span() + y_span()*y_span(); +} + +int Bbox_2::largest_span_index() const { + return (x_span()>=y_span()) ? 0 : 1; +} + inline bool Bbox_2::operator==(const Bbox_2 &b) const diff --git a/Kernel_23/include/CGAL/Bbox_3.h b/Kernel_23/include/CGAL/Bbox_3.h index dc750e4d0e0e..fa3bdd90bdcc 100644 --- a/Kernel_23/include/CGAL/Bbox_3.h +++ b/Kernel_23/include/CGAL/Bbox_3.h @@ -57,15 +57,16 @@ class Bbox_3 inline bool operator!=(const Bbox_3 &b) const; inline int dimension() const; - double xmin() const; - double ymin() const; - double zmin() const; - double xmax() const; - double ymax() const; - double zmax() const; - double x_span() const; - double y_span() const; - double z_span() const; + inline double xmin() const; + inline double ymin() const; + inline double zmin() const; + inline double xmax() const; + inline double ymax() const; + inline double zmax() const; + inline double x_span() const; + inline double y_span() const; + inline double z_span() const; + inline double squared_diagonal_length() const; inline double min BOOST_PREVENT_MACRO_SUBSTITUTION (int i) const; inline double max BOOST_PREVENT_MACRO_SUBSTITUTION (int i) const; @@ -73,6 +74,8 @@ class Bbox_3 inline double min_coord(int i) const { return (min)(i); } inline double max_coord(int i) const { return (max)(i); } + inline int largest_span_index() const; + Bbox_3 operator+(const Bbox_3& b) const; Bbox_3& operator+=(const Bbox_3& b); @@ -122,6 +125,14 @@ inline double Bbox_3::z_span() const { return zmax() - zmin(); } +inline double Bbox_3::squared_diagonal_length() const { + return x_span()*x_span() + y_span()*y_span() + z_span()*z_span(); +} + +inline int Bbox_3::largest_span_index() const { + return (x_span()>=y_span()) ? ((x_span()>=z_span()) ? 0 : 2) : ((y_span()>=z_span()) ? 1 : 2); +} + inline bool Bbox_3::operator==(const Bbox_3 &b) const diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_collision_detector_2.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_collision_detector_2.h index 28e51503f8aa..55c22ddbcf79 100644 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_collision_detector_2.h +++ b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_collision_detector_2.h @@ -15,9 +15,10 @@ #include -#include -#include -#include +#include +#include +#include +#include namespace CGAL { @@ -35,10 +36,10 @@ class AABB_collision_detector_2 typedef typename CGAL::Polygon_with_holes_2 Polygon_with_holes_2; typedef typename Polygon_2::Edge_const_iterator Edge_iterator; - typedef AABB_segment_2_primitive + typedef AABB_segment_primitive_2 Tree_segment_2; - typedef Minkowski_sum::AABB_traits_2 Tree_traits; - typedef AABB_tree_with_join Tree_2; + typedef AABB_traits_2 Tree_traits; + typedef AABB_tree Tree_2; public: AABB_collision_detector_2(const Polygon_with_holes_2& p, @@ -70,7 +71,8 @@ class AABB_collision_detector_2 // completely inside of the other one. Q is translated by t. bool check_collision(const Point_2 &t) { - if (m_stationary_tree.do_intersect(m_translating_tree, t)) return true; + if(AABB_trees::do_intersect(m_translating_tree, m_stationary_tree, parameters::transformation(Aff_transformation_2(Translation(), t-ORIGIN)))) + return true; // If t_q is inside of P, or t_p is inside of Q, one polygon is completely // inside of the other. diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_node_with_join.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_node_with_join.h deleted file mode 100644 index 27a3bd4679dd..000000000000 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_node_with_join.h +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) 2008 INRIA Sophia-Antipolis (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Camille Wormser, Pierre Alliez, Stephane Tayeb - -#ifndef CGAL_AABB_NODE_WITH_JOIN_H -#define CGAL_AABB_NODE_WITH_JOIN_H - -#include - - -#include -#include -#include -#include -#include - -namespace CGAL { - -/** - * @class AABB_node_with_join - * - * - */ -template -class AABB_node_with_join -{ -public: - typedef typename AABBTraits::Bounding_box Bounding_box; - - /// Constructor - AABB_node_with_join() - : m_bbox() - , m_p_left_child(nullptr) - , m_p_right_child(nullptr) { }; - - /// Non virtual Destructor - /// Do not delete children because the tree hosts and delete them - ~AABB_node_with_join() { }; - - /// Returns the bounding box of the node - const Bounding_box& bbox() const { return m_bbox; } - - /** - * @brief Builds the tree by recursive expansion. - * @param first the first primitive to insert - * @param last the last primitive to insert - * @param range the number of primitive of the range - * - * [first,last[ is the range of primitives to be added to the tree. - */ - template - void expand(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond, - const std::size_t range, - const AABBTraits&); - - /** - * @brief General traversal query - * @param query the query - * @param traits the traversal traits that define the traversal behavior - * @param nb_primitives the number of primitive - * - * General traversal query. The traits class allows using it for the various - * traversal methods we need: listing, counting, detecting intersections, - * drawing the boxes. - */ - template - void traversal(const Query& query, - Traversal_traits& traits, - const std::size_t nb_primitives) const; - - /** - * @param other_node root node of a tree which we want to traverse in parallel - * @param traits the traversal traits that define the traversal behavior - * @param nb_primitives the number of primitives in this tree - * @param nb_primitives_other the number of primitives in the other tree - * @param first_stationary if true, the other_node is the translatable tree's root - * - * General traversal query for two trees. - */ - template - void traversal(const AABB_node_with_join &other_node, - Traversal_traits &traits, - const std::size_t nb_primitives, - const std::size_t nb_primitives_other, - bool first_stationary) const; - -private: - typedef AABBTraits AABB_traits; - typedef AABB_node_with_join Node; - typedef typename AABB_traits::Primitive Primitive; - - /// Helper functions - const Node& left_child() const - { return *static_cast(m_p_left_child); } - const Node& right_child() const - { return *static_cast(m_p_right_child); } - const Primitive& left_data() const - { return *static_cast(m_p_left_child); } - const Primitive& right_data() const - { return *static_cast(m_p_right_child); } - - Node& left_child() { return *static_cast(m_p_left_child); } - Node& right_child() { return *static_cast(m_p_right_child); } - Primitive& left_data() { return *static_cast(m_p_left_child); } - Primitive& right_data() { return *static_cast(m_p_right_child); } - -private: - /// node bounding box - Bounding_box m_bbox; - - /// children nodes, either pointing towards children (if children are not leaves), - /// or pointing toward input primitives (if children are leaves). - void *m_p_left_child; - void *m_p_right_child; - -private: - // Disabled copy constructor & assignment operator - typedef AABB_node_with_join Self; - AABB_node_with_join(const Self& src); - Self& operator=(const Self& src); - -}; // end class AABB_node_with_join - - -template -template -void -AABB_node_with_join::expand(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond, - const std::size_t range, - const Tr& traits) -{ - m_bbox = traits.compute_bbox_object()(first, beyond); - - // sort primitives along longest axis aabb - traits.split_primitives_object()(first, beyond, m_bbox); - - switch(range) - { - case 2: - m_p_left_child = &(*first); - m_p_right_child = &(*(++first)); - break; - case 3: - m_p_left_child = &(*first); - m_p_right_child = static_cast(this)+1; - right_child().expand(first+1, beyond, 2,traits); - break; - default: - const std::size_t new_range = range/2; - m_p_left_child = static_cast(this) + 1; - m_p_right_child = static_cast(this) + new_range; - left_child().expand(first, first + new_range, new_range,traits); - right_child().expand(first + new_range, beyond, range - new_range,traits); - } -} - - -template -template -void -AABB_node_with_join::traversal(const Query& query, - Traversal_traits& traits, - const std::size_t nb_primitives) const -{ - // Recursive traversal - switch(nb_primitives) - { - case 2: - traits.intersection(query, left_data()); - if( traits.go_further() ) - { - traits.intersection(query, right_data()); - } - break; - case 3: - traits.intersection(query, left_data()); - if( traits.go_further() && traits.do_intersect(query, right_child()) ) - { - right_child().traversal(query, traits, 2); - } - break; - default: - if( traits.do_intersect(query, left_child()) ) - { - left_child().traversal(query, traits, nb_primitives/2); - if( traits.go_further() && traits.do_intersect(query, right_child()) ) - { - right_child().traversal(query, traits, nb_primitives-nb_primitives/2); - } - } - else if( traits.do_intersect(query, right_child()) ) - { - right_child().traversal(query, traits, nb_primitives-nb_primitives/2); - } - } -} - -template -template -void -AABB_node_with_join::traversal(const AABB_node_with_join &other_node, - Traversal_traits &traits, - const std::size_t nb_primitives, - const std::size_t nb_primitives_other, - bool first_stationary) const -{ - if (nb_primitives >= nb_primitives_other) - { - switch(nb_primitives) - { - case 2: // Both trees contain 2 primitives, test all pairs - traits.intersection(left_data(), other_node.left_data(), first_stationary); - if (!traits.go_further()) return; - traits.intersection(right_data(), other_node.right_data(), first_stationary); - if (!traits.go_further()) return; - traits.intersection(right_data(), other_node.left_data(), first_stationary); - if (!traits.go_further()) return; - traits.intersection(left_data(), other_node.right_data(), first_stationary); - break; - - case 3: // This tree contains 3 primitives, the other 3 or 2 - // Both left children are primitives: - traits.intersection(left_data(), other_node.left_data(), first_stationary); - if (!traits.go_further()) return; - - // Test left child against all right leaves of the other tree - if (nb_primitives_other == 2) - { - traits.intersection(left_data(), other_node.right_data(), first_stationary); - } - else - { - if (traits.do_intersect(left_data(), other_node.right_child(), first_stationary)) - { - traits.intersection(left_data(), other_node.right_child().left_data(), first_stationary); - if (!traits.go_further()) return; - traits.intersection(left_data(), other_node.right_child().right_data(), first_stationary); - } - } - if (!traits.go_further()) return; - - // Test right child against the other node - if(traits.do_intersect(right_child(), other_node, first_stationary)) - { - right_child().traversal(other_node, traits, 2, nb_primitives_other, first_stationary); - } - break; - - default: // This tree has two node-children, test both against the other node - if( traits.do_intersect(left_child(), other_node, first_stationary) ) - { - left_child().traversal(other_node, traits, nb_primitives/2, nb_primitives_other, first_stationary); - } - if (!traits.go_further()) return; - if( traits.do_intersect(right_child(), other_node, first_stationary) ) - { - right_child().traversal(other_node, traits, nb_primitives-nb_primitives/2, nb_primitives_other, first_stationary); - } - } - } - else - { - // The other node contains more primitives. Call this method the other way around: - other_node.traversal(*this, traits, nb_primitives_other, nb_primitives, !first_stationary); - } -} - -} // end namespace CGAL - -#endif // CGAL_AABB_NODE_WITH_JOIN_H diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_segment_2_primitive.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_segment_2_primitive.h deleted file mode 100644 index 59b5f2080218..000000000000 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_segment_2_primitive.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2015 Tel-Aviv University (Israel). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s): Sebastian Morr - -#ifndef CGAL_AABB_SEGMENT_2_PRIMITIVE_H -#define CGAL_AABB_SEGMENT_2_PRIMITIVE_H - -#include - - -namespace CGAL { - -// Wraps around a Segment_2 and provides its iterator as Id -template -class AABB_segment_2_primitive -{ - -public: - - typedef Iterator_ Id; - typedef typename GeomTraits::Segment_2 Datum; - typedef typename GeomTraits::Point_2 Point; - typedef ContainerType Container; - - AABB_segment_2_primitive() {} - - AABB_segment_2_primitive(Id it) : m_it(it) - { - } - - AABB_segment_2_primitive(const AABB_segment_2_primitive& primitive) = default; - AABB_segment_2_primitive& operator=(const AABB_segment_2_primitive& primitive) = default; - - const Id &id() const - { - return m_it; - } - - const Datum datum() const - { - return *m_it; - } - - // Return a point on the primitive - Point reference_point() const - { - return m_it->source(); - } - -private: - - Id m_it; - -}; - -} // namespace CGAL - -#endif diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traits_2.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traits_2.h deleted file mode 100644 index 8317616c9a00..000000000000 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traits_2.h +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2015 Tel-Aviv University (Israel). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s): Sebastian Morr - -#ifndef CGAL_AABB_TRAITS_2_H -#define CGAL_AABB_TRAITS_2_H - -#include -#include - - -namespace CGAL { - -namespace Minkowski_sum { - -template -class AABB_traits_2 - :public Search_traits_2 -{ - -public: - - typedef AABB_primitive_ Primitive; - typedef typename Primitive::Id Id; - typedef typename Primitive::Datum Datum; - typedef typename Primitive::Container Container; - - typedef typename GeomTraits::Point_2 Point; - typedef typename GeomTraits::Vector_2 Vector_2; - typedef typename CGAL::Bbox_2 Bounding_box; - - typedef typename std::pair Object_and_primitive_id; - typedef typename std::pair Point_and_primitive_id; - - // Types for AABB_tree - typedef typename GeomTraits::FT FT; - typedef typename GeomTraits::Point_2 Point_2; - typedef typename GeomTraits::Circle_2 Circle_2; - typedef typename GeomTraits::Iso_rectangle_2 Iso_rectangle_2; - typedef typename GeomTraits::Construct_center_2 Construct_center_2; - typedef typename GeomTraits::Construct_iso_rectangle_2 Construct_iso_cuboid_2; - typedef typename GeomTraits::Construct_min_vertex_2 Construct_min_vertex_2; - typedef typename GeomTraits::Construct_max_vertex_2 Construct_max_vertex_2; - typedef typename GeomTraits::Compute_squared_radius_2 Compute_squared_radius_2; - typedef typename GeomTraits::Cartesian_const_iterator_2 - Cartesian_const_iterator_2; - typedef typename GeomTraits::Construct_cartesian_const_iterator_2 - Construct_cartesian_const_iterator_2; - - AABB_traits_2(const Point &translation_point): m_translation_point( - translation_point) - { - m_interval_x = Interval_nt(to_interval(translation_point.x())); - m_interval_y = Interval_nt(to_interval(translation_point.y())); - }; - - AABB_traits_2() - { - m_translation_point = Point(0, 0); - m_interval_x = Interval_nt(0); - m_interval_y = Interval_nt(0); - }; - - Interval_nt get_interval_x() const - { - return m_interval_x; - } - - Interval_nt get_interval_y() const - { - return m_interval_y; - } - - Point get_translation_point() const - { - return m_translation_point; - } - - // Put the n/2 smallest primitives in the front, the n/2 largest primitives - // in the back. They are compared along the bbox' longest axis. - class Split_primitives - { - public: - template - void operator()(PrimitiveIterator first, - PrimitiveIterator beyond, - const Bounding_box &bbox) const - { - PrimitiveIterator middle = first + (beyond - first) / 2; - - if (bbox.xmax()-bbox.xmin() >= bbox.ymax()-bbox.ymin()) - { - std::nth_element(first, middle, beyond, AABB_traits_2::less_x); // sort along x - } - else - { - std::nth_element(first, middle, beyond, AABB_traits_2::less_y); // sort along y - } - } - }; - - Split_primitives split_primitives_object() const - { - return Split_primitives(); - } - - // Computes the bounding box of a set of primitives - class Compute_bbox - { - public: - template - Bounding_box operator()(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond) const - { - Bounding_box bbox = first->datum().bbox(); - - for (++first; first != beyond; ++first) - { - bbox = bbox + first->datum().bbox(); - } - - return bbox; - } - }; - - Compute_bbox compute_bbox_object() const - { - return Compute_bbox(); - } - - class Do_intersect - { - - private: - - AABB_traits_2 *m_traits; - - public: - - Do_intersect(AABB_traits_2 *_traits): m_traits(_traits) {} - - bool operator()(const Bounding_box &q, const Bounding_box &bbox) const - { - Interval_nt x1 = Interval_nt(q.xmin(), q.xmax()); - Interval_nt y1 = Interval_nt(q.ymin(), q.ymax()); - Interval_nt x2 = Interval_nt(bbox.xmin(), - bbox.xmax()) + m_traits->get_interval_x(); - Interval_nt y2 = Interval_nt(bbox.ymin(), - bbox.ymax()) + m_traits->get_interval_y(); - - return x1.do_overlap(x2) && y1.do_overlap(y2); - } - - bool operator()(const Primitive &q, const Bounding_box &bbox) const - { - Interval_nt x1 = Interval_nt(q.datum().bbox().xmin(), - q.datum().bbox().xmax()); - Interval_nt y1 = Interval_nt(q.datum().bbox().ymin(), - q.datum().bbox().ymax()); - Interval_nt x2 = Interval_nt(bbox.xmin(), - bbox.xmax()) + m_traits->get_interval_x(); - Interval_nt y2 = Interval_nt(bbox.ymin(), - bbox.ymax()) + m_traits->get_interval_y(); - - return x1.do_overlap(x2) && y1.do_overlap(y2); - } - - bool operator()(const Bounding_box &q, const Primitive &pr) const - { - Datum tr_pr = pr.datum().transform(typename GeomTraits::Aff_transformation_2( - Translation(), - Vector_2(ORIGIN, m_traits->get_translation_point()))); - - return do_overlap(q, tr_pr.bbox()); - } - - bool operator()(const Primitive &q, const Primitive &pr) const - { - Datum tr_pr = pr.datum().transform(typename GeomTraits::Aff_transformation_2( - Translation(), Vector_2(ORIGIN, m_traits->get_translation_point()))); - - if (!do_overlap(q.datum().bbox(), tr_pr.bbox())) - { - return false; - } - - return do_intersect(q.datum(), tr_pr); - } - }; - - Do_intersect do_intersect_object() - { - return Do_intersect(this); - } - -private: - - Point m_translation_point; - Interval_nt m_interval_x; - Interval_nt m_interval_y; - - // Comparison functions - static bool less_x(const Primitive &pr1, const Primitive &pr2) - { - return pr1.reference_point().x() < pr2.reference_point().x(); - } - - static bool less_y(const Primitive &pr1, const Primitive &pr2) - { - return pr1.reference_point().y() < pr2.reference_point().y(); - } -}; - -} // namespace Minkowski_sum - -} // namespace CGAL - -#endif diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traversal_traits_with_join.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traversal_traits_with_join.h deleted file mode 100644 index 50a500300756..000000000000 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_traversal_traits_with_join.h +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (c) 2008-2009 INRIA Sophia-Antipolis (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Camille Wormser, Pierre Alliez, Stephane Tayeb - -#ifndef CGAL_AABB_TRAVERSAL_TRAITS_WITH_JOIN_H -#define CGAL_AABB_TRAVERSAL_TRAITS_WITH_JOIN_H - -#include - - -#include -#include - -namespace CGAL { - -namespace internal { namespace AABB_tree_with_join { - -template -class Counting_output_iterator { - typedef Counting_output_iterator Self; - Integral_type* i; -public: - Counting_output_iterator(Integral_type* i_) : i(i_) {}; - - struct Proxy { - Proxy& operator=(const Value_type&) { return *this; }; - }; - - Proxy operator*() { - return Proxy(); - } - - Self& operator++() { - ++*i; - return *this; - } - - Self& operator++(int) { - ++*i; - return *this; - } -}; - -//------------------------------------------------------- -// Traits classes for traversal computation -//------------------------------------------------------- -/** - * @class First_intersection_traits - */ -template -class First_intersection_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - typedef - std::optional< typename AABBTraits::template Intersection_and_primitive_id::Type > - Result; -public: - First_intersection_traits(const AABBTraits& traits) - : m_result(), m_traits(traits) - {} - - bool go_further() const { - return !m_result; - } - - void intersection(const Query& query, const Primitive& primitive) - { - m_result = m_traits.intersection_object()(query, primitive); - } - - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits.do_intersect_object()(query, node.bbox()); - } - - Result result() const { return m_result; } - bool is_intersection_found() const { - return m_result; - } - -private: - Result m_result; - const AABBTraits& m_traits; -}; - - -/** - * @class Listing_intersection_traits - */ -template -class Listing_intersection_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - Listing_intersection_traits(Output_iterator out_it, const AABBTraits& traits) - : m_out_it(out_it), m_traits(traits) {} - - bool go_further() const { return true; } - - void intersection(const Query& query, const Primitive& primitive) - { - std::optional< typename AABBTraits::template Intersection_and_primitive_id::Type > - intersection = m_traits.intersection_object()(query, primitive); - - if(intersection) - { - *m_out_it++ = *intersection; - } - } - - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits.do_intersect_object()(query, node.bbox()); - } - -private: - Output_iterator m_out_it; - const AABBTraits& m_traits; -}; - - -/** - * @class Listing_primitive_traits - */ -template -class Listing_primitive_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - Listing_primitive_traits(Output_iterator out_it, const AABBTraits& traits) - : m_out_it(out_it), m_traits(traits) {} - - bool go_further() const { return true; } - - void intersection(const Query& query, const Primitive& primitive) - { - if( m_traits.do_intersect_object()(query, primitive) ) - { - *m_out_it++ = primitive.id(); - } - } - - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits.do_intersect_object()(query, node.bbox()); - } - -private: - Output_iterator m_out_it; - const AABBTraits& m_traits; -}; - - -/** - * @class First_primitive_traits - */ -template -class First_primitive_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - First_primitive_traits(const AABBTraits& traits) - : m_is_found(false) - , m_result() - , m_traits(traits) {} - - bool go_further() const { return !m_is_found; } - - void intersection(const Query& query, const Primitive& primitive) - { - if( m_traits.do_intersect_object()(query, primitive) ) - { - m_result = std::optional(primitive.id()); - m_is_found = true; - } - } - - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits.do_intersect_object()(query, node.bbox()); - } - - std::optional result() const { return m_result; } - bool is_intersection_found() const { return m_is_found; } - -private: - bool m_is_found; - std::optional m_result; - const AABBTraits& m_traits; -}; - -/** - * @class Do_intersect_traits - */ -template -class Do_intersect_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - Do_intersect_traits(const AABBTraits& traits) - : m_is_found(false), m_traits(traits) - {} - - bool go_further() const { return !m_is_found; } - - void intersection(const Query& query, const Primitive& primitive) - { - if( m_traits.do_intersect_object()(query, primitive) ) - m_is_found = true; - } - - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits.do_intersect_object()(query, node.bbox()); - } - - bool is_intersection_found() const { return m_is_found; } - -private: - bool m_is_found; - const AABBTraits& m_traits; -}; - - -/** - * @class Do_intersect_joined_traits - */ -template -class Do_intersect_joined_traits -{ - typedef typename AABBTraits::Point Point; - typedef typename AABBTraits::Primitive Primitive; - typedef AABB_node_with_join Node; - -public: - - Do_intersect_joined_traits(const Point &point) : m_is_found(false) - { - m_traits_ptr = new AABBTraits(point); - } - - bool go_further() const { return !m_is_found; } - - void intersection(const Primitive &primitive1, const Primitive &primitive2, bool first_stationary) - { - if (first_stationary) - { - if (m_traits_ptr->do_intersect_object()(primitive1, primitive2)) - { - m_is_found = true; - } - } - else - { - if (m_traits_ptr->do_intersect_object()(primitive2, primitive1)) - { - m_is_found = true; - } - } - } - - bool do_intersect(const Node &node_1, const Node &node_2, bool first_stationary) const - { - if (first_stationary) - { - return m_traits_ptr->do_intersect_object()(node_1.bbox(), node_2.bbox()); - } - else - { - return m_traits_ptr->do_intersect_object()(node_2.bbox(), node_1.bbox()); - } - } - - bool do_intersect(const Node &node_1, const Primitive &primitive2, bool first_stationary) const - { - if (first_stationary) - { - return m_traits_ptr->do_intersect_object()(node_1.bbox(), primitive2); - } - else - { - return m_traits_ptr->do_intersect_object()(primitive2, node_1.bbox()); - } - } - - bool do_intersect(const Primitive &primitive1, const Node &node_2, bool first_stationary) const - { - if (first_stationary) - { - return m_traits_ptr->do_intersect_object()(primitive1, node_2.bbox()); - } - else - { - return m_traits_ptr->do_intersect_object()(node_2.bbox(), primitive1); - } - } - - bool is_intersection_found() const { return m_is_found; } - - ~Do_intersect_joined_traits() { delete m_traits_ptr; } - -private: - - bool m_is_found; - AABBTraits *m_traits_ptr; -}; - - -/** - * @class Projection_traits - */ -template -class Projection_traits -{ - typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; - typedef typename AABBTraits::Primitive Primitive; - typedef typename AABBTraits::Bounding_box Bounding_box; - typedef typename AABBTraits::Primitive::Id Primitive_id; - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - typedef ::CGAL::AABB_node_with_join Node; - -public: - Projection_traits(const Point& hint, - const typename Primitive::Id& hint_primitive, - const AABBTraits& traits) - : m_closest_point(hint), - m_closest_primitive(hint_primitive), - m_traits(traits) - {} - - bool go_further() const { return true; } - - void intersection(const Point& query, const Primitive& primitive) - { - Point new_closest_point = m_traits.closest_point_object() - (query, primitive, m_closest_point); - if(new_closest_point != m_closest_point) - { - m_closest_primitive = primitive.id(); - m_closest_point = new_closest_point; // this effectively shrinks the sphere - } - } - - bool do_intersect(const Point& query, const Node& node) const - { - return m_traits.compare_distance_object() - (query, node.bbox(), m_closest_point) == CGAL::SMALLER; - } - - Point closest_point() const { return m_closest_point; } - Point_and_primitive_id closest_point_and_primitive() const - { - return Point_and_primitive_id(m_closest_point, m_closest_primitive); - } - -private: - Point m_closest_point; - typename Primitive::Id m_closest_primitive; - const AABBTraits& m_traits; -}; - -}}} // end namespace CGAL::internal::AABB_tree_with_join - -#endif // CGAL_AABB_TRAVERSAL_TRAITS_WITH_JOIN_H diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_tree_with_join.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_tree_with_join.h deleted file mode 100644 index 2be3e2d094d9..000000000000 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/AABB_tree_with_join.h +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright (c) 2008,2011 INRIA Sophia-Antipolis (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Camille Wormser, Pierre Alliez, Stephane Tayeb - -#ifndef CGAL_AABB_TREE_WITH_JOIN_H -#define CGAL_AABB_TREE_WITH_JOIN_H - -#include - - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CGAL_HAS_THREADS -#include -#endif - -/// \file AABB_tree.h - -namespace CGAL { - -/// \addtogroup PkgAABBTreeRef -/// @{ - - /** - * Class AABB_tree is a static data structure for efficient - * intersection and distance computations in 3D. It builds a - * hierarchy of axis-aligned bounding boxes (an AABB tree) from a set - * of 3D geometric objects, and can receive intersection and distance - * queries, provided that the corresponding predicates are - * implemented in the traits class AABBTraits. - * An instance of the class `AABBTraits` is internally stored. - * - * \sa `AABBTraits` - * \sa `AABBPrimitive` - * - */ - template - class AABB_tree_with_join - { - private: - // internal KD-tree used to accelerate the distance queries - typedef AABB_search_tree Search_tree; - - // type of the primitives container - typedef std::vector Primitives; - - public: - typedef AABBTraits AABB_traits; - - /// \name Types - ///@{ - - /// Number type returned by the distance queries. - typedef typename AABBTraits::FT FT; - - - /// Type of 3D point. - typedef typename AABBTraits::Point Point; - - /// Type of input primitive. - typedef typename AABBTraits::Primitive Primitive; - /// Identifier for a primitive in the tree. - typedef typename Primitive::Id Primitive_id; - /// Unsigned integral size type. - typedef typename Primitives::size_type size_type; - /// Type of bounding box. - typedef typename AABBTraits::Bounding_box Bounding_box; - /// - typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; - typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; - - /*! - An alias to `AABBTraits::Intersection_and_primitive_id` - */ - #ifdef DOXYGEN_RUNNING - template - using Intersection_and_primitive_id = AABBTraits::Intersection_and_primitive_id; - #else - template - struct Intersection_and_primitive_id { - typedef typename AABBTraits::template Intersection_and_primitive_id::Type Type; - }; - #endif - - - ///@} - - public: - /// \name Creation - ///@{ - - /// Constructs an empty tree, and initializes the internally stored traits - /// class using `traits`. - AABB_tree_with_join(const AABBTraits& traits = AABBTraits()); - - /** - * @brief Builds the datastructure from a sequence of primitives. - * @param first iterator over first primitive to insert - * @param beyond past-the-end iterator - * - * It is equivalent to constructing an empty tree and calling `insert(first,last,t...)`. - * The tree stays empty if the memory allocation is not successful. - */ - template - AABB_tree_with_join(InputIterator first, InputIterator beyond,T...); - - ///@} - - /// \name Operations - ///@{ - - /// Equivalent to calling `clear()` and then `insert(first,last,t...)`. - template - void rebuild(ConstPrimitiveIterator first, ConstPrimitiveIterator beyond,T...); - - /// Add a sequence of primitives to the set of primitives of the AABB tree. - /// `%InputIterator` is any iterator and the parameter pack `T` are any types - /// such that `Primitive` has a constructor with the following signature: - /// `Primitive(%InputIterator, T...)`. If `Primitive` is a model of the concept - /// `AABBPrimitiveWithSharedData`, a call to `AABBTraits::set_shared_data(t...)` - /// is made using the internally stored traits. - template - void insert(InputIterator first, InputIterator beyond,T...); - - /// Adds a primitive to the set of primitives of the tree. - inline void insert(const Primitive& p); - - /// Clears and destroys the tree. - ~AABB_tree_with_join() - { - clear(); - } - /// Returns a const reference to the internally stored traits class. - const AABBTraits& traits() const{ - return m_traits; - } - - /// Clears the tree. - void clear() - { - // clear AABB tree - clear_nodes(); - m_primitives.clear(); - clear_search_tree(); - } - - /// Returns the axis-aligned bounding box of the whole tree. - /// \pre `!empty()` - const Bounding_box bbox() const { - CGAL_precondition(!empty()); - if(size() > 1) - return root_node()->bbox(); - else - return AABB_traits().compute_bbox_object()(m_primitives.begin(), - m_primitives.end()); - } - - /// Returns the number of primitives in the tree. - size_type size() const { return m_primitives.size(); } - - /// Returns \c true, iff the tree contains no primitive. - bool empty() const { return m_primitives.empty(); } - ///@} - - /// \name Advanced - ///@{ - - /// After one or more calls to `AABB_tree_with_join::insert()` the internal data - /// structure of the tree must be reconstructed. This procedure - /// has a complexity of \cgalBigO{n log(n)}, where \f$n\f$ is the number of - /// primitives of the tree. This procedure is called implicitly - /// at the first call to a query member function. You can call - /// AABB_tree_with_join::build() explicitly to ensure that the next call to - /// query functions will not trigger the reconstruction of the - /// data structure. - void build(); - - ///@} - -private: - template - void set_primitive_data_impl(CGAL::Boolean_tag,T ... ){} - template - void set_primitive_data_impl(CGAL::Boolean_tag,T ... t) - {m_traits.set_shared_data(t...);} - - template - void set_shared_data(T...t){ - set_primitive_data_impl(CGAL::Boolean_tag::value>(),t...); - } - - template - bool accelerate_distance_queries_impl(ConstPointIterator first, - ConstPointIterator beyond) const; -public: - - /// \name Intersection Tests - ///@{ - - /// Returns `true`, iff the query intersects at least one of - /// the input primitives. \tparam Query must be a type for - /// which `do_intersect` predicates are - /// defined in the traits class `AABBTraits`. - template - bool do_intersect(const Query& query) const; - - /// Returns `true`, iff at least one pair of primitives in the - /// two trees intersect. The `other` tree is translated by - /// `translation` before this is tested. The traits class `AABBTraits` - /// needs to define `do_intersect` predicates for the tree's primitive. - bool do_intersect(const AABB_tree_with_join &other, - const Point &translation) const; - - /// Returns the number of primitives intersected by the - /// query. \tparam Query must be a type for which - /// `do_intersect` predicates are defined - /// in the traits class `AABBTraits`. - template - size_type number_of_intersected_primitives(const Query& query) const; - - /// Outputs to the iterator the list of all intersected primitives - /// ids. This function does not compute the intersection points - /// and is hence faster than the function `all_intersections()` - /// function below. \tparam Query must be a type for which - /// `do_intersect` predicates are defined - /// in the traits class `AABBTraits`. - template - OutputIterator all_intersected_primitives(const Query& query, OutputIterator out) const; - - - /// Returns the first encountered intersected primitive id, iff - /// the query intersects at least one of the input primitives. No - /// particular order is guaranteed over the tree traversal, such - /// that, e.g, the primitive returned is not necessarily the - /// closest from the source point of a ray query. \tparam Query - /// must be a type for which - /// `do_intersect` predicates are defined - /// in the traits class `AABBTraits`. - template - std::optional any_intersected_primitive(const Query& query) const; - - ///@} - - /// \name Intersections - ///@{ - - /// Outputs the list of all intersections, as objects of - /// `Intersection_and_primitive_id::%Type`, - /// between the query and the input data to - /// the iterator. `do_intersect()` - /// predicates and intersections must be defined for `Query` - /// in the `AABBTraits` class. - template - OutputIterator all_intersections(const Query& query, OutputIterator out) const; - - - /// Returns the first encountered intersection. No particular - /// order is guaranteed over the tree traversal, e.g, the - /// primitive returned is not necessarily the closest from the - /// source point of a ray query. Type `Query` must be a type - /// for which `do_intersect` predicates - /// and intersections are defined in the traits class AABBTraits. - template - std::optional< typename Intersection_and_primitive_id::Type > - any_intersection(const Query& query) const; - - ///@} - - /// \name Distance Queries - ///@{ - - /// Returns the minimum squared distance between the query point - /// and all input primitives. Method - /// `accelerate_distance_queries()` should be called before the - /// first distance query, so that an internal secondary search - /// structure is built, for improving performance. - /// \pre `!empty()` - FT squared_distance(const Point& query) const; - - /// Returns the point in the union of all input primitives which - /// is closest to the query. In case there are several closest - /// points, one arbitrarily chosen closest point is - /// returned. Method `accelerate_distance_queries()` should be - /// called before the first distance query, so that an internal - /// secondary search structure is built, for improving - /// performance. - /// \pre `!empty()` - Point closest_point(const Point& query) const; - - - /// Returns a `Point_and_primitive_id` which realizes the - /// smallest distance between the query point and all input - /// primitives. Method `accelerate_distance_queries()` should be - /// called before the first distance query, so that an internal - /// secondary search structure is built, for improving - /// performance. - /// \pre `!empty()` - Point_and_primitive_id closest_point_and_primitive(const Point& query) const; - - - ///@} - - /// \name Accelerating the Distance Queries - /// - /// In the following paragraphs, we discuss details of the - /// implementation of the distance queries. We explain the - /// internal use of hints, how the user can pass his own hints to - /// the tree, and how the user can influence the construction of - /// the secondary data structure used for accelerating distance - /// queries. - /// Internally, the distance queries algorithms are initialized - /// with some hint, which has the same type as the return type of - /// the query, and this value is refined along a traversal of the - /// tree, until it is optimal, that is to say until it realizes - /// the shortest distance to the primitives. In particular, the - /// exact specification of these internal algorithms is that they - /// minimize the distance to the object composed of the union of - /// the primitives and the hint. - /// It follows that - /// - in order to return the exact distance to the set of - /// primitives, the algorithms need the hint to be exactly on the - /// primitives; - /// - if this is not the case, and if the hint happens to be closer - /// to the query point than any of the primitives, then the hint - /// is returned. - /// - /// This second observation is reasonable, in the sense that - /// providing a hint to the algorithm means claiming that this - /// hint belongs to the union of the primitives. These - /// considerations about the hints being exactly on the primitives - /// or not are important: in the case where the set of primitives - /// is a triangle soup, and if some of the primitives are large, - /// one may want to provide a much better hint than a vertex of - /// the triangle soup could be. It could be, for example, the - /// barycenter of one of the triangles. But, except with the use - /// of an exact constructions kernel, one cannot easily construct - /// points other than the vertices, that lie exactly on a triangle - /// soup. Hence, providing a good hint sometimes means not being - /// able to provide it exactly on the primitives. In rare - /// occasions, this hint can be returned as the closest point. - /// In order to accelerate distance queries significantly, the - /// AABB tree builds an internal KD-tree containing a set of - /// potential hints, when the method - /// `accelerate_distance_queries()` is called. This KD-tree - /// provides very good hints that allow the algorithms to run much - /// faster than with a default hint (such as the - /// `reference_point` of the first primitive). The set of - /// potential hints is a sampling of the union of the primitives, - /// which is obtained, by default, by calling the method - /// `reference_point` of each of the primitives. However, such - /// a sampling with one point per primitive may not be the most - /// relevant one: if some primitives are very large, it helps - /// inserting more than one sample on them. Conversely, a sparser - /// sampling with less than one point per input primitive is - /// relevant in some cases. - ///@{ - - /// Constructs internal search tree from - /// a point set taken on the internal primitives - /// returns `true` iff successful memory allocation - bool accelerate_distance_queries() const; - - /// Constructs an internal KD-tree containing the specified point - /// set, to be used as the set of potential hints for accelerating - /// the distance queries. - /// \tparam ConstPointIterator is an iterator with - /// value type `Point_and_primitive_id`. - template - bool accelerate_distance_queries(ConstPointIterator first, - ConstPointIterator beyond) const - { - #ifdef CGAL_HAS_THREADS - //this ensures that this is done once at a time - CGAL_SCOPED_LOCK(kd_tree_mutex); - #endif - clear_search_tree(); - return accelerate_distance_queries_impl(first,beyond); - - } - - /// Returns the minimum squared distance between the query point - /// and all input primitives. The internal KD-tree is not used. - /// \pre `!empty()` - FT squared_distance(const Point& query, const Point& hint) const; - - /// Returns the point in the union of all input primitives which - /// is closest to the query. In case there are several closest - /// points, one arbitrarily chosen closest point is returned. The - /// internal KD-tree is not used. - /// \pre `!empty()` - Point closest_point(const Point& query, const Point& hint) const; - - /// Returns a `Point_and_primitive_id` which realizes the - /// smallest distance between the query point and all input - /// primitives. The internal KD-tree is not used. - /// \pre `!empty()` - Point_and_primitive_id closest_point_and_primitive(const Point& query, const Point_and_primitive_id& hint) const; - - ///@} - - private: - // clear nodes - void clear_nodes() - { - if( size() > 1 ) { - delete [] m_p_root_node; - } - m_p_root_node = nullptr; - } - - // clears internal KD tree - void clear_search_tree() const - { - if ( m_search_tree_constructed ) - { - CGAL_assertion( m_p_search_tree!=nullptr ); - delete m_p_search_tree; - m_p_search_tree = nullptr; - m_search_tree_constructed = false; - m_default_search_tree_constructed = false; - } - } - - public: - - /// \internal - template - void traversal(const Query& query, Traversal_traits& traits) const - { - switch(size()) - { - case 0: - break; - case 1: - traits.intersection(query, singleton_data()); - break; - default: // if(size() >= 2) - root_node()->template traversal(query, traits, m_primitives.size()); - } - } - - /// \internal - template - void traversal(const AABB_tree_with_join &other_tree, Traversal_traits &traits) const - { - if (size() > 1 && other_tree.size() > 1) - { - root_node()->template traversal(*(other_tree.root_node()), - traits, - m_primitives.size(), - other_tree.m_primitives.size(), - true); - } - else // at least tree has less than 2 primitives - { - // TODO not implemented yet, cannot happen with two polygons - } - } - - private: - typedef AABB_node_with_join Node; - - - public: - // returns a point which must be on one primitive - Point_and_primitive_id any_reference_point_and_id() const - { - CGAL_assertion(!empty()); - return Point_and_primitive_id( - internal::Primitive_helper::get_reference_point(m_primitives[0],m_traits), m_primitives[0].id() - ); - } - - public: - Point_and_primitive_id best_hint(const Point& query) const - { - if(m_search_tree_constructed) - return m_p_search_tree->closest_point(query); - else - return this->any_reference_point_and_id(); - } - - private: - //Traits class - AABBTraits m_traits; - // set of input primitives - Primitives m_primitives; - // single root node - Node* m_p_root_node; - #ifdef CGAL_HAS_THREADS - mutable CGAL_MUTEX internal_tree_mutex;//mutex used to protect const calls inducing build() - mutable CGAL_MUTEX kd_tree_mutex;//mutex used to protect calls to accelerate_distance_queries - #endif - - const Node* root_node() const { - CGAL_assertion(size() > 1); - if(m_need_build){ - #ifdef CGAL_HAS_THREADS - //this ensures that build() will be called once - CGAL_SCOPED_LOCK(internal_tree_mutex); - if(m_need_build) - #endif - const_cast< AABB_tree_with_join* >(this)->build(); - } - return m_p_root_node; - } - - const Primitive& singleton_data() const { - CGAL_assertion(size() == 1); - return *m_primitives.begin(); - } - - // search KD-tree - mutable const Search_tree* m_p_search_tree; - mutable bool m_search_tree_constructed; - mutable bool m_default_search_tree_constructed; - bool m_need_build; - - private: - // Disabled copy constructor & assignment operator - typedef AABB_tree_with_join Self; - AABB_tree_with_join(const Self& src); - Self& operator=(const Self& src); - - }; // end class AABB_tree_with_join - -/// @} - - template - AABB_tree_with_join::AABB_tree_with_join(const Tr& traits) - : m_traits(traits) - , m_primitives() - , m_p_root_node(nullptr) - , m_p_search_tree(nullptr) - , m_search_tree_constructed(false) - , m_default_search_tree_constructed(false) - , m_need_build(false) - {} - - template - template - AABB_tree_with_join::AABB_tree_with_join(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond, - T ... t) - : m_traits() - , m_primitives() - , m_p_root_node(nullptr) - , m_p_search_tree(nullptr) - , m_search_tree_constructed(false) - , m_default_search_tree_constructed(false) - , m_need_build(false) - { - // Insert each primitive into tree - insert(first, beyond,t...); - } - - template - template - void AABB_tree_with_join::insert(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond, - T ... t) - { - set_shared_data(t...); - while(first != beyond) - { - m_primitives.push_back(Primitive(first,t...)); - ++first; - } - m_need_build = true; - } - - // Clears tree and insert a set of primitives - template - template - void AABB_tree_with_join::rebuild(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond, - T ... t) - { - // cleanup current tree and internal KD tree - clear(); - - // inserts primitives - insert(first, beyond,t...); - - build(); - } - - template - void AABB_tree_with_join::insert(const Primitive& p) - { - m_primitives.push_back(p); - m_need_build = true; - } - - // Build the data structure, after calls to insert(..) - template - void AABB_tree_with_join::build() - { - clear_nodes(); - - if(m_primitives.size() > 1) { - - // allocates tree nodes - m_p_root_node = new Node[m_primitives.size()-1](); - if(m_p_root_node == nullptr) - { - std::cerr << "Unable to allocate memory for AABB tree" << std::endl; - CGAL_assertion(m_p_root_node != nullptr); - m_primitives.clear(); - clear(); - } - - // constructs the tree - m_p_root_node->expand(m_primitives.begin(), m_primitives.end(), - m_primitives.size(), m_traits); - } - - // In case the users has switched on the accelerated distance query - // data structure with the default arguments, then it has to be - // rebuilt. - if(m_default_search_tree_constructed) - accelerate_distance_queries(); - - m_need_build = false; - } - - - // constructs the search KD tree from given points - // to accelerate the distance queries - template - template - bool AABB_tree_with_join::accelerate_distance_queries_impl(ConstPointIterator first, - ConstPointIterator beyond) const - { - m_p_search_tree = new Search_tree(first, beyond); - if(m_p_search_tree != nullptr) - { - m_search_tree_constructed = true; - return true; - } - else - { - std::cerr << "Unable to allocate memory for accelerating distance queries" << std::endl; - return false; - } - } - - // constructs the search KD tree from internal primitives - template - bool AABB_tree_with_join::accelerate_distance_queries() const - { - if(m_primitives.empty()) return true; - #ifdef CGAL_HAS_THREADS - //this ensures that this function will be done once - CGAL_SCOPED_LOCK(kd_tree_mutex); - #endif - - //we only redo computation only if needed - if (!m_need_build && m_default_search_tree_constructed) - return m_search_tree_constructed; - - // iterate over primitives to get reference points on them - std::vector points; - points.reserve(m_primitives.size()); - typename Primitives::const_iterator it; - for(it = m_primitives.begin(); it != m_primitives.end(); ++it) - points.push_back( - Point_and_primitive_id( - internal::Primitive_helper::get_reference_point(*it,m_traits), it->id() - ) - ); - - // clears current KD tree - clear_search_tree(); - m_default_search_tree_constructed = true; - return accelerate_distance_queries_impl(points.begin(), points.end()); - } - - template - template - bool - AABB_tree_with_join::do_intersect(const Query& query) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Do_intersect_traits traversal_traits(m_traits); - this->traversal(query, traversal_traits); - return traversal_traits.is_intersection_found(); - } - - template - bool AABB_tree_with_join::do_intersect(const AABB_tree_with_join &other, - const Point &translation) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Do_intersect_joined_traits traversal_traits(translation); - this->traversal(other, traversal_traits); - return traversal_traits.is_intersection_found(); - } - - template - template - typename AABB_tree_with_join::size_type - AABB_tree_with_join::number_of_intersected_primitives(const Query& query) const - { - using namespace CGAL::internal::AABB_tree_with_join; - using CGAL::internal::AABB_tree_with_join::Counting_output_iterator; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - typedef Counting_output_iterator Counting_iterator; - - size_type counter = 0; - Counting_iterator out(&counter); - - Listing_primitive_traits traversal_traits(out,m_traits); - this->traversal(query, traversal_traits); - return counter; - } - - template - template - OutputIterator - AABB_tree_with_join::all_intersected_primitives(const Query& query, - OutputIterator out) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Listing_primitive_traits traversal_traits(out,m_traits); - this->traversal(query, traversal_traits); - return out; - } - - template - template - OutputIterator - AABB_tree_with_join::all_intersections(const Query& query, - OutputIterator out) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Listing_intersection_traits traversal_traits(out,m_traits); - this->traversal(query, traversal_traits); - return out; - } - - - template - template - std::optional< typename AABB_tree_with_join::template Intersection_and_primitive_id::Type > - AABB_tree_with_join::any_intersection(const Query& query) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - First_intersection_traits traversal_traits(m_traits); - this->traversal(query, traversal_traits); - return traversal_traits.result(); - } - - template - template - std::optional::Primitive_id> - AABB_tree_with_join::any_intersected_primitive(const Query& query) const - { - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - First_primitive_traits traversal_traits(m_traits); - this->traversal(query, traversal_traits); - return traversal_traits.result(); - } - - // closest point with user-specified hint - template - typename AABB_tree_with_join::Point - AABB_tree_with_join::closest_point(const Point& query, - const Point& hint) const - { - CGAL_precondition(!empty()); - typename Primitive::Id hint_primitive = m_primitives[0].id(); - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Projection_traits projection_traits(hint,hint_primitive,m_traits); - this->traversal(query, projection_traits); - return projection_traits.closest_point(); - } - - // closest point without hint, the search KD-tree is queried for the - // first closest neighbor point to get a hint - template - typename AABB_tree_with_join::Point - AABB_tree_with_join::closest_point(const Point& query) const - { - CGAL_precondition(!empty()); - const Point_and_primitive_id hint = best_hint(query); - return closest_point(query,hint.first); - } - - // squared distance with user-specified hint - template - typename AABB_tree_with_join::FT - AABB_tree_with_join::squared_distance(const Point& query, - const Point& hint) const - { - CGAL_precondition(!empty()); - const Point closest = this->closest_point(query, hint); - return Tr().squared_distance_object()(query, closest); - } - - // squared distance without user-specified hint - template - typename AABB_tree_with_join::FT - AABB_tree_with_join::squared_distance(const Point& query) const - { - CGAL_precondition(!empty()); - const Point closest = this->closest_point(query); - return Tr().squared_distance_object()(query, closest); - } - - // closest point with user-specified hint - template - typename AABB_tree_with_join::Point_and_primitive_id - AABB_tree_with_join::closest_point_and_primitive(const Point& query) const - { - CGAL_precondition(!empty()); - return closest_point_and_primitive(query,best_hint(query)); - } - - // closest point with user-specified hint - template - typename AABB_tree_with_join::Point_and_primitive_id - AABB_tree_with_join::closest_point_and_primitive(const Point& query, - const Point_and_primitive_id& hint) const - { - CGAL_precondition(!empty()); - using namespace CGAL::internal::AABB_tree_with_join; - typedef typename AABB_tree_with_join::AABB_traits AABBTraits; - Projection_traits projection_traits(hint.first,hint.second,m_traits); - this->traversal(query, projection_traits); - return projection_traits.closest_point_and_primitive(); - } - -} // end namespace CGAL - -#endif // CGAL_AABB_TREE_WITH_JOIN_H - -/***EMACS SETTINGS** */ -/* Local Variables: */ -/* tab-width: 2 */ -/* indent-tabs-mode: t */ -/* End: */ diff --git a/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies b/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies index a71a4c66f559..73b1a8c7075e 100644 --- a/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies +++ b/Minkowski_sum_2/package_info/Minkowski_sum_2/dependencies @@ -3,6 +3,7 @@ Algebraic_foundations Arithmetic_kernel Arrangement_on_surface_2 Boolean_set_operations_2 +BGL CGAL_Core Cartesian_kernel Circulator diff --git a/Polygon/include/CGAL/Polygon_2/Polygon_2_edge_iterator.h b/Polygon/include/CGAL/Polygon_2/Polygon_2_edge_iterator.h index b1d5febe462f..e897bd43985d 100644 --- a/Polygon/include/CGAL/Polygon_2/Polygon_2_edge_iterator.h +++ b/Polygon/include/CGAL/Polygon_2/Polygon_2_edge_iterator.h @@ -54,7 +54,7 @@ class Polygon_2_edge_iterator { typedef typename Container_::const_iterator const_iterator; typedef typename Container_::difference_type difference_type; typedef value_type* pointer; - typedef value_type& reference; + typedef value_type reference; // The values are built on fly and thus it leads to a compilation error when trying to get a reference private: const Container_* container; // needed for dereferencing the last edge const_iterator first_vertex; // points to the first vertex of the edge @@ -75,7 +75,7 @@ class Polygon_2_edge_iterator { return !(first_vertex == x.first_vertex); } - value_type operator*() const { + reference operator*() const { return make_value_type(ConstructSegment()); } diff --git a/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/CMakeLists.txt index ede30624b931..f3ebdd2b83ab 100644 --- a/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/CMakeLists.txt @@ -42,3 +42,12 @@ else() endif() create_single_source_cgal_program("fast.cpp") + +create_single_source_cgal_program("meshes_intersections_comparison.cpp") +find_package(TBB QUIET) +include(CGAL_TBB_support) +if(TARGET CGAL::TBB_support) + target_link_libraries(meshes_intersections_comparison PRIVATE CGAL::TBB_support) +else() + message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.") +endif() diff --git a/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/include/AABB_meshes_intersections.h b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/include/AABB_meshes_intersections.h new file mode 100644 index 000000000000..79762ccf7b17 --- /dev/null +++ b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/include/AABB_meshes_intersections.h @@ -0,0 +1,737 @@ +// Copyright (c) 2026 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Léo Valque + +#ifndef CGAL_AABB_MESHES_INTERSECTIONS_H +#define CGAL_AABB_MESHES_INTERSECTIONS_H + +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef CGAL_LINKED_WITH_TBB +#include +#include +#endif + +namespace CGAL { + +namespace Polygon_mesh_processing{ + +namespace experimental{ + +template< typename TriangleMesh1, + typename TriangleMesh2, + typename OutputIterator, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> +void mixed_meshes_intersections(const TriangleMesh1 &tm1, + const TriangleMesh2 &tm2, + OutputIterator out, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor_1 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_1 = typename boost::graph_traits::halfedge_descriptor; + + using face_descriptor_2 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_2 = typename boost::graph_traits::halfedge_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + GT gt = choose_parameter(get_parameter(np1, internal_np::geom_traits)); + + using Face_bbox_tag = typename CGAL::dynamic_face_property_t; + using Primitive_1 = AABB_face_graph_triangle_primitive; + using Bbox_pmap_1 = typename boost::property_map::const_type; + using Traits_1 = AABB_traits_3; + using Tree_1 = AABB_tree; + using Node_1 = AABB_node; + using ConstPrimitiveIterator_1 = typename std::vector< Primitive_1 >::const_iterator; + using Box_1 = Box_intersection_d::Box_with_info_d; + + using Primitive_2 = AABB_face_graph_triangle_primitive; + using Bbox_pmap_2 = typename boost::property_map::const_type; + using Traits_2 = AABB_traits_3; + using Tree_2 = AABB_tree; + using Node_2 = AABB_node; + using ConstPrimitiveIterator_2 = typename std::vector< Primitive_2 >::const_iterator; + using Box_2 = Box_intersection_d::Box_with_info_d; + + using InternOutputIterator= std::back_insert_iterator>>; + + const std::size_t cutoff = 50000; + + auto vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point), + get_const_property_map(vertex_point, tm1)); + auto vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point), + get_const_property_map(vertex_point, tm2)); + + auto triangle = [&](auto fd, const auto &vpm, const auto &tm){ + auto hd = halfedge(fd,tm); + auto a = get(vpm, source(hd,tm)); + auto b = get(vpm, target(hd,tm)); + auto c = get(vpm, target(next(hd,tm),tm)); + return typename GT::Triangle_3(a, b, c); + }; + + auto bbox = [](auto fd, const auto &vpm, const auto &tm){ + auto hd = halfedge(fd,tm); + Bbox_3 res = get(vpm, source(hd,tm)).bbox(); + res += get(vpm, target(hd,tm)).bbox(); + res += get(vpm, target(next(hd,tm),tm)).bbox(); + return res; + }; + + Bbox_pmap_1 bb1 = get(Face_bbox_tag(), tm1); + Bbox_pmap_2 bb2 = get(Face_bbox_tag(), tm2); +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(0, faces(tm1).size()), + [&](const oneapi::tbb::blocked_range& r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + face_descriptor_1 fd = *(faces(tm1).begin() + i); + put(bb1, fd, bbox(fd, vpm1, tm1)); + } + } + ); + + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(0, faces(tm2).size()), + [&](const oneapi::tbb::blocked_range& r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + face_descriptor_2 fd = *(faces(tm2).begin() + i); + put(bb2, fd, bbox(fd, vpm2, tm2)); + } + } + ); + } + else +#endif + { + for(face_descriptor_1 fd : faces(tm1)) + put(bb1, fd, bbox(fd, vpm1, tm1)); + for(face_descriptor_2 fd : faces(tm2)) + put(bb2, fd, bbox(fd, vpm2, tm2)); + } + + Traits_1 traits1(bb1); + Tree_1 tree1(traits1); + tree1.insert(faces(tm1).first, faces(tm1).second, tm1); + + Traits_2 traits2(bb2); + Tree_2 tree2(traits2); + tree2.insert(faces(tm2).first, faces(tm2).second, tm2); + +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + CGAL_MUTEX m; + oneapi::tbb::task_group tg; + tg.run([&]{ tree1.template partial_build(cutoff); }); + tree2.template partial_build(cutoff); + tg.wait(); + + tbb::concurrent_vector> inter; + CGAL::internal::AABB_tree::experimental::Two_trees_intersecting_nodes_traits traversal_traits(tree1.traits(), tree2.traits(), std::back_inserter(inter)); + CGAL::internal::AABB_tree::experimental::two_trees_partial_traversal(tree1, tree2, cutoff, traversal_traits); + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(0, inter.size()), + [&](const oneapi::tbb::blocked_range& r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + const auto [begin_1, end_1] = tree1.partial_node_to_primitives_iterator(*inter[i].first); + const auto [begin_2, end_2] = tree2.partial_node_to_primitives_iterator(*inter[i].second); + std::vector< Box_1 > boxes_1; + std::vector< Box_2 > boxes_2; + std::vector< Box_1* > boxes_ptr_1; + std::vector< Box_2* > boxes_ptr_2; + boxes_1.reserve(std::distance(begin_1, end_1)); + boxes_2.reserve(std::distance(begin_2, end_2)); + boxes_ptr_1.reserve(boxes_1.size()); + boxes_ptr_2.reserve(boxes_2.size()); + + for(auto it=begin_1; it!=end_1; ++it) + boxes_1.emplace_back(get(bb1, it->id()), it->id()); + for(auto it=begin_2; it!=end_2; ++it) + boxes_2.emplace_back(get(bb2, it->id()), it->id()); + for(auto &b: boxes_1) + boxes_ptr_1.push_back(std::addressof(b)); + for(auto &b: boxes_2) + boxes_ptr_2.push_back(std::addressof(b)); + + std::vector< std::pair> patch_out; + const std::ptrdiff_t cutoff = 2000; + box_intersection_d(boxes_ptr_1.begin(), boxes_ptr_1.end(), boxes_ptr_2.begin(), boxes_ptr_2.end(), + [&](const Box_1 *b1, const Box_2 *b2) + { + if(CGAL::do_intersect(triangle(b1->info(), vpm1, tm1), triangle(b2->info(), vpm2, tm2))) + patch_out.emplace_back(b1->info(), b2->info()); + }, cutoff); + CGAL_SCOPED_LOCK(m); + for(auto p: patch_out) + *out ++ = p; + } + } + ); + } + else +#endif + { + tree1.template partial_build(cutoff); + tree2.template partial_build(cutoff); + + std::vector> inter; + CGAL::internal::AABB_tree::experimental::Two_trees_intersecting_nodes_traits traversal_traits(tree1.traits(), tree2.traits(), std::back_inserter(inter)); + CGAL::internal::AABB_tree::experimental::two_trees_partial_traversal(tree1, tree2, cutoff, traversal_traits); + + for(const auto& [n_1, n_2]: inter){ + const auto [begin_1, end_1] = tree1.partial_node_to_primitives_iterator(*n_1); + const auto [begin_2, end_2] = tree2.partial_node_to_primitives_iterator(*n_2); + std::vector< Box_1 > boxes_1; + std::vector< Box_2 > boxes_2; + std::vector< Box_1* > boxes_ptr_1; + std::vector< Box_2* > boxes_ptr_2; + boxes_1.reserve(std::distance(begin_1, end_1)); + boxes_2.reserve(std::distance(begin_2, end_2)); + boxes_ptr_1.reserve(boxes_1.size()); + boxes_ptr_2.reserve(boxes_2.size()); + + for(auto it=begin_1; it!=end_1; ++it) + boxes_1.emplace_back(get(bb1, it->id()), it->id()); + for(auto it=begin_2; it!=end_2; ++it) + boxes_2.emplace_back(get(bb2, it->id()), it->id()); + for(auto &b: boxes_1) + boxes_ptr_1.push_back(std::addressof(b)); + for(auto &b: boxes_2) + boxes_ptr_2.push_back(std::addressof(b)); + + std::vector< std::pair> patch_out; + const std::ptrdiff_t cutoff = 2000; + box_intersection_d(boxes_ptr_1.begin(), boxes_ptr_1.end(), boxes_ptr_2.begin(), boxes_ptr_2.end(), + [&](const Box_1 *b1, const Box_2 *b2) + { + if(CGAL::do_intersect(triangle(b1->info(), vpm1, tm1), triangle(b2->info(), vpm2, tm2))) + patch_out.emplace_back(b1->info(), b2->info()); + }, cutoff); + for(auto p: patch_out) + *out ++ = p; + } + } +} + +template< typename TriangleMesh1, + typename TriangleMesh2, + typename OutputIterator, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> +void box_meshes_intersections(const TriangleMesh1 &tm1, + const TriangleMesh2 &tm2, + OutputIterator out, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor_1 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_1 = typename boost::graph_traits::halfedge_descriptor; + + using face_descriptor_2 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_2 = typename boost::graph_traits::halfedge_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + GT gt = choose_parameter(get_parameter(np1, internal_np::geom_traits)); + + using Box_1 = Box_intersection_d::Box_with_info_d; + using Box_2 = Box_intersection_d::Box_with_info_d; + + auto vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point), + get_const_property_map(vertex_point, tm1)); + auto vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point), + get_const_property_map(vertex_point, tm2)); + + auto triangle = [&](auto fd, const auto &vpm, const auto &tm){ + auto hd = halfedge(fd,tm); + auto a = get(vpm, source(hd,tm)); + auto b = get(vpm, target(hd,tm)); + auto c = get(vpm, target(next(hd,tm),tm)); + return typename GT::Triangle_3(a, b, c); + }; + + auto bbox = [](auto fd, const auto &vpm, const auto &tm){ + auto hd = halfedge(fd,tm); + Bbox_3 res = get(vpm, source(hd,tm)).bbox(); + res += get(vpm, target(hd,tm)).bbox(); + res += get(vpm, target(next(hd,tm),tm)).bbox(); + return res; + }; + + std::vector< Box_1 > boxes_1; + std::vector< Box_2 > boxes_2; + boxes_1.reserve(faces(tm1).size()); + boxes_2.reserve(faces(tm2).size()); + for(auto fd: faces(tm1)) + boxes_1.emplace_back(bbox(fd, vpm1, tm1), fd); + for(auto fd: faces(tm2)) + boxes_2.emplace_back(bbox(fd, vpm2, tm2), fd); + + std::vector< Box_1* > boxes_ptr_1; + std::vector< Box_2* > boxes_ptr_2; + boxes_ptr_1.reserve(faces(tm1).size()); + boxes_ptr_2.reserve(faces(tm2).size()); + for(auto &b: boxes_1) + boxes_ptr_1.emplace_back(std::addressof(b)); + for(auto &b: boxes_2) + boxes_ptr_2.emplace_back(std::addressof(b)); + + CGAL_MUTEX m; + auto callback=[&](const Box_1 *b1, const Box_2 *b2){ + if(CGAL::do_intersect(triangle(b1->info(), vpm1, tm1), triangle(b2->info(), vpm2, tm2))){ + CGAL_SCOPED_LOCK(m); + *out ++ = std::make_pair(b1->info(), b2->info()); + } + }; + + const std::ptrdiff_t cutoff = 2000; + box_intersection_d(boxes_ptr_1.begin(), boxes_ptr_1.end(), boxes_ptr_2.begin(), boxes_ptr_2.end(), callback, cutoff); +} + +template< typename TriangleMesh, + typename OutputIterator, + typename NamedParameters = parameters::Default_named_parameters> +void AABB_two_trees_self_intersections(const TriangleMesh &tm, OutputIterator out, const NamedParameters& np = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + using Primitive = AABB_face_graph_triangle_primitive; + using Face_bbox_tag = typename CGAL::dynamic_face_property_t ; + using Bbox_pmap = typename boost::property_map::const_type; + using Traits = AABB_traits_3; + using Tree = AABB_tree; + + auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + + auto bbox = [&](face_descriptor fd){ + halfedge_descriptor hd = halfedge(fd,tm); + Bbox_3 res = get(vpm, source(hd,tm)).bbox(); + res += get(vpm, target(hd,tm)).bbox(); + res += get(vpm, target(next(hd,tm),tm)).bbox(); + return res; + }; + + Bbox_pmap bb = get(Face_bbox_tag(), tm); + for(face_descriptor fd : faces(tm)) + put(bb, fd, bbox(fd)); + + Traits traits(bb); + Tree tree(traits); + tree.insert(faces(tm).first, faces(tm).second, tm); + tree.template build(); + +#ifdef CGAL_LINKED_WITH_TBB + +#endif + using InternOutputIterator= std::back_insert_iterator>>; + std::vector> inter; + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree, std::back_inserter(inter)); + for(const auto& [f_1, f_2]: inter) + if(f_1 < f_2) + if(Polygon_mesh_processing::internal::do_faces_intersect(f_1, f_2, tm, tm.points(), gt.construct_segment_3_object(), gt.construct_triangle_3_object(), gt.do_intersect_3_object())) + *out ++ = std::make_pair(f_1, f_2); +} + +template +struct Split_primitives +{ + Split_primitives(RPM rpm) + : rpm(rpm) + {} + + template + void operator()(PrimitiveIterator first, + PrimitiveIterator beyond, + const CGAL::Bbox_3& bbox) const + { + auto longest_axis=[](const CGAL::Bbox_3& bbox){ + const double dx = bbox.xmax() - bbox.xmin(); + const double dy = bbox.ymax() - bbox.ymin(); + const double dz = bbox.zmax() - bbox.zmin(); + return (dx>=dy) ? ((dx>=dz) ? 0 : 2) : ((dy>=dz) ? 1 : 2); + }; + + PrimitiveIterator middle = first + (beyond - first)/2; + typedef typename std::iterator_traits::value_type Primitive; + const int crd=longest_axis(bbox); + const RPM& l_rpm=rpm; + std::nth_element(first, middle, beyond, + [l_rpm, crd](const Primitive& p1, const Primitive& p2){ return get(l_rpm, p1.id())[crd] < get(l_rpm, p2.id())[crd];}); + } + RPM rpm; +}; + +template +struct Compute_bbox { + Compute_bbox(const BBM& bbm) + : bbm(bbm) + {} + + template + CGAL::Bbox_3 operator()(ConstPrimitiveIterator first, + ConstPrimitiveIterator beyond) const + { + CGAL::Bbox_3 bbox = get(bbm, first->id()); + for(++first; first != beyond; ++first) + { + bbox += get(bbm, first->id()); + } + return bbox; + } + BBM bbm; +}; + +template< typename TriangleMesh1, + typename TriangleMesh2, + typename OutputIterator, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> +void AABB_two_trees_meshes_intersections(const TriangleMesh1 &tm1, + const TriangleMesh2 &tm2, + OutputIterator out, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor_1 = typename boost::graph_traits::face_descriptor; + using face_descriptor_2 = typename boost::graph_traits::face_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + // GT gt = choose_parameter(get_parameter(np1, internal_np::geom_traits)); + + using Primitive_1 = AABB_face_graph_triangle_primitive; + using Traits_1 = AABB_traits_3; + using Tree_1 = AABB_tree; + + using Primitive_2 = AABB_face_graph_triangle_primitive; + using Traits_2 = AABB_traits_3; + using Tree_2 = AABB_tree; + + auto vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point), + get_const_property_map(vertex_point, tm1)); + auto vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point), + get_const_property_map(vertex_point, tm2)); + + // Custom build functor using pointers + // TODO THIS IS SURFACE MESH SPECIFIC + + typedef Pointer_property_map::type BBM; + typedef Pointer_property_map::type RPM; // EPIC on purpose here + + std::vector v_bb1; + std::vector v_bb2; + std::vector v_rp1; + std::vector v_rp2; + + std::size_t nbf1 = faces(tm1).size(); + std::size_t nbf2 = faces(tm2).size(); + v_bb1.resize(nbf1); + v_bb2.resize(nbf2); + v_rp1.resize(nbf1); + v_rp2.resize(nbf2); + BBM bbmap1 = make_property_map(v_bb1); + BBM bbmap2 = make_property_map(v_bb2); + RPM rpm1 = make_property_map(v_rp1); + RPM rpm2 = make_property_map(v_rp2); + + CGAL::Cartesian_converter to_input; + #ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + tbb::parallel_for(std::size_t(0), faces(tm1).size(), [&](std::size_t i){ + face_descriptor_1 f(i); + v_bb1[f]=face_bbox(f, tm1); + v_rp1[f]=to_input(get(vpm1, (target(halfedge(f, tm1), tm1)))); + }); + tbb::parallel_for(std::size_t(0), faces(tm2).size(), [&](std::size_t i){ + face_descriptor_2 f(i); + v_bb2[f]=face_bbox(f, tm2); + v_rp2[f]=to_input(get(vpm2, (target(halfedge(f, tm2), tm2)))); + }); + } + else + #endif + { + for(face_descriptor_1 f : faces(tm1)){ + v_bb1[f]=face_bbox(f, tm1); + v_rp1[f]=to_input(get(vpm1, (target(halfedge(f, tm1), tm1)))); + } + for(face_descriptor_2 f : faces(tm2)){ + v_bb2[f]=face_bbox(f, tm2); + v_rp2[f]=to_input(get(vpm2, (target(halfedge(f, tm2), tm2)))); + } + } + + Compute_bbox compute_bbox1(bbmap1); + Compute_bbox compute_bbox2(bbmap2); + Split_primitives split_primitives1(rpm1); + Split_primitives split_primitives2(rpm2); + + Tree_1 tree1; + Tree_2 tree2; + tree1.insert(faces(tm1).first, faces(tm1).second, tm1); + tree2.insert(faces(tm2).first, faces(tm2).second, tm2); + +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + oneapi::tbb::task_group tg; + tg.run([&]{ tree1.template custom_build(compute_bbox1, split_primitives1); }); + tree2.template custom_build(compute_bbox2, split_primitives2); + tg.wait(); + + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, out); + } + else +#endif + { + tree1.template custom_build(compute_bbox1, split_primitives1); + tree2.template custom_build(compute_bbox2, split_primitives2); + CGAL::AABB_trees::all_pairs_of_intersecting_primitives(tree1, tree2, out); + } +} + +} // end of namespace experimental + +template< typename TriangleMesh, + typename OutputIterator, + typename NamedParameters = parameters::Default_named_parameters> +void AABB_self_intersections(const TriangleMesh &tm, OutputIterator out, const NamedParameters& np = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + using Primitive = AABB_face_graph_triangle_primitive; + using Face_bbox_tag = typename CGAL::dynamic_face_property_t ; + using Bbox_pmap = typename boost::property_map::const_type; + using Traits = AABB_traits_3; + using Tree = AABB_tree; + + auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + + auto bbox = [&](face_descriptor fd){ + halfedge_descriptor hd = halfedge(fd,tm); + Bbox_3 res = get(vpm, source(hd,tm)).bbox(); + res += get(vpm, target(hd,tm)).bbox(); + res += get(vpm, target(next(hd,tm),tm)).bbox(); + return res; + }; + + Bbox_pmap bb = get(Face_bbox_tag(), tm); + for(face_descriptor fd : faces(tm)) + put(bb, fd, bbox(fd)); + + Traits traits(bb); + Tree tree(traits); + tree.insert(faces(tm).first, faces(tm).second, tm); + tree.template build(); + +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + CGAL_MUTEX mutex; + tbb::concurrent_vector> inter; + std::vector face_vec(faces(tm).begin(), faces(tm).end()); + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(0, face_vec.size()), + [&](const oneapi::tbb::blocked_range& r) { + for (std::size_t i = r.begin(); i != r.end(); ++i) { + face_descriptor f_1 = face_vec[i]; + std::vector inter; + tree.all_intersected_primitives(get(bb, f_1), std::back_inserter(inter)); + for(face_descriptor f_2: inter) + if(f_1 < f_2) + if(Polygon_mesh_processing::internal::do_faces_intersect(f_1, f_2, tm, tm.points(), gt.construct_segment_3_object(), gt.construct_triangle_3_object(), gt.do_intersect_3_object())){ + CGAL_SCOPED_LOCK(mutex); + *out ++ = std::make_pair(f_1, f_2); + } + } + } + ); + } + else +#endif + { + for (face_descriptor f_1: faces(tm)){ + std::vector inter; + tree.all_intersected_primitives(get(bb, f_1), std::back_inserter(inter)); + for(face_descriptor f_2: inter) + if(f_1 < f_2) + if(Polygon_mesh_processing::internal::do_faces_intersect(f_1, f_2, tm, tm.points(), gt.construct_segment_3_object(), gt.construct_triangle_3_object(), gt.do_intersect_3_object())) + *out ++ = std::make_pair(f_1, f_2); + } + } +} + +template< typename TriangleMesh1, + typename TriangleMesh2, + typename OutputIterator, + typename NamedParameters1 = parameters::Default_named_parameters, + typename NamedParameters2 = parameters::Default_named_parameters> +void AABB_meshes_intersections(const TriangleMesh1 &tm1, + const TriangleMesh2 &tm2, + OutputIterator out, + const NamedParameters1& np1 = parameters::default_values(), + const NamedParameters2& np2 = parameters::default_values()) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + using face_descriptor_1 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_1 = typename boost::graph_traits::halfedge_descriptor; + + using face_descriptor_2 = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor_2 = typename boost::graph_traits::halfedge_descriptor; + + using Concurrency_tag = typename internal_np::Lookup_named_param_def < + internal_np::concurrency_tag_t, + NamedParameters1, + Sequential_tag + > ::type; + using GT = typename GetGeomTraits::type; + GT gt = choose_parameter(get_parameter(np1, internal_np::geom_traits)); + + using Primitive = AABB_face_graph_triangle_primitive; + using Face_bbox_tag = typename CGAL::dynamic_face_property_t ; + using Bbox_pmap = typename boost::property_map::const_type; + using Traits = AABB_traits_3; + using Tree = AABB_tree; + + auto vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point), + get_const_property_map(vertex_point, tm1)); + auto vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point), + get_const_property_map(vertex_point, tm2)); + + auto triangle = [&](face_descriptor_1 fd){ + halfedge_descriptor_1 hd = halfedge(fd,tm1); + auto a = get(vpm1, source(hd,tm1)); + auto b = get(vpm1, target(hd,tm1)); + auto c = get(vpm1, target(next(hd,tm1),tm1)); + return typename GT::Triangle_3(a, b, c); + }; + + auto bbox = [&](face_descriptor_2 fd){ + halfedge_descriptor_2 hd = halfedge(fd,tm2); + Bbox_3 res = get(vpm2, source(hd,tm2)).bbox(); + res += get(vpm2, target(hd,tm2)).bbox(); + res += get(vpm2, target(next(hd,tm2),tm2)).bbox(); + return res; + }; + + Bbox_pmap bb = get(Face_bbox_tag(), tm2); + for(face_descriptor_2 fd : faces(tm2)) + put(bb, fd, bbox(fd)); + + Traits traits(bb); + Tree tree(traits); + tree.insert(faces(tm2).first, faces(tm2).second, tm2); + tree.template build(); + +#ifdef CGAL_LINKED_WITH_TBB + if constexpr(std::is_same_v) + { + CGAL_MUTEX mutex; + tbb::concurrent_vector> inter; + std::vector face_vec(faces(tm1).begin(), faces(tm1).end()); + oneapi::tbb::parallel_for( + oneapi::tbb::blocked_range(0, face_vec.size()), + [&](const oneapi::tbb::blocked_range& r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + face_descriptor_1 f_1 = face_vec[i]; + std::vector inter; + tree.all_intersected_primitives(triangle(f_1), std::back_inserter(inter)); + CGAL_SCOPED_LOCK(mutex); + for(face_descriptor_2 f_2: inter) + *out ++ = std::make_pair(f_1, f_2); + } + } + ); + } + else +#endif + { + for (face_descriptor_1 f_1: faces(tm1)){ + std::vector inter; + tree.all_intersected_primitives(triangle(f_1), std::back_inserter(inter)); + for(face_descriptor_2 f_2: inter) + *out ++ = std::make_pair(f_1, f_2); + } + } +} + +} +} // end namespace CGAL + +#endif diff --git a/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/meshes_intersections_comparison.cpp b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/meshes_intersections_comparison.cpp new file mode 100644 index 000000000000..98c6e42e0750 --- /dev/null +++ b/Polygon_mesh_processing/benchmark/Polygon_mesh_processing/meshes_intersections_comparison.cpp @@ -0,0 +1,148 @@ +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "include/AABB_meshes_intersections.h" +#include + +#include +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::Point_3 Point_3; +typedef CGAL::Surface_mesh Surface_mesh; + +typedef boost::graph_traits::face_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +void two_meshes_intersection(std::string fname1, std::string fname2){ + + Surface_mesh tm1; + Surface_mesh tm2; + CGAL::IO::read_polygon_mesh(fname1, tm1); + CGAL::IO::read_polygon_mesh(fname2, tm2); + PMP::triangulate_faces(tm1); + PMP::triangulate_faces(tm2); + + CGAL::Timer t; + CGAL::Real_timer rt; + t.start(); + rt.start(); + +#if CGAL_LINKED_WITH_TBB + tbb::concurrent_vector> out; +#else + std::vector> out; +#endif + + out.clear(); + PMP::experimental::AABB_two_trees_meshes_intersections(tm1, tm2, std::back_inserter(out), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag())); + std::cout << "number intersections: " << out.size() << std::endl; + std::cout << "Two tree AABB intersecton time: " << rt.time() << "sec (" << t.time() << "s all cpu)." << std::endl; + + t.stop(); rt.stop(); + t.reset(); rt.reset(); + t.start(); rt.start(); + + out.clear(); + PMP::experimental::AABB_two_trees_meshes_intersections(tm1, tm2, std::back_inserter(out)); + std::cout << "number intersections: " << out.size() << std::endl; + std::cout << "Two tree AABB intersecton time (Sequential): " << rt.time() << "sec (" << t.time() << "s all cpu)." << std::endl; + + t.stop(); rt.stop(); + t.reset(); rt.reset(); + t.start(); rt.start(); + + out.clear(); + PMP::experimental::mixed_meshes_intersections(tm1, tm2, std::back_inserter(out), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag())); + std::cout << "number intersections: " << out.size() << std::endl; + std::cout << "Mixed intersecton time: " << rt.time() << "sec (" << t.time() << "s all cpu)." << std::endl; + + t.stop(); rt.stop(); + t.reset(); rt.reset(); + t.start(); rt.start(); + + out.clear(); + PMP::experimental::mixed_meshes_intersections(tm1, tm2, std::back_inserter(out)); + std::cout << "number intersections: " << out.size() << std::endl; + std::cout << "Mixed intersecton time (Sequential): " << rt.time() << "sec (" << t.time() << "s all cpu)." << std::endl; + + t.stop(); rt.stop(); + t.reset(); rt.reset(); + t.start(); rt.start(); + + out.clear(); + PMP::AABB_meshes_intersections(tm1, tm2, std::back_inserter(out), CGAL::parameters::concurrency_tag(CGAL::Parallel_tag())); + std::cout << "number intersections: " << out.size() << std::endl; + std::cout << "AABB intersecton time: " << rt.time() << "sec (" << t.time() << "s all cpu)." << std::endl; + std::sort(out.begin(), out.end(), [](const auto &a, const auto &b){ return a.first1)?argv[1]:CGAL::data_file_path("meshes/tetrahedron.off"), + (argc>2)?argv[2]:CGAL::data_file_path("meshes/beam.off")); + // two_meshes_intersection((argc>1)?argv[1]:CGAL::data_file_path("meshes/beam.off"), + // (argc>2)?argv[2]:"beam_transformed.off"); + return EXIT_SUCCESS; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/AABB_traversal_traits_with_transformation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/AABB_traversal_traits_with_transformation.h deleted file mode 100644 index 7dad2004ecd0..000000000000 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/AABB_traversal_traits_with_transformation.h +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (c) 2018 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Maxime Gimeno -// Sebastien Loriot -// - -#ifndef CGAL_PMP_INTERNAL_AABB_TRAVERSAL_TRAITS_WITH_TRANSFORMATION -#define CGAL_PMP_INTERNAL_AABB_TRAVERSAL_TRAITS_WITH_TRANSFORMATION - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace CGAL { - -template -class Traversal_traits_with_transformation_helper -{ - Bbox_3 - compute_transformed_bbox_impl(const CGAL::Aff_transformation_3& at, - const Bbox_3& bbox, - bool has_rotation, - /*SUPPORTS_ROTATION*/ Tag_true) const - { - CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_UPWARD); - - if(!has_rotation) - return compute_transformed_bbox_impl(at, bbox, has_rotation, Tag_false()); - - typedef Simple_cartesian AK; - typedef Cartesian_converter C2F; - C2F c2f; - - AK::Aff_transformation_3 a_at = c2f(at); - - AK::FT xtrm[6] = { c2f((bbox.min)(0)), c2f((bbox.max)(0)), - c2f((bbox.min)(1)), c2f((bbox.max)(1)), - c2f((bbox.min)(2)), c2f((bbox.max)(2)) }; - - typename AK::Point_3 ps[8]; - ps[0] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[4]) ); - ps[1] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[5]) ); - ps[2] = a_at( AK::Point_3(xtrm[0], xtrm[3], xtrm[4]) ); - ps[3] = a_at( AK::Point_3(xtrm[0], xtrm[3], xtrm[5]) ); - - ps[4] = a_at( AK::Point_3(xtrm[1], xtrm[2], xtrm[4]) ); - ps[5] = a_at( AK::Point_3(xtrm[1], xtrm[2], xtrm[5]) ); - ps[6] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[4]) ); - ps[7] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[5]) ); - - return bbox_3(ps, ps+8); - } - - Bbox_3 - compute_transformed_bbox_impl(const CGAL::Aff_transformation_3& at, - const Bbox_3& bbox, - bool, - /*SUPPORTS_ROTATION*/ Tag_false) const - { - CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_UPWARD); - - typedef Simple_cartesian AK; - typedef Cartesian_converter C2F; - C2F c2f; - - AK::Aff_transformation_3 a_at = c2f(at); - - AK::FT xtrm[6] = { c2f((bbox.min)(0)), c2f((bbox.max)(0)), - c2f((bbox.min)(1)), c2f((bbox.max)(1)), - c2f((bbox.min)(2)), c2f((bbox.max)(2)) }; - - typename AK::Point_3 ps[2]; - ps[0] = a_at( AK::Point_3(xtrm[0], xtrm[2], xtrm[4]) ); - ps[1] = a_at( AK::Point_3(xtrm[1], xtrm[3], xtrm[5]) ); - - return bbox_3(ps, ps+2); - } - -public: - - bool has_rotation(const CGAL::Aff_transformation_3& at) const - { - return ( at.m(0,1) != 0 || at.m(0,2) != 0 || at.m(1,0) != 0 - || at.m(1,2) != 0 || at.m(2,0) != 0 || at.m(2,1) !=0); - } - - Bbox_3 - compute_transformed_bbox(const CGAL::Aff_transformation_3& at, - const Bbox_3& bbox, - bool has_rotation) const - { - return compute_transformed_bbox_impl(at, bbox, has_rotation, SUPPORTS_ROTATION()); - } -}; - -// traversal traits for a tree vs a primitive -template -class Do_intersect_traversal_traits_with_transformation - : public Traversal_traits_with_transformation_helper -{ - typedef typename AABBTraits::Primitive Primitive; - typedef ::CGAL::AABB_node Node; - typedef Traversal_traits_with_transformation_helper Base; - - void register_transformation(CGAL::Tag_true) - { - m_has_rotation = this->has_rotation(m_transfo); - } - - void register_transformation(CGAL::Tag_false) - {} - -public: - Do_intersect_traversal_traits_with_transformation(): - m_traits_ptr(nullptr) - {} - - Do_intersect_traversal_traits_with_transformation(const AABBTraits& traits) - : m_is_found(false), m_traits_ptr(&traits), m_has_rotation(false) - {} - - bool go_further() const { return !m_is_found; } - - template - void intersection(const Query& query, const Primitive& primitive) - { - if( CGAL::do_intersect(query, - internal::Primitive_helper::get_datum(primitive, *m_traits_ptr).transform(m_transfo)) ) - m_is_found = true; - } - - template - bool do_intersect(const Query& query, const Node& node) const - { - return m_traits_ptr->do_intersect_object()(query, compute_transformed_bbox(node.bbox())); - } - - bool is_intersection_found() const { return m_is_found; } - - void reset() - { - m_is_found = false; - } - - const Aff_transformation_3& - transformation() const - { - return m_transfo; - } - - void set_transformation(const Aff_transformation_3& transfo) - { - m_transfo = transfo; - register_transformation(SUPPORTS_ROTATION()); - } - - Bbox_3 - compute_transformed_bbox(const Bbox_3& bbox) const - { - return Base::compute_transformed_bbox(m_transfo, bbox, m_has_rotation); - } - - // helper for Point_inside_vertical_ray_cast - class Transformed_tree_helper - { - typedef AABB_tree Tree; - typedef CGAL::AABB_node Node; - typedef Do_intersect_traversal_traits_with_transformation Traversal_traits; - - Traversal_traits m_tt; - - public: - - Transformed_tree_helper(const Traversal_traits& tt) - : m_tt(tt) - {} - - Bbox_3 get_tree_bbox(const AABB_tree& tree) const - { - return m_tt.compute_transformed_bbox(tree.bbox()); - } - - typename AABBTraits::Primitive::Datum - get_primitive_datum(const typename AABBTraits::Primitive& primitive, const AABBTraits& traits) const - { - return internal::Primitive_helper::get_datum(primitive, traits).transform(m_tt.transformation()); - } - - Bbox_3 get_node_bbox(const Node& node) const - { - return m_tt.compute_transformed_bbox(node.bbox()); - } - }; - - Transformed_tree_helper get_helper() const - { - return Transformed_tree_helper(*this); - } - -private: - bool m_is_found; - const AABBTraits* m_traits_ptr; - Aff_transformation_3 m_transfo; - bool m_has_rotation; -}; - - -// traversal traits for a tree -template -class Do_intersect_traversal_traits_for_two_trees - : public Traversal_traits_with_transformation_helper -{ - typedef typename AABBTraits::Primitive Primitive; - typedef ::CGAL::AABB_node Node; - typedef Traversal_traits_with_transformation_helper Base; - typedef Do_intersect_traversal_traits_with_transformation Query_traversal_traits; - - void register_transformation(CGAL::Tag_true) - { - m_has_rotation = this->has_rotation(m_transfo); - } - - void register_transformation(CGAL::Tag_false) - {} - - Bbox_3 - compute_transformed_bbox(const Bbox_3& bbox) const - { - return Base::compute_transformed_bbox(m_transfo, bbox, m_has_rotation); - } - -public: - Do_intersect_traversal_traits_for_two_trees(const AABBTraits& traits, - const Aff_transformation_3& transfo, - const Query_traversal_traits& query_traversal_traits) - : m_is_found(false) - , m_traits(traits) - , m_transfo(transfo) - , m_has_rotation(false) - , m_query_traversal_traits(query_traversal_traits) - - { - register_transformation(SUPPORTS_ROTATION()); - } - - bool go_further() const { return !m_is_found; } - - void intersection(const AABB_tree& query, const Primitive& primitive) - { - query.traversal( internal::Primitive_helper::get_datum(primitive,m_traits).transform(m_transfo), m_query_traversal_traits ); - m_is_found = m_query_traversal_traits.is_intersection_found(); - m_query_traversal_traits.reset(); - } - - bool do_intersect(const AABB_tree& query, const Node& node) - { - query.traversal( compute_transformed_bbox(node.bbox()), m_query_traversal_traits ); - bool res = m_query_traversal_traits.is_intersection_found(); - m_query_traversal_traits.reset(); - return res; - } - - bool is_intersection_found() const { return m_is_found; } - -private: - bool m_is_found; - const AABBTraits& m_traits; - const Aff_transformation_3& m_transfo; - bool m_has_rotation; - Do_intersect_traversal_traits_with_transformation m_query_traversal_traits; -}; - -}//end CGAL - -#endif //CGAL_AABB_AABB_do_intersect_transform_traits_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h index 664782e72b45..68b7da1f05c2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h @@ -80,18 +80,18 @@ class Ray_3_Triangle_3_traversal_traits //specialization for vertical ray -template -class Ray_3_Triangle_3_traversal_traits: - public Ray_3_Triangle_3_traversal_traits +template +class Ray_3_Triangle_3_traversal_traits: + public Ray_3_Triangle_3_traversal_traits { - typedef Ray_3_Triangle_3_traversal_traits Base; + typedef Ray_3_Triangle_3_traversal_traits Base; typedef typename Kernel::Point_3 Point; typedef typename Base::Primitive Primitive; typedef CGAL::AABB_node Node; public: - Ray_3_Triangle_3_traversal_traits(std::pair& status, const AABBTraits& aabb_traits, const TraversalTraits& tt) - :Base(status, aabb_traits, tt){} + Ray_3_Triangle_3_traversal_traits(std::pair& status, const AABBTraits& aabb_traits, const Helper& h) + :Base(status, aabb_traits, h){} template bool do_intersect(const Query& query, const Bbox_3& bbox) const diff --git a/Polygon_mesh_processing/include/CGAL/Rigid_triangle_mesh_collision_detection.h b/Polygon_mesh_processing/include/CGAL/Rigid_triangle_mesh_collision_detection.h index e67096e1a85c..5f1c508b931c 100644 --- a/Polygon_mesh_processing/include/CGAL/Rigid_triangle_mesh_collision_detection.h +++ b/Polygon_mesh_processing/include/CGAL/Rigid_triangle_mesh_collision_detection.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -36,6 +36,42 @@ namespace CGAL { +namespace internal { + +// helper for Point_inside_vertical_ray_cast +template +class Transformed_tree_helper +{ + typedef CGAL::AABB_tree Tree; + typedef CGAL::AABB_node Node; + typedef Aff_transformation_3 Transformation; + + Transformation m_tr; + +public: + + Transformed_tree_helper(){} + Transformed_tree_helper(const Transformation& tr): m_tr(tr){} + + Bbox_3 get_tree_bbox(const Tree& tree) const + { + return compute_transformed_bbox(m_tr, tree.bbox()); + } + + typename AABBTraits::Primitive::Datum + get_primitive_datum(const typename AABBTraits::Primitive& primitive, const AABBTraits& traits) const + { + return internal::Primitive_helper::get_datum(primitive, traits).transform(m_tr); + } + + Bbox_3 get_node_bbox(const Node& node) const + { + return compute_transformed_bbox(m_tr, node.bbox()); + } +}; + +} + /*! * \ingroup PMP_intersection_grp * @@ -56,13 +92,16 @@ namespace CGAL { * @tparam Has_rotation tag indicating whether the transformations applied to meshes may contain rotations (\link Tag_true `Tag_true`\endlink) * or if only translations and scalings are applied (\link Tag_false `Tag_false`\endlink). Some optimizations are * switched on in case there are no rotations. + * @tparam Use_inverse_transformation if true, the inverse of the transformations are used to accelerate the queries. + The result may be less accurate than using the original transformations. */ template -class Rigid_triangle_mesh_collision_detection + class Has_rotation = CGAL::Tag_true, + bool Use_inverse_transformation = true> +struct Rigid_triangle_mesh_collision_detection { // Vertex point map type typedef typename property_map_selector - Traversal_traits; + typedef internal::AABB_tree::Do_intersect_traits Traversal_traits; // Data members std::vector m_own_aabb_trees; std::vector m_aabb_trees; + std::vector > m_transformations; std::vector m_is_closed; std::vector< std::vector > m_points_per_cc; - std::vector m_traversal_traits; std::size_t m_free_id; // position in m_id_pool of the first free element std::vector m_id_pool; // 0-> m_id_pool-1 are valid mesh ids #if CGAL_RMCD_CACHE_BOXES @@ -112,7 +148,7 @@ class Rigid_triangle_mesh_collision_detection m_aabb_trees.resize(m_free_id, nullptr); m_is_closed.resize(m_free_id); m_points_per_cc.resize(m_free_id); - m_traversal_traits.resize(m_free_id); + m_transformations.resize(m_free_id); #if CGAL_RMCD_CACHE_BOXES m_bboxes.resize(m_free_id); m_bboxes_is_invalid.resize(m_free_id, true); @@ -133,15 +169,27 @@ class Rigid_triangle_mesh_collision_detection { typename K::Construct_ray_3 ray_functor; typename K::Construct_vector_3 vector_functor; - typedef typename Traversal_traits::Transformed_tree_helper Helper; for(const typename K::Point_3& q : m_points_per_cc[id_B]) { - if( internal::Point_inside_vertical_ray_cast(m_traversal_traits[id_A].get_helper())( - m_traversal_traits[id_B].transformation()( q ), *m_aabb_trees[id_A], + if constexpr(Use_inverse_transformation) + { + if( internal::Point_inside_vertical_ray_cast()( + m_transformations[id_A].inverse()(m_transformations[id_B](q)), *m_aabb_trees[id_A], ray_functor, vector_functor) == CGAL::ON_BOUNDED_SIDE) + { + return true; + } + } + else { - return true; + internal::Transformed_tree_helper helper(m_transformations[id_A]); + if( internal::Point_inside_vertical_ray_cast >(helper)( + m_transformations[id_B](q), *m_aabb_trees[id_A], + ray_functor, vector_functor) == CGAL::ON_BOUNDED_SIDE) + { + return true; + } } } return false; @@ -153,11 +201,8 @@ class Rigid_triangle_mesh_collision_detection #if CGAL_RMCD_CACHE_BOXES if (!do_overlap(m_bboxes[id_B], m_bboxes[id_A])) continue; #endif - - Do_intersect_traversal_traits_for_two_trees traversal_traits( - m_aabb_trees[id_B]->traits(), m_traversal_traits[id_B].transformation(), m_traversal_traits[id_A]); - m_aabb_trees[id_B]->traversal(*m_aabb_trees[id_A], traversal_traits); - return traversal_traits.is_intersection_found(); + if(AABB_trees::do_intersect(*m_aabb_trees[id_A], *m_aabb_trees[id_B], parameters::transformation(m_transformations[id_A]), parameters::transformation(m_transformations[id_B]))) return true; + return false; } public: @@ -203,7 +248,7 @@ class Rigid_triangle_mesh_collision_detection m_aabb_trees = std::move(other.m_aabb_trees); m_is_closed = std::move(other.m_is_closed); m_points_per_cc = std::move(other.m_points_per_cc); - m_traversal_traits = std::move(other.m_traversal_traits); + m_transformations = std::move(other.m_transformations); m_free_id = std::move(other.m_free_id); m_id_pool = std::move(other.m_id_pool); @@ -264,7 +309,7 @@ class Rigid_triangle_mesh_collision_detection Tree* t = new Tree(std::begin(faces(tm)), std::end(faces(tm)), tm, vpm); t->build(); m_aabb_trees[id] = t; - m_traversal_traits[id] = Traversal_traits(m_aabb_trees[id]->traits()); + m_transformations[id] = Aff_transformation_3(CGAL::IDENTITY); add_cc_points(tm, id, np); return id; @@ -312,7 +357,7 @@ class Rigid_triangle_mesh_collision_detection m_is_closed[id] = is_closed(tm); m_own_aabb_trees[id] = false ; m_aabb_trees[id] = const_cast(&tree); - m_traversal_traits[id] = Traversal_traits(m_aabb_trees[id]->traits()); + m_transformations[id] = Aff_transformation_3(CGAL::IDENTITY); collect_one_point_per_connected_component(tm, m_points_per_cc[id], np); return id; } @@ -323,7 +368,7 @@ class Rigid_triangle_mesh_collision_detection void set_transformation(std::size_t mesh_id, const Aff_transformation_3& aff_trans) { CGAL_assertion(m_aabb_trees[mesh_id] != nullptr); - m_traversal_traits[mesh_id].set_transformation(aff_trans); + m_transformations[mesh_id] = aff_trans; #if CGAL_RMCD_CACHE_BOXES m_bboxes_is_invalid.set(mesh_id); #endif @@ -468,7 +513,7 @@ class Rigid_triangle_mesh_collision_detection m_aabb_trees.reserve(size); m_is_closed.reserve(size); m_points_per_cc.reserve(size); - m_traversal_traits.reserve(size); + m_transformations.reserve(size); #if CGAL_RMCD_CACHE_BOXES m_bboxes.reserve(size); #endif @@ -627,7 +672,7 @@ class Rigid_triangle_mesh_collision_detection m_is_closed[id] = is_closed; m_own_aabb_trees[id] = false ; m_aabb_trees[id] = const_cast(&tree); - m_traversal_traits[id] = Traversal_traits(m_aabb_trees[id]->traits()); + m_transformations[id] = Aff_transformation_3(CGAL::IDENTITY); m_points_per_cc[id] = points_per_cc; return id; diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/pmp_do_intersect_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/pmp_do_intersect_test.cpp index 690f536e98b1..da3563d9310f 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/pmp_do_intersect_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/pmp_do_intersect_test.cpp @@ -390,7 +390,7 @@ int main() const std::string filename6 = "data/polylines_inter.polylines.txt"; const std::string filename7 = "data/tetra2.off"; const std::string filename8 = "data/tetra4.off"; - const std::string filename9 = "data/small_spheres.off"; + const std::string filename9 = CGAL::data_file_path("meshes/small_spheres.off"); const std::string filename10 = "data/hollow_sphere.off"; const std::string filename11 = CGAL::data_file_path("meshes/sphere.off"); diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_hausdorff_bounded_error_distance.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_hausdorff_bounded_error_distance.cpp index 3e011b42feea..9921b3a9ac97 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_hausdorff_bounded_error_distance.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_hausdorff_bounded_error_distance.cpp @@ -1165,7 +1165,7 @@ int main(int argc, char** argv) // test_real_meshes(filepath1, filepath2, naive_hd, bound_hd, error_bound); test_real_meshes(filepath1, filepath2, bound_hd, apprx_hd, error_bound); // test_real_meshes("data/elephant_concave_hole.off", CGAL::data_file_path("meshes/mech-holes-shark.off"), bound_hd, apprx_hd, error_bound); // commenting because approx hausdorff is annoyingly rough - test_real_meshes("data/small_spheres.off", "data/overlapping_triangles.off", bound_hd, apprx_hd, error_bound); + test_real_meshes(CGAL::data_file_path("meshes/small_spheres.off"), "data/overlapping_triangles.off", bound_hd, apprx_hd, error_bound); // --- Test realizing triangles. test_realizing_triangles(error_bound); diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_collision_detection.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_collision_detection.cpp index e2e302d565af..06a4bd547ca7 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_collision_detection.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_collision_detection.cpp @@ -77,7 +77,7 @@ void test_intersections(Index index, const char* type) { std::cout << "test_intersections<"<()" << std::endl; TriangleMesh tm1, tm2, tm3; - std::ifstream input("data/small_spheres.off"); + std::ifstream input(CGAL::data_file_path("meshes/small_spheres.off")); assert(input); input >> tm1; input.close(); @@ -85,7 +85,7 @@ void test_intersections(Index index, const char* type) assert(input); input >> tm2; input.close(); - input.open("data/large_cube_coplanar.off"); + input.open(CGAL::data_file_path("meshes/large_cube_coplanar.off")); assert(input); input >> tm3; input.close(); diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index d9d76484094c..bba1615f7ff0 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -194,6 +194,7 @@ CGAL_add_named_parameter(refitting_t, refitting, refitting) CGAL_add_named_parameter(volume_error_t, volume_error, volume_error) CGAL_add_named_parameter(maximum_number_of_convex_volumes_t, maximum_number_of_convex_volumes, maximum_number_of_convex_volumes) CGAL_add_named_parameter(split_at_concavity_t, split_at_concavity, split_at_concavity) +CGAL_add_named_parameter(use_inverse_transformation_t, use_inverse_transformation, use_inverse_transformation) #ifndef CGAL_NO_DEPRECATED_CODE CGAL_add_named_parameter(erase_all_duplicates_t, erase_all_duplicates, erase_all_duplicates) diff --git a/Spatial_searching/include/CGAL/Kd_tree.h b/Spatial_searching/include/CGAL/Kd_tree.h index d23f225713ee..5e8390f10831 100644 --- a/Spatial_searching/include/CGAL/Kd_tree.h +++ b/Spatial_searching/include/CGAL/Kd_tree.h @@ -300,11 +300,6 @@ class Kd_tree { return pts.empty(); } - void build() - { - build(); - } - /* Note about parallel `build()`. Several different strategies have been tried, among which: @@ -325,7 +320,7 @@ class Kd_tree { * the parallel computations are launched using `tbb::parallel_invoke` */ - template + template void build() {