diff --git a/.github/test.sh b/.github/test.sh index cf69ebee9552..e69a77b29959 100755 --- a/.github/test.sh +++ b/.github/test.sh @@ -2,11 +2,12 @@ /usr/local/bin/cmake --version FACTOR=$1 set -ex -cd Polyhedron/demo -/usr/local/bin/cmake -S Polyhedron -B build -DCGAL_DIR=$2 +cd Lab/demo +/usr/local/bin/cmake -S Lab -B build -DCGAL_DIR=$2 LIST_OF_PLUGINS=$(/usr/local/bin/cmake --build build -t help | egrep 'plugin$' |& cut -d\ -f2) PLUGINS_ARRAY=(${LIST_OF_PLUGINS}); NB_OF_PLUGINS=${#PLUGINS_ARRAY[@]} DEL=$(($NB_OF_PLUGINS / 4)) cd build -make -j2 ${PLUGINS_ARRAY[@]:$(($FACTOR * $DEL)):$((($FACTOR + 1) * $DEL))} +NUM_PROCS=$(nproc) +make -j${NUM_PROCS} ${PLUGINS_ARRAY[@]:$(($FACTOR * $DEL)):$((($FACTOR + 1) * $DEL))} diff --git a/.github/workflows/build_doc.yml b/.github/workflows/build_doc.yml index d523cc2079d1..9735cd3d3c84 100644 --- a/.github/workflows/build_doc.yml +++ b/.github/workflows/build_doc.yml @@ -104,8 +104,8 @@ jobs: echo "DoxygenError=No package affected." >> $GITHUB_OUTPUT exit 1 fi - cd build_doc && make -j2 doc - make -j2 doc_with_postprocessing 2>tmp.log + cd build_doc && make -j$(nproc) doc + make -j$(nproc) doc_with_postprocessing 2>tmp.log if [ -s tmp.log ]; then content=`cat ./tmp.log` delimiter="$(openssl rand -hex 8)" diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index a98dfba56b83..51bffe93ef25 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -1,4 +1,4 @@ -name: Test Polyhedron Demo +name: Compile CGAL Lab on: [push, pull_request,workflow_dispatch] diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index c55ba6df40d9..f76a99b18dd1 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -12,24 +12,24 @@ jobs: steps: - uses: actions/checkout@v4 - name: REUSE version - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --version - name: REUSE lint - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --include-submodules lint - name: REUSE SPDX SBOM - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: spdx - name: install dependencies - run: sudo apt-get install -y cmake + run: sudo apt-get update && sudo apt-get install -y cmake - name: Create CGAL internal release run: | mkdir -p ./release cmake -DDESTINATION=./release -DCGAL_VERSION=9.9 -P ./Scripts/developer_scripts/cgal_create_release_with_cmake.cmake - name: REUSE lint release tarball - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --root ./release/CGAL-9.9 --include-submodules lint diff --git a/AABB_tree/benchmark/AABB_tree/test.cpp b/AABB_tree/benchmark/AABB_tree/test.cpp index eb2bb6318e4b..623b608da019 100644 --- a/AABB_tree/benchmark/AABB_tree/test.cpp +++ b/AABB_tree/benchmark/AABB_tree/test.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -6,8 +6,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -18,12 +18,12 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Surface_mesh Surface_mesh; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_do_intersect_transform_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; namespace PMP = CGAL::Polygon_mesh_processing; -void naive_test(int k, const char* fname, +void naive_test(int k, const std::string& fname, int& nb_inter, int& nb_no_inter, int& nb_include) { std::ifstream input(fname); @@ -33,7 +33,7 @@ void naive_test(int k, const char* fname, CGAL::Aff_transformation_3 init1(CGAL::SCALING, 6.0); PMP::transform(init1, tm); CGAL::Bbox_3 box = PMP::bbox(tm); - typedef CGAL::AABB_tree > Tree; + Tree tmTree(tm.faces_begin(), tm.faces_end(), tm); Tree tmTree2(tm2.faces_begin(), tm2.faces_end(), tm2); CGAL::Aff_transformation_3 init2(CGAL::TRANSLATION, - K::Vector_3( @@ -86,7 +86,8 @@ void naive_test(int k, const char* fname, T0 = CGAL::Aff_transformation_3(CGAL::TRANSLATION, -i*unit_vec); } } -void test_no_collision(int k, const char* fname, + +void test_no_collision(int k, const std::string &fname, int& nb_inter, int& nb_no_inter, int& nb_include) { std::ifstream input(fname); @@ -97,9 +98,11 @@ void test_no_collision(int k, const char* fname, PMP::transform(init1, tm); CGAL::Bbox_3 box = PMP::bbox(tm); Tree tmTree(tm.faces_begin(), tm.faces_end(), tm); + Tree tmTree2(tm2.faces_begin(), tm2.faces_end(), tm2); CGAL::Aff_transformation_3 init2(CGAL::TRANSLATION, - K::Vector_3( (box.xmax()-box.xmin()),0,0)); + PMP::transform(init2, tm2); tmTree.build(); @@ -113,6 +116,12 @@ void test_no_collision(int k, const char* fname, CGAL::Side_of_triangle_mesh sotm1(tmTree); + + CGAL::Rigid_triangle_mesh_collision_detection collision_detection; + + collision_detection.add_mesh(tm); + collision_detection.add_mesh(tm2); + for(int i=1; i T1 = CGAL::Aff_transformation_3(CGAL::TRANSLATION, i*unit_vec); CGAL::Aff_transformation_3 transfo = R*T1; - tmTree2.traits().set_transformation(transfo); - CGAL::Interval_nt_advanced::Protector protector; - if(tmTree2.do_intersect(tmTree)) + + collision_detection.set_transformation(1, transfo); + + std::vector< std::pair > res = collision_detection.get_all_intersections_and_inclusions(0); + + if (res.empty()) + nb_no_inter++; + else if(!res[0].second) ++nb_inter; else - { - if(sotm1(transfo.transform(vpm2[*tm2.vertices().begin()])) != CGAL::ON_UNBOUNDED_SIDE) - { - ++nb_include; - } - else - { - CGAL::Side_of_triangle_mesh sotm2(tmTree2); - if(sotm2(tm.point(*tm.vertices().begin())) != CGAL::ON_UNBOUNDED_SIDE) - ++nb_include; - else - ++nb_no_inter; - } - } + ++nb_include; } } int main(int argc, const char** argv) { - int k = (argc>1) ? atoi(argv[1]) : 10; - const char* path = (argc>2)?argv[2]:"data/handle" - ".off"; + int k = (argc>1) ? atoi(argv[1]) : 20; + std::string path = (argc>2)?argv[2]: CGAL::data_file_path("meshes/handle.off"); std::cout<< k<<" steps in "<(end - start).count() << "μs." << std::endl; + std::cout<<"Naive test : "<(end - start).count() << " ms." << std::endl; start = std::chrono::steady_clock::now(); 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() << "μs." << std::endl; + <(end - start).count() << " ms." << std::endl; return 0; } diff --git a/AABB_tree/demo/AABB_tree/Scene.cpp b/AABB_tree/demo/AABB_tree/Scene.cpp index 41e11ac62091..a84d9d3f2542 100644 --- a/AABB_tree/demo/AABB_tree/Scene.cpp +++ b/AABB_tree/demo/AABB_tree/Scene.cpp @@ -867,7 +867,7 @@ void Scene::generate_points_in(const unsigned int nb_points, } typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; std::cout << "Construct AABB tree..."; @@ -920,7 +920,7 @@ void Scene::generate_inside_points(const unsigned int nb_points) } typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; std::cout << "Construct AABB tree..."; @@ -962,7 +962,7 @@ void Scene::generate_boundary_segments(const unsigned int nb_slices) } typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Object_and_primitive_id Object_and_primitive_id; @@ -1012,7 +1012,7 @@ void Scene::generate_boundary_points(const unsigned int nb_points) } typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Object_and_primitive_id Object_and_primitive_id; @@ -1062,7 +1062,7 @@ void Scene::generate_edge_points(const unsigned int nb_points) } typedef CGAL::AABB_halfedge_graph_segment_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Object_and_primitive_id Object_and_primitive_id; diff --git a/AABB_tree/demo/AABB_tree/Scene.h b/AABB_tree/demo/AABB_tree/Scene.h index ae898f43daa4..86854e4b9885 100644 --- a/AABB_tree/demo/AABB_tree/Scene.h +++ b/AABB_tree/demo/AABB_tree/Scene.h @@ -9,7 +9,7 @@ #include "Color_ramp.h" #include -#include +#include #include #include #include @@ -62,11 +62,11 @@ class Scene : public QObject private: typedef CGAL::AABB_face_graph_triangle_primitive Facet_Primitive; - typedef CGAL::AABB_traits Facet_Traits; + typedef CGAL::AABB_traits_3 Facet_Traits; typedef CGAL::AABB_tree Facet_tree; typedef CGAL::AABB_halfedge_graph_segment_primitive Edge_Primitive; - typedef CGAL::AABB_traits Edge_Traits; + typedef CGAL::AABB_traits_3 Edge_Traits; typedef CGAL::AABB_tree Edge_tree; typedef CGAL::qglviewer::ManipulatedFrame ManipulatedFrame; diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_2.h b/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_2.h new file mode 100644 index 000000000000..82ab0e5a2e0c --- /dev/null +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_2.h @@ -0,0 +1,177 @@ + +/*! +\ingroup PkgAABBTreeConcepts +\cgalConcept + +The concept `AABBGeomTraits_2` defines the requirements for the first template parameter of the class +`CGAL::AABB_traits_2`. It provides predicates and constructors to detect +and compute intersections between query objects and the primitives stored in the AABB tree. +In addition, it contains predicates and constructors to compute distances between a point query +and the primitives stored in the AABB tree. + +\cgalRefines{SearchGeomTraits_2} + +\cgalHasModelsBegin +\cgalHasModelsBare{All models of the concept `Kernel`} +\cgalHasModelsEnd + +\sa `CGAL::AABB_traits_2` +\sa `CGAL::AABB_tree` +\sa `AABBPrimitive` + +*/ + +class AABBGeomTraits_2 { +public: + +/// \name Types +/// @{ + +/*! +A functor object to detect intersections between two geometric objects. +Provides the following operators: + +`bool operator()(const Query& q, const Bbox_2& b)`, + +`bool operator()(const Query& q, const Primitive::Datum& d)`, + +`bool operator()(const Circle_2& c, const Bbox_2& b)`. + +The operator returns `true` iff there is an intersection. +*/ +typedef unspecified_type Do_intersect_2; + +/*! +A functor object to construct the intersection between two geometric objects. + +Provides the operator: + +`return_type operator()(const Query& q, const Primitive::Datum& d)`, + +which computes the intersection between `q` and `d`. The type of the returned object +must be a `std::optional` of a `std::variant` of the possible intersection types. +*/ +typedef unspecified_type Intersect_2; + +/*! +A functor object to construct the circle specified by its center and squared radius. +Provides the operator: + +`Circle_2 operator()(const Point_2& p, const FT & sr)`, + +which returns the circle centered at `p` with `sr` as squared radius. +*/ +typedef unspecified_type Construct_circle_2; + +/*! +A functor object to compute the point on a geometric primitive which is closest from a query point. +Provides the operator: + +`Point_2 operator()(const Primitive::Datum& d, const Point_2& p)`, + +which returns the point on `d` that is closest to `p`. +*/ +typedef unspecified_type Construct_projected_point_2; + +/*! +A functor object to compare the distance of two points wrt a third one. Provides the operator: + +`CGAL::Comparison_result operator()(const Point_2& p1, const Point_2& p2, const Point_2& p3)`, + +which compares the distance between `p1` and `p2`, to the distance between `p1` and `p3`. +*/ +typedef unspecified_type Compare_distance_2; + + +/*! +A functor object to compute the squared distance between two points. Provides the operator: + +`FT operator()(const Point_2& p, const Point_2& q),` + +which returns the squared distance between `p` and `q`. +*/ +typedef unspecified_type Compute_squared_distance_2; + +/*! +A functor object to compare the x-coordinates of two points. Provides the operator: + +`bool operator()(const Point_2& p, const Point_2& q)`, + + which returns `true` iff the x-coordinate of `p` is smaller than the x-coordinate of `q`. +*/ +typedef unspecified_type Less_x_2; + +/*! +A functor object to compare the y-coordinates of two points. Provides the operator: + +`bool operator()(const Point_2& p, const Point_2& q)`, + +which returns `true` iff the y-coordinate of `p` is smaller than the y-coordinate of `q`. +*/ +typedef unspecified_type Less_y_2; + + +/*! +A functor object to compare two points. Provides the operator: + +`bool operator()(const Point_2& p, const Point_2& q)`, + +which returns `true` iff `p` is equal to `q`. +*/ +typedef unspecified_type Equal_2; + +/// @} + +/// \name Operations +/// @{ + +/*! +returns the intersection detection predicate. +*/ +Do_intersect_2 do_intersect_2_object(); + +/*! +returns the intersection constructor. +*/ +Intersect_2 intersect_2_object(); + +/*! +returns the circle constructor. +*/ +Construct_circle_2 construct_circle_2_object(); + +/*! +returns the closest point constructor. +*/ +Construct_projected_point_2 construct_projected_point_2_object(); + +/*! +returns the compare distance predicate. +*/ +Compare_distance_2 compare_distance_2_object(); + + +/*! +returns the squared distance functor. +*/ +Compute_squared_distance_2 compute_squared_distance_2_object(); + +/*! +returns the `Less_x_2` predicate. +*/ +Less_x_2 less_x_2_object(); + +/*! +returns the `Less_y_2` predicate. +*/ +Less_y_2 less_y_2_object(); + + +/*! +returns the equal predicate. +*/ +Equal_2 equal_2_object(); + +/// @} + +}; /* end AABBGeomTraits_2 */ diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits.h b/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_3.h similarity index 79% rename from AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits.h rename to AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_3.h index ca49f3ab247b..2f453f54670b 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBGeomTraits_3.h @@ -3,8 +3,8 @@ \ingroup PkgAABBTreeConcepts \cgalConcept -The concept `AABBGeomTraits` defines the requirements for the first template parameter of the class -`CGAL::AABB_traits`. It provides predicates and constructors to detect +The concept `AABBGeomTraits_3` defines the requirements for the first template parameter of the class +`CGAL::AABB_traits_3`. It provides predicates and constructors to detect and compute intersections between query objects and the primitives stored in the AABB tree. In addition, it contains predicates and constructors to compute distances between a point query and the primitives stored in the AABB tree. @@ -15,13 +15,13 @@ and the primitives stored in the AABB tree. \cgalHasModelsBare{All models of the concept `Kernel`} \cgalHasModelsEnd -\sa `CGAL::AABB_traits` +\sa `CGAL::AABB_traits_3` \sa `CGAL::AABB_tree` \sa `AABBPrimitive` */ -class AABBGeomTraits { +class AABBGeomTraits_3 { public: /// \name Types @@ -31,13 +31,13 @@ class AABBGeomTraits { A functor object to detect intersections between two geometric objects. Provides the following operators: -`bool operator()(Query, Bbox_3)`, +`bool operator()(const Query& q, const Bbox_3& b)`, -`bool operator()(Query, Primitive::Datum)`, +`bool operator()(const Query& q, const Primitive::Datum& d)`, -`bool operator()(Sphere_3, Bbox_3)`. +`bool operator()(const Sphere_3& s, const Bbox_3& b)`. -The operator returns `true` iff there exists a non-empty intersection. +The operator returns `true` iff there is an intersection. */ typedef unspecified_type Do_intersect_3; @@ -54,7 +54,7 @@ must be a `std::optional` of a `std::variant` of the possible intersection types typedef unspecified_type Intersect_3; /*! -A functor object to construct the sphere centered at one point and passing through another one. +A functor object to construct the sphere specified by its center and squared radius. Provides the operator: `Sphere_3 operator()(const Point_3& p, const FT & sr)`, @@ -64,7 +64,7 @@ which returns the sphere centered at `p` with `sr` as squared radius. typedef unspecified_type Construct_sphere_3; /*! -A functor object to compute the point on a geometric primitive which is closest from a query. +A functor object to compute the point on a geometric primitive which is closest from a query point. Provides the operator: `Point_3 operator()(const Primitive::Datum& d, const Point_3& p)`, @@ -78,19 +78,10 @@ A functor object to compare the distance of two points wrt a third one. Provides `CGAL::Comparison_result operator()(const Point_3& p1, const Point_3& p2, const Point_3& p3)`, -which compares the distance between `p1` and `p2`, and between `p2` and `p3`. +which compares the distance between `p1` and `p2`, to the distance between `p1` and `p3`. */ typedef unspecified_type Compare_distance_3; -/*! -A functor object to compute the squared radius of a sphere. -Provides the operator: - -`FT operator()(const Sphere_3& s),` - -which returns the squared radius of `s`. -*/ -typedef unspecified_type Compute_squared_radius_3; /*! A functor object to compute the squared distance between two points. Provides the operator: @@ -167,10 +158,6 @@ returns the compare distance predicate. */ Compare_distance_3 compare_distance_3_object(); -/*! -returns the squared radius functor. -*/ -Compute_squared_radius_3 compute_squared_radius_3_object(); /*! returns the squared distance functor. @@ -199,5 +186,4 @@ Equal_3 equal_3_object(); /// @} -}; /* end AABBGeomTraits */ - +}; /* end AABBGeomTraits_3 */ diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitive.h b/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitive.h index 3abb0cf153bd..5d8e1246a7c8 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitive.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitive.h @@ -14,8 +14,10 @@ The `Primitive` type can be, e.g., a wrapper around a `Handle`. Assume for insta \cgalHasModelsBegin \cgalHasModels{CGAL::AABB_primitive} -\cgalHasModels{CGAL::AABB_segment_primitive} -\cgalHasModels{CGAL::AABB_triangle_primitive} +\cgalHasModels{CGAL::AABB_segment_primitive_2} +\cgalHasModels{CGAL::AABB_segment_primitive_3} +\cgalHasModels{CGAL::AABB_triangle_primitive_2} +\cgalHasModels{CGAL::AABB_triangle_primitive_3} \cgalHasModels{CGAL::AABB_halfedge_graph_segment_primitive} \cgalHasModels{CGAL::AABB_face_graph_triangle_primitive} \cgalHasModelsEnd @@ -28,7 +30,7 @@ class AABBPrimitive { /// @{ /*! -3D point type. +Point type. */ typedef unspecified_type Point; @@ -68,7 +70,7 @@ returns the corresponding identifier. This identifier is only used as a referenc Id id(); /*! -returns a 3D point located on the geometric object represented by the primitive. This function is used to sort the primitives during the AABB tree construction as well as to construct the search KD-tree internal to the AABB tree used to accelerate distance queries. +returns a point located on the geometric object represented by the primitive. This function is used to sort the primitives during the AABB tree construction as well as to construct the search KD-tree internal to the AABB tree used to accelerate distance queries. */ Point_reference reference_point(); diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitiveWithSharedData.h b/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitiveWithSharedData.h index 416cc785468d..284c90b60386 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitiveWithSharedData.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBPrimitiveWithSharedData.h @@ -34,7 +34,7 @@ class AABBPrimitiveWithSharedData { /// \name Types /// @{ /*! -3D point type. +Point type. */ typedef unspecified_type Point; @@ -78,7 +78,7 @@ returns the corresponding identifier. This identifier is only used as a referenc Id id(); /*! -returns a 3D point located on the geometric object represented by the primitive. This function is used to sort the primitives during the AABB tree construction as well as to construct the search KD-tree internal to the AABB tree used to accelerate distance queries. +returns a point located on the geometric object represented by the primitive. This function is used to sort the primitives during the AABB tree construction as well as to construct the search KD-tree internal to the AABB tree used to accelerate distance queries. */ Point_reference reference_point(const Shared_data& data); diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_2.h b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_2.h new file mode 100644 index 000000000000..15e3050edee5 --- /dev/null +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_2.h @@ -0,0 +1,69 @@ +/*! +\ingroup PkgAABBTreeConcepts +\cgalConcept + +The concept `AABBRayIntersectionGeomTraits_2` is a refinement of the +concept `AABBGeomTraits_2`. In addition to the types and functors required by +`AABBGeomTraits_2` it also requires types and functors necessary to +define the Intersection_distance functor (see `AABBRayIntersectionTraits`). + +\cgalRefines{AABBGeomTraits_2} + +\cgalHasModelsBegin +\cgalHasModelsBare{All models of the concept `Kernel`} +\cgalHasModelsEnd + +\sa `CGAL::AABB_traits_2` +\sa `CGAL::AABB_tree` +\sa `AABBPrimitive` + +*/ +class AABBRayIntersectionGeomTraits_2 { +public: + /*! + Type of a 2D ray. + */ + typedef unspecified_type Ray_2; + + /*! + Type of a 2D vector. + */ + typedef unspecified_type Vector_2; + + /*! + A functor object to construct the source point of a ray. Provides the operator: + `Point_2 operator()(const Ray_2&);` + */ + typedef unspecified_type Construct_source_2; + + /*! + returns the `Construct_source_2` functor. + */ + Construct_source_2 construct_source_2_object(); + + /*! + A model of `CartesianConstIterator_2`. + */ + typedef unspecified_type Cartesian_const_iterator_2; + + /*! + A model of `ConstructCartesianConstIterator_2`. + */ + typedef unspecified_type Construct_cartesian_const_iterator_2; + + /*! + returns the `Construct_cartesian_const_iterator_2` functor. + */ + Construct_cartesian_const_iterator_2 construct_cartesian_const_iterator_2_object(); + + /*! + A functor object to construct a vector having the same direction as a ray. Provides the operator: + `Vector_2 operator()(const Ray_2&);` + */ + typedef unspecified_type Construct_vector_2; + + /*! + returns the `Construct_vector_2` functor. + */ + Construct_vector_2 construct_vector_2_object(); +}; diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits.h b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_3.h similarity index 52% rename from AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits.h rename to AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_3.h index c34d88be491e..508b656d1cd2 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionGeomTraits_3.h @@ -2,23 +2,23 @@ \ingroup PkgAABBTreeConcepts \cgalConcept -The concept `AABBRayIntersectionGeomTraits` is a refinement of the -concept `AABBGeomTraits`. In addition to the types required by -`AABBGeomTraits` it also requires types and functors necessary to +The concept `AABBRayIntersectionGeomTraits_3` is a refinement of the +concept `AABBGeomTraits_3`. In addition to the types required by +`AABBGeomTraits_3` it also requires types and functors necessary to define the Intersection_distance functor. -\cgalRefines{AABBGeomTraits} +\cgalRefines{AABBGeomTraits_3} \cgalHasModelsBegin \cgalHasModelsBare{All models of the concept `Kernel`} \cgalHasModelsEnd -\sa `CGAL::AABB_traits` +\sa `CGAL::AABB_traits_3` \sa `CGAL::AABB_tree` \sa `AABBPrimitive` */ -class AABBRayIntersectionGeomTraits { +class AABBRayIntersectionGeomTraits_3 { public: /*! Type of a 3D ray. @@ -37,30 +37,33 @@ class AABBRayIntersectionGeomTraits { typedef unspecified_type Construct_source_3; /*! + returns the `Construct_source_3` functor. */ Construct_source_3 construct_source_3_object(); /*! - A model of `CartesianConstIterator3`. + A model of `CartesianConstIterator_3`. */ typedef unspecified_type Cartesian_const_iterator_3; /*! - A model of `ConstructCartesianConstIterator3`. + A model of `ConstructCartesianConstIterator_3`. */ typedef unspecified_type Construct_cartesian_const_iterator_3; /*! + returns the `Construct_cartesian_const_iterator_3` functor. */ - Construct_source_3 construct_cartesian_const_iterator_3_object(); + Construct_cartesian_const_iterator_3 construct_cartesian_const_iterator_3_object(); /*! - A functor object to construct a vector giving the direction of a ray. Provides the operator: + A functor object to construct a vector having the same direction as a ray. Provides the operator: `Vector_3 operator()(const Ray_3&);` */ typedef unspecified_type Construct_vector_3; /*! + returns the `Construct_vector_3` functor. */ - Construct_source_3 construct_vector_3_object(); + Construct_vector_3 construct_vector_3_object(); }; diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionTraits.h b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionTraits.h index 7b8b6dd4ec45..3940817c7abd 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionTraits.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBRayIntersectionTraits.h @@ -7,8 +7,11 @@ The concept `AABBRayIntersectionTraits` is a refinement of the concept `AABBTraits` it also requires function objects to calculate the distance of an intersection along a ray. +\cgalRefines{AABBTraits} + \cgalHasModelsBegin -\cgalHasModels{CGAL::AABB_traits} +\cgalHasModels{CGAL::AABB_traits_2} +\cgalHasModels{CGAL::AABB_traits_3} \cgalHasModelsEnd \sa `CGAL::AABB_tree` @@ -17,11 +20,57 @@ distance of an intersection along a ray. */ class AABBRayIntersectionTraits { public: + /*! + Type of a ray. + */ + typedef unspecified_type Ray; + + /*! + Type of a vector. + */ + typedef unspecified_type Vector; /*! - Type of a 3D ray. + Type of bounding box. */ - typedef unspecified_type Ray_3; + typedef AABBTraits::Bounding_box Bounding_box; + + /*! + A functor object to construct the source point of a ray. Provides the operator: + `Point operator()(const Ray&);` + */ + typedef unspecified_type Construct_source; + + /*! + returns the `Construct_source` functor. + */ + Construct_source construct_source_object(); + + /*! + A model of `CartesianConstIterator_2` or `CartesianConstIterator_3`, depending on the dimension of `Vector`. + */ + typedef unspecified_type Cartesian_const_iterator; + + /*! + A model of `ConstructCartesianConstIterator_2` or `ConstructCartesianConstIterator_3`, depending on the dimension of `Vector`. + */ + typedef unspecified_type Construct_cartesian_const_iterator; + + /*! + returns the `Construct_cartesian_const_iterator` functor. + */ + Construct_cartesian_const_iterator construct_cartesian_const_iterator_object(); + + /*! + A functor object to construct a vector having the same direction as a ray. Provides the operator: + `Vector operator()(const Ray&);` + */ + typedef unspecified_type Construct_vector; + + /*! + returns the `Construct_vector` functor. + */ + Construct_vector construct_vector_object(); /*! @@ -34,9 +83,9 @@ class AABBRayIntersectionTraits { respectively. Provides the operators: - `std::optional operator()(const Ray_3& r, const Bounding_box& bbox)`. - `std::optional::%Type > > - operator()(const Ray_3& r, const Primitive& primitive)`. + `std::optional operator()(const Ray& r, const Bounding_box& bbox)`. + `std::optional::%Type > > + operator()(const Ray& r, const Primitive& primitive)`. A common algorithm to compute the intersection between a bounding box and a ray is the diff --git a/AABB_tree/doc/AABB_tree/Concepts/AABBTraits.h b/AABB_tree/doc/AABB_tree/Concepts/AABBTraits.h index 3c101a2211b7..40413193d549 100644 --- a/AABB_tree/doc/AABB_tree/Concepts/AABBTraits.h +++ b/AABB_tree/doc/AABB_tree/Concepts/AABBTraits.h @@ -6,12 +6,12 @@ The concept `AABBTraits` provides the geometric primitive types and methods for the class `CGAL::AABB_tree`. \cgalHasModelsBegin -\cgalHasModels{CGAL::AABB_traits} +\cgalHasModels{CGAL::AABB_traits_2} +\cgalHasModels{CGAL::AABB_traits_3} \cgalHasModelsEnd -\cgalRefines{SearchGeomTraits_3} +\cgalRefines{SearchTraits} -\sa `CGAL::AABB_traits` \sa `CGAL::AABB_tree` \sa `AABBPrimitive` @@ -28,9 +28,9 @@ Value type of the `Squared_distance` functor. typedef unspecified_type FT; /*! -Type of a 3D point. +Type of a point. */ -typedef unspecified_type Point_3; +typedef unspecified_type Point; /*! Type of primitive. @@ -43,19 +43,10 @@ Bounding box type. */ typedef unspecified_type Bounding_box; - /*! - enum required for axis selection - */ - enum Axis { - CGAL_X_AXIS, - CGAL_Y_AXIS, - CGAL_Z_AXIS - }; - /*! -3D Point and Primitive Id type +Point and Primitive Id type */ -typedef std::pair Point_and_primitive_id; +typedef std::pair Point_and_primitive_id; /*! \deprecated @@ -144,22 +135,22 @@ A functor object to compute distance comparisons between the query and the nodes typedef unspecified_type Compare_distance; /*! -A functor object to compute closest point from the query on a primitive. Provides the operator: -`Point_3 operator()(const Query& query, const Primitive& primitive, const Point_3 & closest);` which returns the closest point to `query`, among `closest` and all points of the primitive. +A functor object to compute the closest point from the query on a primitive. Provides the operator: +`Point operator()(const Query& query, const Primitive& primitive, const Point & closest);` which returns the closest point to `query`, among `closest` and all points of the primitive. */ typedef unspecified_type Closest_point; /*! A functor object to compute the squared distance between two points. Provides the operator: -`FT operator()(const Point& query, const Point_3 & p);` which returns the squared distance between `p` and `q`. +`FT operator()(const Point& query, const Point & p);` which returns the squared distance between `p` and `q`. */ typedef unspecified_type Squared_distance; /*! A functor object to compare two points. Provides the operator: -`bool operator()(const Point_3& p, const Point_3& q);}` which returns `true` if `p` is equal to `q`. +`bool operator()(const Point& p, const Point& q);}` which returns `true` if `p` is equal to `q`. */ -typedef unspecified_type Equal_3; +typedef unspecified_type Equal; /// @} /// \name Operations @@ -203,7 +194,7 @@ Squared_distance squared_distance_object(); /*! returns the equal functor. */ -Equal_3 equal_3_object(); +Equal equal_object(); /// @} @@ -231,4 +222,3 @@ const Primitive::Shared_data& shared_data() const; }; /* end AABBTraits */ - diff --git a/AABB_tree/doc/AABB_tree/Doxyfile.in b/AABB_tree/doc/AABB_tree/Doxyfile.in index b626c95c2da2..fb005db4f918 100644 --- a/AABB_tree/doc/AABB_tree/Doxyfile.in +++ b/AABB_tree/doc/AABB_tree/Doxyfile.in @@ -1,10 +1,7 @@ @INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} -PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Fast Intersection and Distance Computation (AABB Tree)" +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Fast Intersection and Distance Computation (AABB Tree)" EXTRACT_ALL = false HIDE_UNDOC_MEMBERS = true HIDE_UNDOC_CLASSES = true - - - diff --git a/AABB_tree/doc/AABB_tree/PackageDescription.txt b/AABB_tree/doc/AABB_tree/PackageDescription.txt index c032420bb769..4aea07e8e6a4 100644 --- a/AABB_tree/doc/AABB_tree/PackageDescription.txt +++ b/AABB_tree/doc/AABB_tree/PackageDescription.txt @@ -5,12 +5,12 @@ /*! \addtogroup PkgAABBTreeRef -\cgalPkgDescriptionBegin{3D Fast Intersection and Distance Computation,PkgAABBTree} +\cgalPkgDescriptionBegin{2D and 3D Fast Intersection and Distance Computation,PkgAABBTree} \cgalPkgPicture{aabb-teaser-thumb.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Pierre Alliez, Stéphane Tayeb, and Camille Wormser} -\cgalPkgDesc{The AABB (axis-aligned bounding box) tree component offers a static data structure and algorithms to perform efficient intersection and distance queries on sets of finite 3D geometric objects.} -\cgalPkgManuals{Chapter_3D_Fast_Intersection_and_Distance_Computation,PkgAABBTreeRef} +\cgalPkgDesc{The AABB (axis-aligned bounding box) tree component offers a static data structure and algorithms to perform efficient intersection and distance queries on sets of finite 2D and 3D geometric objects.} +\cgalPkgManuals{Chapter_Fast_Intersection_and_Distance_Computation,PkgAABBTreeRef} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin \cgalPkgSince{3.5} @@ -25,18 +25,28 @@ \cgalCRPSection{Concepts} - `AABBPrimitive` - `AABBPrimitiveWithSharedData` -- `AABBGeomTraits` +- `AABBGeomTraits_2` +- `AABBGeomTraits_3` - `AABBTraits` -- `AABBRayIntersectionGeomTraits` +- `AABBRayIntersectionGeomTraits_2` +- `AABBRayIntersectionGeomTraits_3` - `AABBRayIntersectionTraits` \cgalCRPSection{Classes} -- `CGAL::AABB_traits` +- `CGAL::AABB_traits` (deprecated, use `CGAL::AABB_traits_3`) +- `CGAL::AABB_traits_2` +- `CGAL::AABB_traits_3` - `CGAL::AABB_tree` \cgalCRPSection{Primitives} -- `CGAL::AABB_triangle_primitive` -- `CGAL::AABB_segment_primitive` +- `CGAL::AABB_triangle_primitive_2` +- `CGAL::AABB_indexed_triangle_primitive_2` +- `CGAL::AABB_segment_primitive_2` +- `CGAL::AABB_polyline_segment_primitive_2` +- `CGAL::AABB_triangle_primitive` (deprecated, use `CGAL::AABB_triangle_primitive_3`) +- `CGAL::AABB_triangle_primitive_3` +- `CGAL::AABB_segment_primitive` (deprecated, use `CGAL::AABB_segment_primitive_3`) +- `CGAL::AABB_segment_primitive_3` - `CGAL::AABB_primitive` - `CGAL::AABB_halfedge_graph_segment_primitive` - `CGAL::AABB_face_graph_triangle_primitive` diff --git a/AABB_tree/doc/AABB_tree/aabb_tree.txt b/AABB_tree/doc/AABB_tree/aabb_tree.txt index bdeefbdd0323..ca5f84e2fbfa 100644 --- a/AABB_tree/doc/AABB_tree/aabb_tree.txt +++ b/AABB_tree/doc/AABB_tree/aabb_tree.txt @@ -3,7 +3,7 @@ namespace CGAL { /*! \mainpage User Manual -\anchor Chapter_3D_Fast_Intersection_and_Distance_Computation +\anchor Chapter_Fast_Intersection_and_Distance_Computation \cgalAutoToc \authors Pierre Alliez, Stephane Tayeb, and Camille Wormser @@ -12,7 +12,7 @@ namespace CGAL { The AABB tree component offers a static data structure and algorithms to perform efficient intersection and distance queries against sets of -finite 3D geometric objects. The set of geometric objects stored in +finite 2D or 3D geometric objects. The set of geometric objects stored in the data structure can be queried for intersection detection, intersection computation and distance. The intersection queries can be of any type, provided that the corresponding intersection predicates @@ -123,7 +123,7 @@ the squared distance from a point query. \subsection aabb_tree_examples_2 Tree of Polyhedron Triangle Facets for Intersection Queries - In the following example the AABB primitive wraps a facet handle of a +In the following example the AABB primitive wraps a facet handle of a triangle polyhedral surface as `id` and the corresponding 3D triangle as geometric object. From a segment query we test the intersections, then compute the number of intersections, compute the @@ -163,12 +163,17 @@ handle. \subsection aabb_tree_examples_4 Tree of Segments for Intersection and Distance Queries - In the following example the segments are stored into a list, and the +In the following example the segments are stored into a list, and the AABB primitive wraps a segment as `datum` and an iterator in the list as `id`. We compute the number of intersections with plane and triangles queries, and the closest point from a point query. \cgalExample{AABB_tree/AABB_segment_3_example.cpp} +\subsection aabb_tree_examples_8 Tree of Polyline and Polygon Segments for Intersection and Distance Queries + +The following example uses segments given by a polyline and a polygon. The difference is that the polygon is closed. The `id` in this case is the iterator pointing to a `Point_2` in the polyline or the polygon. The datum, a `Segment_2`, is created on the fly from the points using the `id` as the source and the following `Point_2` as the target. We count the intersections with a `Segment_2` and the closest point and id from a point query. +\cgalExample{AABB_tree/AABB_polyline_segment_2_example.cpp} + \subsection aabb_tree_examples_5 Tree of Polyhedron Edge Segments for Intersection and Distance Queries In the following example the AABB primitive wraps a halfedge handle as @@ -233,7 +238,7 @@ subdivision scheme which multiplies the number of triangles by four. \subsection aabb_tree_perf_mem Memory When using the polyhedron triangle facet primitive (defined in -`AABB_polyhedron_triangle_primitive.h`) the AABB tree occupies +`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 @@ -426,7 +431,7 @@ initial ball radius to a small value still guaranteed to intersect the input primitives. This is achieved by constructing an internal secondary data structure which provides a good hint to the algorithm at the beginning of the traversal (done by default). -Calling `do_not_accelerate_distance_queries()` will disable +Calling `AABB_tree::do_not_accelerate_distance_queries()` will disable the construction and the usage of this internal secondary data structure. \section aabb_tree_history Design and Implementation History @@ -437,9 +442,14 @@ implementing both intersection and distance queries, and for generic queries and primitives was developed by Camille Wormser. In 2009, Pierre Alliez, Stéphane Tayeb and Camille Wormser made the implementation CGAL-compliant, with the help of Laurent Rineau for -optimizing the tree construction. The authors wish to thank Andreas -Fabri, Jane Tournois, Mariette Yvinec and Sylvain Lefèbvre for -helpful comments and discussions. +optimizing the tree construction. Additionally, Andreas +Fabri, Jane Tournois, Mariette Yvinec and Sylvain Lefèbvre are +given thanks for helpful comments and discussions during that period. +Later, Sébastien Loriot contributed several improvements: +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. */ diff --git a/AABB_tree/doc/AABB_tree/dependencies b/AABB_tree/doc/AABB_tree/dependencies index 81058527aaf9..6c6aa39dfe36 100644 --- a/AABB_tree/doc/AABB_tree/dependencies +++ b/AABB_tree/doc/AABB_tree/dependencies @@ -8,3 +8,4 @@ Box_intersection_d Polyhedron BGL Spatial_searching +Property_map diff --git a/AABB_tree/doc/AABB_tree/examples.txt b/AABB_tree/doc/AABB_tree/examples.txt index cc274a274697..d986357796cd 100644 --- a/AABB_tree/doc/AABB_tree/examples.txt +++ b/AABB_tree/doc/AABB_tree/examples.txt @@ -7,8 +7,12 @@ \example AABB_tree/AABB_polyhedron_edge_example.cpp \example AABB_tree/AABB_polyhedron_facet_distance_example.cpp \example AABB_tree/AABB_polyhedron_facet_intersection_example.cpp +\example AABB_tree/AABB_polyline_segment_2_example.cpp \example AABB_tree/AABB_segment_3_example.cpp +\example AABB_tree/AABB_segment_2_example.cpp \example AABB_tree/AABB_ray_shooting_example.cpp +\example AABB_tree/AABB_indexed_triangle_2_example.cpp +\example AABB_tree/AABB_triangle_2_example.cpp \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 diff --git a/AABB_tree/examples/AABB_tree/AABB_cached_bbox_example.cpp b/AABB_tree/examples/AABB_tree/AABB_cached_bbox_example.cpp index b8af3f3f442c..129d3e65c1de 100644 --- a/AABB_tree/examples/AABB_tree/AABB_cached_bbox_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_cached_bbox_example.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include #include @@ -23,7 +23,7 @@ template void triangle_mesh(std::string fname) { typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; TriangleMesh tmesh; @@ -56,7 +56,7 @@ void surface_mesh_cache_bbox(std::string fname) typedef boost::graph_traits::face_descriptor face_descriptor; typedef Surface_mesh::Property_map Bbox_pmap; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; Surface_mesh tmesh; diff --git a/AABB_tree/examples/AABB_tree/AABB_custom_example.cpp b/AABB_tree/examples/AABB_tree/AABB_custom_example.cpp index 5f225eef950c..60696a0d144b 100644 --- a/AABB_tree/examples/AABB_tree/AABB_custom_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_custom_example.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include @@ -91,7 +91,7 @@ struct My_triangle_primitive { -typedef CGAL::AABB_traits My_AABB_traits; +typedef CGAL::AABB_traits_3 My_AABB_traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_array_example.cpp b/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_array_example.cpp index ab8f529f54c5..0952cc15146f 100644 --- a/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_array_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_array_example.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include @@ -97,7 +97,7 @@ struct My_triangle_primitive { // types -typedef CGAL::AABB_traits My_AABB_traits; +typedef CGAL::AABB_traits_3 My_AABB_traits; typedef CGAL::AABB_tree Tree; const double* My_triangle_primitive::point_container = nullptr; diff --git a/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_example.cpp b/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_example.cpp index b864c08adbac..f778a06edf82 100644 --- a/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_custom_indexed_triangle_set_example.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include @@ -104,7 +104,7 @@ struct My_triangle_primitive { // types -typedef CGAL::AABB_traits My_AABB_traits; +typedef CGAL::AABB_traits_3 My_AABB_traits; typedef CGAL::AABB_tree Tree; const std::vector* My_triangle_primitive::point_container = nullptr; diff --git a/AABB_tree/examples/AABB_tree/AABB_custom_triangle_soup_example.cpp b/AABB_tree/examples/AABB_tree/AABB_custom_triangle_soup_example.cpp index 0fe8ffb713ad..62361baba69f 100644 --- a/AABB_tree/examples/AABB_tree/AABB_custom_triangle_soup_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_custom_triangle_soup_example.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include @@ -95,7 +95,7 @@ struct My_triangle_primitive { }; // types -typedef CGAL::AABB_traits My_AABB_traits; +typedef CGAL::AABB_traits_3 My_AABB_traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/examples/AABB_tree/AABB_face_graph_triangle_example.cpp b/AABB_tree/examples/AABB_tree/AABB_face_graph_triangle_example.cpp index 244909cb1b16..2ce90dd2cfd1 100644 --- a/AABB_tree/examples/AABB_tree/AABB_face_graph_triangle_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_face_graph_triangle_example.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -14,7 +14,7 @@ typedef K::Point_3 Point; typedef K::Segment_3 Segment; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; template diff --git a/AABB_tree/examples/AABB_tree/AABB_halfedge_graph_edge_example.cpp b/AABB_tree/examples/AABB_tree/AABB_halfedge_graph_edge_example.cpp index 57eade14b284..76cb7eec1777 100644 --- a/AABB_tree/examples/AABB_tree/AABB_halfedge_graph_edge_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_halfedge_graph_edge_example.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -14,7 +14,7 @@ typedef K::Point_3 Point; typedef K::Triangle_3 Triangle; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_halfedge_graph_segment_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; template diff --git a/AABB_tree/examples/AABB_tree/AABB_indexed_triangle_2_example.cpp b/AABB_tree/examples/AABB_tree/AABB_indexed_triangle_2_example.cpp new file mode 100644 index 000000000000..053c49467a48 --- /dev/null +++ b/AABB_tree/examples/AABB_tree/AABB_indexed_triangle_2_example.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; +typedef K::Point_3 Point_3; +typedef K::Point_2 Point_2; +typedef K::Ray_2 Ray; + +template +struct Projection_xy_point_map { + + typedef typename GeomTraits::Point_3 key_type; + typedef typename GeomTraits::Point_2 value_type; + typedef value_type reference; + + typedef boost::readable_property_map_tag category; + typedef Projection_xy_point_map Self; + + Projection_xy_point_map() {} + + inline friend value_type get(Self, key_type p) + { + return value_type(p.x(), p.y()); + } +}; + +typedef std::vector >::const_iterator IndexIterator; +typedef std::vector PointRange; +typedef CGAL::AABB_indexed_triangle_primitive_2> Primitive; +typedef CGAL::AABB_traits_2 AABB_triangle_traits; +typedef CGAL::AABB_tree Tree; +typedef Tree::Point_and_primitive_id Point_and_primitive_id; +typedef std::optional::Type> Ray_intersection; + +int main() +{ + Point_3 a(0.0, 0.0, 0.0); + Point_3 b(0.0, 1.0, 0.0); + Point_3 c(1.0, 0.0, 0.0); + Point_3 d(1.0, 1.0, 0.0); + Point_3 e(2.0, 0.0, 0.0); + Point_3 f(2.0, 1.0, 0.0); + + std::vector points = { a, b, c, d, e, f }; + + std::vector > 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 + Tree tree(triangles.begin(), triangles.end(), points); + + // point sampling + Point_and_primitive_id id; + id = tree.closest_point_and_primitive(Point_2(0.5, 0.4)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + id = tree.closest_point_and_primitive(Point_2(0.5, 0.6)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + id = tree.closest_point_and_primitive(Point_2(1.5, 0.5)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + id = tree.closest_point_and_primitive(Point_2(1.5, 0.6)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + id = tree.closest_point_and_primitive(Point_2(1.0, 0.0)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + id = tree.closest_point_and_primitive(Point_2(3.0, 0.5)); + std::cout << std::distance(triangles.cbegin(), id.second) << ". triangle" << std::endl; + + Ray ray(Point_2(5.5, 0.5), Point_2(1.5, 0.5)); + Ray_intersection intersection = tree.first_intersection(ray); + + if (!intersection) { + std::cout << "Ray does not intersect with triangles although it should!" << std::endl; + return EXIT_FAILURE; + } + else { + std::cout << std::distance(triangles.cbegin(), intersection->second) << ". triangle" << std::endl; + } + + std::list intersections; + tree.all_intersections(ray, std::back_inserter(intersections)); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/examples/AABB_tree/AABB_insertion_example.cpp b/AABB_tree/examples/AABB_tree/AABB_insertion_example.cpp index 90719fda49f9..a4db367629d5 100644 --- a/AABB_tree/examples/AABB_tree/AABB_insertion_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_insertion_example.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -13,7 +13,7 @@ typedef K::Point_3 Point; typedef K::Segment_3 Segment; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Point_and_primitive_id Point_and_primitive_id; diff --git a/AABB_tree/examples/AABB_tree/AABB_polyhedron_edge_example.cpp b/AABB_tree/examples/AABB_tree/AABB_polyhedron_edge_example.cpp index 8e8c776032ca..cd4c959c4ac6 100644 --- a/AABB_tree/examples/AABB_tree/AABB_polyhedron_edge_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_polyhedron_edge_example.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -14,7 +14,7 @@ typedef K::Point_3 Point; typedef K::Triangle_3 Triangle; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_halfedge_graph_segment_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_distance_example.cpp b/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_distance_example.cpp index 5da2e15bd849..416954a86852 100644 --- a/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_distance_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_distance_example.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -15,7 +15,7 @@ typedef K::Point_3 Point; typedef K::Segment_3 Segment; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Point_and_primitive_id Point_and_primitive_id; diff --git a/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_intersection_example.cpp b/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_intersection_example.cpp index 6ced34c02340..fa7d6e19bf5c 100644 --- a/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_intersection_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_polyhedron_facet_intersection_example.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -17,7 +17,7 @@ typedef K::Segment_3 Segment; typedef K::Ray_3 Ray; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef std::optional< Tree::Intersection_and_primitive_id::Type > Segment_intersection; typedef std::optional< Tree::Intersection_and_primitive_id::Type > Plane_intersection; diff --git a/AABB_tree/examples/AABB_tree/AABB_polyline_segment_2_example.cpp b/AABB_tree/examples/AABB_tree/AABB_polyline_segment_2_example.cpp new file mode 100644 index 000000000000..74d2f6c03210 --- /dev/null +++ b/AABB_tree/examples/AABB_tree/AABB_polyline_segment_2_example.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + + +typedef CGAL::Simple_cartesian K; +typedef K::Segment_2 Segment; +typedef K::Point_2 Point; + +typedef std::vector PointRange; +typedef PointRange::const_iterator Iterator_pr; +typedef CGAL::AABB_polyline_segment_primitive_2 Primitive_pr; +typedef CGAL::AABB_traits_2 Traits_pr; +typedef CGAL::AABB_tree Tree_pr; +typedef Tree_pr::Point_and_primitive_id Point_and_primitive_id_pr; + +typedef CGAL::Polygon_2 Polygon_2; +typedef Polygon_2::const_iterator Iterator_poly; +typedef CGAL::AABB_polyline_segment_primitive_2 Primitive_poly; +typedef CGAL::AABB_traits_2 Traits_poly; +typedef CGAL::AABB_tree Tree_poly; +typedef Tree_poly::Point_and_primitive_id Point_and_primitive_id_poly; + +template +void test(AABBTree tree) { + tree.build(); + + tree.accelerate_distance_queries(); + + // counts #intersections with a segment query + Segment segment_query(Point(1.0, 0.0), Point(0.0, 7.0)); + + std::cout << tree.number_of_intersected_primitives(segment_query) + << " intersections(s) with segment" << std::endl; + + // computes the closest point from a point query + Point point_query(1.5, 3.0); + Point closest = tree.closest_point(point_query); + std::cerr << "closest point is: " << closest << std::endl; + + PPId id = tree.closest_point_and_primitive(point_query); + std::cout << id.first << "\n"; +} + + +int main() +{ + Point a(0.0, 0.0); + Point b(2.0, 1.0); + Point c(3.0, 4.0); + Point d(1.0, 6.0); + Point e(-1.0, 3.0); + + std::vector polyline = { a, b, c, d, e }; + + Polygon_2 poly(polyline.begin(), polyline.end()); + + test(Tree_poly(poly.begin(), poly.end(), poly)); + + // For a point range, the second iterator must be of the second-last point in the range. + // If it points to the end of the range, to polyline is considered to be closed. + test(Tree_pr(polyline.begin(), std::prev(polyline.end()), polyline)); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/examples/AABB_tree/AABB_ray_shooting_example.cpp b/AABB_tree/examples/AABB_tree/AABB_ray_shooting_example.cpp index e9ed0fe87c5d..cd19eb947566 100644 --- a/AABB_tree/examples/AABB_tree/AABB_ray_shooting_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_ray_shooting_example.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -21,7 +21,7 @@ typedef boost::graph_traits::face_descriptor face_descriptor; typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef std::optional::Type> Ray_intersection; diff --git a/AABB_tree/examples/AABB_tree/AABB_segment_2_example.cpp b/AABB_tree/examples/AABB_tree/AABB_segment_2_example.cpp new file mode 100644 index 000000000000..a599f01ff6c2 --- /dev/null +++ b/AABB_tree/examples/AABB_tree/AABB_segment_2_example.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Segment_2 Segment; +typedef K::Point_2 Point; + +typedef std::list SegmentRange; +typedef SegmentRange::const_iterator Iterator; +typedef CGAL::AABB_segment_primitive_2 Primitive; +typedef CGAL::AABB_traits_2 Traits; +typedef CGAL::AABB_tree Tree; +typedef Tree::Point_and_primitive_id Point_and_primitive_id; + +int main() +{ + Point a(0.0, 0.0); + Point b(2.0, 1.0); + Point c(3.0, 4.0); + Point d(1.0, 6.0); + Point e(-1.0, 3.0); + + std::list seg; + seg.push_back(Segment(a, b)); + seg.push_back(Segment(b, c)); + seg.push_back(Segment(c, d)); + seg.push_back(Segment(d, e)); + seg.push_back(Segment(e, a)); + + // constructs the AABB tree and the internal search tree for + // efficient distance computations. + Tree tree(seg.begin(), seg.end()); + tree.build(); + + tree.accelerate_distance_queries(); + + // counts #intersections with a segment query + Segment segment_query(Point(1.0, 0.0), Point(0.0, 7.0)); + std::cout << tree.number_of_intersected_primitives(segment_query) + << " intersections(s) with segment" << std::endl; + + // computes the closest point from a point query + Point point_query(1.5, 3.0); + Point closest = tree.closest_point(point_query); + std::cerr << "closest point is: " << closest << std::endl; + + Point_and_primitive_id id = tree.closest_point_and_primitive(point_query); + std::cout << id.second->source() << " " << id.second->target() << std::endl; + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/examples/AABB_tree/AABB_segment_3_example.cpp b/AABB_tree/examples/AABB_tree/AABB_segment_3_example.cpp index 73b4d56e2d03..f3dd2ed5802d 100644 --- a/AABB_tree/examples/AABB_tree/AABB_segment_3_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_segment_3_example.cpp @@ -5,8 +5,8 @@ #include #include -#include -#include +#include +#include typedef CGAL::Simple_cartesian K; @@ -17,8 +17,8 @@ typedef K::Segment_3 Segment; typedef K::Triangle_3 Triangle; typedef std::list::iterator Iterator; -typedef CGAL::AABB_segment_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_segment_primitive_3 Primitive; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/examples/AABB_tree/AABB_triangle_2_example.cpp b/AABB_tree/examples/AABB_tree/AABB_triangle_2_example.cpp new file mode 100644 index 000000000000..631990224e90 --- /dev/null +++ b/AABB_tree/examples/AABB_tree/AABB_triangle_2_example.cpp @@ -0,0 +1,52 @@ +// Author(s) : Camille Wormser, Pierre Alliez + +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Ray_2 Ray; +typedef K::Point_2 Point; +typedef K::Triangle_2 Triangle; + +typedef std::list::iterator Iterator; +typedef CGAL::AABB_triangle_primitive_2 Primitive; +typedef CGAL::AABB_traits_2 AABB_triangle_traits; +typedef CGAL::AABB_tree Tree; + +int main() +{ + Point a(1.0, 0.0); + Point b(0.0, 1.0); + Point c(1.0, 1.0); + Point d(0.0, 0.0); + + std::list triangles; + triangles.push_back(Triangle(a,b,c)); + triangles.push_back(Triangle(a,b,d)); + triangles.push_back(Triangle(a,d,c)); + + // constructs AABB tree + Tree tree(triangles.begin(),triangles.end()); + + // counts #intersections + Ray ray_query(a,b); + std::cout << tree.number_of_intersected_primitives(ray_query) + << " intersections(s) with ray query" << std::endl; + + // compute closest point and squared distance + Point point_query(2.0, 2.0); + Point closest_point = tree.closest_point(point_query); + std::cerr << "closest point is: " << closest_point << std::endl; + + FT sqd = tree.squared_distance(point_query); + std::cout << "squared distance: " << sqd << std::endl; + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/examples/AABB_tree/AABB_triangle_3_example.cpp b/AABB_tree/examples/AABB_tree/AABB_triangle_3_example.cpp index 1898ccc4edfb..6b963379b3bd 100644 --- a/AABB_tree/examples/AABB_tree/AABB_triangle_3_example.cpp +++ b/AABB_tree/examples/AABB_tree/AABB_triangle_3_example.cpp @@ -5,20 +5,19 @@ #include #include -#include -#include +#include +#include typedef CGAL::Simple_cartesian K; typedef K::FT FT; typedef K::Ray_3 Ray; -typedef K::Line_3 Line; typedef K::Point_3 Point; typedef K::Triangle_3 Triangle; -typedef std::list::iterator Iterator; -typedef CGAL::AABB_triangle_primitive Primitive; -typedef CGAL::AABB_traits AABB_triangle_traits; +typedef std::list::const_iterator Iterator; +typedef CGAL::AABB_triangle_primitive_3 Primitive; +typedef CGAL::AABB_traits_3 AABB_triangle_traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/include/CGAL/AABB_face_graph_triangle_primitive.h b/AABB_tree/include/CGAL/AABB_face_graph_triangle_primitive.h index 5422c84b2ae6..0a826f8c24c4 100644 --- a/AABB_tree/include/CGAL/AABB_face_graph_triangle_primitive.h +++ b/AABB_tree/include/CGAL/AABB_face_graph_triangle_primitive.h @@ -21,7 +21,6 @@ #include #include #include -#include namespace CGAL { @@ -49,7 +48,7 @@ namespace CGAL { * The default is `CGAL::Tag_false` (datum is not stored). *\sa `AABBPrimitive` *\sa `AABB_primitive` - *\sa `AABB_halfedge_graph_segment_primitive` + *\sa `AABB_halfedge_graph_segment_primitive` */ template < class FaceGraph, class VertexPointPMap = Default, @@ -57,9 +56,9 @@ template < class FaceGraph, class CacheDatum=Tag_false > class AABB_face_graph_triangle_primitive #ifndef DOXYGEN_RUNNING - : public AABB_primitive::face_descriptor, - std::pair::face_descriptor, const FaceGraph*> >::type, + : public AABB_primitive::face_descriptor, + std::pair::face_descriptor, const FaceGraph*> >, Triangle_from_face_descriptor_map< FaceGraph, typename Default::Get::const_type >::type VertexPointPMap_; typedef typename boost::graph_traits::face_descriptor FD; - typedef typename boost::mpl::if_ >::type Id_; + typedef std::conditional_t > Id_; typedef Triangle_from_face_descriptor_map Triangle_property_map; typedef One_point_from_face_descriptor_map Point_property_map; diff --git a/AABB_tree/include/CGAL/AABB_halfedge_graph_segment_primitive.h b/AABB_tree/include/CGAL/AABB_halfedge_graph_segment_primitive.h index 135682273199..d8005eb4558d 100644 --- a/AABB_tree/include/CGAL/AABB_halfedge_graph_segment_primitive.h +++ b/AABB_tree/include/CGAL/AABB_halfedge_graph_segment_primitive.h @@ -24,7 +24,6 @@ #include #include #include -#include #include @@ -61,7 +60,8 @@ namespace CGAL { * * \sa `AABBPrimitive` * \sa `AABB_primitive` - * \sa `AABB_face_graph_triangle_primitive` + * \sa `AABB_face_graph_triangle_primitive` + * \sa `AABB_segment_primitive_3` * \sa \link BGLPolyGT `boost::graph_traits` \endlink */ template < class HalfedgeGraph, @@ -70,9 +70,9 @@ template < class HalfedgeGraph, class CacheDatum = Tag_false > class AABB_halfedge_graph_segment_primitive #ifndef DOXYGEN_RUNNING - : public AABB_primitive< typename boost::mpl::if_::edge_descriptor, - std::pair::edge_descriptor, const HalfedgeGraph*> >::type, + : public AABB_primitive< std::conditional_t::edge_descriptor, + std::pair::edge_descriptor, const HalfedgeGraph*> >, Segment_from_edge_descriptor_map< HalfedgeGraph, typename Default::Get::const_type >::type VertexPointPMap_; typedef typename boost::graph_traits::edge_descriptor ED; - typedef typename boost::mpl::if_ >::type Id_; + typedef std::conditional_t > Id_; typedef Segment_from_edge_descriptor_map Segment_property_map; typedef Source_point_from_edge_descriptor_map Point_property_map; diff --git a/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_2.h b/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_2.h new file mode 100644 index 000000000000..400cfb6cac82 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_indexed_triangle_primitive_2.h @@ -0,0 +1,134 @@ +// Copyright (c) 2024 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) : Sven Oesau +// + + +#ifndef CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_2_H_ +#define CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_2_H_ + +#include + +#include +#include + +namespace CGAL { + +namespace internal { + +template +struct Triangle_2_from_index_range_iterator_property_map { + //classical typedefs + typedef Iterator key_type; + typedef typename GeomTraits::Triangle_2 value_type; + typedef typename GeomTraits::Triangle_2 reference; + + typedef boost::readable_property_map_tag category; + typedef Triangle_2_from_index_range_iterator_property_map Self; + + Triangle_2_from_index_range_iterator_property_map() {} + Triangle_2_from_index_range_iterator_property_map(PointIterator b, PointMap& pmap) : begin(b), pmap(pmap) {} + + inline friend value_type + get(Self s, key_type it) + { + return typename GeomTraits::Construct_triangle_2()(get(s.pmap, s.begin[(*it)[0]]), get(s.pmap, s.begin[(*it)[1]]), get(s.pmap, s.begin[(*it)[2]])); + } + + PointIterator begin; + PointMap pmap; +}; + +template +struct Point_from_indexed_triangle_2_iterator_property_map { + //classical typedefs + typedef Iterator key_type; + typedef typename PointMap::value_type value_type; + typedef const value_type reference; + + typedef boost::readable_property_map_tag category; + typedef Point_from_indexed_triangle_2_iterator_property_map Self; + + Point_from_indexed_triangle_2_iterator_property_map() {} + Point_from_indexed_triangle_2_iterator_property_map(PointIterator b, PointMap &pmap) : begin(b), pmap(pmap) {} + + inline friend reference + get(Self s, key_type it) + { + return get(s.pmap, s.begin[((*it)[0])]); + } + + PointIterator begin; + PointMap pmap; +}; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a range of three indices as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the primitive is in use. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_2` and `Triangle_2`. + * It also provides the functor `Construct_triangle_2` that has an operator taking three `Point_2` as + * parameters and returns a `Triangle_2` + * \tparam IndexIterator is a model of `ForwardIterator` with its value type being a `RandomAccessRange` of size 3 with an index type as `value_type`, e.g., `uint8_t`, `uint16_t` or int. + * \tparam PointRange is a model of `RandomAccessRange`. Its value type needs to be compatible to `PointMap` or `Point_2` in the default case. + * \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` (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_2`. + * 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 IndexIterator, + class PointRange, + class CacheDatum = Tag_false, + class PointMap = Identity_property_map> +class AABB_indexed_triangle_primitive_2 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< IndexIterator, + internal::Triangle_2_from_index_range_iterator_property_map, + internal::Point_from_indexed_triangle_2_iterator_property_map, + Tag_true, + CacheDatum > +#endif +{ + typedef AABB_primitive< IndexIterator, + internal::Triangle_2_from_index_range_iterator_property_map, + internal::Point_from_indexed_triangle_2_iterator_property_map, + Tag_true, + CacheDatum > Base; +public: + ///constructor from an iterator + AABB_indexed_triangle_primitive_2(IndexIterator it, PointRange&) : Base(it) {} + + /// \internal + static typename Base::Shared_data construct_shared_data(PointRange &range, PointMap pmap = PointMap()) { + return std::make_pair( + internal::Triangle_2_from_index_range_iterator_property_map(range.begin(), pmap), + internal::Point_from_indexed_triangle_2_iterator_property_map(range.begin(), pmap)); + } +}; + +} // end namespace CGAL + +#endif // CGAL_AABB_INDEXED_TRIANGLE_PRIMITIVE_2_H_ diff --git a/AABB_tree/include/CGAL/AABB_polyhedron_segment_primitive.h b/AABB_tree/include/CGAL/AABB_polyhedron_segment_primitive.h deleted file mode 100644 index 7b8b72697f9a..000000000000 --- a/AABB_tree/include/CGAL/AABB_polyhedron_segment_primitive.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 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) : Pierre Alliez, Stephane Tayeb -// -//****************************************************************************** -// File Description : -// -//****************************************************************************** - -#ifndef CGAL_AABB_POLYHEDRON_SEGMENT_PRIMITIVE_H_ -#define CGAL_AABB_POLYHEDRON_SEGMENT_PRIMITIVE_H_ - -#include - -#include - -#define CGAL_DEPRECATED_HEADER "" -#define CGAL_REPLACEMENT_HEADER "" -#include - -#include - -namespace CGAL { - -/// \addtogroup PkgAABBTreeRef -/// @{ - /// \deprecated This class is deprecated since \cgal 4.3, the class - /// `AABB_halfedge_graph_segment_primitive` should be used instead. - /// - /// Primitive type that wraps a halfedge handle of a - /// polyhedron, which is used as id, and allows the construction - /// of the datum on the fly. Since only the halfedge handle is - /// stored in this primitive, the polyhedron from which the - /// AABB tree is built should not be deleted while the AABB tree - /// is in use. - /// - /// \cgalModels{AABBPrimitive} - /// \tparam GeomTraits must provide a \c %Point_3 - /// type, used as \c Point, and a \c %Segment_3 type, used as \c - /// Datum and constructible from two arguments of type \c - /// Point. - /// \tparam Polyhedron must be a - /// \c CGAL::Polyhedron_3 whose points have type \c Point. - /// - /// \sa `AABBPrimitive` - /// \sa `AABB_polyhedron_triangle_primitive` - template - class AABB_polyhedron_segment_primitive - { - public: - // AABBTrianglePrimitive types - typedef typename GeomTraits::Point_3 Point; - - /// \name Types - /// @{ - - /// Geometric data type. - typedef typename GeomTraits::Segment_3 Datum; - /// Id type. - typedef typename Polyhedron::Halfedge_handle Id; - /// @} - - // Self - typedef AABB_polyhedron_segment_primitive Self; - - // Constructor - AABB_polyhedron_segment_primitive() {} - AABB_polyhedron_segment_primitive(const Id& handle) - : m_halfedge_handle(handle) { }; - AABB_polyhedron_segment_primitive(const Id* ptr) - : m_halfedge_handle(*ptr) { }; - template - AABB_polyhedron_segment_primitive( Iterator it, - std::enable_if_t< - std::is_same::value - >* =0 - ) : m_halfedge_handle(*it) { } - - AABB_polyhedron_segment_primitive(const Self& primitive) - : m_halfedge_handle(primitive.m_halfedge_handle) {} - - // Default destructor, copy constructor and assignment operator are ok - - // Returns by constructing on the fly the geometric datum wrapped by the primitive - Datum datum() const - { - const Point& a = m_halfedge_handle->vertex()->point(); - const Point& b = m_halfedge_handle->opposite()->vertex()->point(); - return Datum(a,b); // returns a 3D segment - } - - // Returns the identifier - Id& id() { return m_halfedge_handle; } - const Id& id() const { return m_halfedge_handle; } - - // Returns a point on the primitive - Point reference_point() const - { - return m_halfedge_handle->vertex()->point(); - } - - private: - // Id, here a polyhedron halfedge handle - Id m_halfedge_handle; - }; // end class AABB_polyhedron_segment_primitive - - ///@} - - template - class AABB_const_polyhedron_edge_primitive - { - public: - /// AABBTrianglePrimitive types - typedef typename GeomTraits::Point_3 Point; - typedef typename GeomTraits::Segment_3 Datum; - typedef typename Polyhedron::Halfedge_const_handle Id; - - /// Constructor - AABB_const_polyhedron_edge_primitive(const Id& handle) - : m_halfedge_handle(handle) { }; - - // Default destructor, copy constructor and assignment operator are ok - - /// Returns by constructing on the fly the geometric datum wrapped by the primitive - Datum datum() const - { - const Point& a = m_halfedge_handle->vertex()->point(); - const Point& b = m_halfedge_handle->opposite()->vertex()->point(); - return Datum(a,b); // returns a 3D segment - } - - /// Returns the identifier - const Id id() const { return m_halfedge_handle; } - - /// Returns a point on the primitive - Point reference_point() const - { - return m_halfedge_handle->vertex()->point(); - } - - private: - /// Id, here a polyhedron halfedge handle - Id m_halfedge_handle; - }; // end class AABB_const_polyhedron_edge_primitive - -} // end namespace CGAL - -#include - -#endif // CGAL_AABB_POLYHEDRON_SEGMENT_PRIMITIVE_H_ diff --git a/AABB_tree/include/CGAL/AABB_polyhedron_triangle_primitive.h b/AABB_tree/include/CGAL/AABB_polyhedron_triangle_primitive.h deleted file mode 100644 index 647ae48b4156..000000000000 --- a/AABB_tree/include/CGAL/AABB_polyhedron_triangle_primitive.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 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) : Stéphane Tayeb, Pierre Alliez -// - -#ifndef CGAL_AABB_POLYHEDRON_TRIANGLE_PRIMITIVE_H_ -#define CGAL_AABB_POLYHEDRON_TRIANGLE_PRIMITIVE_H_ - -#include - -#include - -#define CGAL_DEPRECATED_HEADER "" -#define CGAL_REPLACEMENT_HEADER "" -#include - -#include - -namespace CGAL { - /// \ingroup PkgAABBTreeRef - /// \deprecated This class is deprecated since \cgal 4.3, the class - /// `AABB_face_graph_triangle_primitive` should be used instead. - /// - /// Primitive type that wraps a facet handle of a polyhedron, - /// which is used as id, and allows the construction of the datum on - /// the fly. Since only the facet handle is stored in this primitive, - /// the polyhedron from which the AABB tree is built should not be - /// deleted while the AABB tree is in use. - /// - /// \cgalModels{AABBPrimitive} - /// \tparam GeomTraits must provides a \c %Point_3 - /// type, used as \c Point, and a \c %Triangle_3 type, used as \c - /// Datum and constructible from three arguments of type \c - /// Point. - /// \tparam Polyhedron must be a - /// \c CGAL::Polyhedron_3 whose points have type \c Point. - /// - /// \sa `AABBPrimitive` - /// \sa `AABB_polyhedron_segment_primitive` - template - class AABB_polyhedron_triangle_primitive - { - public: - typedef typename GeomTraits::Point_3 Point; - /// \name Types - /// @{ - - /// Id type. - typedef typename Polyhedron::Facet_handle Id; - /// Geometric data type. - typedef typename GeomTraits::Triangle_3 Datum; - - /// @} - - // Self - typedef AABB_polyhedron_triangle_primitive Self; - - // Constructors - AABB_polyhedron_triangle_primitive() {} - AABB_polyhedron_triangle_primitive(const AABB_polyhedron_triangle_primitive& primitive) - { - m_facet_handle = primitive.id(); - } - AABB_polyhedron_triangle_primitive(const Id& handle) - : m_facet_handle(handle) { }; - AABB_polyhedron_triangle_primitive(const Id* ptr) - : m_facet_handle(*ptr) { }; - template - AABB_polyhedron_triangle_primitive( Iterator it, - std::enable_if_t< - std::is_same::value - >* =0 - ) : m_facet_handle(*it) { } - - - // Default destructor, copy constructor and assignment operator are ok - - // Returns by constructing on the fly the geometric datum wrapped by the primitive - Datum datum() const - { - const Point& a = m_facet_handle->halfedge()->vertex()->point(); - const Point& b = m_facet_handle->halfedge()->next()->vertex()->point(); - const Point& c = m_facet_handle->halfedge()->next()->next()->vertex()->point(); - return Datum(a,b,c); - } - - // Returns a point on the primitive - Point reference_point() const - { - return m_facet_handle->halfedge()->vertex()->point(); - } - - // Returns the identifier - const Id& id() const { return m_facet_handle; } - Id& id() { return m_facet_handle; } - - private: - /// The id, here a polyhedron facet handle - Id m_facet_handle; - }; // end class AABB_polyhedron_triangle_primitive - - template - class AABB_const_polyhedron_triangle_primitive - { - public: - /// AABBPrimitive types - typedef typename GeomTraits::Point_3 Point; - typedef typename GeomTraits::Triangle_3 Datum; - typedef typename Polyhedron::Facet_const_handle Id; - - /// Constructors - AABB_const_polyhedron_triangle_primitive(const Id& handle) - : m_facet_handle(handle) { }; - - // Default destructor, copy constructor and assignment operator are ok - - /// Returns by constructing on the fly the geometric datum wrapped by the primitive - Datum datum() const - { - const Point& a = m_facet_handle->halfedge()->vertex()->point(); - const Point& b = m_facet_handle->halfedge()->next()->vertex()->point(); - const Point& c = m_facet_handle->halfedge()->next()->next()->vertex()->point(); - return Datum(a,b,c); - } - - /// Returns a point on the primitive - Point reference_point() const - { - return m_facet_handle->halfedge()->vertex()->point(); - } - - /// Returns the identifier - Id id() const { return m_facet_handle; } - - private: - /// The id, here a polyhedron facet handle - Id m_facet_handle; - }; // end class AABB_polyhedron_triangle_primitive - - - -} // end namespace CGAL - -#include - -#endif // CGAL_AABB_POLYHEDRON_TRIANGLE_PRIMITIVE_H_ diff --git a/AABB_tree/include/CGAL/AABB_polyline_segment_primitive_2.h b/AABB_tree/include/CGAL/AABB_polyline_segment_primitive_2.h new file mode 100644 index 000000000000..696d3c9b31ab --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_polyline_segment_primitive_2.h @@ -0,0 +1,140 @@ +// Copyright (c) 2024 GeometryFactory. +// 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) : Sven Oesau +// + + +#ifndef CGAL_AABB_POLYLINE_SEGMENT_PRIMITIVE_2_H_ +#define CGAL_AABB_POLYLINE_SEGMENT_PRIMITIVE_2_H_ + +#include + + +#include +#include + +namespace CGAL { + +namespace internal { + template + struct Segment_2_from_point_iterator_property_map { + //classical typedefs + typedef Iterator key_type; + typedef typename GeomTraits::Segment_2 value_type; + typedef typename GeomTraits::Segment_2 reference; // The segments are created on the fly, so working with references is not possible. + typedef boost::readable_property_map_tag category; + typedef Segment_2_from_point_iterator_property_map Self; + + Segment_2_from_point_iterator_property_map(Iterator b, Iterator e, PointMap& pmap) : begin(b), end(e), pmap(pmap) {} + Segment_2_from_point_iterator_property_map() {} + + inline friend reference // Cannot return reference as the Segment does not exist, only the points exist. + get(Self s, key_type it) + { + Iterator it2 = std::next(it); + if (it2 == s.end) + return typename GeomTraits::Construct_segment_2()(get(s.pmap, *it), get(s.pmap, *s.begin)); + else + return typename GeomTraits::Construct_segment_2()(get(s.pmap, *it), get(s.pmap, *it2)); + } + + Iterator begin, end; + PointMap pmap; + }; + + template + struct Point_from_iterator_property_map { + //classical typedefs + typedef Iterator key_type; + typedef typename PointMap::value_type value_type; + typedef const value_type reference; + + typedef boost::readable_property_map_tag category; + typedef Point_from_iterator_property_map Self; + + Point_from_iterator_property_map() {} + Point_from_iterator_property_map(PointMap& pmap) : pmap(pmap) {} + + inline friend reference + get(Self s, key_type it) + { + return get(s.pmap, *it); + } + + PointMap pmap; + }; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a 2D point as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the primitive is in use. The `Segment_2` is constructed on the fly using the `Point_2` + * the identifier is pointing to as source and the `Point_2` the next identifier is pointing to as target. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_2` and `Segment_2`. + * It also provides the functor `Construct_segment_2` that has an operator taking two `Point_2` + * and returning a `Segment_2`. + * \tparam Iterator is a model of `ForwardIterator` whose value type is convertible to `GeomTraits::Point_2` + * \tparam PointRange is a model of `ConstRange` whose iterator is a model of `ForwardIterator`. Its value type needs to be the key type of `PointMap`. + * \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` (datum is not stored). + * \tparam PointMap is a model of `ReadablePropertyMap` whose key type is the value type of `PointRange` and whose value type is `Point_2`. + * The default is \link Identity_property_map `CGAL::Identity_property_map`\endlink. + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_2` + * \sa `AABB_segment_primitive_3` + * \sa `AABB_halfedge_graph_segment_primitive` + */ +template < class GeomTraits, + class Iterator, + class PointRange, + class CacheDatum = Tag_false, + class PointMap = Identity_property_map> +class AABB_polyline_segment_primitive_2 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< Iterator, + internal::Segment_2_from_point_iterator_property_map, + internal::Point_from_iterator_property_map, + Tag_true, + CacheDatum > +#endif +{ + typedef internal::Segment_2_from_point_iterator_property_map Segment_map; + typedef internal::Point_from_iterator_property_map Point_primitive_map; + typedef AABB_primitive< Iterator, + Segment_map, + Point_primitive_map, + Tag_true, + CacheDatum > Base; + +public: + AABB_polyline_segment_primitive_2(Iterator it, PointRange& poly, PointMap pmap = PointMap()) + : Base(it,Segment_map(poly.begin(), poly.end(), pmap), Point_primitive_map(pmap)) + {} + + /// \internal + static typename Base::Shared_data construct_shared_data(PointRange& range, PointMap pmap = PointMap()) { + return std::make_pair(internal::Segment_2_from_point_iterator_property_map(range.begin(), range.end(), pmap), internal::Point_from_iterator_property_map(pmap)); + } +}; + +} // end namespace CGAL + + +#endif // CGAL_AABB_POLYLINE_SEGMENT_PRIMITIVE_2_H_ diff --git a/AABB_tree/include/CGAL/AABB_primitive.h b/AABB_tree/include/CGAL/AABB_primitive.h index 4c58b83f4dba..de71fb89b8ef 100644 --- a/AABB_tree/include/CGAL/AABB_primitive.h +++ b/AABB_tree/include/CGAL/AABB_primitive.h @@ -71,10 +71,12 @@ struct AABB_primitive_base * it is constructed on the fly to reduce the memory footprint. * The default is `CGAL::Tag_false` (datum is not stored). * - * \sa `AABB_segment_primitive` - * \sa `AABB_triangle_primitive` - * \sa `AABB_halfedge_graph_segment_primitive` - * \sa `AABB_face_graph_triangle_primitive` + * \sa `AABB_segment_primitive_2` + * \sa `AABB_segment_primitive_3` + * \sa `AABB_triangle_primitive_2` + * \sa `AABB_triangle_primitive_3` + * \sa `AABB_halfedge_graph_segment_primitive` + * \sa `AABB_face_graph_triangle_primitive` */ template < class Id, class ObjectPropertyMap, @@ -251,4 +253,3 @@ class AABB_primitive #include #endif // CGAL_AABB_PRIMITIVE_H - diff --git a/AABB_tree/include/CGAL/AABB_segment_primitive.h b/AABB_tree/include/CGAL/AABB_segment_primitive.h index f13523ba40ca..2ecb294c83cb 100644 --- a/AABB_tree/include/CGAL/AABB_segment_primitive.h +++ b/AABB_tree/include/CGAL/AABB_segment_primitive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 INRIA Sophia-Antipolis (France). +// Copyright (c) 2024 INRIA Sophia-Antipolis (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -11,91 +11,38 @@ // Author(s) : Sebastien Loriot // - #ifndef CGAL_AABB_SEGMENT_PRIMITIVE_H_ #define CGAL_AABB_SEGMENT_PRIMITIVE_H_ #include -#include +#define CGAL_DEPRECATED_HEADER "" +#define CGAL_REPLACEMENT_HEADER "" +#include + +#ifndef CGAL_NO_DEPRECATED_CODE + +#include -#include -#include +/// \file AABB_segment_primitive.h namespace CGAL { -namespace internal { - template - struct Source_of_segment_3_iterator_property_map{ - //classical typedefs - typedef Iterator key_type; - typedef typename GeomTraits::Point_3 value_type; - // typedef decltype( - // std::declval()( - // std::declval())) reference; - typedef decltype( - typename GeomTraits::Construct_source_3()( - *std::declval())) reference; - typedef boost::readable_property_map_tag category; - typedef Source_of_segment_3_iterator_property_map Self; - inline friend reference - get(Self, key_type it) - { - return typename GeomTraits::Construct_source_3()( *it ); - } - }; -}//namespace internal +/// \addtogroup PkgAABBTreeRef +/// @{ +/// template alias for backward compatibility -/*! - * \ingroup PkgAABBTreeRef - * Primitive type that uses as identifier an iterator with a 3D segment as `value_type`. - * The iterator from which the primitive is built should not be invalided - * while the AABB tree holding the primitive is in use. - * - * \cgalModels{AABBPrimitive} - * - * \tparam GeomTraits is a traits class providing the nested type `Point_3` and `Segment_3`. - * It also provides the functor `Construct_source_3` that has an operator taking a `Segment_3` - * and returning its source as a type convertible to `Point_3`. - * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Segment_3` - * \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` (datum is not stored). - * - * \sa `AABBPrimitive` - * \sa `AABB_primitive` - * \sa `AABB_triangle_primitive` - * \sa `AABB_halfedge_graph_segment_primitive` - * \sa `AABB_face_graph_triangle_primitive` - */ template < class GeomTraits, class Iterator, class CacheDatum=Tag_false> -class AABB_segment_primitive -#ifndef DOXYGEN_RUNNING - : public AABB_primitive< Iterator, - Input_iterator_property_map, - internal::Source_of_segment_3_iterator_property_map, - Tag_false, - CacheDatum > -#endif -{ - typedef AABB_primitive< Iterator, - Input_iterator_property_map, - internal::Source_of_segment_3_iterator_property_map, - Tag_false, - CacheDatum > Base; -public: - ///constructor from an iterator - AABB_segment_primitive(Iterator it) : Base(it){} -}; +using AABB_segment_primitive = AABB_segment_primitive_3; -} // end namespace CGAL +///@} -#include +} // CGAL namespace -#endif // CGAL_AABB_SEGMENT_PRIMITIVE_H_ +#endif // CGAL_NO_DEPRECATED_CODE +#endif //CGAL_AABB_SEGMENT_PRIMITIVE_H_ diff --git a/AABB_tree/include/CGAL/AABB_segment_primitive_2.h b/AABB_tree/include/CGAL/AABB_segment_primitive_2.h new file mode 100644 index 000000000000..71e76f72cdee --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_segment_primitive_2.h @@ -0,0 +1,99 @@ +// Copyright (c) 2012 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) : Sebastien Loriot +// + + +#ifndef CGAL_AABB_SEGMENT_PRIMITIVE_2_H_ +#define CGAL_AABB_SEGMENT_PRIMITIVE_2_H_ + +#include + +#include + +#include +#include + +namespace CGAL { + +namespace internal { + template + struct Source_of_segment_2_iterator_property_map{ + //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; + typedef boost::readable_property_map_tag category; + typedef Source_of_segment_2_iterator_property_map Self; + + inline friend reference + get(Self, key_type it) + { + return typename GeomTraits::Construct_source_2()( *it ); + } + }; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a 2D segment as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the primitive is in use. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_2` and `Segment_2`. + * It also provides the functor `Construct_source_2` that has an operator taking a `Segment_2` + * and returning its source as a type convertible to `Point_2`. + * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Segment_2` + * \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` (datum is not stored). + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_3` + * \sa `AABB_triangle_primitive_2` + */ +template < class GeomTraits, + class Iterator, + class CacheDatum=Tag_false> +class AABB_segment_primitive_2 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Source_of_segment_2_iterator_property_map, + Tag_false, + CacheDatum > +#endif +{ + typedef AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Source_of_segment_2_iterator_property_map, + Tag_false, + CacheDatum > Base; +public: + ///constructor from an iterator + AABB_segment_primitive_2(Iterator it) : Base(it){} +}; + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_SEGMENT_PRIMITIVE_2_H_ diff --git a/AABB_tree/include/CGAL/AABB_segment_primitive_3.h b/AABB_tree/include/CGAL/AABB_segment_primitive_3.h new file mode 100644 index 000000000000..a1af115ae28b --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_segment_primitive_3.h @@ -0,0 +1,100 @@ +// Copyright (c) 2012 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) : Sebastien Loriot +// + + +#ifndef CGAL_AABB_SEGMENT_PRIMITIVE_3_H_ +#define CGAL_AABB_SEGMENT_PRIMITIVE_3_H_ + +#include + +#include + +#include +#include + +namespace CGAL { + +namespace internal { + template + struct Source_of_segment_3_iterator_property_map{ + //classical typedefs + typedef Iterator key_type; + typedef typename GeomTraits::Point_3 value_type; + // typedef decltype( + // std::declval()( + // std::declval())) reference; + typedef decltype( + typename GeomTraits::Construct_source_3()( + *std::declval())) reference; + typedef boost::readable_property_map_tag category; + typedef Source_of_segment_3_iterator_property_map Self; + + inline friend reference + get(Self, key_type it) + { + return typename GeomTraits::Construct_source_3()( *it ); + } + }; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a 3D segment as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the primitive is in use. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_3` and `Segment_3`. + * It also provides the functor `Construct_source_3` that has an operator taking a `Segment_3` + * and returning its source as a type convertible to `Point_3`. + * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Segment_3` + * \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` (datum is not stored). + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_2` + * \sa `AABB_triangle_primitive_3` + * \sa `AABB_halfedge_graph_segment_primitive` + */ +template < class GeomTraits, + class Iterator, + class CacheDatum=Tag_false> +class AABB_segment_primitive_3 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Source_of_segment_3_iterator_property_map, + Tag_false, + CacheDatum > +#endif +{ + typedef AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Source_of_segment_3_iterator_property_map, + Tag_false, + CacheDatum > Base; +public: + ///constructor from an iterator + AABB_segment_primitive_3(Iterator it) : Base(it){} +}; + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_SEGMENT_PRIMITIVE_3_H_ diff --git a/AABB_tree/include/CGAL/AABB_traits.h b/AABB_tree/include/CGAL/AABB_traits.h index f7d52f9968ca..9b70f622c76f 100644 --- a/AABB_tree/include/CGAL/AABB_traits.h +++ b/AABB_tree/include/CGAL/AABB_traits.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 INRIA Sophia-Antipolis (France). +// Copyright (c) 2024 INRIA Sophia-Antipolis (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -14,524 +14,35 @@ #ifndef CGAL_AABB_TRAITS_H_ #define CGAL_AABB_TRAITS_H_ -#include - -#include +#define CGAL_DEPRECATED_HEADER "" +#define CGAL_REPLACEMENT_HEADER "" +#include -#include -#include -#include -#include -#include -#include -#include +#ifndef CGAL_NO_DEPRECATED_CODE +#include -#include +#include /// \file AABB_traits.h -namespace CGAL { - -namespace internal{ namespace AABB_tree { - -template -struct Remove_optional { typedef T type; }; - -template -struct Remove_optional< ::std::optional > { typedef T type; }; - -//helper controlling whether extra data should be stored in the AABB_tree traits class -template ::value> -struct AABB_traits_base; - -template -struct AABB_traits_base{}; - -template -struct AABB_traits_base{ - typename Primitive::Shared_data m_primitive_data; - - template - void set_shared_data(T&& ... t){ - m_primitive_data=Primitive::construct_shared_data(std::forward(t)...); - } - const typename Primitive::Shared_data& shared_data() const {return m_primitive_data;} -}; - -// AABB_traits_base_2 brings in the Intersection_distance predicate, -// if GeomTraits is a model RayIntersectionGeomTraits. -template ::value> -struct AABB_traits_base_2; - -template -struct AABB_traits_base_2{}; - -template -struct AABB_traits_base_2{ - typedef typename GeomTraits::Ray_3 Ray_3; - typedef typename GeomTraits::Point_3 Point_3; - typedef typename GeomTraits::Vector_3 Vector_3; - typedef typename GeomTraits::FT FT; - typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_3; - typedef typename GeomTraits::Construct_cartesian_const_iterator_3 Construct_cartesian_const_iterator_3; - typedef typename GeomTraits::Construct_source_3 Construct_source_3; - typedef typename GeomTraits::Construct_vector_3 Construct_vector_3; - - // Defining Bounding_box and other types from the full AABB_traits - // here is might seem strange, but otherwise we would need to use - // CRTP to get access to the derived class, which would bloat the - // code more. - typedef typename CGAL::Bbox_3 Bounding_box; - - struct Intersection_distance { - std::optional operator()(const Ray_3& ray, const Bounding_box& bbox) const { - FT t_near = -DBL_MAX; // std::numeric_limits::lowest(); C++1903 - FT t_far = DBL_MAX; - - const Construct_cartesian_const_iterator_3 construct_cartesian_const_iterator_3 - = GeomTraits().construct_cartesian_const_iterator_3_object(); - const Construct_source_3 construct_source_3 = GeomTraits().construct_source_3_object(); - const Construct_vector_3 construct_vector_3 = GeomTraits().construct_vector_3_object(); - const Point_3 source = construct_source_3(ray); - const Vector_3 direction = construct_vector_3(ray); - Cartesian_const_iterator_3 source_iter = construct_cartesian_const_iterator_3(source); - Cartesian_const_iterator_3 direction_iter = construct_cartesian_const_iterator_3(direction); - - for(int i = 0; i < 3; ++i, ++source_iter, ++direction_iter) { - if(*direction_iter == 0) { - if((*source_iter < (bbox.min)(i)) || (*source_iter > (bbox.max)(i))) { - return std::nullopt; - } - } else { - FT t1 = ((bbox.min)(i) - *source_iter) / *direction_iter; - FT t2 = ((bbox.max)(i) - *source_iter) / *direction_iter; - - t_near = (std::max)(t_near, (std::min)(t1, t2)); - t_far = (std::min)(t_far, (std::max)(t1, t2)); - - // if(t1 > t2) - // std::swap(t1, t2); - // if(t1 > t_near) - // t_near = t1; - // if(t2 < t_far) - // t_far = t2; - - if(t_near > t_far || t_far < FT(0.)) - return std::nullopt; - } - } - if(t_near < FT(0.)) - return FT(0.); - else - return t_near; - } - }; +namespace CGAL +{ - Intersection_distance intersection_distance_object() const { return Intersection_distance(); } -}; -} } //end of namespace internal::AABB_tree /// \addtogroup PkgAABBTreeRef /// @{ -// forward declaration -template< typename AABBTraits> -class AABB_tree; - - -/// This traits class handles any type of 3D geometric -/// primitives provided that the proper intersection tests and -/// constructions are implemented. It handles points, rays, lines and -/// segments as query types for intersection detection and -/// computations, and it handles points as query type for distance -/// queries. -/// -/// \cgalModels{AABBTraits,AABBRayIntersectionTraits} -/// -/// \tparam GeomTraits must be a model of the concept \ref AABBGeomTraits, -/// and provide the geometric types as well as the intersection tests and computations. -/// \tparam Primitive provide the type of primitives stored in the AABB_tree. -/// It is a model of the concept `AABBPrimitive` or `AABBPrimitiveWithSharedData`. -/// -/// \tparam BboxMap must be a model of `ReadablePropertyMap` that has as key type a primitive id, -/// and as value type a `Bounding_box`. -/// If the type is `Default` the `Datum` must have the -/// member function `bbox()` that returns the bounding box of the primitive. -/// -/// If the argument `GeomTraits` is a model of the concept \ref -/// AABBRayIntersectionGeomTraits, this class is also a model of \ref -/// AABBRayIntersectionTraits. -/// -/// \sa `AABBTraits` -/// \sa `AABB_tree` -/// \sa `AABBPrimitive` -/// \sa `AABBPrimitiveWithSharedData` - - template -class AABB_traits -#ifndef DOXYGEN_RUNNING -: public internal::AABB_tree::AABB_traits_base, - public internal::AABB_tree::AABB_traits_base_2 -#endif -{ - typedef typename CGAL::Object Object; -public: - typedef GeomTraits Geom_traits; - - typedef AABB_traits AT; - // AABBTraits concept types - typedef typename GeomTraits::FT FT; - typedef AABBPrimitive Primitive; - - typedef typename std::pair Object_and_primitive_id; - - typedef typename std::pair Point_and_primitive_id; - - /// `Intersection_and_primitive_id::%Type::first_type` is found according to - /// the result type of `GeomTraits::Intersect_3::operator()`. If it is - /// `std::optional` then it is `T`, and the result type otherwise. - template - struct Intersection_and_primitive_id { - typedef decltype( - std::declval()( - std::declval(), - std::declval())) Intersection_type; - - typedef std::pair< - typename internal::AABB_tree::Remove_optional::type, - typename Primitive::Id > Type; - }; - - // types for search tree - /// \name Types - /// @{ - - /// Point query type. - typedef typename GeomTraits::Point_3 Point_3; - - /// additional types for the search tree, required by the RangeSearchTraits concept - /// \bug This is not documented for now in the AABBTraits concept. - typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3; - - /// Bounding box type. - typedef typename CGAL::Bbox_3 Bounding_box; - - /// @} - - typedef typename GeomTraits::Sphere_3 Sphere_3; - typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_3; - typedef typename GeomTraits::Construct_cartesian_const_iterator_3 Construct_cartesian_const_iterator_3; - typedef typename GeomTraits::Construct_center_3 Construct_center_3; - typedef typename GeomTraits::Compute_squared_radius_3 Compute_squared_radius_3; - typedef typename GeomTraits::Construct_min_vertex_3 Construct_min_vertex_3; - typedef typename GeomTraits::Construct_max_vertex_3 Construct_max_vertex_3; - typedef typename GeomTraits::Construct_iso_cuboid_3 Construct_iso_cuboid_3; - - BboxMap bbm; - - /// Default constructor. - AABB_traits() { } - - AABB_traits(BboxMap bbm) - : bbm(bbm) - {} - - - typedef typename GeomTraits::Compute_squared_distance_3 Squared_distance; - Squared_distance squared_distance_object() const { return GeomTraits().compute_squared_distance_3_object(); } - - typedef typename GeomTraits::Equal_3 Equal_3; - Equal_3 equal_3_object() const { return GeomTraits().equal_3_object(); } - - /** - * @internal - * @brief Sorts [first,beyond[ - * @param first iterator on first element - * @param beyond iterator on beyond element - * @param bbox the bounding box of [first,beyond[ - * - * Sorts the range defined by [first,beyond[. Sort is achieved on bbox longest - * axis, using the comparison function `_less_than` (dim in {x,y,z}) - */ - class Split_primitives - { - typedef AABB_traits Traits; - const Traits& m_traits; - public: - Split_primitives(const AABB_traits& traits) - : m_traits(traits) {} - - typedef void result_type; - template - void operator()(PrimitiveIterator first, - PrimitiveIterator beyond, - const typename AT::Bounding_box& bbox) const - { - PrimitiveIterator middle = first + (beyond - first)/2; - switch(Traits::longest_axis(bbox)) - { - case AT::CGAL_AXIS_X: // sort along x - std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_x(p1, p2, this->m_traits); }); - break; - case AT::CGAL_AXIS_Y: // sort along y - std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_y(p1, p2, this->m_traits); }); - break; - case AT::CGAL_AXIS_Z: // sort along z - std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_z(p1, p2, this->m_traits); }); - break; - default: - CGAL_error(); - } - } - }; - - Split_primitives split_primitives_object() const {return Split_primitives(*this);} - - - /* - * Computes the bounding box of a set of primitives - * @param first an iterator on the first primitive - * @param beyond an iterator on the past-the-end primitive - * @return the bounding box of the primitives of the iterator range - */ - class Compute_bbox { - const AABB_traits& m_traits; - public: - Compute_bbox(const AABB_traits& traits) - :m_traits (traits) {} - - template - typename AT::Bounding_box operator()(ConstPrimitiveIterator first, - ConstPrimitiveIterator beyond) const - { - typename AT::Bounding_box bbox = m_traits.compute_bbox(*first,m_traits.bbm); - for(++first; first != beyond; ++first) - { - bbox = bbox + m_traits.compute_bbox(*first,m_traits.bbm); - } - return bbox; - } - - }; - - Compute_bbox compute_bbox_object() const {return Compute_bbox(*this);} - - /// \brief Function object using `GeomTraits::Do_intersect`. - /// In the case the query is a `CGAL::AABB_tree`, the `do_intersect()` - /// function of this tree is used. - class Do_intersect { - const AABB_traits& m_traits; - public: - Do_intersect(const AABB_traits& traits) - :m_traits(traits) {} - - template - bool operator()(const Query& q, const Bounding_box& bbox) const - { - return GeomTraits().do_intersect_3_object()(q, bbox); - } - - template - bool operator()(const Query& q, const Primitive& pr) const - { - return GeomTraits().do_intersect_3_object()(q, internal::Primitive_helper::get_datum(pr,m_traits)); - } - - // intersection with AABB-tree - template - bool operator()(const CGAL::AABB_tree& other_tree, const Primitive& pr) const - { - return other_tree.do_intersect( internal::Primitive_helper::get_datum(pr,m_traits) ); - } - - template - bool operator()(const CGAL::AABB_tree& other_tree, const Bounding_box& bbox) const - { - return other_tree.do_intersect(bbox); - } - }; - - Do_intersect do_intersect_object() const {return Do_intersect(*this);} - - - class Intersection { - const AABB_traits& m_traits; - public: - Intersection(const AABB_traits& traits) - :m_traits(traits) {} - template - std::optional< typename Intersection_and_primitive_id::Type > - operator()(const Query& query, const typename AT::Primitive& primitive) const { - auto inter_res = GeomTraits().intersect_3_object()(query, internal::Primitive_helper::get_datum(primitive,m_traits)); - if (!inter_res) - return std::nullopt; - return std::make_optional( std::make_pair(*inter_res, primitive.id()) ); - } - }; - - Intersection intersection_object() const {return Intersection(*this);} - - - // This should go down to the GeomTraits, i.e. the kernel - class Closest_point { - typedef typename AT::Point_3 Point; - typedef typename AT::Primitive Primitive; - const AABB_traits& m_traits; - public: - Closest_point(const AABB_traits& traits) - : m_traits(traits) {} - - - Point operator()(const Point& p, const Primitive& pr, const Point& bound) const - { - GeomTraits geom_traits; - Point closest_point = geom_traits.construct_projected_point_3_object()( - internal::Primitive_helper::get_datum(pr,m_traits), p); - - return (geom_traits.compare_distance_3_object()(p, closest_point, bound) == LARGER) ? - bound : closest_point; - } - }; - - // This should go down to the GeomTraits, i.e. the kernel - // and the internal implementation should change its name from - // do_intersect to something like does_contain (this is what we compute, - // this is not the same do_intersect as the spherical kernel) - class Compare_distance { - typedef typename AT::Point_3 Point; - typedef typename AT::FT FT; - typedef typename AT::Primitive Primitive; - public: - CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_true) const - { - return GeomTraits().do_intersect_3_object() - (GeomTraits().construct_sphere_3_object() - (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb,true)? - CGAL::SMALLER : CGAL::LARGER; - } - - CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_false) const - { - return GeomTraits().do_intersect_3_object() - (GeomTraits().construct_sphere_3_object() - (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb)? - CGAL::SMALLER : CGAL::LARGER; - } - - CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const - { - return (*this)(p, bb, bound, Boolean_tag::value>()); - } - - // The following functions seem unused...? - template - CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const - { - return GeomTraits().do_intersect_3_object() - (GeomTraits().construct_sphere_3_object() - (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), pr)? - CGAL::SMALLER : CGAL::LARGER; - } - - template - CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const - { - return GeomTraits().do_intersect_3_object() - (GeomTraits().construct_sphere_3_object()(p, sq_distance), - pr) ? - CGAL::SMALLER : - CGAL::LARGER; - } - }; - - Closest_point closest_point_object() const {return Closest_point(*this);} - Compare_distance compare_distance_object() const {return Compare_distance();} - - typedef enum { CGAL_AXIS_X = 0, - CGAL_AXIS_Y = 1, - CGAL_AXIS_Z = 2} Axis; - - static Axis longest_axis(const Bounding_box& bbox); - -private: - /** - * @brief Computes bounding box of one primitive - * @param pr the primitive - * @return the bounding box of the primitive \c pr - */ - template - Bounding_box compute_bbox(const Primitive& pr, const PM&)const - { - return get(bbm, pr.id()); - } - - Bounding_box compute_bbox(const Primitive& pr, const Default&)const - { - return internal::Primitive_helper::get_datum(pr,*this).bbox(); - } - - /// Comparison functions - static bool less_x(const Primitive& pr1, const Primitive& pr2,const AABB_traits& traits) - { - return GeomTraits().less_x_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), - internal::Primitive_helper::get_reference_point(pr2,traits) ); - } - static bool less_y(const Primitive& pr1, const Primitive& pr2,const AABB_traits& traits) - { - return GeomTraits().less_y_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), - internal::Primitive_helper::get_reference_point(pr2,traits) ); - } - static bool less_z(const Primitive& pr1, const Primitive& pr2,const AABB_traits& traits) - { - return GeomTraits().less_z_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), - internal::Primitive_helper::get_reference_point(pr2,traits) ); - } - -}; // end class AABB_traits - - -//------------------------------------------------------- -// Private methods -//------------------------------------------------------- - template - typename AABB_traits::Axis - AABB_traits::longest_axis(const Bounding_box& bbox) -{ - const double dx = bbox.xmax() - bbox.xmin(); - const double dy = bbox.ymax() - bbox.ymin(); - const double dz = bbox.zmax() - bbox.zmin(); - - if(dx>=dy) - { - if(dx>=dz) - { - return CGAL_AXIS_X; - } - else // dz>dx and dx>=dy - { - return CGAL_AXIS_Z; - } - } - else // dy>dx - { - if(dy>=dz) - { - return CGAL_AXIS_Y; - } - else // dz>dy and dy>dx - { - return CGAL_AXIS_Z; - } - } -} +/// template alias for backward compatibility +template +using AABB_traits = AABB_traits_3; -/// @} +///@} -} // end namespace CGAL +} // namespace CGAL -#include +#endif // CGAL_NO_DEPRECATED_CODE #endif // CGAL_AABB_TRAITS_H_ diff --git a/AABB_tree/include/CGAL/AABB_traits_2.h b/AABB_tree/include/CGAL/AABB_traits_2.h new file mode 100644 index 000000000000..fe863e2d0084 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_traits_2.h @@ -0,0 +1,504 @@ +// Copyright (c) 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) : Stéphane Tayeb, Pierre Alliez, Camille Wormser +// + +#ifndef CGAL_AABB_TRAITS_2_H_ +#define CGAL_AABB_TRAITS_2_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// \file AABB_traits_2.h + +namespace CGAL { + +namespace internal{ namespace AABB_tree { + + +// AABB_traits_intersection_base_2 brings in the Intersection_distance predicate, +// if GeomTraits is a model RayIntersectionGeomTraits. +template ::value> +struct AABB_traits_intersection_base_2; + +template +struct AABB_traits_intersection_base_2{}; + +template +struct AABB_traits_intersection_base_2{ + template + friend class AABB_ray_intersection; +private: + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Point_2 Point; + typedef typename GeomTraits::Cartesian_const_iterator_2 Cartesian_const_iterator; + typedef typename GeomTraits::Construct_cartesian_const_iterator_2 Construct_cartesian_const_iterator; + + static Construct_cartesian_const_iterator construct_cartesian_const_iterator_object() { + return GeomTraits().construct_cartesian_const_iterator_2_object(); + } + +public: + typedef typename GeomTraits::Ray_2 Ray; + typedef typename GeomTraits::Vector_2 Vector; + typedef typename GeomTraits::Construct_source_2 Construct_source; + typedef typename GeomTraits::Construct_vector_2 Construct_vector; + + static Construct_source construct_source_object() { + return GeomTraits().construct_source_2_object(); + } + + static Construct_vector construct_vector_object() { + return GeomTraits().construct_vector_2_object(); + } + + // Defining Bounding_box and other types from the full AABB_traits_2 + // here might seem strange, but otherwise we would need to use + // CRTP to get access to the derived class, which would bloat the + // code more. + typedef typename CGAL::Bbox_2 Bounding_box; + + struct Intersection_distance { + std::optional operator()(const Ray& ray, const Bounding_box& bbox) const { + FT t_near = -DBL_MAX; // std::numeric_limits::lowest(); C++1903 + FT t_far = DBL_MAX; + + const Construct_cartesian_const_iterator construct_cartesian_const_iterator_2 + = GeomTraits().construct_cartesian_const_iterator_2_object(); + const Construct_source construct_source_2 = GeomTraits().construct_source_2_object(); + const Construct_vector construct_vector_2 = GeomTraits().construct_vector_2_object(); + const Point source = construct_source_2(ray); + const Vector direction = construct_vector_2(ray); + Cartesian_const_iterator source_iter = construct_cartesian_const_iterator_2(source); + Cartesian_const_iterator direction_iter = construct_cartesian_const_iterator_2(direction); + + for(int i = 0; i < 2; ++i, ++source_iter, ++direction_iter) { + if(*direction_iter == 0) { + if((*source_iter < (bbox.min)(i)) || (*source_iter > (bbox.max)(i))) { + return std::nullopt; + } + } else { + FT t1 = ((bbox.min)(i) - *source_iter) / *direction_iter; + FT t2 = ((bbox.max)(i) - *source_iter) / *direction_iter; + + t_near = (std::max)(t_near, (std::min)(t1, t2)); + t_far = (std::min)(t_far, (std::max)(t1, t2)); + + if(t_near > t_far || t_far < FT(0.)) + return std::nullopt; + } + } + + if(t_near < FT(0.)) + return FT(0.); + else + return t_near; + } + }; + + Intersection_distance intersection_distance_object() const { return Intersection_distance(); } +}; + +} } //end of namespace internal::AABB_tree + +/// \addtogroup PkgAABBTreeRef +/// @{ + +// forward declaration +template< typename AABBTraits> +class AABB_tree; + + +/// This traits class handles any type of 2D geometric +/// primitives provided that the proper intersection tests and +/// constructions are implemented. It handles points, rays, lines and +/// segments as query types for intersection detection and +/// computations, and it handles points as query type for distance +/// queries. +/// +/// \cgalModels{AABBTraits,AABBRayIntersectionTraits} +/// +/// \tparam GeomTraits must be a model of the concept \ref AABBGeomTraits_2, +/// and provide the geometric types as well as the intersection tests and computations. +/// \tparam Primitive provide the type of primitives stored in the AABB_tree. +/// It is a model of the concept `AABBPrimitive` or `AABBPrimitiveWithSharedData`. +/// +/// \tparam BboxMap must be a model of `ReadablePropertyMap` that has as key type a primitive id, +/// and as value type a `Bounding_box`. +/// If the type is `Default` the `Datum` must have the +/// member function `bbox()` that returns the bounding box of the primitive. +/// +/// If the argument `GeomTraits` is a model of the concept \ref +/// AABBRayIntersectionGeomTraits_2, this class is also a model of \ref +/// AABBRayIntersectionTraits. +/// +/// \sa `AABBTraits` +/// \sa `AABB_tree` +/// \sa `AABBPrimitive` +/// \sa `AABBPrimitiveWithSharedData` + +template +class AABB_traits_2 +#ifndef DOXYGEN_RUNNING +: public internal::AABB_tree::AABB_traits_base, + public internal::AABB_tree::AABB_traits_intersection_base_2, + public Search_traits_2 +#endif +{ + typedef typename CGAL::Object Object; + typedef GeomTraits Geom_traits; +public: + + typedef AABB_traits_2 AT; + // AABBTraits concept types + typedef typename GeomTraits::FT FT; + typedef AABBPrimitive Primitive; + + typedef typename std::pair Object_and_primitive_id; + + typedef typename std::pair Point_and_primitive_id; + + /// `Intersection_and_primitive_id::%Type::first_type` is found according to + /// the result type of `GeomTraits::Intersect_2::operator()`. If it is + /// `std::optional` then it is `T`, and the result type otherwise. + template + struct Intersection_and_primitive_id { + typedef decltype( + std::declval()( + std::declval(), + std::declval())) Intersection_type; + + typedef std::pair< + typename internal::Remove_optional::type, + typename Primitive::Id > Type; + }; + + // types for search tree + /// \name Types + /// @{ + + /// + /// point type + /// + typedef typename GeomTraits::Point_2 Point; + + /// additional types for the search tree, required by the RangeSearchTraits concept + /// \bug This is not documented for now in the AABBTraits concept. + typedef typename GeomTraits::Iso_rectangle_2 Iso_rectangle_2; + + /// Bounding box type. + typedef typename CGAL::Bbox_2 Bounding_box; + + /// @} + + typedef typename GeomTraits::Circle_2 Circle_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; + typedef typename GeomTraits::Construct_center_2 Construct_center_2; + typedef typename GeomTraits::Compute_squared_radius_2 Compute_squared_radius_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::Construct_iso_rectangle_2 Construct_iso_rectangle_2; + + BboxMap bbm; + + /// Default constructor. + AABB_traits_2() { } + + AABB_traits_2(BboxMap bbm) + : bbm(bbm) + {} + + + typedef typename GeomTraits::Compute_squared_distance_2 Squared_distance; + Squared_distance squared_distance_object() const { return GeomTraits().compute_squared_distance_2_object(); } + + typedef typename GeomTraits::Equal_2 Equal; + Equal equal_object() const { return GeomTraits().equal_2_object(); } + + /** + * @internal + * @brief Sorts [first,beyond[ + * @param first iterator on first element + * @param beyond iterator on beyond element + * @param bbox the bounding box of [first,beyond[ + * + * Sorts the range defined by [first,beyond[. Sort is achieved on bbox longest + * axis, using the comparison function `_less_than` (dim in {x,y,z}) + */ + class Split_primitives + { + typedef AABB_traits_2 Traits; + const Traits& m_traits; + public: + Split_primitives(const AABB_traits_2& traits) + : m_traits(traits) {} + + typedef void result_type; + template + void operator()(PrimitiveIterator first, + PrimitiveIterator beyond, + const typename AT::Bounding_box& bbox) const + { + PrimitiveIterator middle = first + (beyond - first)/2; + switch(Traits::longest_axis(bbox)) + { + case AT::CGAL_AXIS_X: // sort along x + std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_x(p1, p2, this->m_traits); }); + break; + case AT::CGAL_AXIS_Y: // sort along y + std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_y(p1, p2, this->m_traits); }); + break; + default: + CGAL_error(); + } + } + }; + + Split_primitives split_primitives_object() const {return Split_primitives(*this);} + + + /* + * Computes the bounding box of a set of primitives + * @param first an iterator on the first primitive + * @param beyond an iterator on the past-the-end primitive + * @return the bounding box of the primitives of the iterator range + */ + class Compute_bbox { + const AABB_traits_2& m_traits; + public: + Compute_bbox(const AABB_traits_2& traits) + :m_traits (traits) {} + + template + typename AT::Bounding_box operator()(ConstPrimitiveIterator first, + ConstPrimitiveIterator beyond) const + { + typename AT::Bounding_box bbox = m_traits.compute_bbox(*first,m_traits.bbm); + for(++first; first != beyond; ++first) + { + bbox = bbox + m_traits.compute_bbox(*first,m_traits.bbm); + } + return bbox; + } + + }; + + Compute_bbox compute_bbox_object() const {return Compute_bbox(*this);} + + /// \brief Function object using `GeomTraits::Do_intersect`. + /// In the case the query is a `CGAL::AABB_tree`, the `do_intersect()` + /// function of this tree is used. + class Do_intersect { + const AABB_traits_2& m_traits; + public: + Do_intersect(const AABB_traits_2& traits) + :m_traits(traits) {} + + template + bool operator()(const Query& q, const Bounding_box& bbox) const + { + return GeomTraits().do_intersect_2_object()(q, bbox); + } + + template + bool operator()(const Query& q, const Primitive& pr) const + { + return GeomTraits().do_intersect_2_object()(q, internal::Primitive_helper::get_datum(pr,m_traits)); + } + + // intersection with AABB-tree + template + bool operator()(const CGAL::AABB_tree& other_tree, const Primitive& pr) const + { + return other_tree.do_intersect( internal::Primitive_helper::get_datum(pr,m_traits) ); + } + + template + bool operator()(const CGAL::AABB_tree& other_tree, const Bounding_box& bbox) const + { + return other_tree.do_intersect(bbox); + } + }; + + Do_intersect do_intersect_object() const {return Do_intersect(*this);} + + + class Intersection { + const AABB_traits_2& m_traits; + public: + Intersection(const AABB_traits_2& traits) + :m_traits(traits) {} + template + std::optional< typename Intersection_and_primitive_id::Type > + operator()(const Query& query, const typename AT::Primitive& primitive) const { + auto inter_res = GeomTraits().intersect_2_object()(query, internal::Primitive_helper::get_datum(primitive,m_traits)); + if (!inter_res) + return std::nullopt; + return std::make_optional( std::make_pair(*inter_res, primitive.id()) ); + } + }; + + Intersection intersection_object() const {return Intersection(*this);} + + + // This should go down to the GeomTraits, i.e. the kernel + class Closest_point { + typedef typename AT::Point Point; + typedef typename AT::Primitive Primitive; + const AABB_traits_2& m_traits; + public: + Closest_point(const AABB_traits_2& traits) + : m_traits(traits) {} + + + Point operator()(const Point& p, const Primitive& pr, const Point& bound) const + { + GeomTraits geom_traits; + Point closest_point = geom_traits.construct_projected_point_2_object()( + internal::Primitive_helper::get_datum(pr,m_traits), p); + + return (geom_traits.compare_distance_2_object()(p, closest_point, bound) == LARGER) ? + bound : closest_point; + } + }; + + // This should go down to the GeomTraits, i.e. the kernel + // and the internal implementation should change its name from + // do_intersect to something like does_contain (this is what we compute, + // this is not the same do_intersect as the spherical kernel) + class Compare_distance { + typedef typename AT::Point Point; + typedef typename AT::FT FT; + typedef typename AT::Primitive Primitive; + public: + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_true) const + { + return GeomTraits().do_intersect_2_object() + (GeomTraits().construct_circle_2_object() + (p, GeomTraits().compute_squared_distance_2_object()(p, bound)), bb,true)? + CGAL::SMALLER : CGAL::LARGER; + } + + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_false) const + { + return GeomTraits().do_intersect_2_object() + (GeomTraits().construct_circle_2_object() + (p, GeomTraits().compute_squared_distance_2_object()(p, bound)), bb)? + CGAL::SMALLER : CGAL::LARGER; + } + + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const + { + return (*this)(p, bb, bound, Boolean_tag::value>()); + } + + // The following functions seem unused...? + template + CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const + { + return GeomTraits().do_intersect_2_object() + (GeomTraits().construct_circle_2_object() + (p, GeomTraits().compute_squared_distance_2_object()(p, bound)), pr)? + CGAL::SMALLER : CGAL::LARGER; + } + + template + CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const + { + return GeomTraits().do_intersect_2_object() + (GeomTraits().construct_circle_2_object()(p, sq_distance), + pr) ? + CGAL::SMALLER : + CGAL::LARGER; + } + }; + + Closest_point closest_point_object() const {return Closest_point(*this);} + Compare_distance compare_distance_object() const {return Compare_distance();} + + typedef enum { CGAL_AXIS_X = 0, + CGAL_AXIS_Y = 1} Axis; + + static Axis longest_axis(const Bounding_box& bbox); + +private: + /** + * @brief Computes bounding box of one primitive + * @param pr the primitive + * @return the bounding box of the primitive \c pr + */ + template + Bounding_box compute_bbox(const Primitive& pr, const PM&)const + { + return get(bbm, pr.id()); + } + + Bounding_box compute_bbox(const Primitive& pr, const Default&)const + { + return GeomTraits().construct_bbox_2_object()(internal::Primitive_helper::get_datum(pr, *this)); + } + + /// Comparison functions + static bool less_x(const Primitive& pr1, const Primitive& pr2,const AABB_traits_2& traits) + { + return GeomTraits().less_x_2_object()( internal::Primitive_helper::get_reference_point(pr1,traits), + internal::Primitive_helper::get_reference_point(pr2,traits) ); + } + static bool less_y(const Primitive& pr1, const Primitive& pr2,const AABB_traits_2& traits) + { + return GeomTraits().less_y_2_object()( internal::Primitive_helper::get_reference_point(pr1,traits), + internal::Primitive_helper::get_reference_point(pr2,traits) ); + } + +}; // end class AABB_traits_2 + + +//------------------------------------------------------- +// Private methods +//------------------------------------------------------- + template + typename AABB_traits_2::Axis + AABB_traits_2::longest_axis(const Bounding_box& bbox) +{ + const double dx = bbox.xmax() - bbox.xmin(); + const double dy = bbox.ymax() - bbox.ymin(); + + if(dx>=dy) + { + return CGAL_AXIS_X; + } + else + { + return CGAL_AXIS_Y; + } +} + +/// @} + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_TRAITS_2_H_ diff --git a/AABB_tree/include/CGAL/AABB_traits_3.h b/AABB_tree/include/CGAL/AABB_traits_3.h new file mode 100644 index 000000000000..30a3fa77cd3f --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_traits_3.h @@ -0,0 +1,532 @@ +// Copyright (c) 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) : Stéphane Tayeb, Pierre Alliez, Camille Wormser +// + +#ifndef CGAL_AABB_TRAITS_3_H_ +#define CGAL_AABB_TRAITS_3_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/// \file AABB_traits_3.h + +namespace CGAL { + +namespace internal { + +namespace AABB_tree { + +// AABB_traits_intersection_base_3 brings in the Intersection_distance predicate, +// if GeomTraits is a model RayIntersectionGeomTraits. +template ::value> +struct AABB_traits_intersection_base_3; + +template +struct AABB_traits_intersection_base_3{}; + +template +struct AABB_traits_intersection_base_3 { + template + friend class AABB_ray_intersection; + +private: + typedef typename GeomTraits::Point_3 Point; + typedef typename GeomTraits::FT FT; + + // Defining Bounding_box and other types from the full AABB_traits_3 + // here might seem strange, but otherwise we would need to use + // CRTP to get access to the derived class, which would bloat the + // code more. + typedef typename CGAL::Bbox_3 Bounding_box; + + typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator; + typedef typename GeomTraits::Construct_cartesian_const_iterator_3 Construct_cartesian_const_iterator; + + Construct_cartesian_const_iterator construct_cartesian_const_iterator_object() { + return GeomTraits().construct_cartesian_const_iterator_3_object(); + } + +public: + typedef typename GeomTraits::Ray_3 Ray; + typedef typename GeomTraits::Vector_3 Vector; + typedef typename GeomTraits::Construct_source_3 Construct_source; + typedef typename GeomTraits::Construct_vector_3 Construct_vector; + + Construct_source construct_source_object() { + return GeomTraits().construct_source_3_object(); + } + + Construct_vector construct_vector_object() { + return GeomTraits().construct_vector_3_object(); + } + + struct Intersection_distance { + std::optional operator()(const Ray& ray, const Bounding_box& bbox) const { + FT t_near = -DBL_MAX; // std::numeric_limits::lowest(); C++1903 + FT t_far = DBL_MAX; + + const Construct_cartesian_const_iterator construct_cartesian_const_iterator + = GeomTraits().construct_cartesian_const_iterator_3_object(); + const Construct_source construct_source = GeomTraits().construct_source_3_object(); + const Construct_vector construct_vector = GeomTraits().construct_vector_3_object(); + const Point source = construct_source(ray); + const Vector direction = construct_vector(ray); + Cartesian_const_iterator source_iter = construct_cartesian_const_iterator(source); + Cartesian_const_iterator direction_iter = construct_cartesian_const_iterator(direction); + + for(int i = 0; i < 3; ++i, ++source_iter, ++direction_iter) { + if(*direction_iter == 0) { + if((*source_iter < (bbox.min)(i)) || (*source_iter > (bbox.max)(i))) { + return std::nullopt; + } + } else { + FT t1 = ((bbox.min)(i) - *source_iter) / *direction_iter; + FT t2 = ((bbox.max)(i) - *source_iter) / *direction_iter; + + t_near = (std::max)(t_near, (std::min)(t1, t2)); + t_far = (std::min)(t_far, (std::max)(t1, t2)); + + if(t_near > t_far || t_far < FT(0.)) + return std::nullopt; + } + } + + if(t_near < FT(0.)) + return FT(0.); + else + return t_near; + } + }; + + Intersection_distance intersection_distance_object() const { return Intersection_distance(); } +}; + +} } //end of namespace internal::AABB_tree + +/// \addtogroup PkgAABBTreeRef +/// @{ + +// forward declaration +template< typename AABBTraits> +class AABB_tree; + + +/// This traits class handles any type of 3D geometric +/// primitives provided that the proper intersection tests and +/// constructions are implemented. It handles points, rays, lines and +/// segments as query types for intersection detection and +/// computations, and it handles points as query type for distance +/// queries. +/// +/// \cgalModels{AABBTraits,AABBRayIntersectionTraits} +/// +/// \tparam GeomTraits must be a model of the concept \ref AABBGeomTraits_3, +/// and provide the geometric types as well as the intersection tests and computations. +/// \tparam Primitive provide the type of primitives stored in the AABB_tree. +/// It is a model of the concept `AABBPrimitive` or `AABBPrimitiveWithSharedData`. +/// +/// \tparam BboxMap must be a model of `ReadablePropertyMap` that has as key type a primitive id, +/// and as value type a `Bounding_box`. +/// If the type is `Default` the `Datum` must have the +/// member function `bbox()` that returns the bounding box of the primitive. +/// +/// If the argument `GeomTraits` is a model of the concept \ref +/// AABBRayIntersectionGeomTraits_3, this class is also a model of \ref +/// AABBRayIntersectionTraits. +/// +/// \sa `AABBTraits` +/// \sa `AABB_tree` +/// \sa `AABBPrimitive` +/// \sa `AABBPrimitiveWithSharedData` + +template +class AABB_traits_3 +#ifndef DOXYGEN_RUNNING +: public internal::AABB_tree::AABB_traits_base, + public internal::AABB_tree::AABB_traits_intersection_base_3, + public Search_traits_3 +#endif +{ + typedef typename CGAL::Object Object; +public: + typedef GeomTraits Geom_traits; + + typedef AABB_traits_3 AT; + // AABBTraits concept types + typedef typename GeomTraits::FT FT; + typedef AABBPrimitive Primitive; + + typedef typename std::pair Object_and_primitive_id; + + typedef typename std::pair Point_and_primitive_id; + + /// `Intersection_and_primitive_id::%Type::first_type` is found according to + /// the result type of `GeomTraits::Intersect_3::operator()`. If it is + /// `std::optional` then it is `T`, and the result type otherwise. + template + struct Intersection_and_primitive_id { + typedef decltype( + std::declval()( + std::declval(), + std::declval())) Intersection_type; + + typedef std::pair< + typename internal::Remove_optional::type, + typename Primitive::Id > Type; + }; + + // types for search tree + /// \name Types + /// @{ + + /// Point type + typedef typename GeomTraits::Point_3 Point; // because the AABB_tree is dimension agnostic + /// Deprecated point type + typedef typename GeomTraits::Point_3 Point_3; // kept for backward compatibility + + /// additional types for the search tree, required by the RangeSearchTraits concept + /// \bug This is not documented for now in the AABBTraits concept. + typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3; + + /// Bounding box type. + typedef typename CGAL::Bbox_3 Bounding_box; + + /// @} + + typedef typename GeomTraits::Sphere_3 Sphere_3; + typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_3; + typedef typename GeomTraits::Construct_cartesian_const_iterator_3 Construct_cartesian_const_iterator_3; + typedef typename GeomTraits::Construct_center_3 Construct_center_3; + typedef typename GeomTraits::Compute_squared_radius_3 Compute_squared_radius_3; + typedef typename GeomTraits::Construct_min_vertex_3 Construct_min_vertex_3; + typedef typename GeomTraits::Construct_max_vertex_3 Construct_max_vertex_3; + typedef typename GeomTraits::Construct_iso_cuboid_3 Construct_iso_cuboid_3; + + BboxMap bbm; + + /// Default constructor. + AABB_traits_3() { } + + AABB_traits_3(BboxMap bbm) + : bbm(bbm) + {} + + + typedef typename GeomTraits::Compute_squared_distance_3 Squared_distance; + Squared_distance squared_distance_object() const { return GeomTraits().compute_squared_distance_3_object(); } + + typedef typename GeomTraits::Equal_3 Equal; + Equal equal_object() const { return GeomTraits().equal_3_object(); } + + /** + * @internal + * @brief Sorts [first,beyond[ + * @param first iterator on first element + * @param beyond iterator on beyond element + * @param bbox the bounding box of [first,beyond[ + * + * Sorts the range defined by [first,beyond[. Sort is achieved on bbox longest + * axis, using the comparison function `_less_than` (dim in {x,y,z}) + */ + class Split_primitives + { + typedef AABB_traits_3 Traits; + const Traits& m_traits; + public: + Split_primitives(const AABB_traits_3& traits) + : m_traits(traits) {} + + typedef void result_type; + template + void operator()(PrimitiveIterator first, + PrimitiveIterator beyond, + const typename AT::Bounding_box& bbox) const + { + PrimitiveIterator middle = first + (beyond - first)/2; + switch(Traits::longest_axis(bbox)) + { + case AT::CGAL_AXIS_X: // sort along x + std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_x(p1, p2, this->m_traits); }); + break; + case AT::CGAL_AXIS_Y: // sort along y + std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_y(p1, p2, this->m_traits); }); + break; + case AT::CGAL_AXIS_Z: // sort along z + std::nth_element(first, middle, beyond, [this](const Primitive& p1, const Primitive& p2){ return Traits::less_z(p1, p2, this->m_traits); }); + break; + default: + CGAL_error(); + } + } + }; + + Split_primitives split_primitives_object() const {return Split_primitives(*this);} + + + /* + * Computes the bounding box of a set of primitives + * @param first an iterator on the first primitive + * @param beyond an iterator on the past-the-end primitive + * @return the bounding box of the primitives of the iterator range + */ + class Compute_bbox { + const AABB_traits_3& m_traits; + public: + Compute_bbox(const AABB_traits_3& traits) + :m_traits (traits) {} + + template + typename AT::Bounding_box operator()(ConstPrimitiveIterator first, + ConstPrimitiveIterator beyond) const + { + typename AT::Bounding_box bbox = m_traits.compute_bbox(*first,m_traits.bbm); + for(++first; first != beyond; ++first) + { + bbox = bbox + m_traits.compute_bbox(*first,m_traits.bbm); + } + return bbox; + } + + }; + + Compute_bbox compute_bbox_object() const {return Compute_bbox(*this);} + + /// \brief Function object using `GeomTraits::Do_intersect`. + /// In the case the query is a `CGAL::AABB_tree`, the `do_intersect()` + /// function of this tree is used. + class Do_intersect { + const AABB_traits_3& m_traits; + public: + Do_intersect(const AABB_traits_3& traits) + :m_traits(traits) {} + + template + bool operator()(const Query& q, const Bounding_box& bbox) const + { + return GeomTraits().do_intersect_3_object()(q, bbox); + } + + template + bool operator()(const Query& q, const Primitive& pr) const + { + return GeomTraits().do_intersect_3_object()(q, internal::Primitive_helper::get_datum(pr,m_traits)); + } + + // intersection with AABB-tree + template + bool operator()(const CGAL::AABB_tree& other_tree, const Primitive& pr) const + { + return other_tree.do_intersect( internal::Primitive_helper::get_datum(pr,m_traits) ); + } + + template + bool operator()(const CGAL::AABB_tree& other_tree, const Bounding_box& bbox) const + { + return other_tree.do_intersect(bbox); + } + }; + + Do_intersect do_intersect_object() const {return Do_intersect(*this);} + + + class Intersection { + const AABB_traits_3& m_traits; + public: + Intersection(const AABB_traits_3& traits) + :m_traits(traits) {} + template + std::optional< typename Intersection_and_primitive_id::Type > + operator()(const Query& query, const typename AT::Primitive& primitive) const { + auto inter_res = GeomTraits().intersect_3_object()(query, internal::Primitive_helper::get_datum(primitive,m_traits)); + if (!inter_res) + return std::nullopt; + return std::make_optional( std::make_pair(*inter_res, primitive.id()) ); + } + }; + + Intersection intersection_object() const {return Intersection(*this);} + + + // This should go down to the GeomTraits, i.e. the kernel + class Closest_point { + typedef typename AT::Point Point; + typedef typename AT::Primitive Primitive; + const AABB_traits_3& m_traits; + public: + Closest_point(const AABB_traits_3& traits) + : m_traits(traits) {} + + + Point operator()(const Point& p, const Primitive& pr, const Point& bound) const + { + GeomTraits geom_traits; + Point closest_point = geom_traits.construct_projected_point_3_object()( + internal::Primitive_helper::get_datum(pr,m_traits), p); + + return (geom_traits.compare_distance_3_object()(p, closest_point, bound) == LARGER) ? + bound : closest_point; + } + }; + + // This should go down to the GeomTraits, i.e. the kernel + // and the internal implementation should change its name from + // do_intersect to something like does_contain (this is what we compute, + // this is not the same do_intersect as the spherical kernel) + class Compare_distance { + typedef typename AT::Point Point; + typedef typename AT::FT FT; + typedef typename AT::Primitive Primitive; + public: + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_true) const + { + return GeomTraits().do_intersect_3_object() + (GeomTraits().construct_sphere_3_object() + (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb,true)? + CGAL::SMALLER : CGAL::LARGER; + } + + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_false) const + { + return GeomTraits().do_intersect_3_object() + (GeomTraits().construct_sphere_3_object() + (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb)? + CGAL::SMALLER : CGAL::LARGER; + } + + CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const + { + return (*this)(p, bb, bound, Boolean_tag::value>()); + } + + // The following functions seem unused...? + template + CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const + { + return GeomTraits().do_intersect_3_object() + (GeomTraits().construct_sphere_3_object() + (p, GeomTraits().compute_squared_distance_3_object()(p, bound)), pr)? + CGAL::SMALLER : CGAL::LARGER; + } + + template + CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const + { + return GeomTraits().do_intersect_3_object() + (GeomTraits().construct_sphere_3_object()(p, sq_distance), + pr) ? + CGAL::SMALLER : + CGAL::LARGER; + } + }; + + Closest_point closest_point_object() const {return Closest_point(*this);} + Compare_distance compare_distance_object() const {return Compare_distance();} + + typedef enum { CGAL_AXIS_X = 0, + CGAL_AXIS_Y = 1, + CGAL_AXIS_Z = 2} Axis; + + static Axis longest_axis(const Bounding_box& bbox); + +private: + /** + * @brief Computes bounding box of one primitive + * @param pr the primitive + * @return the bounding box of the primitive \c pr + */ + template + Bounding_box compute_bbox(const Primitive& pr, const PM&)const + { + return get(bbm, pr.id()); + } + + Bounding_box compute_bbox(const Primitive& pr, const Default&)const + { + return internal::Primitive_helper::get_datum(pr,*this).bbox(); + } + + /// Comparison functions + static bool less_x(const Primitive& pr1, const Primitive& pr2,const AABB_traits_3& traits) + { + return GeomTraits().less_x_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), + internal::Primitive_helper::get_reference_point(pr2,traits) ); + } + static bool less_y(const Primitive& pr1, const Primitive& pr2,const AABB_traits_3& traits) + { + return GeomTraits().less_y_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), + internal::Primitive_helper::get_reference_point(pr2,traits) ); + } + static bool less_z(const Primitive& pr1, const Primitive& pr2,const AABB_traits_3& traits) + { + return GeomTraits().less_z_3_object()( internal::Primitive_helper::get_reference_point(pr1,traits), + internal::Primitive_helper::get_reference_point(pr2,traits) ); + } + +}; // end class AABB_traits_3 + + +//------------------------------------------------------- +// Private methods +//------------------------------------------------------- + template + typename AABB_traits_3::Axis + AABB_traits_3::longest_axis(const Bounding_box& bbox) +{ + const double dx = bbox.xmax() - bbox.xmin(); + const double dy = bbox.ymax() - bbox.ymin(); + const double dz = bbox.zmax() - bbox.zmin(); + + if(dx>=dy) + { + if(dx>=dz) + { + return CGAL_AXIS_X; + } + else // dz>dx and dx>=dy + { + return CGAL_AXIS_Z; + } + } + else // dy>dx + { + if(dy>=dz) + { + return CGAL_AXIS_Y; + } + else // dz>dy and dy>dx + { + return CGAL_AXIS_Z; + } + } +} + +/// @} + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_TRAITS_3_H_ diff --git a/AABB_tree/include/CGAL/AABB_tree.h b/AABB_tree/include/CGAL/AABB_tree.h index 16c39099b383..c63fdf38679d 100644 --- a/AABB_tree/include/CGAL/AABB_tree.h +++ b/AABB_tree/include/CGAL/AABB_tree.h @@ -41,9 +41,9 @@ namespace CGAL { /** * Static data structure for efficient - * intersection and distance computations in 3D. It builds a + * intersection and distance computations in 2D and 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 + * of 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. @@ -74,9 +74,8 @@ namespace CGAL { /// Number type returned by the distance queries. typedef typename AABBTraits::FT FT; - - /// Type of 3D point. - typedef typename AABBTraits::Point_3 Point; + /// Type of point. + typedef typename AABBTraits::Point Point; /// Type of input primitive. typedef typename AABBTraits::Primitive Primitive; @@ -86,7 +85,7 @@ namespace CGAL { typedef typename Primitives::size_type size_type; /// Type of bounding box. typedef typename AABBTraits::Bounding_box Bounding_box; - /// 3D Point and Primitive Id type + /// Point and Primitive Id type typedef typename AABBTraits::Point_and_primitive_id Point_and_primitive_id; /// \deprecated typedef typename AABBTraits::Object_and_primitive_id Object_and_primitive_id; @@ -125,7 +124,7 @@ namespace CGAL { Self& operator=(const Self&) = delete; /** - * @brief Builds the data structure from a sequence of primitives. + * @brief builds the data structure from a sequence of primitives. * @param first iterator over first primitive to insert * @param beyond past-the-end iterator * @@ -300,7 +299,7 @@ namespace CGAL { /// returns the intersection and primitive id closest to the source point of the ray /// query. - /// \tparam Ray must be the same as `AABBTraits::Ray_3` and + /// \tparam Ray must be the same as `AABBTraits::Ray` and /// `do_intersect` predicates and intersections for it must be /// defined. /// \tparam Skip a functor with an operator @@ -331,7 +330,7 @@ namespace CGAL { /// returns the primitive id closest to the source point of the ray /// query. - /// \tparam Ray must be the same as `AABBTraits::Ray_3` and + /// \tparam Ray must be the same as `AABBTraits::Ray` and /// `do_intersect` predicates and intersections for it must be /// defined. /// \tparam Skip a functor with an operator diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_ray_intersection.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_ray_intersection.h index fd11f41492c5..9aa8ba6980ba 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_ray_intersection.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_ray_intersection.h @@ -36,9 +36,13 @@ namespace CGAL { template class AABB_ray_intersection { typedef typename AABBTree::AABB_traits AABB_traits; - typedef typename AABB_traits::Ray_3 Ray; + static const int dimension = AABB_traits::Point::Ambient_dimension::value; + typedef typename AABB_traits::Ray Ray; + typedef typename AABB_traits::Vector Vector; + typedef typename AABBTree::template Intersection_and_primitive_id::Type Ray_intersection_and_primitive_id; typedef typename Ray_intersection_and_primitive_id::first_type Ray_intersection; + public: AABB_ray_intersection(const AABBTree& tree) : tree_(tree) {} @@ -48,8 +52,7 @@ class AABB_ray_intersection { // nb_primitives through a variable in each Node on the stack. In // BVH_node::traversal this is done through the function parameter // nb_primitives in the recursion. - typedef - boost::heap::priority_queue< Node_ptr_with_ft, boost::heap::compare< std::greater > > + typedef boost::heap::priority_queue< Node_ptr_with_ft, boost::heap::compare< std::greater > > Heap_type; typename AABB_traits::Intersection @@ -167,8 +170,8 @@ class AABB_ray_intersection { as_ray_param_visitor(const Ray* ray) : ray(ray), max_i(0) { - typename AABB_traits::Geom_traits::Vector_3 v = ray->to_vector(); - for (int i=1; i<3; ++i) + Vector v = AABB_traits().construct_vector_object()(*ray); + for (int i=1; i CGAL::abs(v[max_i]) ) max_i = i; } @@ -184,8 +187,8 @@ class AABB_ray_intersection { } FT operator()(const Point& point) { - typename AABB_traits::Geom_traits::Vector_3 x(ray->source(), point); - typename AABB_traits::Geom_traits::Vector_3 v = ray->to_vector(); + Vector x = Vector(AABB_traits().construct_source_object()(*ray), point); + Vector v = AABB_traits().construct_vector_object()(*ray); return x[max_i] / v[max_i]; } @@ -200,8 +203,8 @@ template std::optional< typename AABB_tree::template Intersection_and_primitive_id::Type > AABB_tree::first_intersection(const Ray& query, const SkipFunctor& skip) const { - static_assert(std::is_same::value, - "Ray and Ray_3 must be the same type"); + static_assert(std::is_same::value, + "Ray and AABBTraits::Ray must be the same type"); switch(size()) // copy-paste from AABB_tree::traversal { 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 9ba3cf2481b9..f44130eda28b 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 @@ -15,110 +15,60 @@ #include - #include -#include +#include +#include namespace CGAL { - template - class Add_decorated_point: public Underlying - { - class Decorated_point: public Underlying::Point_3 - { - public: - const Id& id() const { return m_id; } - - Decorated_point() - : Underlying::Point_3() - , m_id() - , m_is_id_initialized(false) {} - - // Allows the user not to provide the id - // so that we don't break existing code - Decorated_point(const typename Underlying::Point_3& p) - : Underlying::Point_3(p) - , m_id() - , m_is_id_initialized(false) {} - - Decorated_point(const typename Underlying::Point_3& p, - const Id& id) - : Underlying::Point_3(p) - , m_id(id) - , m_is_id_initialized(true) {} - - Decorated_point(const Decorated_point& rhs) - : Underlying::Point_3(rhs) - , m_id() - , m_is_id_initialized(rhs.m_is_id_initialized) - { - if ( m_is_id_initialized ) - m_id = rhs.m_id; - } - - Decorated_point& operator=(const Decorated_point&)=default; - private: - Id m_id; - // Needed to avoid exception (depending on Id type) - // "error: attempt to copy-construct an iterator from a singular iterator." - // This exception may appear if we copy-construct an Id - // which has Id() as value (It is done when constructing - // Neighbor_search since we pass the Point only as query) - bool m_is_id_initialized; - }; - public: - typedef Decorated_point Point_3; - }; - - template - class AABB_search_tree - { - public: - typedef typename Traits::FT FT; - typedef typename Traits::Point_3 Point; - typedef typename Traits::Primitive Primitive; - typedef typename Traits::Point_and_primitive_id Point_and_primitive_id; - typedef typename CGAL::Search_traits_3 > TreeTraits; - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; - private: - Tree m_tree; - - - Point_and_primitive_id get_p_and_p(const Point_and_primitive_id& p) - { - return p; - } - Point_and_primitive_id get_p_and_p(const Point& p) - { - return Point_and_primitive_id(p, typename Primitive::Id()); - } - - public: - template - AABB_search_tree(ConstPointIterator begin, ConstPointIterator beyond) - : m_tree{} - { - typedef typename Add_decorated_point::Point_3 Decorated_point; - std::vector points; - while(begin != beyond) { - Point_and_primitive_id pp = get_p_and_p(*begin); - points.emplace_back(pp.first, pp.second); - ++begin; - } - m_tree.insert(points.begin(), points.end()); - m_tree.build(); - } +template +struct AABB_search_tree +{ - Point_and_primitive_id closest_point(const Point& query) const - { - Neighbor_search search(m_tree, query, 1); - return Point_and_primitive_id(static_cast(search.begin()->first), search.begin()->first.id()); - } - }; + typedef typename Traits::Point_and_primitive_id Point_and_primitive_id; + typedef typename Point_and_primitive_id::first_type Point; + typedef typename Point_and_primitive_id::second_type Id; + typedef First_of_pair_property_map Pmap; + typedef Search_traits_adapter TreeTraits; + typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; + typedef typename Neighbor_search::Tree Tree; +private: + Tree m_tree; + + Point_and_primitive_id get_p_and_p(const Point_and_primitive_id& p) + { + return p; + } + + Point_and_primitive_id get_p_and_p(const Point& p) + { + return Point_and_primitive_id(p, Id()); + } + +public: + template + AABB_search_tree(ConstPointIterator begin, ConstPointIterator beyond) + : m_tree{} + { + std::vector points; + while (begin != beyond) { + Point_and_primitive_id pp = get_p_and_p(*begin); + points.emplace_back(pp); + ++begin; + } + m_tree.insert(points.begin(), points.end()); + m_tree.build(); + } + + template + Point_and_primitive_id closest_point(const Point& query) const + { + Neighbor_search search(m_tree, query, 1); + return search.begin()->first; + } +}; } #endif // CGAL_AABB_SEARCH_TREE_H - diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traits_base.h b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traits_base.h new file mode 100644 index 000000000000..3fc4968c3c62 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_tree/internal/AABB_traits_base.h @@ -0,0 +1,47 @@ +// Copyright (c) 2024 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) : Stéphane Tayeb, Pierre Alliez, Camille Wormser +// + +#ifndef CGAL_AABB_TRAITS_BASE_H +#define CGAL_AABB_TRAITS_BASE_H + +#include + +#include + +namespace CGAL { +namespace internal { +namespace AABB_tree { + +//helper controlling whether extra data should be stored in the AABB_tree traits class +template ::value> +struct AABB_traits_base; + +template +struct AABB_traits_base {}; + +template +struct AABB_traits_base { + typename Primitive::Shared_data m_primitive_data; + + template + void set_shared_data(T&& ... t) { + m_primitive_data = Primitive::construct_shared_data(std::forward(t)...); + } + const typename Primitive::Shared_data& shared_data() const { return m_primitive_data; } +}; + +} +} +} + +#endif 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 645ce1347cb2..11fe903df932 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 @@ -59,7 +59,7 @@ template class First_intersection_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -73,7 +73,7 @@ class First_intersection_traits Result; public: First_intersection_traits(const AABBTraits& traits) - : m_result(), m_traits(traits) + : m_result(std::nullopt), m_traits(traits) {} bool go_further() const { @@ -108,7 +108,7 @@ template class Listing_intersection_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -151,7 +151,7 @@ template class Listing_primitive_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -191,7 +191,7 @@ template class First_primitive_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -202,7 +202,7 @@ class First_primitive_traits public: First_primitive_traits(const AABBTraits& traits) : m_is_found(false) - , m_result() + , m_result(std::nullopt) , m_traits(traits) {} bool go_further() const { return !m_is_found; } @@ -237,7 +237,7 @@ template class Do_intersect_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -278,7 +278,7 @@ template class Projection_traits { typedef typename AABBTraits::FT FT; - typedef typename AABBTraits::Point_3 Point; + typedef typename AABBTraits::Point Point; typedef typename AABBTraits::Primitive Primitive; typedef typename AABBTraits::Bounding_box Bounding_box; typedef typename AABBTraits::Primitive::Id Primitive_id; @@ -301,7 +301,7 @@ class Projection_traits { Point new_closest_point = m_traits.closest_point_object() (query, primitive, m_closest_point); - if( !m_traits.equal_3_object()(new_closest_point, m_closest_point) ) + if( !m_traits.equal_object()(new_closest_point, m_closest_point) ) { m_closest_primitive = primitive.id(); m_closest_point = new_closest_point; // this effectively shrinks the sphere diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h b/AABB_tree/include/CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h index e662b13be90c..f77b59d60852 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h @@ -24,19 +24,34 @@ namespace CGAL { namespace internal { namespace AABB_tree { -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_ray_3,Ray_3,false) -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_source_3,Construct_source_3,false) -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_vector_3,Construct_vector_3,false) -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_cartesian_const_iterator_3,Construct_cartesian_const_iterator_3,false) -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_cartesian_const_iterator_3,Cartesian_const_iterator_3,false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_ray_3, Ray_3, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_source_3, Construct_source_3, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_vector_3, Construct_vector_3, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_cartesian_const_iterator_3, Construct_cartesian_const_iterator_3, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_cartesian_const_iterator_3, Cartesian_const_iterator_3, false) + +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_ray_2, Ray_2, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_source_2, Construct_source_2, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_vector_2, Construct_vector_2, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_construct_cartesian_const_iterator_2, Construct_cartesian_const_iterator_2, false) +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_cartesian_const_iterator_2, Cartesian_const_iterator_2, false) + +template +struct Is_ray_intersection_geomtraits_2 +: std::bool_constant< Has_ray_2::value && + Has_construct_source_2::value && + Has_vector_2::value && + Has_construct_cartesian_const_iterator_2::value && + Has_cartesian_const_iterator_2::value > +{}; template struct Is_ray_intersection_geomtraits -: boost::mpl::and_< Has_ray_3, - Has_construct_source_3, - Has_vector_3, - Has_construct_cartesian_const_iterator_3, - Has_cartesian_const_iterator_3 >::type +: std::bool_constant< Has_ray_3::value&& + Has_construct_source_3::value&& + Has_vector_3::value&& + Has_construct_cartesian_const_iterator_3::value&& + Has_cartesian_const_iterator_3::value > {}; diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/Remove_optional.h b/AABB_tree/include/CGAL/AABB_tree/internal/Remove_optional.h new file mode 100644 index 000000000000..f831d84fa9f1 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_tree/internal/Remove_optional.h @@ -0,0 +1,33 @@ +// Copyright (c) 2024 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) : Stéphane Tayeb, Pierre Alliez, Camille Wormser +// + +#ifndef CGAL_REMOVE_OPTIONAL_H_ +#define CGAL_REMOVE_OPTIONAL_H_ + +#include + +#include + +namespace CGAL { +namespace internal { + +template +struct Remove_optional { typedef T type; }; + +template +struct Remove_optional< ::std::optional > { typedef T type; }; + +} // end namespace internal +} // end namespace CGAL + +#endif \ No newline at end of file diff --git a/AABB_tree/include/CGAL/AABB_tree/internal/triangle_datum_covering.h b/AABB_tree/include/CGAL/AABB_tree/internal/triangle_datum_covering.h index 69babb2c160c..ca017919e3ff 100644 --- a/AABB_tree/include/CGAL/AABB_tree/internal/triangle_datum_covering.h +++ b/AABB_tree/include/CGAL/AABB_tree/internal/triangle_datum_covering.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -161,7 +161,7 @@ struct AABB_covered_triangle_tree_traits CGAL::Tag_false /*no caching*/>; using AABB_geom_traits = Kernel; - using AABB_traits = CGAL::AABB_traits; + using AABB_traits = CGAL::AABB_traits_3; using AABB_tree = CGAL::AABB_tree; }; diff --git a/AABB_tree/include/CGAL/AABB_triangle_primitive.h b/AABB_tree/include/CGAL/AABB_triangle_primitive.h index 1fc1b140c582..f37a3540d92b 100644 --- a/AABB_tree/include/CGAL/AABB_triangle_primitive.h +++ b/AABB_tree/include/CGAL/AABB_triangle_primitive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 INRIA Sophia-Antipolis (France). +// Copyright (c) 2024 INRIA Sophia-Antipolis (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -11,92 +11,38 @@ // Author(s) : Sebastien Loriot // - #ifndef CGAL_AABB_TRIANGLE_PRIMITIVE_H_ #define CGAL_AABB_TRIANGLE_PRIMITIVE_H_ #include -#include +#define CGAL_DEPRECATED_HEADER "" +#define CGAL_REPLACEMENT_HEADER "" +#include + +#ifndef CGAL_NO_DEPRECATED_CODE + +#include -#include -#include +/// \file AABB_triangle_primitive.h namespace CGAL { -namespace internal { - template - struct Point_from_triangle_3_iterator_property_map{ - //classical typedefs - typedef Iterator key_type; - typedef typename GeomTraits::Point_3 value_type; - // typedef decltype( - // std::declval()( - // std::declval(), - // std::declval())) reference; - typedef decltype( - typename GeomTraits::Construct_vertex_3()( - *std::declval(), 0)) reference; - typedef boost::readable_property_map_tag category; - typedef Point_from_triangle_3_iterator_property_map Self; - inline friend reference - get(Self, key_type it) - { - return typename GeomTraits::Construct_vertex_3()( *it, 0 ); - } - }; -}//namespace internal +/// \addtogroup PkgAABBTreeRef +/// @{ +/// template alias for backward compatibility -/*! - * \ingroup PkgAABBTreeRef - * Primitive type that uses as identifier an iterator with a 3D triangle as `value_type`. - * The iterator from which the primitive is built should not be invalided - * while the AABB tree holding the 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_vertex_3` that has an operator taking a `Triangle_3` - * and an integer as parameters and returning a triangle point as a type convertible to `Point_3`. - * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Triangle_3` - * \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` (datum is not stored). - * - * \sa `AABBPrimitive` - * \sa `AABB_primitive` - * \sa `AABB_segment_primitive` - * \sa `AABB_halfedge_graph_segment_primitive` - * \sa `AABB_face_graph_triangle_primitive` - */ template < class GeomTraits, class Iterator, class CacheDatum=Tag_false> -class AABB_triangle_primitive -#ifndef DOXYGEN_RUNNING - : public AABB_primitive< Iterator, - Input_iterator_property_map, - internal::Point_from_triangle_3_iterator_property_map, - Tag_false, - CacheDatum > -#endif -{ - typedef AABB_primitive< Iterator, - Input_iterator_property_map, - internal::Point_from_triangle_3_iterator_property_map, - Tag_false, - CacheDatum > Base; -public: - ///constructor from an iterator - AABB_triangle_primitive(Iterator it) : Base(it){} -}; +using AABB_triangle_primitive = AABB_triangle_primitive_3; -} // end namespace CGAL +///@} -#include +} //CGAL namespace -#endif // CGAL_AABB_TRIANGLE_PRIMITIVE_H_ +#endif // CGAL_NO_DEPRECATED_CODE +#endif //CGAL_AABB_TRIANGLE_PRIMITIVE_H_ diff --git a/AABB_tree/include/CGAL/AABB_triangle_primitive_2.h b/AABB_tree/include/CGAL/AABB_triangle_primitive_2.h new file mode 100644 index 000000000000..0472b4ab08a1 --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_triangle_primitive_2.h @@ -0,0 +1,100 @@ +// Copyright (c) 2012 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) : Sebastien Loriot +// + + +#ifndef CGAL_AABB_TRIANGLE_PRIMITIVE_2_H_ +#define CGAL_AABB_TRIANGLE_PRIMITIVE_2_H_ + +#include + +#include + +#include +#include + +namespace CGAL { + +namespace internal { + template + struct Point_from_triangle_2_iterator_property_map{ + //classical typedefs + typedef Iterator key_type; + typedef typename GeomTraits::Point_2 value_type; + // typedef decltype( + // std::declval()( + // std::declval(), + // std::declval())) reference; + typedef decltype( + typename GeomTraits::Construct_vertex_2()( + *std::declval(), 0)) reference; + typedef boost::readable_property_map_tag category; + typedef Point_from_triangle_2_iterator_property_map Self; + + inline friend reference + get(Self, key_type it) + { + return typename GeomTraits::Construct_vertex_2()( *it, 0 ); + } + }; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a 2D triangle as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the primitive is in use. + * + * \cgalModels{AABBPrimitive} + * + * \tparam GeomTraits is a traits class providing the nested type `Point_2` and `Triangle_2`. + * It also provides the functor `Construct_vertex_2` that has an operator taking a `Triangle_2` + * and an integer as parameters and returning a triangle point as a type convertible to `Point_2`. + * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Triangle_2` + * \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` (datum is not stored). + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_2` + * \sa `AABB_triangle_primitive_3` + */ +template < class GeomTraits, + class Iterator, + class CacheDatum=Tag_false> +class AABB_triangle_primitive_2 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Point_from_triangle_2_iterator_property_map, + Tag_false, + CacheDatum > +#endif +{ + typedef AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Point_from_triangle_2_iterator_property_map, + Tag_false, + CacheDatum > Base; +public: + ///constructor from an iterator + AABB_triangle_primitive_2(Iterator it) : Base(it){} +}; + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_TRIANGLE_PRIMITIVE_2_H_ diff --git a/AABB_tree/include/CGAL/AABB_triangle_primitive_3.h b/AABB_tree/include/CGAL/AABB_triangle_primitive_3.h new file mode 100644 index 000000000000..0b709d855d6b --- /dev/null +++ b/AABB_tree/include/CGAL/AABB_triangle_primitive_3.h @@ -0,0 +1,101 @@ +// Copyright (c) 2012 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) : Sebastien Loriot +// + + +#ifndef CGAL_AABB_TRIANGLE_PRIMITIVE_3_H_ +#define CGAL_AABB_TRIANGLE_PRIMITIVE_3_H_ + +#include + +#include + +#include +#include + +namespace CGAL { + +namespace internal { + template + struct Point_from_triangle_3_iterator_property_map{ + //classical typedefs + typedef Iterator key_type; + typedef typename GeomTraits::Point_3 value_type; + // typedef decltype( + // std::declval()( + // std::declval(), + // std::declval())) reference; + typedef decltype( + typename GeomTraits::Construct_vertex_3()( + *std::declval(), 0)) reference; + typedef boost::readable_property_map_tag category; + typedef Point_from_triangle_3_iterator_property_map Self; + + inline friend reference + get(Self, key_type it) + { + return typename GeomTraits::Construct_vertex_3()( *it, 0 ); + } + }; +}//namespace internal + + +/*! + * \ingroup PkgAABBTreeRef + * Primitive type that uses as identifier an iterator with a 3D triangle as `value_type`. + * The iterator from which the primitive is built should not be invalided + * while the AABB tree holding the 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_vertex_3` that has an operator taking a `Triangle_3` + * and an integer as parameters and returning a triangle point as a type convertible to `Point_3`. + * \tparam Iterator is a model of `ForwardIterator` with its value type convertible to `GeomTraits::Triangle_3` + * \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` (datum is not stored). + * + * \sa `AABBPrimitive` + * \sa `AABB_primitive` + * \sa `AABB_segment_primitive_3` + * \sa `AABB_triangle_primitive_2` + * \sa `AABB_face_graph_triangle_primitive` + */ +template < class GeomTraits, + class Iterator, + class CacheDatum=Tag_false> +class AABB_triangle_primitive_3 +#ifndef DOXYGEN_RUNNING + : public AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Point_from_triangle_3_iterator_property_map, + Tag_false, + CacheDatum > +#endif +{ + typedef AABB_primitive< Iterator, + Input_iterator_property_map, + internal::Point_from_triangle_3_iterator_property_map, + Tag_false, + CacheDatum > Base; +public: + ///constructor from an iterator + AABB_triangle_primitive_3(Iterator it) : Base(it){} +}; + +} // end namespace CGAL + +#include + +#endif // CGAL_AABB_TRIANGLE_PRIMITIVE_3_H_ diff --git a/AABB_tree/include/CGAL/AABB_triangulation_3_cell_primitive.h b/AABB_tree/include/CGAL/AABB_triangulation_3_cell_primitive.h index 826e1690ccbd..d9c681ea9950 100644 --- a/AABB_tree/include/CGAL/AABB_triangulation_3_cell_primitive.h +++ b/AABB_tree/include/CGAL/AABB_triangulation_3_cell_primitive.h @@ -36,7 +36,7 @@ namespace CGAL std::declval())) reference; // typedef decltype( // typename GeomTraits::Construct_vertex_3()( - // *std::declval(), 0)) reference; // fails polyhedron demo! + // *std::declval(), 0)) reference; // fails CGAL Lab! typedef boost::readable_property_map_tag category; typedef Point_from_cell_iterator_proprety_map Self; diff --git a/AABB_tree/package_info/AABB_tree/description.txt b/AABB_tree/package_info/AABB_tree/description.txt index 0f6f1e2664c2..4defc25a8582 100644 --- a/AABB_tree/package_info/AABB_tree/description.txt +++ b/AABB_tree/package_info/AABB_tree/description.txt @@ -1 +1 @@ -Data structure for efficient intersection and distance queries over sets of 3D geometric primitives. +Data structure for efficient intersection and distance queries over sets of 2D and 3D geometric primitives. diff --git a/AABB_tree/package_info/AABB_tree/long_description.txt b/AABB_tree/package_info/AABB_tree/long_description.txt index ad49fc371b0e..73985b7a90fa 100644 --- a/AABB_tree/package_info/AABB_tree/long_description.txt +++ b/AABB_tree/package_info/AABB_tree/long_description.txt @@ -1 +1 @@ -This component implements a hierarchy of axis-aligned bounding boxes (a AABB tree) for efficient intersection and distance computations between 3D queries and sets of input 3D geometric objects. +This component implements a hierarchy of axis-aligned bounding boxes (a AABB tree) for efficient intersection and distance computations between 2D/3D queries and sets of input 2D/3D geometric objects. diff --git a/AABB_tree/test/AABB_tree/AABB_test_util.h b/AABB_tree/test/AABB_tree/AABB_test_util.h index fae5abfe17c6..a0cbef01e49f 100644 --- a/AABB_tree/test/AABB_tree/AABB_test_util.h +++ b/AABB_tree/test/AABB_tree/AABB_test_util.h @@ -246,7 +246,7 @@ void test(const std::string filename, typedef CGAL::Polyhedron_3 Polyhedron; typedef Primitive_generator Pr_generator; typedef typename Pr_generator::Primitive Pr; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; Polyhedron polyhedron; @@ -316,13 +316,13 @@ class Naive_implementations { typedef Primitive_generator Pr_generator; typedef typename Pr_generator::Primitive Pr; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef typename Pr_generator::iterator Polyhedron_primitive_iterator; typedef unsigned int size_type; typedef typename Traits::Object_and_primitive_id Object_and_primitive_id; typedef typename Pr::Id Primitive_id; typedef typename Traits::FT FT; - typedef typename Traits::Point_3 Point; + typedef typename Traits::Point Point; typedef typename Traits::Point_and_primitive_id Point_and_primitive_id; typedef std::optional Intersection_result; diff --git a/AABB_tree/test/AABB_tree/aabb_any_all_benchmark.cpp b/AABB_tree/test/AABB_tree/aabb_any_all_benchmark.cpp index eb6fef9082a5..d4af45426aa8 100644 --- a/AABB_tree/test/AABB_tree/aabb_any_all_benchmark.cpp +++ b/AABB_tree/test/AABB_tree/aabb_any_all_benchmark.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -64,7 +64,7 @@ std::tuple test(const char* name) { typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; std::ifstream ifs(name); diff --git a/AABB_tree/test/AABB_tree/aabb_correctness_triangle_test.cpp b/AABB_tree/test/AABB_tree/aabb_correctness_triangle_test.cpp index 54ef4c600f6b..39a1cfc9baac 100644 --- a/AABB_tree/test/AABB_tree/aabb_correctness_triangle_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_correctness_triangle_test.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include template @@ -43,7 +43,7 @@ int test() // construct tree from facets typedef typename CGAL::AABB_face_graph_triangle_primitive Primitive; - typedef typename CGAL::AABB_traits Traits; + typedef typename CGAL::AABB_traits_3 Traits; typedef typename CGAL::AABB_tree Tree; typedef typename Tree::Object_and_primitive_id Object_and_primitive_id; Tree tree(faces(polyhedron).first, faces(polyhedron).second, polyhedron); diff --git a/AABB_tree/test/AABB_tree/aabb_deprecated_segment_test.cpp b/AABB_tree/test/AABB_tree/aabb_deprecated_segment_test.cpp new file mode 100644 index 000000000000..f7f13eaf7b56 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_deprecated_segment_test.cpp @@ -0,0 +1,56 @@ +#include + +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Point_3 Point; +typedef K::Plane_3 Plane; +typedef K::Segment_3 Segment; +typedef K::Triangle_3 Triangle; + +typedef std::list::iterator Iterator; +typedef CGAL::AABB_segment_primitive Primitive; +typedef CGAL::AABB_traits 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); + + std::list segments; + segments.push_back(Segment(a,b)); + segments.push_back(Segment(a,c)); + segments.push_back(Segment(c,d)); + + // constructs the AABB tree and the internal search tree for + // efficient distance computations. + Tree tree(segments.begin(),segments.end()); + + // counts #intersections with a plane query + Plane plane_query(a,b,d); + std::cout << tree.number_of_intersected_primitives(plane_query) + << " intersections(s) with plane" << std::endl; + + // counts #intersections with a triangle query + Triangle triangle_query(a,b,c); + std::cout << tree.number_of_intersected_primitives(triangle_query) + << " intersections(s) with triangle" << std::endl; + + // computes the closest point from a point query + Point point_query(2.0, 2.0, 2.0); + Point closest = tree.closest_point(point_query); + + std::cerr << "closest point is: " << closest << std::endl; + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_deprecated_triangle_primitive_test.cpp b/AABB_tree/test/AABB_tree/aabb_deprecated_triangle_primitive_test.cpp new file mode 100644 index 000000000000..4881e0a8f8b4 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_deprecated_triangle_primitive_test.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Ray_3 Ray; +typedef K::Point_3 Point; +typedef K::Triangle_3 Triangle; + +typedef std::list::iterator Iterator; +typedef CGAL::AABB_triangle_primitive Primitive; +typedef CGAL::AABB_traits 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); + + std::list triangles; + triangles.push_back(Triangle(a,b,c)); + triangles.push_back(Triangle(a,b,d)); + triangles.push_back(Triangle(a,d,c)); + + // constructs AABB tree + Tree tree(triangles.begin(),triangles.end()); + + // counts #intersections + Ray ray_query(a,b); + assert(tree.number_of_intersected_primitives(ray_query) != 0); + + // compute closest point and squared distance + Point point_query(2.0, 2.0, 2.0); + Point closest_point = tree.closest_point(point_query); + std::cout << closest_point << std::endl; + + FT sqd = tree.squared_distance(point_query); + std::cout << sqd << std::endl; + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_distance_edge_test.cpp b/AABB_tree/test/AABB_tree/aabb_distance_edge_test.cpp index 36c3ab9f83ab..de9ec85e7641 100644 --- a/AABB_tree/test/AABB_tree/aabb_distance_edge_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_distance_edge_test.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include diff --git a/AABB_tree/test/AABB_tree/aabb_distance_triangle_hint_test.cpp b/AABB_tree/test/AABB_tree/aabb_distance_triangle_hint_test.cpp index 28186a346c88..5d1c7cf12719 100644 --- a/AABB_tree/test/AABB_tree/aabb_distance_triangle_hint_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_distance_triangle_hint_test.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/AABB_tree/test/AABB_tree/aabb_distance_triangle_test.cpp b/AABB_tree/test/AABB_tree/aabb_distance_triangle_test.cpp index 4bc056680f1c..6e52a364d927 100644 --- a/AABB_tree/test/AABB_tree/aabb_distance_triangle_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_distance_triangle_test.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include "AABB_test_util.h" diff --git a/AABB_tree/test/AABB_tree/aabb_intersection_triangle_test.cpp b/AABB_tree/test/AABB_tree/aabb_intersection_triangle_test.cpp index 366820567009..b335cd5adfed 100644 --- a/AABB_tree/test/AABB_tree/aabb_intersection_triangle_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_intersection_triangle_test.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include "AABB_test_util.h" diff --git a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_segment_test.cpp b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_segment_test.cpp index 24d7ae9f5024..85507b16b55e 100644 --- a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_segment_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_segment_test.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include "AABB_test_util.h" diff --git a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_triangle_test.cpp b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_triangle_test.cpp index dc63bbb52534..ceea242df176 100644 --- a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_triangle_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_distance_triangle_test.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include "AABB_test_util.h" diff --git a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_triangle_test.cpp b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_triangle_test.cpp index 2d8b5e7a3ac4..4c1660be97e5 100644 --- a/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_triangle_test.cpp +++ b/AABB_tree/test/AABB_tree/aabb_naive_vs_tree_triangle_test.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include "AABB_test_util.h" diff --git a/AABB_tree/test/AABB_tree/aabb_test_all_intersected_primitives_tree.cpp b/AABB_tree/test/AABB_tree/aabb_test_all_intersected_primitives_tree.cpp index c0c7d160fd0a..d3d735a6d65d 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_all_intersected_primitives_tree.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_all_intersected_primitives_tree.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include @@ -24,8 +24,8 @@ CGAL::Tag_false> S_Primitive; typedef CGAL::AABB_face_graph_triangle_primitive T_Primitive; -typedef CGAL::AABB_traits T_Traits; -typedef CGAL::AABB_traits S_Traits; +typedef CGAL::AABB_traits_3 T_Traits; +typedef CGAL::AABB_traits_3 S_Traits; typedef CGAL::AABB_tree T_Tree; typedef CGAL::AABB_tree S_Tree; typedef T_Tree::Primitive_id T_Primitive_id; diff --git a/AABB_tree/test/AABB_tree/aabb_test_datum.cpp b/AABB_tree/test/AABB_tree/aabb_test_datum.cpp index dc732d402656..5cac9a6f3f28 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_datum.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_datum.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -15,22 +15,22 @@ typedef K::Point_3 Point; typedef K::Triangle_3 Triangle; 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; typedef CGAL::AABB_face_graph_triangle_primitive Primitive2; -typedef CGAL::AABB_traits Traits2; +typedef CGAL::AABB_traits_3 Traits2; typedef CGAL::AABB_tree Tree2; typedef CGAL::AABB_face_graph_triangle_primitive Primitive3; -typedef CGAL::AABB_traits Traits3; +typedef CGAL::AABB_traits_3 Traits3; typedef CGAL::AABB_tree Tree3; typedef CGAL::AABB_face_graph_triangle_primitive Primitive4; -typedef CGAL::AABB_traits Traits4; +typedef CGAL::AABB_traits_3 Traits4; typedef CGAL::AABB_tree Tree4; int main(void) diff --git a/AABB_tree/test/AABB_tree/aabb_test_empty_tree.cpp b/AABB_tree/test/AABB_tree/aabb_test_empty_tree.cpp index c10ea5f1acf6..9c939a58314e 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_empty_tree.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_empty_tree.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include typedef CGAL::Simple_cartesian K; @@ -16,8 +16,8 @@ typedef K::Segment_3 Segment; typedef K::Triangle_3 Triangle; typedef std::vector::iterator Iterator; -typedef CGAL::AABB_segment_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_segment_primitive_3 Primitive; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/test/AABB_tree/aabb_test_indexed_triangle_2.cpp b/AABB_tree/test/AABB_tree/aabb_test_indexed_triangle_2.cpp new file mode 100644 index 000000000000..3f7b5fc5bb1f --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_indexed_triangle_2.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; +typedef K::Point_3 Point_3; +typedef K::Point_2 Point_2; +typedef K::Ray_2 Ray; + +template +struct Projection_xy_point_map { + + typedef typename GeomTraits::Point_3 key_type; + typedef typename GeomTraits::Point_2 value_type; + typedef value_type reference; + + typedef boost::readable_property_map_tag category; + typedef Projection_xy_point_map Self; + + Projection_xy_point_map() {} + + inline friend value_type get(Self, key_type p) + { + return value_type(p.x(), p.y()); + } +}; + +typedef std::vector >::const_iterator IndexIterator; +typedef std::vector PointRange; +typedef CGAL::AABB_indexed_triangle_primitive_2> Primitive; +typedef CGAL::AABB_traits_2 AABB_triangle_traits; +typedef CGAL::AABB_tree Tree; +typedef Tree::Point_and_primitive_id Point_and_primitive_id; +typedef std::optional::Type> Ray_intersection; + +int main() +{ + Point_3 a(0.0, 0.0, 0.0); + Point_3 b(0.0, 1.0, 0.0); + Point_3 c(1.0, 0.0, 0.0); + Point_3 d(1.0, 1.0, 0.0); + Point_3 e(2.0, 0.0, 0.0); + Point_3 f(2.0, 1.0, 0.0); + + std::vector points = { a, b, c, d, e, f }; + + std::vector > 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 + Tree tree(triangles.begin(), triangles.end(), points); + + // point sampling + Point_and_primitive_id id; + id = tree.closest_point_and_primitive(Point_2(0.5, 0.4)); + assert(std::distance(triangles.cbegin(), id.second) == 0); + id = tree.closest_point_and_primitive(Point_2(0.5, 0.6)); + assert(std::distance(triangles.cbegin(), id.second) == 1); + id = tree.closest_point_and_primitive(Point_2(1.5, 0.4)); + assert(std::distance(triangles.cbegin(), id.second) == 2); + id = tree.closest_point_and_primitive(Point_2(1.5, 0.6)); + assert(std::distance(triangles.cbegin(), id.second) == 3); + id = tree.closest_point_and_primitive(Point_2(3.0, 0.5)); + assert(std::distance(triangles.cbegin(), id.second) == 3); + + Ray ray(Point_2(5.5, 0.5), Point_2(1.5, 0.4)); + Ray_intersection intersection = tree.first_intersection(ray); + + assert(intersection.has_value()); + + assert(std::distance(triangles.cbegin(), intersection->second) == 3); + + std::list intersections; + tree.all_intersections(ray, std::back_inserter(intersections)); + assert(intersections.size() == 4); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_move_constructor.cpp b/AABB_tree/test/AABB_tree/aabb_test_move_constructor.cpp index 7f61faf6b7c7..b9e61b4ece2c 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_move_constructor.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_move_constructor.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -32,8 +32,8 @@ class TestCase<0> typedef K::Segment_3 Segment; typedef K::Triangle_3 Triangle; typedef std::vector::iterator Iterator; - typedef CGAL::AABB_segment_primitive Primitive; - typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_segment_primitive_3 Primitive; + typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; public: @@ -106,8 +106,8 @@ class TestCase<1> typedef CGAL::AABB_face_graph_triangle_primitive T_Primitive; - typedef CGAL::AABB_traits T_Traits; - typedef CGAL::AABB_traits S_Traits; + typedef CGAL::AABB_traits_3 T_Traits; + typedef CGAL::AABB_traits_3 S_Traits; typedef CGAL::AABB_tree T_Tree; typedef CGAL::AABB_tree S_Tree; typedef T_Tree::Primitive_id T_Primitive_id; diff --git a/AABB_tree/test/AABB_tree/aabb_test_multi_mesh.cpp b/AABB_tree/test/AABB_tree/aabb_test_multi_mesh.cpp index 3b2307a134b5..858db52e1b40 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_multi_mesh.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_multi_mesh.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -25,14 +25,14 @@ typedef CGAL::Surface_mesh > Mesh; typedef CGAL::AABB_face_graph_triangle_primitive T_Primitive; -typedef CGAL::AABB_traits T_Traits; +typedef CGAL::AABB_traits_3 T_Traits; typedef CGAL::AABB_tree T_Tree; typedef T_Tree::Primitive_id T_Primitive_id; typedef CGAL::AABB_halfedge_graph_segment_primitive E_Primitive; -typedef CGAL::AABB_traits E_Traits; +typedef CGAL::AABB_traits_3 E_Traits; typedef CGAL::AABB_tree E_Tree; typedef E_Tree::Primitive_id E_Primitive_id; diff --git a/AABB_tree/test/AABB_tree/aabb_test_polyline_segment_2.cpp b/AABB_tree/test/AABB_tree/aabb_test_polyline_segment_2.cpp new file mode 100644 index 000000000000..63df363e357b --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_polyline_segment_2.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; +typedef K::Segment_2 Segment; +typedef K::Point_2 Point; + +typedef std::vector PointRange; +typedef PointRange::const_iterator Iterator_pr; +typedef CGAL::AABB_polyline_segment_primitive_2 Primitive_pr; +typedef CGAL::AABB_traits_2 Traits_pr; +typedef CGAL::AABB_tree Tree_pr; +typedef Tree_pr::Point_and_primitive_id Point_and_primitive_id_pr; + +typedef CGAL::Polygon_2 Polygon_2; +typedef Polygon_2::const_iterator Iterator_poly; +typedef CGAL::AABB_polyline_segment_primitive_2 Primitive_poly; +typedef CGAL::AABB_traits_2 Traits_poly; +typedef CGAL::AABB_tree Tree_poly; +typedef Tree_poly::Point_and_primitive_id Point_and_primitive_id_poly; + +template +void test(AABBTree tree) { + tree.build(); + + tree.accelerate_distance_queries(); + + // counts #intersections with a segment query + Segment segment_query(Point(1.0, 0.0), Point(0.0, 7.0)); + + assert(tree.number_of_intersected_primitives(segment_query) == 2); + + // computes the closest point from a point query + Point point_query(4.0, 5.0); + Point closest = tree.closest_point(point_query); + assert(closest == Point(3.0, 4.0)); + + PPId id = tree.closest_point_and_primitive(point_query); + assert(id.first == closest); +} + +int main() +{ + Point a(0.0, 0.0); + Point b(2.0, 1.0); + Point c(3.0, 4.0); + Point d(1.0, 6.0); + Point e(-1.0, 3.0); + + std::vector polyline = { a, b, c, d, e }; + + Polygon_2 poly(polyline.begin(), polyline.end()); + + test(Tree_poly(poly.begin(), poly.end(), poly)); + + test(Tree_pr(polyline.begin(), std::prev(polyline.end()), polyline)); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_ray_intersection.cpp b/AABB_tree/test/AABB_tree/aabb_test_ray_intersection.cpp index 8ca781ff1142..aa4668273aa1 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_ray_intersection.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_ray_intersection.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -23,7 +23,7 @@ typedef K::Segment_3 Segment; typedef K::Ray_3 Ray; typedef CGAL::Polyhedron_3 Polyhedron; typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; typedef Tree::Primitive_id Primitive_id; typedef CGAL::Timer Timer; diff --git a/AABB_tree/test/AABB_tree/aabb_test_segment_2.cpp b/AABB_tree/test/AABB_tree/aabb_test_segment_2.cpp new file mode 100644 index 000000000000..2ccfa1f2f973 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_segment_2.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Segment_2 Segment; +typedef K::Point_2 Point; + +typedef std::list SegmentRange; +typedef SegmentRange::const_iterator Iterator; +typedef CGAL::AABB_segment_primitive_2 Primitive; +typedef CGAL::AABB_traits_2 Traits; +typedef CGAL::AABB_tree Tree; +typedef Tree::Point_and_primitive_id Point_and_primitive_id; + +int main() +{ + Point a(0.0, 0.0); + Point b(2.0, 1.0); + Point c(3.0, 4.0); + Point d(1.0, 6.0); + Point e(-1.0, 3.0); + + std::list seg; + seg.push_back(Segment(a, b)); + seg.push_back(Segment(b, c)); + seg.push_back(Segment(c, d)); + seg.push_back(Segment(d, e)); + seg.push_back(Segment(e, a)); + + // constructs the AABB tree and the internal search tree for + // efficient distance computations. + Tree tree(seg.begin(), seg.end()); + tree.build(); + + tree.accelerate_distance_queries(); + + // counts #intersections with a segment query + Segment segment_query(Point(1.0, 0.0), Point(0.0, 7.0)); + assert(tree.number_of_intersected_primitives(segment_query) == 2); + + // computes the closest point from a point query + Point point_query(5.0, 5.0); + Point closest = tree.closest_point(point_query); + assert(closest == c); + + Point_and_primitive_id id = tree.closest_point_and_primitive(Point(1.5, 3.0)); + assert(id.second->source() == b && id.second->target() == c); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_singleton_tree.cpp b/AABB_tree/test/AABB_tree/aabb_test_singleton_tree.cpp index cc26689d64e1..84e5cd5d934a 100644 --- a/AABB_tree/test/AABB_tree/aabb_test_singleton_tree.cpp +++ b/AABB_tree/test/AABB_tree/aabb_test_singleton_tree.cpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include typedef CGAL::Simple_cartesian K; @@ -16,8 +16,8 @@ typedef K::Segment_3 Segment; typedef K::Triangle_3 Triangle; typedef std::vector::iterator Iterator; -typedef CGAL::AABB_segment_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_segment_primitive_3 Primitive; +typedef CGAL::AABB_traits_3 Traits; typedef CGAL::AABB_tree Tree; int main() diff --git a/AABB_tree/test/AABB_tree/aabb_test_triangle_2.cpp b/AABB_tree/test/AABB_tree/aabb_test_triangle_2.cpp new file mode 100644 index 000000000000..ede2cc4d2af1 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_triangle_2.cpp @@ -0,0 +1,50 @@ +// Author(s) : Camille Wormser, Pierre Alliez + +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Ray_2 Ray; +typedef K::Point_2 Point; +typedef K::Triangle_2 Triangle; + +typedef std::list::const_iterator Iterator; +typedef CGAL::AABB_triangle_primitive_2 Primitive; +typedef CGAL::AABB_traits_2 AABB_triangle_traits; +typedef CGAL::AABB_tree Tree; + +int main() +{ + Point a(1.0, 0.0); + Point b(0.0, 1.0); + Point c(1.0, 1.0); + Point d(0.0, 0.0); + + std::list triangles; + triangles.push_back(Triangle(a, b, c)); + triangles.push_back(Triangle(a, b, d)); + triangles.push_back(Triangle(a, d, c)); + + // constructs AABB tree + Tree tree(triangles.begin(), triangles.end()); + + // counts #intersections + Ray ray_query(a, b); + assert(tree.number_of_intersected_primitives(ray_query) == 3); + + // compute closest point and squared distance + Point point_query(2.0, 2.0); + Point closest_point = tree.closest_point(point_query); + assert(closest_point == c); + + assert(tree.squared_distance(point_query) == 2); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_test_triangle_3.cpp b/AABB_tree/test/AABB_tree/aabb_test_triangle_3.cpp new file mode 100644 index 000000000000..12c2366260e3 --- /dev/null +++ b/AABB_tree/test/AABB_tree/aabb_test_triangle_3.cpp @@ -0,0 +1,49 @@ +// Author(s) : Camille Wormser, Pierre Alliez + +#include +#include + +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K; + +typedef K::FT FT; +typedef K::Ray_3 Ray; +typedef K::Point_3 Point; +typedef K::Triangle_3 Triangle; + +typedef std::list::const_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); + + std::list triangles; + triangles.push_back(Triangle(a, b, c)); + triangles.push_back(Triangle(a, b, d)); + triangles.push_back(Triangle(a, d, c)); + + // constructs AABB tree + Tree tree(triangles.begin(), triangles.end()); + + // counts #intersections + Ray ray_query(a, b); + assert(tree.number_of_intersected_primitives(ray_query) == 3); + + // compute closest point and squared distance + Point point_query(3.0, 2.0, 2.0); + Point closest_point = tree.closest_point(point_query); + assert(closest_point == a); + assert(tree.squared_distance(point_query) == 12); + + return EXIT_SUCCESS; +} diff --git a/AABB_tree/test/AABB_tree/aabb_triangle_datum_covering.cpp b/AABB_tree/test/AABB_tree/aabb_triangle_datum_covering.cpp index bfd756fd0ae7..e13416a98886 100644 --- a/AABB_tree/test/AABB_tree/aabb_triangle_datum_covering.cpp +++ b/AABB_tree/test/AABB_tree/aabb_triangle_datum_covering.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -29,7 +29,7 @@ void test_no_cover(const Mesh& mesh) using Line_3 = typename GT::Line_3; using Primitive = CGAL::AABB_face_graph_triangle_primitive; - using Traits = CGAL::AABB_traits; + using Traits = CGAL::AABB_traits_3; using Tree = CGAL::AABB_tree; // Build @@ -99,7 +99,7 @@ void test_cover(const Mesh& mesh, using AABB_tree = CGAL::AABB_trees::internal::AABB_covered_triangle_tree; using FG_Primitive = CGAL::AABB_face_graph_triangle_primitive; - using FG_Traits = CGAL::AABB_traits; + using FG_Traits = CGAL::AABB_traits_3; using FG_Tree = CGAL::AABB_tree; CGAL::Bbox_3 bbox = CGAL::Polygon_mesh_processing::bbox(mesh); diff --git a/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies b/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies index fffe4db3c74c..e48c91b7e6ac 100644 --- a/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies +++ b/Advancing_front_surface_reconstruction/package_info/Advancing_front_surface_reconstruction/dependencies @@ -2,6 +2,7 @@ Advancing_front_surface_reconstruction Algebraic_foundations Arithmetic_kernel BGL +CGAL_Core Cartesian_kernel Circulator Distance_2 @@ -22,7 +23,6 @@ Number_types Polyhedron Profiling_tools Property_map -Random_numbers STL_Extension Spatial_sorting Stream_support diff --git a/Algebraic_foundations/include/CGAL/Rational_traits.h b/Algebraic_foundations/include/CGAL/Rational_traits.h index a2ac5f1016c5..7fcf808e43b1 100644 --- a/Algebraic_foundations/include/CGAL/Rational_traits.h +++ b/Algebraic_foundations/include/CGAL/Rational_traits.h @@ -53,7 +53,7 @@ struct Rational_traits_base { private: typedef Fraction_traits FT; - typedef typename FT::Decompose Decomose; + typedef typename FT::Decompose Decompose; typedef typename FT::Compose Compose; public: @@ -61,13 +61,13 @@ struct Rational_traits_base RT numerator (const Rational& r) const { RT num,den; - Decomose()(r,num,den); + Decompose()(r,num,den); return num; } RT denominator (const Rational& r) const { RT num,den; - Decomose()(r,num,den); + Decompose()(r,num,den); return den; } diff --git a/Algebraic_foundations/include/CGAL/number_utils.h b/Algebraic_foundations/include/CGAL/number_utils.h index c17823d72b9d..5a5bc2d242d3 100644 --- a/Algebraic_foundations/include/CGAL/number_utils.h +++ b/Algebraic_foundations/include/CGAL/number_utils.h @@ -194,21 +194,21 @@ root_of( int k, Input_iterator begin, Input_iterator end ) { template< class Number_type > inline // select a Is_zero functor -typename boost::mpl::if_c< - ::std::is_same< typename Algebraic_structure_traits< Number_type >::Is_zero, - Null_functor >::value , +typename std::conditional_t< + std::is_same_v< typename Algebraic_structure_traits< Number_type >::Is_zero, + Null_functor >, typename Real_embeddable_traits< Number_type >::Is_zero, typename Algebraic_structure_traits< Number_type >::Is_zero ->::type::result_type +>::result_type is_zero( const Number_type& x ) { // We take the Algebraic_structure_traits<>::Is_zero functor by default. If it // is not available, we take the Real_embeddable_traits functor - typename ::boost::mpl::if_c< - ::std::is_same< + std::conditional_t< + std::is_same_v< typename Algebraic_structure_traits< Number_type >::Is_zero, - Null_functor >::value , + Null_functor > , typename Real_embeddable_traits< Number_type >::Is_zero, - typename Algebraic_structure_traits< Number_type >::Is_zero >::type + typename Algebraic_structure_traits< Number_type >::Is_zero > is_zero; return is_zero( x ); } diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h index bf607e75f152..4cba93085a43 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_E08_tree.h @@ -303,7 +303,7 @@ class Convex_combinator_approx_Integer_log { Integer alpha_num = Integer(1), int log_denom = 1 ) : alpha_num_(alpha_num), beta_num_((Integer(1) << log_denom) - alpha_num), - half_((log_denom > 0) ? (Integer(1) << log_denom-1) : 0), + half_((log_denom > 0) ? Integer(Integer(1) << log_denom-1) : 0), log_denom_(log_denom) { CGAL_precondition(log_denom_ >= 0); diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h index cba01cdc9116..a76e1c6cecae 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree.h @@ -206,7 +206,7 @@ polynomial_power_to_bernstein_approx( std::vector f(n+1); polynomial_affine_transform_approx_log_denom( first, beyond, f.begin(), - upper_num - lower_num, lower_num, log_denom, + Integer(upper_num - lower_num), lower_num, log_denom, p+q, approx, log, logl ); diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h index 155cfbffc68d..8ed8fa5ae0c1 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Bitstream_descartes_rndl_tree_traits.h @@ -27,31 +27,10 @@ #include -#if CGAL_USE_CORE -namespace CORE { class BigInt; } -#endif - namespace CGAL { namespace internal { -#if CGAL_USE_CORE -// bugfix for CORE by Michael Kerber -// why is there a specialized function for CORE? -inline CORE::BigInt shift_integer_by(CORE::BigInt x, long shift){ - if( shift > 0 ){ - while(shift>63) { - x = (x >> 63); - shift-=63; - } - x = (x >> shift); - }else{ - // add 0 bits - x = (x << -shift); - } - return x; -} -#endif template Shiftable shift_integer_by(Shiftable x, long shift){ diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h index 0adc7652b04b..bc2287665f02 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/Descartes.h @@ -68,9 +68,9 @@ class Descartes { Polynomial poly_; int number_of_real_roots_; - IT* numerator; - IT* denominator_exponent; - bool* is_exact; + std::vector numerator; + std::vector denominator_exponent; + std::vector is_exact; IT LEFT,SCALE,DENOM; bool is_strong_; int k; @@ -91,9 +91,9 @@ class Descartes { k(kk), interval_given(false) { - numerator = new IT[CGAL::degree(P)]; - denominator_exponent = new IT[CGAL::degree(P)]; - is_exact = new bool[CGAL::degree(P)]; + numerator.resize(CGAL::degree(P)); + denominator_exponent.resize(CGAL::degree(P)); + is_exact.resize(CGAL::degree(P)); number_of_real_roots_ = 0; if(CGAL::degree(P) == 0) { @@ -116,9 +116,9 @@ class Descartes { k(kk), interval_given(true) { - numerator = new IT[CGAL::degree(P)]; - denominator_exponent = new IT[CGAL::degree(P)]; - is_exact = new bool[CGAL::degree(P)]; + numerator.resize(CGAL::degree(P)); + denominator_exponent.resize(CGAL::degree(P)); + is_exact.resize(CGAL::degree(P)); number_of_real_roots_ = 0; if(CGAL::degree(P) == 0) { @@ -153,9 +153,9 @@ class Descartes { k(D.k), interval_given(D.interval_given) { - numerator = new IT[CGAL::degree(poly_)]; - denominator_exponent = new IT[CGAL::degree(poly_)]; - is_exact = new bool[CGAL::degree(poly_)]; + numerator.resize(CGAL::degree(poly_)); + denominator_exponent.resize(CGAL::degree(poly_)); + is_exact.resize(CGAL::degree(poly_)); for(int i=0; i { long operator()( CORE::BigFloat x ) const { CGAL_precondition(!CGAL::zero_in(x)); x = CGAL::abs(x); - return CORE::floorLg(x.m()-x.err())+x.exp()*CORE::CHUNK_BIT; + return CORE::floorLg(CORE::BigInt(x.m()-x.err()))+x.exp()*CORE::CHUNK_BIT; } }; @@ -337,7 +337,7 @@ class Real_embeddable_extension< CORE::BigFloat > { // (already commented out in EXACUS)... // NiX_precond(!(NiX::in_zero(x) && NiX::singleton(x))); x = CGAL::abs(x); - return CORE::ceilLg(x.m()+x.err())+x.exp()*CORE::CHUNK_BIT; + return CORE::ceilLg(CORE::BigInt(x.m()+x.err()))+x.exp()*CORE::CHUNK_BIT; } }; diff --git a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h index ade0be4a1a59..aaf9185350f6 100644 --- a/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h +++ b/Algebraic_kernel_d/include/CGAL/Algebraic_kernel_d/bound_between_1.h @@ -220,7 +220,7 @@ simple_bound_between(const Algebraic_real& a, final_mantissa = final_mantissa << 1; final_mantissa++; y_log--; - x_m = x_m==0 ? 0 : x_m & ((Integer(1) << x_log) - 1); //x_m - CGAL::ipower(Integer(2),x_log); + x_m = x_m==0 ? 0 : Integer(x_m & ((Integer(1) << x_log) - 1)); //x_m - CGAL::ipower(Integer(2),x_log); x_log = x_m==0 ? -1 : CGAL::internal::floor_log2_abs(x_m); } final_mantissa = final_mantissa << 1; diff --git a/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies b/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies index 8d3b893eeb2b..2345a7f06d44 100644 --- a/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies +++ b/Algebraic_kernel_for_circles/package_info/Algebraic_kernel_for_circles/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_circles Arithmetic_kernel +CGAL_Core Filtered_kernel Installation Interval_support diff --git a/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies b/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies index bfe13a407f4a..e9b3846ab795 100644 --- a/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies +++ b/Algebraic_kernel_for_spheres/package_info/Algebraic_kernel_for_spheres/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Algebraic_kernel_for_spheres Arithmetic_kernel +CGAL_Core Filtered_kernel Installation Interval_support diff --git a/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies b/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies index 6c1597376389..d9c0f7d94a82 100644 --- a/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies +++ b/Alpha_shapes_2/package_info/Alpha_shapes_2/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Alpha_shapes_2 Arithmetic_kernel +CGAL_Core Cartesian_kernel Hash_map Homogeneous_kernel diff --git a/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies b/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies index f0dc76c90d99..516a70a2bdeb 100644 --- a/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies +++ b/Alpha_shapes_3/package_info/Alpha_shapes_3/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Alpha_shapes_3 Arithmetic_kernel +CGAL_Core Cartesian_kernel Circulator Filtered_kernel diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/CMakeLists.txt b/Alpha_wrap_3/benchmark/Alpha_wrap_3/CMakeLists.txt new file mode 100644 index 000000000000..a9aa0d1d63cb --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/CMakeLists.txt @@ -0,0 +1,15 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.20) +project(Alpha_wrap_3_Benchmark) + +find_package(CGAL REQUIRED) + +include_directories (BEFORE ../../include ./Quality ./Robustness) # AW3 includes +include_directories (BEFORE ../../../CGAL-Patches/include) + +# create a target per cppfile +create_single_source_cgal_program("Performance/performance_benchmark.cpp") +create_single_source_cgal_program("Quality/quality_benchmark.cpp") +create_single_source_cgal_program("Robustness/robustness_benchmark.cpp") diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/compute_performance_benchmark_data.py b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/compute_performance_benchmark_data.py new file mode 100644 index 000000000000..86c57d351465 --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/compute_performance_benchmark_data.py @@ -0,0 +1,61 @@ +# Copyright (c) 2019-2023 Google LLC (USA). +# All rights reserved. +# +# This file is part of CGAL (www.cgal.org). +# +# $URL$ +# $Id$ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# +# Author(s) : Pierre Alliez +# Michael Hemmer +# Cedric Portaneri +# +#!/usr/bin/python + +import os, sys, subprocess, datetime, time, getopt + +def compute_performance_benchmark_data(execname, filename, alpha): + + output = "" + cmd = ("/usr/bin/time", "-v", + execname, "-i", + filename, "-a", alpha) + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + start_new_session=True) + + outs, errs = proc.communicate() + output = outs.decode("utf-8") + errs.decode("utf-8") + + for output_line in output.split("\n"): + if "User time (seconds): " in output_line: + print(output_line[len("User time (seconds): "):]) + continue + if "Maximum resident set size (kbytes): " in output_line: + print(output_line[len("Maximum resident set size (kbytes): "):]) + continue + +def main(argv): + execname="" + filename="" + alpha="" + try: + opts, args = getopt.getopt(sys.argv[1:], 'e:i:a:') + except getopt.GetoptError: + sys.exit(2) + for opt, arg in opts: + if opt == "-e": + execname = arg + elif opt == "-i": + filename = arg + elif opt == "-a": + alpha = arg + + compute_performance_benchmark_data(execname, filename, alpha) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/generate_performance_benchmark_charts.py b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/generate_performance_benchmark_charts.py new file mode 100644 index 000000000000..445b69232778 --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/generate_performance_benchmark_charts.py @@ -0,0 +1,156 @@ +# Copyright (c) 2019-2023 Google LLC (USA). +# All rights reserved. +# +# This file is part of CGAL (www.cgal.org). +# +# $URL$ +# $Id$ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# +# Author(s) : Pierre Alliez +# Michael Hemmer +# Cedric Portaneri +# +#!/usr/bin/python + +import os, sys, subprocess, datetime, time, signal, getopt +import numpy as np +import matplotlib.pyplot as plt + +def main(argv): + + inputdir="" + outputdir="" + commit_hash="" + alpha="" + do_diff=False + diffdir="" + diff_hash="" + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:a:o:c:d:p:') + except getopt.GetoptError: + sys.exit(2) + for opt, arg in opts: + if opt == "-i": + inputdir = arg + elif opt == "-a": + alpha = arg + elif opt == "-o": + outputdir = arg + elif opt == "-c": + commit_hash = arg + elif opt == "-d": + diff_hash = arg + do_diff = True + elif opt == "-p": + diffdir = arg + + all_metric = { + "Time_(second)" : {}, + "Memory_Peak_(kbytes)" : {}} + num_input = 0 + for filename in os.listdir(inputdir) : + new_path = os.path.join(inputdir,filename) + new_file = open(new_path) + is_empty_new = os.path.getsize(new_path) <= 1 + if do_diff : + old_path = os.path.join(diffdir,filename) + old_file = open(old_path) + is_empty_old = os.path.getsize(old_path) <= 1 + for key in all_metric: + if is_empty_new or is_empty_old : + new_val = 0. + old_val = 0. + else : + new_val = float(new_file.readline().rstrip('\n')) + old_val = float(old_file.readline().rstrip('\n')) + mesh_id = str(filename.split('.')[0]) + all_metric[key][mesh_id] = [new_val, old_val] + else : + for key in all_metric: + if is_empty_new : + new_val = 0. + else : + new_val = float(new_file.readline().rstrip('\n')) + mesh_id = str(filename.split('.')[0]) + all_metric[key][mesh_id] = [new_val, new_val] + num_input = num_input+1 + + # update .pdf chart + date_now = datetime.datetime.now() + date_for_filename = str(date_now.year) +"_"+ str(date_now.month) +"_"+ str(date_now.day) +"_"+ str(date_now.hour) +"h"+ str(date_now.minute) +"mn" + for key in all_metric: + goal = 0 + num_el = range(len(all_metric[key])) + avg_diff_to_goal = 0. + avg = 0. + x1 = [] + x2 = [] + for value in all_metric[key].values() : + avg += value[0] + diff_to_goal = abs(value[1]-goal) - abs(value[0]-goal) + avg_diff_to_goal += diff_to_goal + x1.append(value[0]) + x2.append(value[1]) + avg_diff_to_goal /= float(len(all_metric[key])) + avg /= float(len(all_metric[key])) + + plt.figure(figsize=(8,8)) + if do_diff : + plt.hist(x2, bins=100, color='tab:green', alpha=0.5) + plt.hist(x1, bins=100, color='tab:blue', alpha=0.5) + plt.vlines(x = goal, ymin=plt.ylim()[0], ymax=plt.ylim()[1], linestyles='dashed') + + title = "" + if do_diff : + title += "Diff between " + commit_hash + " and " + diff_hash + " on " + str(num_input) + " meshes from Thingi10K\nAlpha = Bbox diag length / " + alpha + else : + title += "Benchmarking on " + str(num_input) + " meshes from Thingi10K\nAlpha = Bbox diag length / " + alpha + + avg_str = str(format(abs(avg), '.2f')) + if key == "Time_(second)" : + title += "\nIn average we spend " + avg_str + " seconds" + else : + title += "\nIn average we use up to " + avg_str + " kbytes" + + if do_diff and avg_diff_to_goal == 0. : + title += "\nNo change between the two commits" + elif do_diff : + avg_diff_str = str(format(abs(avg_diff_to_goal), '.2f')) + if key == "Time_(second)" : + if avg_diff_to_goal < 0 : + title += "\nIn average we get slower by " + else : + title += "\nIn average we get faster " + title += avg_diff_str + " seconds" + else : + if avg_diff_to_goal < 0 : + title += "\nIn average we use " + avg_diff_str + " more" + else : + title += "\nIn average we use " + avg_diff_str + " less" + title += " kbytes" + + plt.title(title, fontsize=15) + plt.xlabel(key.replace("_"," "), fontsize=14) + plt.ylabel("# of meshes", fontsize=14) + plt.tick_params(axis="x", labelsize=9) + plt.tick_params(axis="y", labelsize=9) + + chart_filename = "" + if do_diff : + chart_filename += "diff_"+commit_hash+"_"+diff_hash+"_"+key+"_"+date_for_filename+".pdf" + else : + chart_filename += "results_"+commit_hash+"_"+key+"_"+date_for_filename+".pdf" + chart_path = os.path.join(outputdir+"/charts",chart_filename) + if os.path.isfile(chart_path) : + os.remove(chart_path) + plt.savefig(chart_path, bbox_inches="tight") + plt.close() + + print("pdf updated") + + sys.exit() + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/performance_benchmark.cpp b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/performance_benchmark.cpp new file mode 100644 index 000000000000..207b4704635a --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/performance_benchmark.cpp @@ -0,0 +1,65 @@ +#include +#include + +#include + +#include + +#include +#include +#include +#include + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = K::Point_3; +using Vector_3 = K::Vector_3; + +using Mesh = CGAL::Surface_mesh; + +namespace PMP = CGAL::Polygon_mesh_processing; + +int main(int argc, char** argv) +{ + const int argc_check = argc - 1; + const char* entry_name_ptr = nullptr; + double relative_alpha_ratio = 20., relative_offset_ratio = 600.; + + for(int i=1; i points; + std::vector > faces; + if(!CGAL::IO::read_polygon_soup(entry_name_ptr, points, faces) || faces.empty()) + { + std::cerr << "Error: Invalid input data." << std::endl; + return EXIT_FAILURE; + } + + CGAL::Bbox_3 bbox; + for(const Point_3& p : points) + bbox += p.bbox(); + + const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) + + CGAL::square(bbox.ymax() - bbox.ymin()) + + CGAL::square(bbox.zmax() - bbox.zmin())); + const double alpha = diag_length / relative_alpha_ratio; + const double offset = diag_length / relative_offset_ratio; + + Mesh wrap; + CGAL::alpha_wrap_3(points, faces, alpha, offset, wrap); + + return EXIT_SUCCESS; +} diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/compute_quality_benchmark_data.py b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/compute_quality_benchmark_data.py new file mode 100644 index 000000000000..3b996cb57490 --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/compute_quality_benchmark_data.py @@ -0,0 +1,54 @@ +# Copyright (c) 2019-2023 Google LLC (USA). +# All rights reserved. +# +# This file is part of CGAL (www.cgal.org). +# +# $URL$ +# $Id$ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# +# Author(s) : Pierre Alliez +# Michael Hemmer +# Cedric Portaneri +# +#!/usr/bin/python + +import os, sys, subprocess, datetime, time, getopt + +def compute_quality_benchmark_data(execname, filename, alpha): + + output = "" + cmd = (execname, "-i", + filename, "-a", alpha) + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + start_new_session=True) + + outs, errs = proc.communicate() + output = outs.decode("utf-8") + errs.decode("utf-8") + + print(output) + +def main(argv): + execname="" + filename="" + alpha="" + try: + opts, args = getopt.getopt(sys.argv[1:], 'e:i:a:') + except getopt.GetoptError: + sys.exit(2) + for opt, arg in opts: + if opt == "-e": + execname = arg + elif opt == "-i": + filename = arg + elif opt == "-a": + alpha = arg + + compute_quality_benchmark_data(execname, filename, alpha) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/distance_utils.h b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/distance_utils.h new file mode 100644 index 000000000000..28af30181d49 --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/distance_utils.h @@ -0,0 +1,151 @@ +// Copyright (c) 2019-2022 Google LLC (USA). +// 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) : Pierre Alliez +// Michael Hemmer +// Cedric Portaneri + +#ifndef CGAL_ALPHA_WRAP_3_BENCHMARK_ALPHA_WRAP_3_QUALITY_DISTANCE_H_ +#define CGAL_ALPHA_WRAP_3_BENCHMARK_ALPHA_WRAP_3_QUALITY_DISTANCE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Aw3i { + +enum Distance_metric { HAUSDORFF = 0, MEAN = 1, RMS = 2 }; + +template +inline double approximate_hausdorff_distance(const std::vector& sample_points, + const AABBTree& tree, + Point& hint) +{ + double hdist = 0; + for(const Point& pt : sample_points) + { + hint = tree.closest_point(pt, hint); + auto dist = CGAL::squared_distance(hint, pt); + double d = CGAL::to_double(CGAL::approximate_sqrt(dist)); + if(d > hdist) + hdist = d; + } + + return hdist; +} + +template +inline double approximate_mean_distance(const std::vector& sample_points, + const AABBTree& tree, + Point& hint) +{ + double mdist = 0; + for(const Point& pt : sample_points) + { + hint = tree.closest_point(pt, hint); + auto dist = CGAL::squared_distance(hint, pt); + double d = CGAL::to_double(CGAL::approximate_sqrt(dist)); + mdist += d; + } + + return mdist / sample_points.size(); +} + +template +inline double approximate_rms_distance(const std::vector& sample_points, + const AABBTree& tree, + Point& hint) +{ + double rmsdist = 0; + for(const Point& pt : sample_points) + { + hint = tree.closest_point(pt, hint); + auto dist = CGAL::squared_distance(hint, pt); + rmsdist += CGAL::to_double(dist); + } + + return CGAL::to_double(CGAL::approximate_sqrt(rmsdist / sample_points.size())); +} + +template +inline double approximate_distance(const TriangleMesh& tm1, + const TriangleMesh& tm2, + const Distance_metric& metric) +{ + using GT = typename CGAL::GetGeomTraits::type; + using Point_3 = typename GT::Point_3; + + using Primitive = CGAL::AABB_face_graph_triangle_primitive; + using AABB_traits = CGAL::AABB_traits_3; + using AABB_tree = CGAL::AABB_tree; + + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + std::vector original_sample_points; + CGAL::Polygon_mesh_processing::sample_triangle_mesh(tm1, std::back_inserter(original_sample_points), + CGAL::parameters::all_default()); + + std::vector sample_points(std::begin(original_sample_points), + std::end(original_sample_points)); + CGAL::spatial_sort(sample_points.begin(), sample_points.end()); + + AABB_tree tree(faces(tm2).first, faces(tm2).second, tm2); + tree.build(); + + auto vpm_2 = get(CGAL::vertex_point, tm2); + Point_3 hint = get(vpm_2, *vertices(tm2).first); + + if(metric == HAUSDORFF) + return approximate_hausdorff_distance(sample_points, tree, hint); + else if(metric == MEAN) + return approximate_mean_distance(sample_points, tree, hint); + else if(metric == RMS) + return approximate_rms_distance(sample_points, tree, hint); + else + std::cerr << "Metric unknown\n" << std::endl; + + return -1.0; +} + +template +double get_longest_diag_bbox(const TriangleMesh& tm) +{ + CGAL::Bbox_3 bbox = CGAL::Polygon_mesh_processing::bbox(tm); + return std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) + + CGAL::square(bbox.ymax() - bbox.ymin()) + + CGAL::square(bbox.zmax() - bbox.zmin())); +} + +template +inline double approximate_distance_relative_to_bbox(const TriangleMesh& tm1, + const TriangleMesh& tm2, + const Distance_metric& metric) +{ + double longest_diag_length = get_longest_diag_bbox(tm1); + return approximate_distance(tm1, tm2, metric) / longest_diag_length; +} + +template +inline double approximate_distance_relative_to_bbox(const TriangleMesh& tm1, + const TriangleMesh& tm2, + const Distance_metric& metric, + const FT& longest_diag_length) +{ + return approximate_distance(tm1, tm2, metric) / CGAL::to_double(longest_diag_length); +} + +} // namespace Aw3i + +#endif // CGAL_CGAL_ALPHA_WRAP_3_BENCHMARK_ALPHA_WRAP_3_QUALITY_DISTANCE_H_ diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/generate_quality_benchmark_charts.py b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/generate_quality_benchmark_charts.py new file mode 100644 index 000000000000..0df5ed5e0f4e --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/generate_quality_benchmark_charts.py @@ -0,0 +1,182 @@ +# Copyright (c) 2019-2023 Google LLC (USA). +# All rights reserved. +# +# This file is part of CGAL (www.cgal.org). +# +# $URL$ +# $Id$ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# +# Author(s) : Pierre Alliez +# Michael Hemmer +# Cedric Portaneri +# +#!/usr/bin/python + +import os, sys, subprocess, datetime, time, signal, getopt +import numpy as np +import matplotlib.pyplot as plt + +def main(argv): + + inputdir="" + outputdir="" + commit_hash="" + alpha="" + do_diff=False + diffdir="" + diff_hash="" + try: + opts, args = getopt.getopt(sys.argv[1:], 'i:a:o:c:d:p:') + except getopt.GetoptError: + sys.exit(2) + for opt, arg in opts: + if opt == "-i": + inputdir = arg + elif opt == "-a": + alpha = arg + elif opt == "-o": + outputdir = arg + elif opt == "-c": + commit_hash = arg + elif opt == "-d": + diff_hash = arg + do_diff = True + elif opt == "-p": + diffdir = arg + + all_metric = { + "Mean_Min_Angle_(degree)" : {}, + "Mean_Max_Angle_(degree)" : {}, + "Mean_Radius_Ratio" : {}, + "Mean_Edge_Ratio" : {}, + "Mean_Aspect_Ratio" : {}, + "Complexity_(#_of_triangle)" : {}, + "#_of_almost_degenerate_triangle" : {}, + "Hausdorff_distance_output_to_input_(%_of_bbox_diag)" : {}} + num_input = 0 + print("inputdir = ", inputdir) + for filename in os.listdir(inputdir) : + new_path = os.path.join(inputdir,filename) + new_file = open(new_path) + if do_diff : + old_path = os.path.join(diffdir,filename) + old_file = open(old_path) + is_empty_old = os.path.getsize(old_path) <= 1 + for key in all_metric : + try : + new_val = float(new_file.readline().rstrip('\n')) + old_val = float(old_file.readline().rstrip('\n')) + mesh_id = str(filename.split('.')[0]) + all_metric[key][mesh_id] = [new_val, old_val] + except ValueError: + pass + else : + for key in all_metric : + try : + new_val = float(new_file.readline().rstrip('\n')) + mesh_id = str(filename.split('.')[0]) + all_metric[key][mesh_id] = [new_val, new_val] + except ValueError: + pass + num_input = num_input+1 + + # update .pdf chart + date_now = datetime.datetime.now() + date_for_filename = str(date_now.year) +"_"+ str(date_now.month) +"_"+ str(date_now.day) +"_"+ str(date_now.hour) +"h"+ str(date_now.minute) +"mn" + for key in all_metric: + goal = 0 + if key == "Mean_Min_Angle_(degree)" or key == "Mean_Max_Angle_(degree)": + goal = 60 + elif key == "Mean_Radius_Ratio" or key == "Mean_Edge_Ratio" or key == "Mean_Aspect_Ratio" : + goal = 1 + + num_el = range(len(all_metric[key])) + avg_diff_to_goal = 0. + avg = 0. + x1 = [] + x2 = [] + for value in all_metric[key].values() : + avg += value[0] + diff_to_goal = abs(value[1]-goal) - abs(value[0]-goal) + avg_diff_to_goal += diff_to_goal + x1.append(value[0]) + x2.append(value[1]) + avg_diff_to_goal /= float(len(all_metric[key])) + avg /= float(len(all_metric[key])) + + plt.figure(figsize=(8,8)) + if do_diff : + plt.hist(x2, bins=100, color='tab:green', alpha=0.5) + plt.hist(x1, bins=100, color='tab:blue', alpha=0.5) + plt.vlines(x = goal, ymin=plt.ylim()[0], ymax=plt.ylim()[1], linestyles='dashed') + + title = "" + if do_diff : + title += "Diff between " + commit_hash + " and " + diff_hash + " on " + str(num_input) + " meshes from Thingi10K\nAlpha = Bbox diag length / " + alpha + else : + title += "Benchmarking on " + str(num_input) + " meshes from Thingi10K\nAlpha = Bbox diag length / " + alpha + + avg_str = str(format(abs(avg), '.2f')) + if key == "Mean_Min_Angle_(degree)" or key == "Mean_Max_Angle_(degree)": + title += "\nIn average we have " + avg_str + "°" + elif key == "Mean_Radius_Ratio" or key == "Mean_Edge_Ratio" or key == "Mean_Aspect_Ratio" : + title += "\nIn average we have a ratio of " + avg_str + elif key == "Hausdorff_distance_output_to_input_(%_of_bbox_diag)" : + title += "\nIn average we have a distance of " + avg_str + "% of bbox diag" + elif key == "Complexity_(#_of_triangle)" or key == "#_of_almost_degenerate_triangle" : + title += "\nIn average we have " + avg_str + " triangles" + + if do_diff and avg_diff_to_goal == 0. : + title += "\nNo change between the two commits" + elif do_diff : + avg_diff_str = str(format(abs(avg_diff_to_goal), '.2f')) + if key == "Mean_Min_Angle_(degree)" or key == "Mean_Max_Angle_(degree)": + if avg_diff_to_goal < 0 : + title += "\nIn average we loose " + else : + title += "\nIn average we gain " + title += avg_diff_str + "° toward 60°" + elif key == "Mean_Radius_Ratio" or key == "Mean_Edge_Ratio" or key == "Mean_Aspect_Ratio" : + if avg_diff_to_goal < 0 : + title += "\nIn average we loose " + else : + title += "\nIn average we gain " + title += avg_diff_str + " of ratio toward 1" + elif key == "Hausdorff_distance_output_to_input_(%_of_bbox_diag)" : + if avg_diff_to_goal < 0 : + title += "\nIn average we increase by " + else : + title += "\nIn average we reduce by " + title += avg_diff_str + " the bbox ratio" + elif key == "Complexity_(#_of_triangle)" or key == "#_of_almost_degenerate_triangle" : + if avg_diff_to_goal < 0 : + title += "\nIn average we get " + avg_diff_str + " more" + else : + title += "\nIn average we get " + avg_diff_str + " less" + title += " triangles" + + plt.title(title, fontsize=15) + plt.xlabel(key.replace("_"," "), fontsize=14) + plt.ylabel("# of meshes", fontsize=14) + plt.tick_params(axis="x", labelsize=9) + plt.tick_params(axis="y", labelsize=9) + + chart_filename = "" + if do_diff : + chart_filename += "diff_"+commit_hash+"_"+diff_hash+"_"+key+"_"+date_for_filename+".pdf" + else : + chart_filename += "results_"+commit_hash+"_"+key+"_"+date_for_filename+".pdf" + chart_path = os.path.join(outputdir+"/charts",chart_filename) + if os.path.isfile(chart_path) : + os.remove(chart_path) + plt.savefig(chart_path, bbox_inches="tight") + plt.close() + + print("pdf updated") + + sys.exit() + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/quality_benchmark.cpp b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/quality_benchmark.cpp new file mode 100644 index 000000000000..f8e54c488a49 --- /dev/null +++ b/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/quality_benchmark.cpp @@ -0,0 +1,271 @@ +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = Kernel::Point_3; +using Vector_3 = Kernel::Vector_3; +using Triangle_3 = Kernel::Triangle_3; +using FT = Kernel::FT; + +using Mesh = CGAL::Surface_mesh; +using face_descriptor = boost::graph_traits::face_descriptor; + +using Oracle = CGAL::Alpha_wraps_3::internal::Triangle_mesh_oracle; +using Dt = CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3::Triangulation; + +namespace PMP = CGAL::Polygon_mesh_processing; + +std::array triangle_angles(const Triangle_3& tr) +{ + FT sq_a = CGAL::squared_distance(tr[0], tr[1]); + FT sq_b = CGAL::squared_distance(tr[1], tr[2]); + FT sq_c = CGAL::squared_distance(tr[2], tr[0]); + + FT two_ab = 2. * CGAL::sqrt(sq_a) * CGAL::sqrt(sq_b); + FT two_bc = 2. * CGAL::sqrt(sq_b) * CGAL::sqrt(sq_c); + FT two_ca = 2. * CGAL::sqrt(sq_c) * CGAL::sqrt(sq_a); + + FT angle_a = (sq_b + sq_c - sq_a) / two_bc; + FT angle_b = (sq_c + sq_a - sq_b) / two_ca; + FT angle_c = (sq_a + sq_b - sq_c) / two_ab; + if(angle_a < -1.) angle_a = -1.; + if(angle_b < -1.) angle_b = -1.; + if(angle_c < -1.) angle_c = -1.; + if(angle_a > 1.) angle_a = 1.; + if(angle_b > 1.) angle_b = 1.; + if(angle_c > 1.) angle_c = 1.; + angle_a = std::acos(angle_a); + angle_b = std::acos(angle_b); + angle_c = std::acos(angle_c); + + return {angle_a, angle_b, angle_c}; +} + +bool is_almost_degenerate(const Triangle_3& tr, + double threshold) +{ + FT sq_area = tr.squared_area(); + return (CGAL::sqrt(CGAL::to_double(sq_area)) < threshold); +} + +auto surface_mesh_face_to_triangle(const face_descriptor fd, + const Mesh& sm) +{ + typename boost::graph_traits::halfedge_descriptor hd = halfedge(fd,sm); + return Triangle_3(sm.point(target(hd,sm)), + sm.point(target(next(hd,sm),sm)), + sm.point(target(next(next(hd,sm),sm),sm))); +} + +double mean_min_angle(const Mesh& mesh) +{ + double mean_min_angle = 0.; + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + std::array angles = triangle_angles(tr); + + FT min_angle = std::min({angles[0], angles[1], angles[2]}); + + min_angle = min_angle * (180.0 / CGAL_PI); + mean_min_angle += min_angle; + } + + mean_min_angle /= static_cast(mesh.number_of_faces()); + return mean_min_angle; +} + +double mean_max_angle(const Mesh& mesh) +{ + double mean_max_angle = 0.; + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + std::array angles = triangle_angles(tr); + + FT max_angle = std::max({angles[0], angles[1], angles[2]}); + + max_angle = max_angle * (180.0 / CGAL_PI); + mean_max_angle += max_angle; + } + + mean_max_angle /= static_cast(mesh.number_of_faces()); + return mean_max_angle; +} + +double mean_radius_ratio(const Mesh& mesh, + double degenerate_threshold) +{ + double mean_radius_ratio = 0.; + size_t num_almost_degenerate_tri = 0; + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + if(is_almost_degenerate(tr, degenerate_threshold)) + { + ++num_almost_degenerate_tri; + continue; + } + + FT circumsphere_radius = std::sqrt(CGAL::squared_radius(tr[0], tr[1], tr[2])); + + FT a = std::sqrt(CGAL::squared_distance(tr[0], tr[1])); + FT b = std::sqrt(CGAL::squared_distance(tr[1], tr[2])); + FT c = std::sqrt(CGAL::squared_distance(tr[2], tr[0])); + FT s = 0.5 * (a + b + c); + FT inscribed_radius = std::sqrt((s * (s - a) * (s - b) * (s - c)) / s); + FT radius_ratio = circumsphere_radius / inscribed_radius; + radius_ratio /= 2.; // normalized + mean_radius_ratio += radius_ratio; + } + + mean_radius_ratio /= static_cast(mesh.number_of_faces() - num_almost_degenerate_tri); + return mean_radius_ratio; +} + +double mean_edge_ratio(const Mesh& mesh, + double degenerate_threshold) +{ + double mean_edge_ratio = 0.; + size_t num_almost_degenerate_tri = 0; + + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + if(is_almost_degenerate(tr, degenerate_threshold)) + { + ++num_almost_degenerate_tri; + continue; + } + + FT a = std::sqrt(CGAL::squared_distance(tr[0], tr[1])); + FT b = std::sqrt(CGAL::squared_distance(tr[1], tr[2])); + FT c = std::sqrt(CGAL::squared_distance(tr[2], tr[0])); + FT min_edge = std::min({a, b, c}); + FT max_edge = std::max({a, b, c}); + FT edge_ratio = max_edge / min_edge; + + mean_edge_ratio += edge_ratio; + } + + mean_edge_ratio /= static_cast(mesh.number_of_faces() - num_almost_degenerate_tri); + return mean_edge_ratio; +} + +double mean_aspect_ratio(const Mesh& mesh, + double degenerate_threshold) +{ + double mean_aspect_ratio = 0.; + size_t num_almost_degenerate_tri = 0; + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + if(is_almost_degenerate(tr, degenerate_threshold)) + { + ++num_almost_degenerate_tri; + continue; + } + + FT a = std::sqrt(CGAL::squared_distance(tr[0], tr[1])); + FT b = std::sqrt(CGAL::squared_distance(tr[1], tr[2])); + FT c = std::sqrt(CGAL::squared_distance(tr[2], tr[0])); + FT s = 0.5 * (a + b + c); + FT inscribed_radius = std::sqrt((s * (s - a) * (s - b) * (s - c)) / s); + FT max_edge = std::max({a, b, c}); + FT aspect_ratio = max_edge / inscribed_radius; + aspect_ratio /= (2. * std::sqrt(3.)); // normalized + mean_aspect_ratio += aspect_ratio; + } + + mean_aspect_ratio /= static_cast(mesh.number_of_faces() - num_almost_degenerate_tri); + return mean_aspect_ratio; +} + +size_t num_almost_degenerate_tri(const Mesh& mesh, + double degenerate_threshold) +{ + size_t num_almost_degenerate_tri = 0; + for(const face_descriptor f : faces(mesh)) + { + const Triangle_3 tr = surface_mesh_face_to_triangle(f, mesh); + if(is_almost_degenerate(tr, degenerate_threshold)) + { + ++num_almost_degenerate_tri; + } + } + return num_almost_degenerate_tri; +} + +int main(int argc, char** argv) +{ + const int argc_check = argc - 1; + char *entry_name_ptr = nullptr; + double relative_alpha_ratio = 20.; + double relative_offset_ratio = 600.; + + for(int i=1; i +#include + +#include +#include + +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = Kernel::Point_3; +using Mesh = CGAL::Surface_mesh; + +namespace CGAL { +namespace Alpha_wraps_3 { +namespace internal { +namespace { + +enum Robustness_benchmark_exit_code +{ + // Success + VALID_SOLID_OUTPUT = 0, + + // Failure + INPUT_IS_INVALID = 1, + OUTPUT_IS_NOT_TRIANGLE_MESH = 2, + OUTPUT_IS_COMBINATORIAL_NON_MANIFOLD = 3, + OUTPUT_HAS_BORDERS = 4, + OUTPUT_HAS_DEGENERATED_FACES = 5, + OUTPUT_HAS_GEOMETRIC_SELF_INTERSECTIONS = 6, + OUTPUT_DOES_NOT_BOUND_VOLUME = 7, + OUTPUT_DOES_NOT_CONTAIN_INPUT = 8, + OUTPUT_DISTANCE_IS_TOO_LARGE = 9, +}; + +} // namespace +} // namespace internal +} // namespace Alpha_wraps_3 +} // namespace CGAL + +namespace PMP = CGAL::Polygon_mesh_processing; +namespace AW3i = CGAL::Alpha_wraps_3::internal; + +int main(int argc, char** argv) +{ + const int argc_check = argc - 1; + char* entry_name_ptr = nullptr; + double relative_alpha_ratio = 20.; + double relative_offset_ratio = 600.; + + for(int i=1; i $2/Robustness/results/$5/$filename.log + + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/compute_performance_benchmark_data.py \ + -e $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/build-release/performance_benchmark -i $6 -a $3 \ + > $2/Performance/results/$5/$filename.log + + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/compute_quality_benchmark_data.py \ + -e $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/build-release/quality_benchmark -i $6 -a $3 \ + > $2/Quality/results/$5/$filename.log +} +export -f compute_benchmark_data + +# $1: directory containing the alpha wrap project +# $2: directory containing the input data folder +# $3: directory containing the output results +# $4: alpha value +# $5: timeout value for robustness benchmark in seconds +# $6: number of virtual thread used +# $7: hash of the latest commit +# $8: hash of a commit to perform the diff with latest +cd $1 + +mkdir -p $3/Robustness/results/$7 +mkdir -p $3/Performance/results/$7 +mkdir -p $3/Quality/results/$7 +mkdir -p $3/Robustness/charts_data +mkdir -p $3/Performance/charts_data +mkdir -p $3/Quality/charts_data +mkdir -p $3/Robustness/charts +mkdir -p $3/Performance/charts +mkdir -p $3/Quality/charts +mkdir -p $3/Robustness/log +mkdir -p $3/Performance/log +mkdir -p $3/Quality/log +mkdir -p $3/charts + +find $2 -mindepth 1 | parallel -j$6 compute_benchmark_data $1 $3 $4 $5 $7 ::: + +python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Robustness/generate_robustness_benchmark_charts.py -i $3/Robustness/results/$7 -o $3/Robustness -a $4 -c $7 + +if [ -z "$8" ]; then + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/generate_performance_benchmark_charts.py -i $3/Performance/results/$7 -o $3/Performance -a $4 -c $7; +else + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Performance/generate_performance_benchmark_charts.py -i $3/Performance/results/$7 -o $3/Performance -a $4 -c $7 -p $3/Performance/results/$8 -d $8; +fi + +if [ -z "$8" ]; then + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/generate_quality_benchmark_charts.py -i $3/Quality/results/$7 -o $3/Quality -a $4 -c $7; +else + python3 $1/Alpha_wrap_3/benchmark/Alpha_wrap_3/Quality/generate_quality_benchmark_charts.py -i $3/Quality/results/$7 -o $3/Quality -a $4 -c $7 -p $3/Quality/results/$8 -d $8; +fi + +charts_path="$(ls "$3/Robustness/charts"/* -dArt | tail -n 1) $(ls "$3/Performance/charts"/* -dArt | tail -n 2) $(ls "$3/Quality/charts"/* -dArt | tail -n 9)" + +pdfjam --nup 2x6 $charts_path --outfile $3/charts/results_$7_$8_alpha_$4_$(date '+%Y-%m-%d_%H:%M:%S').pdf diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/CMakeLists.txt b/Alpha_wrap_3/examples/Alpha_wrap_3/CMakeLists.txt index 0be54f87eed0..40187ca194cb 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/CMakeLists.txt +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/CMakeLists.txt @@ -13,3 +13,5 @@ create_single_source_cgal_program("point_set_wrap.cpp") create_single_source_cgal_program("wrap_from_cavity.cpp") create_single_source_cgal_program("mixed_inputs_wrap.cpp") create_single_source_cgal_program("volumetric_wrap.cpp") +create_single_source_cgal_program("successive_wraps.cpp") +create_single_source_cgal_program("pause_and_resume_wrapping.cpp") diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/mixed_inputs_wrap.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/mixed_inputs_wrap.cpp index ffcedc7f1cbb..90b488e0407c 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/mixed_inputs_wrap.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/mixed_inputs_wrap.cpp @@ -103,10 +103,10 @@ int main(int argc, char** argv) oracle.add_segment_soup(segments, CGAL::parameters::default_values()); oracle.add_point_set(ps_points, CGAL::parameters::default_values()); - CGAL::Alpha_wraps_3::internal::Alpha_wrap_3 aw3(oracle); + CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3 aw3(oracle); - Mesh output_mesh; - aw3(alpha, offset, output_mesh); + Mesh wrap; + aw3(alpha, offset, wrap); t.stop(); std::cout << "Took " << t.time() << std::endl; @@ -120,10 +120,11 @@ int main(int argc, char** argv) std::string ps_name = std::string(ps_filename); ps_name = ps_name.substr(ps_name.find_last_of("/") + 1, ps_name.length() - 1); ps_name = ps_name.substr(0, ps_name.find_last_of(".")); - std::string output_name = ts_name + "_" + ss_name + "_" + ps_name + "_" + std::to_string(static_cast(relative_alpha)) - + "_" + std::to_string(static_cast(relative_offset)) + ".off"; + std::string output_name = ts_name + "_" + ss_name + "_" + ps_name + "_" + + std::to_string(static_cast(relative_alpha)) + "_" + + std::to_string(static_cast(relative_offset)) + ".off"; std::cout << "Writing to " << output_name << std::endl; - CGAL::IO::write_polygon_mesh(output_name, output_mesh, CGAL::parameters::stream_precision(17)); + CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17)); return EXIT_SUCCESS; } diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/output_helper.h b/Alpha_wrap_3/examples/Alpha_wrap_3/output_helper.h new file mode 100644 index 000000000000..581ac82bff02 --- /dev/null +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/output_helper.h @@ -0,0 +1,19 @@ +#ifndef CGAL_ALPHA_WRAP_3_EXAMPLES_OUTPUT_HELPER_H +#define CGAL_ALPHA_WRAP_3_EXAMPLES_OUTPUT_HELPER_H + +#include + +std::string generate_output_name(std::string input_name, + const double alpha, + const double offset) +{ + input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1); + input_name = input_name.substr(0, input_name.find_last_of(".")); + std::string output_name = input_name + + "_" + std::to_string(static_cast(alpha)) + + "_" + std::to_string(static_cast(offset)) + ".off"; + + return output_name; +} + +#endif // CGAL_ALPHA_WRAP_3_EXAMPLES_OUTPUT_HELPER_H diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/pause_and_resume_wrapping.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/pause_and_resume_wrapping.cpp new file mode 100644 index 000000000000..cee169048c32 --- /dev/null +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/pause_and_resume_wrapping.cpp @@ -0,0 +1,164 @@ +// This example demonstrates how to interrupt the wrapping process before it has terminated, +// and how to resume afterwards. +// +// -------------------------------- !! Warning !! -------------------------------------------------- +// By default, the wrapper uses an unsorted LIFO queue of faces to refine. This means that +// the intermediate result is not very useful because the algorithm carves deep and not wide +// (somewhat like a DFS vs a BFS). +// +// The sorted queue option is enabled with the macro below to make the refinement algorithm +// more uniform. The downside is that it is slower. +// ------------------------------------------------------------------------------------------------- +#define CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + +#include "output_helper.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace AW3 = CGAL::Alpha_wraps_3; +namespace PMP = CGAL::Polygon_mesh_processing; + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = K::Point_3; + +using Points = std::vector; +using Face = std::array; +using Faces = std::vector; + +using Mesh = CGAL::Surface_mesh; +using face_descriptor = boost::graph_traits::face_descriptor; + +struct Interrupter_visitor + : public AW3::internal::Wrapping_default_visitor +{ + using Base = AW3::internal::Wrapping_default_visitor; + + CGAL::Real_timer timer; + double max_time = -1; // in seconds + +public: + void set_max_time(double t) { max_time = t; } + +public: + template + void on_flood_fill_begin(const AlphaWrapper&) + { + std::cout << "Starting timer..." << std::endl; + timer.start(); + } + + template + bool go_further(const Wrapper&) + { + if(timer.time() > max_time) + { + timer.stop(); + std::cout << "Paused after " << timer.time() << " s." << std::endl; + return false; + } + + return true; + } +}; + +int main(int argc, char** argv) +{ + std::cout.precision(17); + std::cerr.precision(17); + + CGAL::Random rng; + std::cout << "Random seed = " << rng.get_seed() << std::endl; + + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/armadillo.off"); + + // = read the soup + Points points; + Faces faces; + if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty()) + { + std::cerr << "Invalid soup input: " << filename << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Input: " << points.size() << " points, " << faces.size() << " faces" << std::endl; + + // Compute the alpha and offset values + const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : rng.get_double(150., 200.); + const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.; + std::cout << "relative_alpha = " << relative_alpha << std::endl; + + CGAL::Bbox_3 bbox; + for(const Point_3& p : points) + bbox += p.bbox(); + + const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) + + CGAL::square(bbox.ymax() - bbox.ymin()) + + CGAL::square(bbox.zmax() - bbox.zmin())); + + const double alpha = diag_length / relative_alpha; + const double offset = diag_length / relative_offset; + + // Build the wrapper + using Oracle = CGAL::Alpha_wraps_3::internal::Triangle_soup_oracle; + Oracle oracle(alpha); + oracle.add_triangle_soup(points, faces, CGAL::parameters::default_values()); + CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3 aw3(oracle); + + // --- Launch the wrapping, and pause when the algorithm has spent 1s flooding + Interrupter_visitor interrupter; + interrupter.set_max_time(1.); + + Mesh wrap; + aw3(alpha, offset, wrap, CGAL::parameters::visitor(interrupter)); + std::cout << ">>> The current wrap has " << num_vertices(wrap) << " vertices" << std::endl; + CGAL::IO::write_polygon_mesh("stopped_1.off", wrap, CGAL::parameters::stream_precision(17)); + + // --- Restart from the previous state, and pause a bit further + interrupter.set_max_time(2.); + aw3(alpha, offset, wrap, CGAL::parameters::visitor(interrupter) + .refine_triangulation(true)); + std::cout << ">>> The current wrap has " << num_vertices(wrap) << " vertices" << std::endl; + CGAL::IO::write_polygon_mesh("stopped_2.off", wrap, CGAL::parameters::stream_precision(17)); + + // --- Restart from the previous state, and let it finish + aw3(alpha, offset, wrap, CGAL::parameters::refine_triangulation(true)); + std::cout << ">>> The final (resumed) wrap has " << num_vertices(wrap) << " vertices" << std::endl; + std::string output_name = generate_output_name(filename, relative_alpha, relative_offset); + std::cout << "Writing to " << "resumed_" + output_name << std::endl; + CGAL::IO::write_polygon_mesh("resumed_" + output_name, wrap, CGAL::parameters::stream_precision(17)); + + // --- Get the final wrap, in one go: + Mesh single_pass_wrap; + CGAL::alpha_wrap_3(points, faces, alpha, offset, single_pass_wrap); + std::cout << ">>> The final (from scratch) wrap has " << num_vertices(single_pass_wrap) << " vertices" << std::endl; + + output_name = generate_output_name(filename, relative_alpha, relative_offset); + std::cout << "Writing to " << output_name << std::endl; + CGAL::IO::write_polygon_mesh(output_name, single_pass_wrap, CGAL::parameters::stream_precision(17)); + + // --- Compare the results to ensure both approaches yield identical meshes + std::vector > common; + std::vector m1_only; + std::vector m2_only; + PMP::match_faces(wrap, single_pass_wrap, + std::back_inserter(common), + std::back_inserter(m1_only), + std::back_inserter(m2_only)); + if(!m1_only.empty() || !m2_only.empty()) + { + std::cerr << "Error: The two wraps should have been identical!" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/point_set_wrap.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/point_set_wrap.cpp index 8742a2a20011..a602cf5c58bf 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/point_set_wrap.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/point_set_wrap.cpp @@ -1,3 +1,5 @@ +#include "output_helper.h" + #include #include @@ -23,7 +25,7 @@ int main(int argc, char** argv) Point_container points; if(!CGAL::IO::read_points(filename, std::back_inserter(points)) || points.empty()) { - std::cerr << "Invalid input." << std::endl; + std::cerr << "Invalid input:" << filename << std::endl; return EXIT_FAILURE; } @@ -53,11 +55,7 @@ int main(int argc, char** argv) std::cout << "Took " << t.time() << " s." << std::endl; // Save the result - std::string input_name = std::string(filename); - input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1); - input_name = input_name.substr(0, input_name.find_last_of(".")); - std::string output_name = input_name + "_" + std::to_string(static_cast(relative_alpha)) - + "_" + std::to_string(static_cast(relative_offset)) + ".off"; + const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset); std::cout << "Writing to " << output_name << std::endl; CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17)); diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/successive_wraps.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/successive_wraps.cpp new file mode 100644 index 000000000000..301aec46e2d5 --- /dev/null +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/successive_wraps.cpp @@ -0,0 +1,135 @@ +// In this example, we reuse the underlying triangulation of the previous state, and carve using +// a new (smaller) alpha value. This enables considerable speed-up: the cumulated time taken +// to run `n` successive instances of `{alpha_wrap(alpha_i)}_(i=1...n)` will be roughly equal +// to the time taken to the single instance of alpha_wrap(alpha_n) from scratch. +// +// The speed-up increases with the number of intermediate results, and on the gap between +// alpha values: if alpha_2 is close to alpha_1, practically no new computation are required, +// and the speed-up is almost 100%. +// +// -------------------------------- !! Warning !! -------------------------------------------------- +// The result of: +// > alpha_wrap(alpha_1, ...) +// > alpha_wrap(alpha_2, ..., reuse) +// is not exactly identical to calling directly: +// > alpha_wrap(alpha_2, ..., do_not_reuse) +// because the queues are sorted slightly differently and the AABB tree is rebuilt differently +// to optimize the runtime. +// ------------------------------------------------------------------------------------------------- + +#include "output_helper.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace PMP = CGAL::Polygon_mesh_processing; + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = K::FT; +using Point_3 = K::Point_3; + +using Mesh = CGAL::Surface_mesh; + +int main(int argc, char** argv) +{ + std::cout.precision(17); + std::cerr.precision(17); + + // Read the input + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cube.off"); + std::cout << "Reading " << filename << "..." << std::endl; + + Mesh mesh; + if(!PMP::IO::read_polygon_mesh(filename, mesh) || is_empty(mesh) || !is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input:" << filename << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Input: " << num_vertices(mesh) << " vertices, " << num_faces(mesh) << " faces" << std::endl; + + const CGAL::Bbox_3 bbox = CGAL::Polygon_mesh_processing::bbox(mesh); + const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) + + CGAL::square(bbox.ymax() - bbox.ymin()) + + CGAL::square(bbox.zmax() - bbox.zmin())); + + // We want decreasing alphas, and these are relative ratios, so they need to be increasing + const std::vector relative_alphas = { 1, 50, 100, 150, 200, 250 }; + const FT relative_offset = 600; + + // =============================================================================================== + // Naive approach: + + CGAL::Real_timer t; + double total_time = 0.; + + for(std::size_t i=0; i>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl; + + Mesh wrap; + CGAL::alpha_wrap_3(mesh, alpha, offset, wrap); + + t.stop(); + std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl; + std::cout << " Elapsed time: " << t.time() << " s." << std::endl; + + total_time += t.time(); + } + + std::cout << "Total elapsed time (naive): " << total_time << " s.\n" << std::endl; + + // =============================================================================================== + // Re-use approach + + total_time = 0.; + t.reset(); + + using Oracle = CGAL::Alpha_wraps_3::internal::Triangle_mesh_oracle; + using Wrapper = CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3; + Wrapper wrapper; // contains the triangulation that is being refined iteratively + + for(std::size_t i=0; i>> [" << i << "] alpha: " << alpha << " offset: " << offset << std::endl; + + // The triangle mesh oracle should be initialized with alpha to internally perform a split + // of too-big facets while building the AABB Tree. This split in fact yields a significant + // speed-up for meshes with elements that are large compared to alpha. This speed-up makes it + // faster to re-build the AABB tree for every value of alpha than to use a non-optimized tree. + Oracle oracle(alpha); + oracle.add_triangle_mesh(mesh, CGAL::parameters::default_values()); + wrapper.oracle() = oracle; + + Mesh wrap; + wrapper(alpha, offset, wrap, CGAL::parameters::refine_triangulation((i != 0))); + + t.stop(); + std::cout << " Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl; + std::cout << " Elapsed time: " << t.time() << " s." << std::endl; + + total_time += t.time(); + } + + std::cout << "Total elapsed time (successive): " << total_time << " s." << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_mesh_wrap.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_mesh_wrap.cpp index 00e2e4fd9fc5..d49534904461 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_mesh_wrap.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_mesh_wrap.cpp @@ -1,3 +1,5 @@ +#include "output_helper.h" + #include #include @@ -25,7 +27,7 @@ int main(int argc, char** argv) Mesh mesh; if(!PMP::IO::read_polygon_mesh(filename, mesh) || is_empty(mesh) || !is_triangle_mesh(mesh)) { - std::cerr << "Invalid input." << std::endl; + std::cerr << "Invalid input:" << filename << std::endl; return EXIT_FAILURE; } @@ -56,12 +58,7 @@ int main(int argc, char** argv) std::cout << "Took " << t.time() << " s." << std::endl; // Save the result - std::string input_name = std::string(filename); - input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1); - input_name = input_name.substr(0, input_name.find_last_of(".")); - std::string output_name = input_name - + "_" + std::to_string(static_cast(relative_alpha)) - + "_" + std::to_string(static_cast(relative_offset)) + ".off"; + const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset); std::cout << "Writing to " << output_name << std::endl; CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17)); diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_soup_wrap.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_soup_wrap.cpp index 626e3bdc3ba4..51e04974c28a 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_soup_wrap.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/triangle_soup_wrap.cpp @@ -1,3 +1,5 @@ +#include "output_helper.h" + #include #include @@ -30,7 +32,7 @@ int main(int argc, char** argv) std::vector > faces; if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty()) { - std::cerr << "Invalid input." << std::endl; + std::cerr << "Invalid input:" << filename << std::endl; return EXIT_FAILURE; } @@ -63,12 +65,7 @@ int main(int argc, char** argv) std::cout << "Took " << t.time() << " s." << std::endl; // Save the result - std::string input_name = std::string(filename); - input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1); - input_name = input_name.substr(0, input_name.find_last_of(".")); - std::string output_name = input_name - + "_" + std::to_string(static_cast(relative_alpha)) - + "_" + std::to_string(static_cast(relative_offset)) + ".off"; + const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset); std::cout << "Writing to " << output_name << std::endl; CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17)); diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/volumetric_wrap.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/volumetric_wrap.cpp index 113215b631a1..ddc6f765612b 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/volumetric_wrap.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/volumetric_wrap.cpp @@ -1,3 +1,5 @@ +#include "output_helper.h" + #include #include @@ -70,7 +72,7 @@ int main(int argc, char** argv) Faces faces; if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty()) { - std::cerr << "Invalid input." << std::endl; + std::cerr << "Invalid input:" << filename << std::endl; return EXIT_FAILURE; } @@ -101,7 +103,7 @@ int main(int argc, char** argv) Oracle oracle(K{}); oracle.add_triangle_soup(points, faces, CGAL::parameters::default_values()); - CGAL::Alpha_wraps_3::internal::Alpha_wrap_3 aw3(oracle); + CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3 aw3(oracle); Mesh wrap; aw3(alpha, offset, wrap); @@ -113,12 +115,7 @@ int main(int argc, char** argv) auto dt = aw3.triangulation(); // Save the result - std::string input_name = std::string(filename); - input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1); - input_name = input_name.substr(0, input_name.find_last_of(".")); - std::string output_name = input_name - + "_" + std::to_string(static_cast(relative_alpha)) - + "_" + std::to_string(static_cast(relative_offset)) + ".off"; + const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset); std::cout << "Writing to " << output_name << std::endl; CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17)); diff --git a/Alpha_wrap_3/examples/Alpha_wrap_3/wrap_from_cavity.cpp b/Alpha_wrap_3/examples/Alpha_wrap_3/wrap_from_cavity.cpp index 1c1d6c7b3d72..970bb583484f 100644 --- a/Alpha_wrap_3/examples/Alpha_wrap_3/wrap_from_cavity.cpp +++ b/Alpha_wrap_3/examples/Alpha_wrap_3/wrap_from_cavity.cpp @@ -1,3 +1,5 @@ +#include "output_helper.h" + #include #include @@ -25,13 +27,13 @@ int main(int argc, char** argv) if(!PMP::IO::read_polygon_mesh(filename, input) || is_empty(input) || !is_triangle_mesh(input)) { - std::cerr << "Invalid input." << std::endl; + std::cerr << "Invalid input:" << filename << std::endl; return EXIT_FAILURE; } std::cout << "Input: " << num_vertices(input) << " vertices, " << num_faces(input) << " faces" << std::endl; - const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 30.; + const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 40.; const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.; // Compute the alpha and offset values diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h index 04b30cf76783..b886add0ff6c 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_3.h @@ -49,7 +49,9 @@ #include #include #include -#include +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + #include +#endif #include #include #include @@ -92,6 +94,10 @@ struct Wrapping_default_visitor template void on_flood_fill_begin(const AlphaWrapper&) { } + // Whether the flood filling process should continue + template + constexpr bool go_further(const Wrapper&) { return true; } + template void before_facet_treatment(const AlphaWrapper&, const Gate&) { } @@ -110,7 +116,7 @@ struct Wrapping_default_visitor template -class Alpha_wrap_3 +class Alpha_wrapper_3 { using Oracle = Oracle_; @@ -120,22 +126,36 @@ class Alpha_wrap_3 using Default_Vb = Alpha_wrap_triangulation_vertex_base_3; using Default_Cb = Alpha_wrap_triangulation_cell_base_3; - using Default_Cbt = Cell_base_with_timestamp; // determinism + using Default_Cbt = Cell_base_with_timestamp; // for determinism using Default_Tds = CGAL::Triangulation_data_structure_3; using Default_Triangulation = CGAL::Delaunay_triangulation_3; +public: using Triangulation = typename Default::Get::type; + // Use the geom traits from the triangulation, and trust the (advanced) user that provided it + using Geom_traits = typename Triangulation::Geom_traits; + +private: using Cell_handle = typename Triangulation::Cell_handle; using Facet = typename Triangulation::Facet; using Vertex_handle = typename Triangulation::Vertex_handle; using Locate_type = typename Triangulation::Locate_type; using Gate = internal::Gate; - using Alpha_PQ = Modifiable_priority_queue, CGAL_BOOST_PAIRING_HEAP>; - // Use the geom traits from the triangulation, and trust the (advanced) user that provided it - using Geom_traits = typename Triangulation::Geom_traits; + // A sorted queue is a priority queue sorted by circumradius, and is experimentally significantly + // slower. However, intermediate results are usable: at each point of the algorithm, the wrap + // has a somewhat uniform mesh element size. + // + // An unsorted queue is a LIFO queue, and is experimentally much faster (~35%), + // but intermediate results are not useful: a LIFO carves deep before than wide, + // and can thus for example leave scaffolding faces till almost the end of the refinement. +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + using Alpha_PQ = Modifiable_priority_queue, CGAL_BOOST_PAIRING_HEAP>; +#else + using Alpha_PQ = std::stack; +#endif using FT = typename Geom_traits::FT; using Point_3 = typename Geom_traits::Point_3; @@ -149,33 +169,48 @@ class Alpha_wrap_3 using SC_Iso_cuboid_3 = SC::Iso_cuboid_3; using SC2GT = Cartesian_converter; + using Seeds = std::vector; protected: - const Oracle m_oracle; + Oracle m_oracle; SC_Iso_cuboid_3 m_bbox; - FT m_alpha, m_sq_alpha; - FT m_offset, m_sq_offset; + FT m_alpha = FT(-1), m_sq_alpha = FT(-1); + FT m_offset = FT(-1), m_sq_offset = FT(-1); + + Seeds m_seeds; Triangulation m_tr; + Alpha_PQ m_queue; public: - // Main constructor - Alpha_wrap_3(const Oracle& oracle) - : - m_oracle(oracle), - m_tr(Geom_traits(oracle.geom_traits())), - // used to set up the initial MPQ, use some arbitrary not-too-small value - m_queue(4096) + Alpha_wrapper_3() +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + // '4096' is an arbitrary, not-too-small value for the largest ID in queue initialization + : m_queue(4096) +#endif { // Due to the Steiner point computation being a dichotomy, the algorithm is inherently inexact // and passing exact kernels is explicitly disabled to ensure no misunderstanding. static_assert(std::is_floating_point::value); } + Alpha_wrapper_3(const Oracle& oracle) + : + m_oracle(oracle), + m_tr(Geom_traits(oracle.geom_traits())) +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + , m_queue(4096) +#endif + { + static_assert(std::is_floating_point::value); + } + public: const Geom_traits& geom_traits() const { return m_tr.geom_traits(); } + Oracle& oracle() { return m_oracle; } + const Oracle& oracle() const { return m_oracle; } Triangulation& triangulation() { return m_tr; } const Triangulation& triangulation() const { return m_tr; } const Alpha_PQ& queue() const { return m_queue; } @@ -222,26 +257,53 @@ class Alpha_wrap_3 using parameters::get_parameter_reference; using parameters::choose_parameter; + // using OVPM = typename CGAL::GetVertexPointMap::type; OVPM ovpm = choose_parameter(get_parameter(out_np, internal_np::vertex_point), get_property_map(vertex_point, output_mesh)); - typedef typename internal_np::Lookup_named_param_def < - internal_np::visitor_t, - InputNamedParameters, - Wrapping_default_visitor // default - >::reference Visitor; + // + using Visitor = typename internal_np::Lookup_named_param_def< + internal_np::visitor_t, + InputNamedParameters, + Wrapping_default_visitor // default + >::reference; Wrapping_default_visitor default_visitor; Visitor visitor = choose_parameter(get_parameter_reference(in_np, internal_np::visitor), default_visitor); - std::vector no_seeds; - using Seeds = typename internal_np::Lookup_named_param_def< - internal_np::seed_points_t, InputNamedParameters, std::vector >::reference; - Seeds seeds = choose_parameter(get_parameter_reference(in_np, internal_np::seed_points), no_seeds); + // Points used to create initial cavities + m_seeds = choose_parameter(get_parameter_reference(in_np, internal_np::seed_points), Seeds()); + // Whether or not some cells should be reflagged as "inside" after the refinement+carving loop + // as ended, as to ensure that the result is not only combinatorially manifold, but also + // geometrically manifold. + // + // -- Warning -- + // Manifoldness postprocessing will be performed even if the wrapping is interrupted (and + // this option is enabled). const bool do_enforce_manifoldness = choose_parameter(get_parameter(in_np, internal_np::do_enforce_manifoldness), true); + // Whether to keep pockets of "outside" cells that are not connected to the exterior (or to the + // initial cavities, if used). + // + // -- Warning -- + // Pockets of "outside" cells will be purged even if the wrapping is interrupted (and + // this option is enabled). + const bool keep_inner_ccs = choose_parameter(get_parameter(in_np, internal_np::keep_inner_connected_components), false); + + // This parameter enables avoiding recomputing the triangulation from scratch when wrapping + // the same input for multiple values of alpha (and typically the same offset values). + // + // -- Warning -- + // If this is enabled, the 3D triangulation will NOT be re-initialized at launch. + // This means that the triangulation is NOT cleared, even if: + // - you use an alpha value that is greater than what was used in a previous run; you will + // obtain the same result as the last run. + // - you use a different offset value between runs, you might then get points that are not + // on the offset surface corresponding to that corresponding to the latter offset value. + const bool refining = choose_parameter(get_parameter(in_np, internal_np::refine_triangulation), false); + #ifdef CGAL_AW3_TIMER CGAL::Real_timer t; t.start(); @@ -249,76 +311,59 @@ class Alpha_wrap_3 visitor.on_alpha_wrapping_begin(*this); - if(!initialize(alpha, offset, seeds)) + if(!initialize(alpha, offset, refining)) return; -#ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP - extract_surface(output_mesh, ovpm, true /*tolerate non manifoldness*/); - CGAL::IO::write_polygon_mesh("initial_cavities.off", output_mesh, - CGAL::parameters::vertex_point_map(ovpm).stream_precision(17)); -#endif - - alpha_flood_fill(visitor); - #ifdef CGAL_AW3_TIMER t.stop(); - std::cout << "Flood filling took: " << t.time() << " s." << std::endl; + std::cout << "Initialization took: " << t.time() << " s." << std::endl; + t.reset(); + t.start(); #endif - if(do_enforce_manifoldness) - { -#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS - std::cout << "> Make manifold..." << std::endl; - - extract_surface(output_mesh, ovpm, true /*tolerate non manifoldness*/); - -#ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP - dump_triangulation_faces("intermediate_dt3.off", false /*only_boundary_faces*/); - IO::write_polygon_mesh("intermediate.off", output_mesh, - CGAL::parameters::vertex_point_map(ovpm).stream_precision(17)); +#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + dump_triangulation_faces("starting_wrap.off", true /*only_boundary_faces*/); #endif - FT base_vol = 0; - if(is_closed(output_mesh)) // might not be due to manifoldness - base_vol = PMP::volume(output_mesh, CGAL::parameters::vertex_point_map(ovpm)); - else - std::cerr << "Warning: couldn't compute volume before manifoldness fixes (mesh is not closed)" << std::endl; + alpha_flood_fill(visitor); + +#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + dump_triangulation_faces("flood_filled_wrap.off", true /*only_boundary_faces*/); #endif #ifdef CGAL_AW3_TIMER + t.stop(); + std::cout << "Flood filling took: " << t.time() << " s." << std::endl; t.reset(); t.start(); #endif + if(do_enforce_manifoldness) + { make_manifold(); +#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + dump_triangulation_faces("manifold_wrap.off", true /*only_boundary_faces*/); +#endif + #ifdef CGAL_AW3_TIMER t.stop(); - std::cout << "\nManifoldness post-processing took: " << t.time() << " s." << std::endl; + std::cout << "Manifoldness post-processing took: " << t.time() << " s." << std::endl; + t.reset(); + t.start(); #endif + } -#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS - if(!is_zero(base_vol)) - { - extract_surface(output_mesh, ovpm, false /*do not tolerate non-manifoldness*/); - - const FT manifold_vol = PMP::volume(output_mesh, CGAL::parameters::vertex_point_map(ovpm)); - const FT ratio = manifold_vol / base_vol; - - std::cout << "Volumes post-manifoldness fix:\n" - << "before: " << base_vol << "\n" - << "after: " << manifold_vol << "\n" - << "ratio: " << ratio << std::endl; - if(ratio > 1.1) // more than 10% extra volume - std::cerr << "Warning: large increase of volume after manifoldness resolution" << std::endl; - } -#endif - } // do_enforce_manifoldness + if(!keep_inner_ccs) + { + // We could purge *before* manifold enforcement, but making the mesh manifold is + // very cheap in most cases, so it is better to keep the code simpler. + purge_inner_connected_components(); -#ifdef CGAL_AW3_TIMER - t.reset(); - t.start(); +#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + dump_triangulation_faces("purged_wrap.off", true /*only_boundary_faces*/); #endif + } extract_surface(output_mesh, ovpm, !do_enforce_manifoldness); @@ -331,9 +376,9 @@ class Alpha_wrap_3 std::cout << "Alpha wrap vertices: " << vertices(output_mesh).size() << std::endl; std::cout << "Alpha wrap faces: " << faces(output_mesh).size() << std::endl; - #ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP - IO::write_polygon_mesh("final.off", output_mesh, CGAL::parameters::stream_precision(17)); - dump_triangulation_faces("final_dt3.off", false /*only_boundary_faces*/); + #ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + IO::write_polygon_mesh("final_wrap.off", output_mesh, CGAL::parameters::stream_precision(17)); + dump_triangulation_faces("final_tr.off", false /*only_boundary_faces*/); #endif #endif @@ -382,6 +427,20 @@ class Alpha_wrap_3 return bbox; } +private: + // The distinction between inside boundary and outside boundary is the presence of cells + // being flagged for manifoldness: inside boundary considers those outside, and outside + // boundary considers them inside. + bool is_on_inside_boundary(Cell_handle ch, Cell_handle nh) const + { + return (ch->is_inside() != nh->is_inside()); // one is "inside", the other is not + } + + bool is_on_outside_boundary(Cell_handle ch, Cell_handle nh) const + { + return (ch->is_outside() != nh->is_outside()); // one is "outside", the other is not + } + private: // Adjust the bbox & insert its corners to construct the starting triangulation void insert_bbox_corners() @@ -409,20 +468,20 @@ class Alpha_wrap_3 // - Cells whose circumcenter is in the offset volume are inside: this is because // we need to have outside cell circumcenters outside of the volume to ensure // that the refinement point is separated from the existing point set. - bool cavity_cell_outside_tag(const Cell_handle ch) + Cell_label cavity_cell_label(const Cell_handle ch) { CGAL_precondition(!m_tr.is_infinite(ch)); const Tetrahedron_with_outside_info tet(ch, geom_traits()); if(m_oracle.do_intersect(tet)) - return false; + return Cell_label::INSIDE; const Point_3& ch_cc = circumcenter(ch); typename Geom_traits::Construct_ball_3 ball = geom_traits().construct_ball_3_object(); const Ball_3 ch_cc_offset_ball = ball(ch_cc, m_sq_offset); const bool is_cc_in_offset = m_oracle.do_intersect(ch_cc_offset_ball); - return !is_cc_in_offset; + return is_cc_in_offset ? Cell_label::INSIDE : Cell_label::OUTSIDE; } // Create a cavity using seeds rather than starting from the infinity. @@ -461,15 +520,14 @@ class Alpha_wrap_3 // // Another way is to simply make faces incident to the seed always traversable, and then // we only have to ensure faces opposite of the seed are traversable (i.e., radius ~= 1.65 * alpha) - template - bool initialize_with_cavities(const SeedRange& seeds) + bool initialize_with_cavities() { #ifdef CGAL_AW3_DEBUG_INITIALIZATION std::cout << "> Dig cavities" << std::endl; - std::cout << seeds.size() << " seed(s)" << std::endl; + std::cout << m_seeds.size() << " seed(s)" << std::endl; #endif - CGAL_precondition(!seeds.empty()); + CGAL_precondition(!m_seeds.empty()); // Get a double value approximating the scaling factors // std::cout << sqrt(3) * sin(2pi / 5) << std::endl; @@ -478,7 +536,7 @@ class Alpha_wrap_3 Iso_cuboid_3 bbox = SC2GT()(m_bbox); std::vector seed_vs; - for(const Point_3& seed_p : seeds) + for(const Point_3& seed_p : m_seeds) { #ifdef CGAL_AW3_DEBUG_INITIALIZATION std::cout << "Initialize from seed " << seed_p << std::endl; @@ -504,7 +562,7 @@ class Alpha_wrap_3 continue; } - // Mark the seeds and icosahedron vertices as "artificial vertices" such that the facets + // Mark the seeds and icosahedron vertices as "scaffolding" vertices such that the facets // incident to these vertices are always traversable regardless of their circumcenter. // This is done because otherwise some cavities can appear on the mesh: non-traversable facets // with two vertices on the offset, and the third being a deeper inside seed / ico_seed. @@ -573,17 +631,19 @@ class Alpha_wrap_3 std::vector inc_cells; inc_cells.reserve(64); m_tr.incident_cells(seed_v, std::back_inserter(inc_cells)); + for(Cell_handle ch : inc_cells) - ch->is_outside() = cavity_cell_outside_tag(ch); + ch->set_label(cavity_cell_label(ch)); } - // Might as well go through the full triangulation since only seeds should have been inserted + // Should be cheap enough to go through the full triangulation as only seeds have been inserted for(Cell_handle ch : m_tr.all_cell_handles()) { - if(!ch->is_outside()) + if(ch->is_inside()) continue; - // When the algorithm starts from a manually dug hole, infinite cells are tagged "inside" + // When the algorithm starts from a manually dug hole, infinite cells are initialized + // as "inside" such that they do not appear on the boundary CGAL_assertion(!m_tr.is_infinite(ch)); for(int i=0; i<4; ++i) @@ -601,7 +661,7 @@ class Alpha_wrap_3 return true; } - // tag all infinite cells OUTSIDE and all finite cells INSIDE + // tag all infinite cells "outside" and all finite cells "inside" // init queue with all convex hull facets bool initialize_from_infinity() { @@ -609,20 +669,52 @@ class Alpha_wrap_3 { if(m_tr.is_infinite(ch)) { - ch->is_outside() = true; + ch->set_label(Cell_label::OUTSIDE); const int inf_index = ch->index(m_tr.infinite_vertex()); push_facet(std::make_pair(ch, inf_index)); } else { - ch->is_outside() = false; + ch->set_label(Cell_label::INSIDE); } } return true; } -public: + void reset_manifold_labels() + { + // No erase counter increment, or it will mess up with a possibly non-empty queue. + for(Cell_handle ch : m_tr.all_cell_handles()) + if(ch->label() == Cell_label::MANIFOLD) + ch->set_label(Cell_label::OUTSIDE); + } + + // This function is used in the case of resumption of a previous run: m_tr is not cleared, + // and we fill the queue with the new parameters. + bool initialize_from_existing_triangulation() + { +#ifdef CGAL_AW3_DEBUG_INITIALIZATION + std::cout << "Restart from a DT of " << m_tr.number_of_cells() << " cells" << std::endl; +#endif + + for(Cell_handle ch : m_tr.all_cell_handles()) + { + if(ch->is_inside()) + continue; + + for(int i=0; i<4; ++i) + { + if(ch->neighbor(i)->is_inside()) + push_facet(std::make_pair(ch, i)); + + } + } + + return true; + } + +private: // Manifoldness is tolerated while debugging and extracting at intermediate states // Not the preferred way because it uses 3*nv storage template @@ -635,7 +727,7 @@ class Alpha_wrap_3 std::cout << "> Extract possibly non-manifold wrap... ()" << std::endl; #endif - clear(output_mesh); + remove_all_elements(output_mesh); CGAL_assertion_code(for(auto cit=m_tr.finite_cells_begin(), cend=m_tr.finite_cells_end(); cit!=cend; ++cit)) CGAL_assertion(cit->tds_data().is_clear()); @@ -662,7 +754,6 @@ class Alpha_wrap_3 if(cell->tds_data().processed()) continue; - cell->tds_data().mark_processed(); for(int fid=0; fid<4; ++fid) @@ -689,8 +780,6 @@ class Alpha_wrap_3 } } - PMP::duplicate_non_manifold_edges_in_polygon_soup(points, faces); - CGAL_assertion(PMP::is_polygon_soup_a_polygon_mesh(faces)); PMP::polygon_soup_to_polygon_mesh(points, faces, output_mesh, CGAL::parameters::default_values(), @@ -708,6 +797,8 @@ class Alpha_wrap_3 CGAL_postcondition(is_closed(output_mesh)); PMP::orient_to_bound_a_volume(output_mesh, CGAL::parameters::vertex_point_map(ovpm)); + + collect_garbage(output_mesh); } template @@ -717,43 +808,38 @@ class Alpha_wrap_3 namespace PMP = Polygon_mesh_processing; #ifdef CGAL_AW3_DEBUG - std::cout << "> Extract wrap... ()" << std::endl; + std::cout << "> Extract manifold wrap... ()" << std::endl; #endif CGAL_assertion_code(for(Vertex_handle v : m_tr.finite_vertex_handles())) CGAL_assertion(!is_non_manifold(v)); - clear(output_mesh); + remove_all_elements(output_mesh); - // boundary faces to polygon soup std::vector points; std::vector > faces; std::unordered_map vertex_to_id; - std::size_t nv = 0; - for(auto fit=m_tr.finite_facets_begin(), fend=m_tr.finite_facets_end(); fit!=fend; ++fit) + for(auto fit=m_tr.all_facets_begin(), fend=m_tr.all_facets_end(); fit!=fend; ++fit) { Facet f = *fit; if(!f.first->is_outside()) f = m_tr.mirror_facet(f); - const Cell_handle c = f.first; + const Cell_handle ch = f.first; const int s = f.second; - const Cell_handle nh = c->neighbor(s); - if(c->is_outside() == nh->is_outside()) + const Cell_handle nh = ch->neighbor(s); + if(!is_on_outside_boundary(ch, nh)) continue; std::array ids; for(int pos=0; pos<3; ++pos) { - Vertex_handle vh = c->vertex(Triangulation::vertex_triple_index(s, pos)); - auto insertion_res = vertex_to_id.emplace(vh, nv); + Vertex_handle vh = ch->vertex(Triangulation::vertex_triple_index(s, pos)); + auto insertion_res = vertex_to_id.emplace(vh, vertex_to_id.size()); if(insertion_res.second) // successful insertion, never-seen-before vertex - { points.push_back(m_tr.point(vh)); - ++nv; - } ids[pos] = insertion_res.first->second; } @@ -761,12 +847,22 @@ class Alpha_wrap_3 faces.emplace_back(std::array{ids[0], ids[1], ids[2]}); } +#ifdef CGAL_AW3_DEBUG + std::cout << "\t" << points.size() << " points" << std::endl; + std::cout << "\t" << faces.size() << " faces" << std::endl; +#endif + if(faces.empty()) + { +#ifdef CGAL_AW3_DEBUG + std::cerr << "Warning: empty wrap?..." << std::endl; +#endif return; + } if(!PMP::is_polygon_soup_a_polygon_mesh(faces)) { - CGAL_warning_msg(false, "Could NOT extract mesh..."); + CGAL_warning_msg(false, "Failed to extract a manifold boundary!"); return; } @@ -777,10 +873,12 @@ class Alpha_wrap_3 CGAL_postcondition(!is_empty(output_mesh)); CGAL_postcondition(is_valid_polygon_mesh(output_mesh)); CGAL_postcondition(is_closed(output_mesh)); + CGAL_postcondition(PMP::does_bound_a_volume(output_mesh, CGAL::parameters::vertex_point_map(ovpm))); PMP::orient_to_bound_a_volume(output_mesh, CGAL::parameters::vertex_point_map(ovpm)); } +public: template void extract_surface(OutputMesh& output_mesh, OVPM ovpm, @@ -886,43 +984,76 @@ class Alpha_wrap_3 } private: - enum Facet_queue_status + // A permissive gate is a gate that we can traverse without checking its circumradius + enum class Facet_status { IRRELEVANT = 0, - ARTIFICIAL_FACET, + HAS_INFINITE_NEIGHBOR, // the cell incident to the mirrored facet is infinite (permissive) + SCAFFOLDING, // incident to a SEED or BBOX vertex (permissive) TRAVERSABLE }; + inline const char* get_status_message(const Facet_status status) + { + constexpr std::size_t status_count = 4; + + // Messages corresponding to Error_code list above. Must be kept in sync! + static const char* message[status_count] = { + "Irrelevant facet", + "Facet incident to infinite neighbor", + "Facet with a bbox/seed vertex", + "Traversable facet" + }; + + const std::size_t status_id = static_cast(status); + if(status_id > status_count || status_id < 0) + return "Unknown status"; + else + return message[status_id]; + } + +public: // @speed some decent time may be spent constructing Facet (pairs) for no reason as it's always // just to grab the .first and .second as soon as it's constructed, and not due to API requirements - // e.g. from DT3 - Facet_queue_status facet_status(const Facet& f) const + Facet_status facet_status(const Facet& f) const { CGAL_precondition(!m_tr.is_infinite(f)); #ifdef CGAL_AW3_DEBUG_FACET_STATUS std::cout << "facet status: " - << f.first->vertex((f.second + 1)&3)->point() << " " - << f.first->vertex((f.second + 2)&3)->point() << " " - << f.first->vertex((f.second + 3)&3)->point() << std::endl; + << m_tr.point(f.first, Triangulation::vertex_triple_index(f.second, 0)) << " " + << m_tr.point(f.first, Triangulation::vertex_triple_index(f.second, 1)) << " " + << m_tr.point(f.first, Triangulation::vertex_triple_index(f.second, 2)) << std::endl; #endif - // skip if neighbor is OUTSIDE or infinite + // skip if neighbor is "outside" or infinite const Cell_handle ch = f.first; const int id = f.second; + CGAL_precondition(ch->label() == Cell_label::INSIDE || ch->label() == Cell_label::OUTSIDE); + + if(!ch->is_outside()) // "inside" or "manifold" + { +#ifdef CGAL_AW3_DEBUG_FACET_STATUS + std::cout << "Facet is inside" << std::endl; +#endif + return Facet_status::IRRELEVANT; + } + const Cell_handle nh = ch->neighbor(id); + CGAL_precondition(ch->label() == Cell_label::INSIDE || ch->label() == Cell_label::OUTSIDE); + if(m_tr.is_infinite(nh)) - return TRAVERSABLE; + return Facet_status::HAS_INFINITE_NEIGHBOR; if(nh->is_outside()) { #ifdef CGAL_AW3_DEBUG_FACET_STATUS std::cout << "Neighbor already outside" << std::endl; #endif - return IRRELEVANT; + return Facet_status::IRRELEVANT; } - // push if facet is connected to artificial vertices + // push if facet is connected to scaffolding vertices for(int i=0; i<3; ++i) { const Vertex_handle vh = ch->vertex(Triangulation::vertex_triple_index(id, i)); @@ -930,9 +1061,9 @@ class Alpha_wrap_3 vh->type() == AW3i::Vertex_type:: SEED_VERTEX) { #ifdef CGAL_AW3_DEBUG_FACET_STATUS - std::cout << "artificial facet due to artificial vertex #" << i << std::endl; + std::cout << "Scaffolding facet due to vertex #" << i << std::endl; #endif - return ARTIFICIAL_FACET; + return Facet_status::SCAFFOLDING; } } @@ -942,78 +1073,145 @@ class Alpha_wrap_3 #ifdef CGAL_AW3_DEBUG_FACET_STATUS std::cout << "traversable" << std::endl; #endif - return TRAVERSABLE; + return Facet_status::TRAVERSABLE; } #ifdef CGAL_AW3_DEBUG_FACET_STATUS std::cout << "not traversable" << std::endl; #endif - return IRRELEVANT; + return Facet_status::IRRELEVANT; } +private: bool push_facet(const Facet& f) { CGAL_precondition(f.first->is_outside()); +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE // skip if f is already in queue if(m_queue.contains_with_bounds_check(Gate(f))) return false; +#endif - const Facet_queue_status s = facet_status(f); - if(s == IRRELEVANT) + // @todo could avoid useless facet_status() calls by doing it after the zombie check + // for the unsorted priority queue, but AFAIR, it doesn't save noticeable time (and that + // increases the queue size). + const Facet_status status = facet_status(f); + if(status == Facet_status::IRRELEVANT) return false; - const Cell_handle ch = f.first; - const int id = f.second; - const Point_3& p0 = m_tr.point(ch, (id+1)&3); - const Point_3& p1 = m_tr.point(ch, (id+2)&3); - const Point_3& p2 = m_tr.point(ch, (id+3)&3); +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + const FT sqr = smallest_squared_radius_3(f, m_tr); + const bool is_permissive = (status == Facet_status::HAS_INFINITE_NEIGHBOR || + status == Facet_status::SCAFFOLDING); + m_queue.resize_and_push(Gate(f, sqr, is_permissive)); +#else + m_queue.push(Gate(f, m_tr)); +#endif - // @todo should prob be the real value we compare to alpha instead of squared_radius - const FT sqr = geom_traits().compute_squared_radius_3_object()(p0, p1, p2); - m_queue.resize_and_push(Gate(f, sqr, (s == ARTIFICIAL_FACET))); +#ifdef CGAL_AW3_DEBUG_QUEUE + const Cell_handle ch = f.first; + const int s = f.second; + const Point_3& p0 = m_tr.point(ch, Triangulation::vertex_triple_index(s, 0)); + const Point_3& p1 = m_tr.point(ch, Triangulation::vertex_triple_index(s, 1)); + const Point_3& p2 = m_tr.point(ch, Triangulation::vertex_triple_index(s, 2)); + + static int gid = 0; + std::cout << "Queue insertion #" << gid++ << "\n" + << " ch = " << &*ch << " (" << m_tr.is_infinite(ch) << ") " << "\n" + << "\t" << p0 << "\n\t" << p1 << "\n\t" << p2 << std::endl; + std::cout << " Status: " << get_status_message(status) << std::endl; + #ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + std::cout << " SQR: " << sqr << std::endl; + std::cout << " Permissiveness: " << is_permissive << std::endl; + + CGAL_assertion(is_permissive || sqr >= m_sq_alpha); + #endif // CGAL_AW3_USE_SORTED_PRIORITY_QUEUE +#endif // CGAL_AW3_DEBUG_QUEUE return true; } private: - template bool initialize(const double alpha, const double offset, - const SeedRange& seeds) + const bool refining) { #ifdef CGAL_AW3_DEBUG std::cout << "> Initialize..." << std::endl; - std::cout << "Alpha: " << alpha << std::endl; - std::cout << "Offset: " << offset << std::endl; +#endif + + const bool resuming = refining && (alpha == m_alpha) && (offset == m_offset); + +#ifdef CGAL_AW3_DEBUG + std::cout << "\tAlpha: " << alpha << std::endl; + std::cout << "\tOffset: " << offset << std::endl; + std::cout << "\tRefining? " << refining << std::endl; + std::cout << "\tResuming? " << resuming << std::endl; #endif if(!is_positive(alpha) || !is_positive(offset)) { #ifdef CGAL_AW3_DEBUG - std::cout << "Error: invalid input parameters" << std::endl; + std::cerr << "Error: invalid input parameters: " << alpha << " and " << offset << std::endl; #endif return false; } +#ifdef CGAL_AW3_DEBUG + if(refining && alpha > m_alpha) + std::cerr << "Warning: refining with an alpha greater than the last iteration's!" << std::endl; + if(refining && offset != m_offset) + std::cerr << "Warning: refining with a different offset value!" << std::endl; +#endif + m_alpha = FT(alpha); m_sq_alpha = square(m_alpha); m_offset = FT(offset); m_sq_offset = square(m_offset); - m_tr.clear(); + // Resuming means that we do not need to re-initialize the queue: either we have finished + // and there is nothing to do, or the interruption was due to a user callback in the visitor, + // and we can resume with the current queue + if(resuming) + { +#ifdef CGAL_AW3_DEBUG + std::cout << "Resuming with a queue of size: " << m_queue.size() << std::endl; +#endif + + reset_manifold_labels(); + return true; + } + +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE m_queue.clear(); +#else + m_queue = { }; +#endif - insert_bbox_corners(); + if(refining) + { + // If we are re-using the triangulation, change the label of the extra elements + // that we have added to ensure a manifold result back to external ("manifold" -> "outside") + reset_manifold_labels(); - if(seeds.empty()) - return initialize_from_infinity(); + return initialize_from_existing_triangulation(); + } else - return initialize_with_cavities(seeds); + { + m_tr.clear(); + + insert_bbox_corners(); + + if(m_seeds.empty()) + return initialize_from_infinity(); + else + return initialize_with_cavities(); + } } template - void alpha_flood_fill(Visitor& visitor) + bool alpha_flood_fill(Visitor& visitor) { #ifdef CGAL_AW3_DEBUG std::cout << "> Flood fill..." << std::endl; @@ -1024,33 +1222,47 @@ class Alpha_wrap_3 // Explore all finite cells that are reachable from one of the initial outside cells. while(!m_queue.empty()) { + if(!visitor.go_further(*this)) + return false; + #ifdef CGAL_AW3_DEBUG_QUEUE_PP check_queue_sanity(); #endif // const& to something that will be popped, but safe as `ch` && `id` are extracted before the pop const Gate& gate = m_queue.top(); + +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + if(gate.is_zombie()) + { + m_queue.pop(); + continue; + } +#endif + const Facet& f = gate.facet(); CGAL_precondition(!m_tr.is_infinite(f)); const Cell_handle ch = f.first; - const int id = f.second; - const Cell_handle neighbor = ch->neighbor(id); + const int s = f.second; + CGAL_precondition(ch->is_outside()); + + const Cell_handle nh = ch->neighbor(s); + CGAL_precondition(nh->label() == Cell_label::INSIDE || nh->label() == Cell_label::OUTSIDE); #ifdef CGAL_AW3_DEBUG_QUEUE static int fid = 0; std::cout << m_tr.number_of_vertices() << " DT vertices" << std::endl; std::cout << m_queue.size() << " facets in the queue" << std::endl; std::cout << "Face " << fid++ << "\n" - << "c = " << &*ch << " (" << m_tr.is_infinite(ch) << "), n = " << &*neighbor << " (" << m_tr.is_infinite(neighbor) << ")" << "\n" - << m_tr.point(ch, (id+1)&3) << "\n" << m_tr.point(ch, (id+2)&3) << "\n" << m_tr.point(ch, (id+3)&3) << std::endl; - std::cout << "Priority: " << gate.priority() << std::endl; + << "c = " << &*ch << " (" << m_tr.is_infinite(ch) << "), n = " << &*nh << " (" << m_tr.is_infinite(nh) << ")" << "\n" + << m_tr.point(ch, Triangulation::vertex_triple_index(s, 0)) << "\n" + << m_tr.point(ch, Triangulation::vertex_triple_index(s, 1)) << "\n" + << m_tr.point(ch, Triangulation::vertex_triple_index(s, 2)) << std::endl; + std::cout << "Priority: " << gate.priority() << " (sq alpha: " << m_sq_alpha << ")" << std::endl; + std::cout << "Permissiveness: " << gate.is_permissive_facet() << std::endl; #endif - visitor.before_facet_treatment(*this, gate); - - m_queue.pop(); - #ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP static int i = 0; std::string step_name = "results/steps/step_" + std::to_string(static_cast(i)) + ".off"; @@ -1059,18 +1271,27 @@ class Alpha_wrap_3 std::string face_name = "results/steps/face_" + std::to_string(static_cast(i++)) + ".xyz"; std::ofstream face_out(face_name); face_out.precision(17); - face_out << "3\n" << m_tr.point(ch, (id+1)&3) << "\n" << m_tr.point(ch, (id+2)&3) << "\n" << m_tr.point(ch, (id+3)&3) << std::endl; + face_out << "3\n" << m_tr.point(ch, Triangulation::vertex_triple_index(s, 0)) << "\n" + << m_tr.point(ch, Triangulation::vertex_triple_index(s, 1)) << "\n" + << m_tr.point(ch, Triangulation::vertex_triple_index(s, 2)) << std::endl; face_out.close(); #endif - if(m_tr.is_infinite(neighbor)) + visitor.before_facet_treatment(*this, gate); + + m_queue.pop(); + + if(m_tr.is_infinite(nh)) { - neighbor->is_outside() = true; + nh->set_label(Cell_label::OUTSIDE); +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + nh->increment_erase_counter(); +#endif continue; } Point_3 steiner_point; - if(compute_steiner_point(ch, neighbor, steiner_point)) + if(compute_steiner_point(ch, nh, steiner_point)) { // std::cout << CGAL::abs(CGAL::approximate_sqrt(m_oracle.squared_distance(steiner_point)) - m_offset) // << " vs " << 1e-2 * m_offset << std::endl; @@ -1079,7 +1300,7 @@ class Alpha_wrap_3 // locate cells that are going to be destroyed and remove their facet from the queue int li, lj = 0; Locate_type lt; - const Cell_handle conflict_cell = m_tr.locate(steiner_point, lt, li, lj, neighbor); + const Cell_handle conflict_cell = m_tr.locate(steiner_point, lt, li, lj, nh); CGAL_assertion(lt != Triangulation::VERTEX); // Using small vectors like in Triangulation_3 does not bring any runtime improvement @@ -1092,6 +1313,7 @@ class Alpha_wrap_3 std::back_inserter(boundary_facets), std::back_inserter(conflict_zone)); +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE // Purge the queue of facets that will be deleted/modified by the Steiner point insertion, // and which might have been gates for(const Cell_handle& cch : conflict_zone) @@ -1110,6 +1332,7 @@ class Alpha_wrap_3 if(m_queue.contains_with_bounds_check(Gate(mf))) m_queue.erase(Gate(mf)); } +#endif visitor.before_Steiner_point_insertion(*this, steiner_point); @@ -1124,10 +1347,10 @@ class Alpha_wrap_3 std::vector new_cells; new_cells.reserve(32); m_tr.incident_cells(vh, std::back_inserter(new_cells)); - for(const Cell_handle& ch : new_cells) + for(const Cell_handle& new_ch : new_cells) { - // std::cout << "new cell has time stamp " << ch->time_stamp() << std::endl; - ch->is_outside() = m_tr.is_infinite(ch); + // std::cout << "new cell has time stamp " << new_ch->time_stamp() << std::endl; + new_ch->set_label(m_tr.is_infinite(new_ch) ? Cell_label::OUTSIDE : Cell_label::INSIDE); } // Push all new boundary facets to the queue. @@ -1135,34 +1358,37 @@ class Alpha_wrap_3 // because we need to handle internal facets, infinite facets, and also more subtle changes // such as a new cell being marked inside which now creates a boundary // with its incident "outside" flagged cell. - for(Cell_handle ch : new_cells) + for(Cell_handle new_ch : new_cells) { for(int i=0; i<4; ++i) { - if(m_tr.is_infinite(ch, i)) + if(m_tr.is_infinite(new_ch, i)) continue; - const Cell_handle nh = ch->neighbor(i); - if(nh->is_outside() == ch->is_outside()) // not on the boundary + const Cell_handle new_nh = new_ch->neighbor(i); + if(new_nh->label() == new_ch->label()) // not on a boundary continue; - const Facet boundary_f = std::make_pair(ch, i); - if(ch->is_outside()) + const Facet boundary_f = std::make_pair(new_ch, i); + if(new_ch->is_outside()) push_facet(boundary_f); else push_facet(m_tr.mirror_facet(boundary_f)); } } } - else + else // no need for a Steiner point, carve through and continue { - // tag neighbor as OUTSIDE - neighbor->is_outside() = true; + nh->set_label(Cell_label::OUTSIDE); +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + nh->increment_erase_counter(); +#endif // for each finite facet of neighbor, push it to the queue - for(int i=0; i<4; ++i) + const int mi = m_tr.mirror_index(ch, s); + for(int i=1; i<4; ++i) { - const Facet neighbor_f = std::make_pair(neighbor, i); + const Facet neighbor_f = std::make_pair(nh, (mi+i)&3); push_facet(neighbor_f); } } @@ -1172,11 +1398,103 @@ class Alpha_wrap_3 // Check that no useful facet has been ignored CGAL_postcondition_code(for(auto fit=m_tr.finite_facets_begin(), fend=m_tr.finite_facets_end(); fit!=fend; ++fit) {) - CGAL_postcondition_code( if(fit->first->is_outside() == fit->first->neighbor(fit->second)->is_outside()) continue;) + CGAL_postcondition_code( Cell_handle ch = fit->first; Cell_handle nh = fit->first->neighbor(fit->second); ) + CGAL_postcondition_code( if(ch->label() == nh->label()) continue;) CGAL_postcondition_code( Facet f = *fit;) - CGAL_postcondition_code( if(!fit->first->is_outside()) f = m_tr.mirror_facet(f);) - CGAL_postcondition( facet_status(f) == IRRELEVANT); + CGAL_postcondition_code( if(ch->is_inside()) f = m_tr.mirror_facet(f);) + CGAL_postcondition( facet_status(f) == Facet_status::IRRELEVANT); CGAL_postcondition_code(}) + + return true; + } + + // Any outside cell that isn't reachable from infinity is a cavity that can be discarded. + std::size_t purge_inner_connected_components() + { +#ifdef CGAL_AW3_DEBUG + std::cout << "> Purge inner islands..." << std::endl; + + std::size_t pre_counter = 0; + for(Cell_handle ch : m_tr.all_cell_handles()) + if(ch->is_outside()) + ++pre_counter; + std::cout << pre_counter << " / " << m_tr.all_cell_handles().size() << " (pre purge)" << std::endl; +#endif + + std::size_t label_change_counter = 0; + + std::stack cells_to_visit; + + if(!m_seeds.empty()) + { + for(const Point_3& seed : m_seeds) + { + Locate_type lt; + int li, lj; + Cell_handle ch = m_tr.locate(seed, lt, li, lj); + + if(!ch->is_outside()) + { + std::cerr << "Warning: cell containing seed is not outside?!" << std::endl; + continue; + } + + cells_to_visit.push(ch); + } + } + else // typical flooding from outside + { + cells_to_visit.push(m_tr.infinite_vertex()->cell()); + } + + while(!cells_to_visit.empty()) + { + Cell_handle curr_c = cells_to_visit.top(); + cells_to_visit.pop(); + + CGAL_assertion(curr_c->is_outside()); + + if(curr_c->tds_data().processed()) + continue; + curr_c->tds_data().mark_processed(); + + for(int j=0; j<4; ++j) + { + Cell_handle neigh_c = curr_c->neighbor(j); + if(neigh_c->tds_data().processed() || !neigh_c->is_outside()) + continue; + + cells_to_visit.push(neigh_c); + } + } + + for(Cell_handle ch : m_tr.all_cell_handles()) + { + if(ch->tds_data().is_clear() && ch->is_outside()) + { + ch->set_label(Cell_label::INSIDE); +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + ch->increment_erase_counter(); +#endif + ++label_change_counter; + } + } + + // reset the conflict flags + for(Cell_handle ch : m_tr.all_cell_handles()) + ch->tds_data().clear(); + +#ifdef CGAL_AW3_DEBUG + std::size_t post_counter = 0; + for(Cell_handle ch : m_tr.all_cell_handles()) + if(ch->is_outside()) + ++post_counter; + std::cout << post_counter << " / " << m_tr.all_cell_handles().size() << " (pre purge)" << std::endl; + + std::cout << label_change_counter << " label changes" << std::endl; +#endif + + return label_change_counter; } private: @@ -1190,7 +1508,7 @@ class Alpha_wrap_3 inc_cells.reserve(64); m_tr.incident_cells(v, std::back_inserter(inc_cells)); - // Flood one inside and outside CC. + // Flood one inside and outside CC within the cell umbrella of the vertex. // Process both an inside and an outside CC to also detect edge pinching. // If there are still unprocessed afterwards, there is a non-manifoldness issue. // @@ -1204,7 +1522,7 @@ class Alpha_wrap_3 if(ic->is_outside()) outside_start = ic; else if(inside_start == Cell_handle()) - inside_start = ic; + inside_start = ic; // can be "inside" or "manifold" } // fully inside / outside @@ -1236,8 +1554,10 @@ class Alpha_wrap_3 CGAL_assertion(neigh_c->has_vertex(v)); if(neigh_c->tds_data().processed() || - neigh_c->is_outside() != curr_c->is_outside()) // do not cross the boundary + is_on_outside_boundary(neigh_c, curr_c)) // do not cross the boundary + { continue; + } cells_to_visit.push(neigh_c); } @@ -1330,22 +1650,47 @@ class Alpha_wrap_3 // Not the best complexity, but it's very cheap compared to the rest of the algorithm. void make_manifold() { - namespace PMP = Polygon_mesh_processing; +#ifdef CGAL_AW3_DEBUG + std::cout << "> Make manifold..." << std::endl; + + auto wrap_volume = [&]() + { + FT vol = 0; + for(Cell_handle ch : m_tr.finite_cell_handles()) + if(!ch->is_outside()) + vol += volume(m_tr.point(ch, 0), m_tr.point(ch, 1), m_tr.point(ch, 2), m_tr.point(ch, 3)); + + return vol; + }; + + #ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS + dump_triangulation_faces("carved_tr.off", true /*only_boundary_faces*/); + #endif - // This seems more harmful than useful after the priority queue has been introduced since - // it adds a lot of flat cells into the triangulation, which then get added to the mesh - // during manifoldness fixing. + FT base_vol = wrap_volume(); + if(!is_positive(base_vol)) + std::cerr << "Warning: wrap with non-positive volume?" << std::endl; +#endif + + // This ends up more harmful than useful after the priority queue has been introduced since + // it usually results in a lot of flat cells into the triangulation, which then get added + // to the mesh during manifoldness fixing. // remove_bbox_vertices(); std::stack non_manifold_vertices; // @todo sort somehow? for(Vertex_handle v : m_tr.finite_vertex_handles()) { if(is_non_manifold(v)) + { +#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS_PP + std::cout << v->point() << " is non-manifold" << std::endl; +#endif non_manifold_vertices.push(v); + } } // Some lambdas for the comparer - auto has_artificial_vertex = [](Cell_handle c) -> bool + auto has_scaffolding_vertex = [](Cell_handle c) -> bool { for(int i=0; i<4; ++i) { @@ -1359,79 +1704,77 @@ class Alpha_wrap_3 return false; }; - auto is_on_boundary = [](Cell_handle c, int i) -> bool - { - return (c->is_outside() != c->neighbor(i)->is_outside()); - }; - - auto count_boundary_facets = [&](Cell_handle c, Vertex_handle v) -> int - { - const int v_index_in_c = c->index(v); - int boundary_facets = 0; - for(int i=0; i<3; ++i) // also take into account the opposite facet? - { - if(i == v_index_in_c) - continue; - - boundary_facets += is_on_boundary(c, i); - } - - return boundary_facets; - }; + // This originally seemed like a good idea, but in the end it can have strong cascading issues, + // whereas some cells with much smaller volume could have solved the non-manifoldness. +// auto is_on_boundary = [](Cell_handle c, int i) -> bool +// { +// return is_on_outside_boundary(c, c->neighbor(i)); +// }; +// +// auto count_boundary_facets = [&](Cell_handle c, Vertex_handle v) -> int +// { +// const int v_index_in_c = c->index(v); +// int boundary_facets = 0; +// for(int i=0; i<3; ++i) // also take into account the opposite facet? +// { +// if(i == v_index_in_c) +// continue; +// +// boundary_facets += is_on_boundary(c, i); +// } +// +// return boundary_facets; +// }; - // longest edge works better + // Experimentally, longest edge works better // auto sq_circumradius = [&](Cell_handle c) -> FT // { // const Point_3& cc = circumcenter(c); // return geom_traits().compute_squared_distance_3_object()(m_tr.point(c, 0), cc); // }; + // The reasoning behind using longest edge rather than volume is that we want to avoid + // spikes (which would have a small volume), and can often happen since we do not spend + // any care on the quality of tetrahedra. auto sq_longest_edge = [&](Cell_handle c) -> FT { return (std::max)({ squared_distance(m_tr.point(c, 0), m_tr.point(c, 1)), squared_distance(m_tr.point(c, 0), m_tr.point(c, 2)), squared_distance(m_tr.point(c, 0), m_tr.point(c, 3)), squared_distance(m_tr.point(c, 1), m_tr.point(c, 2)), - squared_distance(m_tr.point(c, 3), m_tr.point(c, 3)), + squared_distance(m_tr.point(c, 1), m_tr.point(c, 3)), squared_distance(m_tr.point(c, 2), m_tr.point(c, 3)) }); }; -#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS +#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS_PP std::cout << non_manifold_vertices.size() << " initial NMV" << std::endl; #endif while(!non_manifold_vertices.empty()) { -#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS +#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS_PP std::cout << non_manifold_vertices.size() << " NMV in queue" << std::endl; #endif Vertex_handle v = non_manifold_vertices.top(); non_manifold_vertices.pop(); -#ifdef CGAL_AW3_DEBUG_MANIFOLDNESS - std::cout << "·"; -#endif - if(!is_non_manifold(v)) continue; // Prioritize: // - cells without bbox vertices - // - cells that already have a large number of boundary facets // - small cells when equal number of boundary facets - // @todo give topmost priority to cells with > 1 non-manifold vertex? + // + // Note that these are properties that do not depend on the cell labels, and so we only need + // to sort once. However, if a criterion such as the number of incident inside cells were added, + // we would need to sort after each modification of "inside"/"outside" labels. auto comparer = [&](Cell_handle l, Cell_handle r) -> bool { - if(has_artificial_vertex(l)) - return false; - if(has_artificial_vertex(r)) - return true; + CGAL_precondition(!m_tr.is_infinite(l) && !m_tr.is_infinite(r)); - const int l_bf_count = count_boundary_facets(l, v); - const int r_bf_count = count_boundary_facets(r, v); - if(l_bf_count != r_bf_count) - return l_bf_count > r_bf_count; + if(has_scaffolding_vertex(l) != has_scaffolding_vertex(r)) + return has_scaffolding_vertex(r); return sq_longest_edge(l) < sq_longest_edge(r); }; @@ -1440,22 +1783,22 @@ class Alpha_wrap_3 inc_cells.reserve(64); m_tr.finite_incident_cells(v, std::back_inserter(inc_cells)); -#define CGAL_AW3_USE_BRUTE_FORCE_MUTABLE_PRIORITY_QUEUE -#ifndef CGAL_AW3_USE_BRUTE_FORCE_MUTABLE_PRIORITY_QUEUE - std::sort(inc_cells.begin(), inc_cells.end(), comparer); // sort once -#endif + std::vector finite_outside_inc_cells; + finite_outside_inc_cells.reserve(64); + std::copy_if(inc_cells.begin(), inc_cells.end(), std::back_inserter(finite_outside_inc_cells), + [&](Cell_handle c) -> bool { return !m_tr.is_infinite(c) && c->is_outside(); }); - for(auto cit=inc_cells.begin(), cend=inc_cells.end(); cit!=cend; ++cit) + // 'std::stable_sort' to have determinism without having to write something like: + // if(longest_edge(l) == longest_edge(r)) return ... + // in the comparer. It's almost always a small range, so the extra cost does not matter. + std::stable_sort(finite_outside_inc_cells.begin(), finite_outside_inc_cells.end(), comparer); + + for(Cell_handle ic : finite_outside_inc_cells) { -#ifdef CGAL_AW3_USE_BRUTE_FORCE_MUTABLE_PRIORITY_QUEUE - // sort at every iteration since the number of boundary facets evolves - std::sort(cit, cend, comparer); -#endif - Cell_handle ic = *cit; - CGAL_assertion(!m_tr.is_infinite(ic)); + CGAL_assertion(!m_tr.is_infinite(ic) && ic->is_outside()); // This is where new material is added - ic->is_outside() = false; + ic->set_label(Cell_label::MANIFOLD); #ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP static int i = 0; @@ -1470,6 +1813,8 @@ class Alpha_wrap_3 CGAL_assertion(!is_non_manifold(v)); + // Check if the new material has not created a non-manifold configuration. + // @speed this could be done on only the vertices of cells whose labels have changed. std::vector adj_vertices; adj_vertices.reserve(64); m_tr.finite_adjacent_vertices(v, std::back_inserter(adj_vertices)); @@ -1481,12 +1826,34 @@ class Alpha_wrap_3 CGAL_assertion_code(for(Vertex_handle v : m_tr.finite_vertex_handles())) CGAL_assertion(!is_non_manifold(v)); + +#ifdef CGAL_AW3_DEBUG + std::size_t nm_cells_counter = 0; + for(Cell_handle ch : m_tr.all_cell_handles()) + if(ch->label() == Cell_label::MANIFOLD) + ++nm_cells_counter; + std::cout << "Number of added cells: " << nm_cells_counter << std::endl; + + if(!is_zero(base_vol)) + { + const FT manifold_vol = wrap_volume(); + const FT ratio = manifold_vol / base_vol; + + std::cout << "Volumes post-manifoldness fix:\n" + << "before: " << base_vol << "\n" + << "after: " << manifold_vol << "\n" + << "ratio: " << ratio << std::endl; + if(ratio > 1.1) // more than 10% extra volume + std::cerr << "Warning: large increase of volume after manifoldness resolution" << std::endl; + } +#endif } private: void check_queue_sanity() { - std::cout << "Check queue sanity..." << std::endl; + std::cout << "\t~~~ Check queue sanity ~~~" << std::endl; + std::vector queue_gates; Gate previous_top_gate = m_queue.top(); while(!m_queue.empty()) @@ -1496,24 +1863,31 @@ class Alpha_wrap_3 const Facet& current_f = current_gate.facet(); const Cell_handle ch = current_f.first; const int id = current_f.second; - const Point_3& p0 = m_tr.point(ch, (id+1)&3); - const Point_3& p1 = m_tr.point(ch, (id+2)&3); - const Point_3& p2 = m_tr.point(ch, (id+3)&3); - const FT sqr = geom_traits().compute_squared_radius_3_object()(p0, p1, p2); + const Point_3& p0 = m_tr.point(ch, Triangulation::vertex_triple_index(id, 0)); + const Point_3& p1 = m_tr.point(ch, Triangulation::vertex_triple_index(id, 1)); + const Point_3& p2 = m_tr.point(ch, Triangulation::vertex_triple_index(id, 2)); - std::cout << "At Facet with VID " << get(Gate_ID_PM(), current_gate) << std::endl; + std::cout << "Facet with VID " << get(Gate_ID_PM(), current_gate) << "\n"; + std::cout << "\t" << p0 << "\n\t" << p1 << "\n\t" << p2 << "\n"; - if(current_gate.priority() != sqr) - std::cerr << "Error: facet in queue has wrong priority" << std::endl; +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + std::cout << " Permissiveness: " << current_gate.is_permissive_facet() << "\n"; + std::cout << " SQR: " << geom_traits().compute_squared_radius_3_object()(p0, p1, p2) << "\n"; + std::cout << " Priority " << current_gate.priority() << std::endl; if(Less_gate()(current_gate, previous_top_gate)) std::cerr << "Error: current gate has higher priority than the previous top" << std::endl; previous_top_gate = current_gate; +#else + if(current_gate.is_zombie()) + std::cout << "Gate is a zombie!" << std::endl; +#endif m_queue.pop(); } - std::cout << "End sanity check" << std::endl; + + std::cout << "\t~~~ End queue sanity check ~~~" << std::endl; // Rebuild CGAL_assertion(m_queue.empty()); @@ -1536,17 +1910,17 @@ class Alpha_wrap_3 for(auto fit=m_tr.finite_facets_begin(), fend=m_tr.finite_facets_end(); fit!=fend; ++fit) { - Cell_handle c = fit->first; + Cell_handle ch = fit->first; int s = fit->second; - Cell_handle nc = c->neighbor(s); - if(only_boundary_faces && (c->is_outside() == nc->is_outside())) + Cell_handle nh = ch->neighbor(s); + if(only_boundary_faces && ch->label() == nh->label()) continue; std::array ids; for(std::size_t pos=0; pos<3; ++pos) { - Vertex_handle v = c->vertex((s+pos+1)&3); + Vertex_handle v = ch->vertex((s+pos+1)&3); auto insertion_res = vertex_to_id.emplace(v, nv); if(insertion_res.second) { diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_cell_base_3.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_cell_base_3.h index db1df61c8f65..efaeb82d330f 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_cell_base_3.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Alpha_wrap_triangulation_cell_base_3.h @@ -20,18 +20,26 @@ namespace CGAL { namespace Alpha_wraps_3 { namespace internal { +enum class Cell_label +{ + // Cells that have been carved + OUTSIDE, + // Cells that have not yet been carved + INSIDE, + // OUTSIDE cells that have been labeled "inside" again as to make the result manifold + MANIFOLD +}; + template < typename GT, typename Cb = CGAL::Delaunay_triangulation_cell_base_with_circumcenter_3 > class Alpha_wrap_triangulation_cell_base_3 : public Cb { -private: - bool outside = false; - public: typedef typename Cb::Vertex_handle Vertex_handle; typedef typename Cb::Cell_handle Cell_handle; +public: template < typename TDS2 > struct Rebind_TDS { @@ -39,6 +47,14 @@ class Alpha_wrap_triangulation_cell_base_3 using Other = Alpha_wrap_triangulation_cell_base_3; }; +private: + Cell_label m_label = Cell_label::INSIDE; + +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + unsigned int m_erase_counter; +#endif + +public: Alpha_wrap_triangulation_cell_base_3() : Cb() {} @@ -55,8 +71,26 @@ class Alpha_wrap_triangulation_cell_base_3 : Cb(v0, v1, v2, v3, n0, n1, n2, n3) {} - bool is_outside() const { return outside; } - bool& is_outside() { return outside; } +public: + Cell_label label() const { return m_label; } + void set_label(const Cell_label label) { m_label = label; } + bool is_inside() const { return m_label == Cell_label::INSIDE; } + bool is_outside() const { return m_label == Cell_label::OUTSIDE; } + +#ifndef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + unsigned int erase_counter() const + { + return m_erase_counter; + } + void set_erase_counter(unsigned int c) + { + m_erase_counter = c; + } + void increment_erase_counter() + { + ++m_erase_counter; + } +#endif }; template diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Oracle_base.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Oracle_base.h index 85bd11c00b6c..5e105f830477 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Oracle_base.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Oracle_base.h @@ -49,7 +49,7 @@ struct AABB_tree_oracle_helper using GT = typename AABB_traits::Geom_traits; using FT = typename AABB_traits::FT; - using Point_3 = typename AABB_traits::Point_3; + using Point_3 = typename AABB_traits::Point; template static bool do_intersect(const Query& query, @@ -321,7 +321,6 @@ class AABB_tree_oracle typename AABB_tree::Bounding_box bbox() const { CGAL_precondition(!empty()); - return tree().bbox(); } diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Point_set_oracle.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Point_set_oracle.h index 7bad2ff313d4..d35a20c93fa4 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Point_set_oracle.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Point_set_oracle.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace CGAL { @@ -41,6 +42,7 @@ struct PS_oracle_traits using Geom_traits = Alpha_wrap_AABB_geom_traits; // Wrap the kernel to add Ball_3 + custom Do_intersect_3 using Points = std::vector; + using Points_ptr = std::shared_ptr; using PR_iterator = typename Points::const_iterator; using Primitive = AABB_primitive; // no caching - using AABB_traits = CGAL::AABB_traits; + using AABB_traits = CGAL::AABB_traits_3; using AABB_tree = CGAL::AABB_tree; }; @@ -69,26 +71,29 @@ class Point_set_oracle private: using Points = typename PSOT::Points; + using Points_ptr = typename PSOT::Points_ptr; using AABB_tree = typename PSOT::AABB_tree; using Oracle_base = AABB_tree_oracle; private: - Points m_points; + Points_ptr m_points_ptr; public: // Constructors - Point_set_oracle() - : Oracle_base(BaseOracle(), Base_GT()) - { } - Point_set_oracle(const BaseOracle& base_oracle, const Base_GT& gt = Base_GT()) : Oracle_base(base_oracle, gt) - { } + { + m_points_ptr = std::make_shared(); + } Point_set_oracle(const Base_GT& gt, const BaseOracle& base_oracle = BaseOracle()) - : Oracle_base(base_oracle, gt) + : Point_set_oracle(base_oracle, gt) + { } + + Point_set_oracle() + : Point_set_oracle(BaseOracle(), Base_GT()) { } public: @@ -101,21 +106,27 @@ class Point_set_oracle if(points.empty()) { #ifdef CGAL_AW3_DEBUG - std::cout << "Warning: Input is empty " << std::endl; + std::cout << "Warning: Input is empty (PS)" << std::endl; #endif return; } - const std::size_t old_size = m_points.size(); - m_points.insert(std::cend(m_points), std::cbegin(points), std::cend(points)); + const std::size_t old_size = m_points_ptr->size(); + m_points_ptr->insert(std::cend(*m_points_ptr), std::cbegin(points), std::cend(points)); #ifdef CGAL_AW3_DEBUG std::cout << "Insert into AABB tree (points)..." << std::endl; #endif - this->tree().insert(std::next(std::cbegin(m_points), old_size), std::cend(m_points)); + this->tree().insert(std::next(std::cbegin(*m_points_ptr), old_size), std::cend(*m_points_ptr)); + + // Manually constructing it here purely for profiling reasons: if we keep the lazy approach, + // it will be done at the first treatment of a facet that needs a Steiner point. + // So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes + // to accelerate the tree. + this->tree().accelerate_distance_queries(); - CGAL_postcondition(this->tree().size() == m_points.size()); + CGAL_postcondition(this->tree().size() == m_points_ptr->size()); } }; diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Segment_soup_oracle.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Segment_soup_oracle.h index d02a9f9faaf8..88d565c6b664 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Segment_soup_oracle.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Segment_soup_oracle.h @@ -17,9 +17,9 @@ #include #include -#include +#include #include -#include +#include #include #include #include @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace CGAL { @@ -40,7 +41,9 @@ struct SS_oracle_traits { using Geom_traits = Alpha_wrap_AABB_geom_traits; // Wrap the kernel to add Ball_3 + custom Do_intersect_3 - using Segments = std::vector; + using Segment = typename GT_::Segment_3; + using Segments = std::vector; + using Segments_ptr = std::shared_ptr; using SR_iterator = typename Segments::const_iterator; using Primitive = AABB_primitive; // no caching - using AABB_traits = CGAL::AABB_traits; + using AABB_traits = CGAL::AABB_traits_3; using AABB_tree = CGAL::AABB_tree; }; @@ -68,27 +71,31 @@ class Segment_soup_oracle using Geom_traits = typename SSOT::Geom_traits; private: + using Segment = typename SSOT::Segment; using Segments = typename SSOT::Segments; + using Segments_ptr = typename SSOT::Segments_ptr; using AABB_tree = typename SSOT::AABB_tree; using Oracle_base = AABB_tree_oracle; private: - Segments m_segments; + Segments_ptr m_segments_ptr; public: // Constructors - Segment_soup_oracle() - : Oracle_base(BaseOracle(), Base_GT()) - { } - Segment_soup_oracle(const BaseOracle& base_oracle, const Base_GT& gt = Base_GT()) : Oracle_base(base_oracle, gt) - { } + { + m_segments_ptr = std::make_shared(); + } Segment_soup_oracle(const Base_GT& gt, const BaseOracle& base_oracle = BaseOracle()) - : Oracle_base(base_oracle, gt) + : Segment_soup_oracle(base_oracle, gt) + { } + + Segment_soup_oracle() + : Segment_soup_oracle(BaseOracle(), Base_GT()) { } public: @@ -100,20 +107,40 @@ class Segment_soup_oracle if(segments.empty()) { #ifdef CGAL_AW3_DEBUG - std::cout << "Warning: Input is empty " << std::endl; + std::cout << "Warning: Input is empty (SS)" << std::endl; #endif return; } - const std::size_t old_size = m_segments.size(); - m_segments.insert(std::cend(m_segments), std::cbegin(segments), std::cend(segments)); + typename Geom_traits::Is_degenerate_3 is_degenerate = this->geom_traits().is_degenerate_3_object(); + + const std::size_t old_size = m_segments_ptr->size(); + + for(const Segment& s : segments) + { + if(is_degenerate(s)) + { +#ifdef CGAL_AW3_DEBUG + std::cerr << "Warning: ignoring degenerate segment " << s << std::endl; +#endif + continue; + } + + m_segments_ptr->push_back(s); + } #ifdef CGAL_AW3_DEBUG std::cout << "Insert into AABB tree (segments)..." << std::endl; #endif - this->tree().insert(std::next(std::cbegin(m_segments), old_size), std::cend(m_segments)); + this->tree().insert(std::next(std::cbegin(*m_segments_ptr), old_size), std::cend(*m_segments_ptr)); + + // Manually constructing it here purely for profiling reasons: if we keep the lazy approach, + // it will be done at the first treatment of a facet that needs a Steiner point. + // So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes + // to accelerate the tree. + this->tree().accelerate_distance_queries(); - CGAL_postcondition(this->tree().size() == m_segments.size()); + CGAL_postcondition(this->tree().size() == m_segments_ptr->size()); } }; diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_mesh_oracle.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_mesh_oracle.h index c87f82ac75fe..d7f325e1428f 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_mesh_oracle.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_mesh_oracle.h @@ -135,7 +135,7 @@ class Triangle_mesh_oracle if(is_empty(tmesh)) { #ifdef CGAL_AW3_DEBUG - std::cout << "Warning: Input is empty " << std::endl; + std::cout << "Warning: Input is empty (TM)" << std::endl; #endif return; } @@ -153,7 +153,12 @@ class Triangle_mesh_oracle for(face_descriptor f : faces(tmesh)) { if(Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, np)) + { +#ifdef CGAL_AW3_DEBUG + std::cerr << "Warning: ignoring degenerate face " << f << std::endl; +#endif continue; + } const Point_ref p0 = get(vpm, source(halfedge(f, tmesh), tmesh)); const Point_ref p1 = get(vpm, target(halfedge(f, tmesh), tmesh)); @@ -164,6 +169,12 @@ class Triangle_mesh_oracle Splitter_base::split_and_insert_datum(tr, this->tree(), this->geom_traits()); } + // Manually constructing it here purely for profiling reasons: if we keep the lazy approach, + // it will be done at the first treatment of a facet that needs a Steiner point. + // So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes + // to accelerate the tree. + this->tree().accelerate_distance_queries(); + #ifdef CGAL_AW3_DEBUG std::cout << "Tree: " << this->tree().size() << " primitives (" << num_faces(tmesh) << " faces in input)" << std::endl; #endif diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_soup_oracle.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_soup_oracle.h index 0a8f589fc2df..d066107f6207 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_soup_oracle.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/Triangle_soup_oracle.h @@ -18,8 +18,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -133,7 +133,7 @@ class Triangle_soup_oracle if(points.empty() || faces.empty()) { #ifdef CGAL_AW3_DEBUG - std::cout << "Warning: Input is empty " << std::endl; + std::cout << "Warning: Input is empty (TS)" << std::endl; #endif return; } @@ -164,11 +164,22 @@ class Triangle_soup_oracle const Triangle_3 tr = triangle(p0, p1, p2); if(is_degenerate(tr)) + { +#ifdef CGAL_AW3_DEBUG + std::cerr << "Warning: ignoring degenerate face " << tr << std::endl; +#endif continue; + } Splitter_base::split_and_insert_datum(tr, this->tree(), this->geom_traits()); } + // Manually constructing it here purely for profiling reasons: if we keep the lazy approach, + // it will be done at the first treatment of a facet that needs a Steiner point. + // So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes + // to accelerate the tree. + this->tree().accelerate_distance_queries(); + #ifdef CGAL_AW3_DEBUG std::cout << "Tree: " << this->tree().size() << " primitives (" << faces.size() << " faces in input)" << std::endl; #endif @@ -179,12 +190,31 @@ class Triangle_soup_oracle void add_triangle_soup(const TriangleRange& triangles, const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values()) { + if(triangles.empty()) + { +#ifdef CGAL_AW3_DEBUG + std::cout << "Warning: Input is empty (TS)" << std::endl; +#endif + return; + } + +#ifdef CGAL_AW3_DEBUG + std::cout << "Insert into AABB Tree (triangles)..." << std::endl; +#endif + typename Geom_traits::Is_degenerate_3 is_degenerate = this->geom_traits().is_degenerate_3_object(); + Splitter_base::reserve(triangles.size()); + for(const Triangle_3& tr : triangles) { if(is_degenerate(tr)) + { +#ifdef CGAL_AW3_DEBUG + std::cerr << "Warning: ignoring degenerate triangle " << tr << std::endl; +#endif continue; + } Splitter_base::split_and_insert_datum(tr, this->tree(), this->geom_traits()); } diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/gate_priority_queue.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/gate_priority_queue.h index 8d63e34c9e3b..ec48b4f458b6 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/gate_priority_queue.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/gate_priority_queue.h @@ -27,27 +27,29 @@ namespace CGAL { namespace Alpha_wraps_3 { namespace internal { +#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + // Represents an alpha-traversable facet in the mutable priority queue -template +template class Gate { - using Facet = typename DT3::Facet; - using FT = typename DT3::Geom_traits::FT; + using Facet = typename Tr::Facet; + using FT = typename Tr::Geom_traits::FT; private: Facet m_facet; FT m_priority; // circumsphere sq_radius - bool m_is_artificial_facet; + bool m_is_permissive_facet; public: // Constructors Gate(const Facet& facet, const FT& priority, - const bool is_artificial_facet) + const bool is_permissive_facet) : m_facet(facet), m_priority(priority), - m_is_artificial_facet(is_artificial_facet) + m_is_permissive_facet(is_permissive_facet) { CGAL_assertion(priority >= 0); } @@ -60,34 +62,85 @@ class Gate public: const Facet& facet() const { return m_facet; } const FT& priority() const { return m_priority; } - bool is_artificial_facet() const { return m_is_artificial_facet; } + bool is_permissive_facet() const { return m_is_permissive_facet; } }; struct Less_gate { - template - bool operator()(const Gate& a, const Gate& b) const + template + bool operator()(const Gate& a, const Gate& b) const { - // @fixme? make it a total order by comparing addresses if both gates are bbox facets - if(a.is_artificial_facet()) - return true; - else if(b.is_artificial_facet()) - return false; + // If one is permissive and the other is not, give priority to the permissive facet. + // + // The permissive facet are given highest priority because they need to be treated + // regardless of their circumradius. Treating them first allow the part that depends + // on alpha to be treated uniformly in a way: whatever the alpha, all permissive faces + // will first be treated. + if(a.is_permissive_facet() != b.is_permissive_facet()) + return a.is_permissive_facet(); + + if(a.priority() == b.priority()) + { + // arbitrary, the sole purpose is to make it a total order for determinism + if(a.facet().first->time_stamp() == b.facet().first->time_stamp()) + return a.facet().second < b.facet().second; + + return a.facet().first->time_stamp() < b.facet().first->time_stamp(); + } + return a.priority() > b.priority(); } }; -template +#else // CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + +// Represents an alpha-traversable facet in the mutable priority queue +template +class Gate +{ + using Facet = typename Tr::Facet; + using FT = typename Tr::Geom_traits::FT; + +private: + Facet m_facet, m_mirror_facet; + const unsigned int m_erase_counter_mem; + const unsigned int m_mirror_erase_counter_mem; + +public: + // Constructors + Gate(const Facet& facet, + const Tr& tr) + : + m_facet(facet), + m_mirror_facet(tr.mirror_facet(facet)), + m_erase_counter_mem(m_facet.first->erase_counter()), + m_mirror_erase_counter_mem(m_mirror_facet.first->erase_counter()) + { + } + +public: + const Facet& facet() const { return m_facet; } + + bool is_zombie() const + { + return (m_facet.first->erase_counter() != m_erase_counter_mem) || + (m_mirror_facet.first->erase_counter() != m_mirror_erase_counter_mem); + } +}; + +#endif // CGAL_AW3_USE_SORTED_PRIORITY_QUEUE + +template struct Gate_ID_PM { - using key_type = Gate; + using key_type = Gate; using value_type = std::size_t; using reference = std::size_t; using category = boost::readable_property_map_tag; inline friend value_type get(Gate_ID_PM, const key_type& k) { - using Facet = typename DT3::Facet; + using Facet = typename Tr::Facet; const Facet& f = k.facet(); return (4 * f.first->time_stamp() + f.second); diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/geometry_utils.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/geometry_utils.h index d3814d0f3b25..7f5f60de701c 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/geometry_utils.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/geometry_utils.h @@ -40,16 +40,16 @@ struct Orientation_of_circumcenter } }; -template +template bool -less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, - const typename Dt::Facet& fh, - const Dt& dt) +less_squared_radius_of_min_empty_sphere(typename Tr::Geom_traits::FT sq_alpha, + const typename Tr::Facet& fh, + const Tr& tr) { - using Cell_handle = typename Dt::Cell_handle; - using Point = typename Dt::Point; + using Cell_handle = typename Tr::Cell_handle; + using Point = typename Tr::Point; - using CK = typename Dt::Geom_traits; + using CK = typename Tr::Geom_traits; using Exact_kernel = typename Exact_kernel_selector::Exact_kernel; using Approximate_kernel = Simple_cartesian; using C2A = Cartesian_converter; @@ -61,21 +61,30 @@ less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, Orientation_of_circumcenter orientation_of_circumcenter; +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "Checking for traversability of facet" << std::endl; +#endif + const Cell_handle c = fh.first; const int ic = fh.second; const Cell_handle n = c->neighbor(ic); - const Point& p1 = dt.point(c, Dt::vertex_triple_index(ic,0)); - const Point& p2 = dt.point(c, Dt::vertex_triple_index(ic,1)); - const Point& p3 = dt.point(c, Dt::vertex_triple_index(ic,2)); + const Point& p1 = tr.point(c, Tr::vertex_triple_index(ic,0)); + const Point& p2 = tr.point(c, Tr::vertex_triple_index(ic,1)); + const Point& p3 = tr.point(c, Tr::vertex_triple_index(ic,2)); // This is not actually possible in the context of alpha wrapping, but keeping it for genericity // and because it does not cost anything. - if(dt.is_infinite(n)) + if(tr.is_infinite(n)) { +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cerr << "Warning: computing less_squared_radius_of_min_empty_sphere() with an infinite neighbor?" << std::endl; +#endif + CGAL_assertion(!tr.is_infinite(c)); + Orientation ori = orientation_of_circumcenter(p1, p2, p3, - dt.point(c, 0), dt.point(c, 1), - dt.point(c, 2), dt.point(c, 3)); + tr.point(c, 0), tr.point(c, 1), + tr.point(c, 2), tr.point(c, 3)); if(ori == POSITIVE) { @@ -84,18 +93,22 @@ less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, } else { - Comparison_result cr = compare_squared_radius(dt.point(c, 0), dt.point(c, 1), - dt.point(c, 2), dt.point(c, 3), + Comparison_result cr = compare_squared_radius(tr.point(c, 0), tr.point(c, 1), + tr.point(c, 2), tr.point(c, 3), sq_alpha); return cr == LARGER; } } - if(dt.is_infinite(c)) + if(tr.is_infinite(c)) { Orientation ori = orientation_of_circumcenter(p1, p2, p3, - dt.point(n, 0), dt.point(n, 1), - dt.point(n, 2), dt.point(n, 3)); + tr.point(n, 0), tr.point(n, 1), + tr.point(n, 2), tr.point(n, 3)); + +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "Cell 'c' is infinite; Orientation: " << ori << std::endl; +#endif if(ori == NEGATIVE) { @@ -104,8 +117,8 @@ less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, } else { - Comparison_result cr = compare_squared_radius(dt.point(n, 0), dt.point(n, 1), - dt.point(n, 2), dt.point(n, 3), + Comparison_result cr = compare_squared_radius(tr.point(n, 0), tr.point(n, 1), + tr.point(n, 2), tr.point(n, 3), sq_alpha); return cr == LARGER; } @@ -113,40 +126,40 @@ less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, // both c and n are finite if(orientation_of_circumcenter(p1, p2, p3, - dt.point(c, 0), dt.point(c, 1), dt.point(c, 2), dt.point(c, 3)) != + tr.point(c, 0), tr.point(c, 1), tr.point(c, 2), tr.point(c, 3)) != orientation_of_circumcenter(p1, p2, p3, - dt.point(n, 0), dt.point(n, 1), dt.point(n, 2), dt.point(n, 3))) + tr.point(n, 0), tr.point(n, 1), tr.point(n, 2), tr.point(n, 3))) { Comparison_result cr = compare_squared_radius(p1, p2, p3, sq_alpha); #ifdef CGAL_AW3_DEBUG_TRAVERSABILITY std::cout << "dual crosses the face; CR: " - << typename Dt::Geom_traits().compute_squared_radius_3_object()(p1, p2, p3) + << typename Tr::Geom_traits().compute_squared_radius_3_object()(p1, p2, p3) << " sq alpha " << sq_alpha << std::endl; #endif return cr == LARGER; } else { - Comparison_result cr = compare_squared_radius(dt.point(c, 0), dt.point(c, 1), - dt.point(c, 2), dt.point(c, 3), + Comparison_result cr = compare_squared_radius(tr.point(c, 0), tr.point(c, 1), + tr.point(c, 2), tr.point(c, 3), sq_alpha); #ifdef CGAL_AW3_DEBUG_TRAVERSABILITY std::cout << "dual does not cross the face; CR(c): " - << typename Dt::Geom_traits().compute_squared_radius_3_object()(dt.point(c, 0), dt.point(c, 1), - dt.point(c, 2), dt.point(c, 3)) + << typename Tr::Geom_traits().compute_squared_radius_3_object()(tr.point(c, 0), tr.point(c, 1), + tr.point(c, 2), tr.point(c, 3)) << " sq alpha " << sq_alpha << std::endl; #endif if(cr != LARGER) return false; - cr = compare_squared_radius(dt.point(n, 0), dt.point(n, 1), - dt.point(n, 2), dt.point(n, 3), + cr = compare_squared_radius(tr.point(n, 0), tr.point(n, 1), + tr.point(n, 2), tr.point(n, 3), sq_alpha); #ifdef CGAL_AW3_DEBUG_TRAVERSABILITY std::cout << "dual does not cross the face; CR(n): " - << typename Dt::Geom_traits().compute_squared_radius_3_object()(dt.point(n, 0), dt.point(n, 1), - dt.point(n, 2), dt.point(n, 3)) + << typename Tr::Geom_traits().compute_squared_radius_3_object()(tr.point(n, 0), tr.point(n, 1), + tr.point(n, 2), tr.point(n, 3)) << " sq alpha " << sq_alpha << std::endl; #endif @@ -154,6 +167,100 @@ less_squared_radius_of_min_empty_sphere(typename Dt::Geom_traits::FT sq_alpha, } } +template +typename Tr::Geom_traits::FT +smallest_squared_radius_3(const typename Tr::Facet& fh, + const Tr& tr) +{ + using Cell_handle = typename Tr::Cell_handle; + using Point = typename Tr::Point; + using FT = typename Tr::Geom_traits::FT; + + using CK = typename Tr::Geom_traits; + using Exact_kernel = typename Exact_kernel_selector::Exact_kernel; + using Approximate_kernel = Simple_cartesian; + using C2A = Cartesian_converter; + using C2E = typename Exact_kernel_selector::C2E; + + using Orientation_of_circumcenter = Filtered_predicate, + Orientation_of_circumcenter, + C2E, C2A>; + + Orientation_of_circumcenter orientation_of_circumcenter; + + auto squared_radius = tr.geom_traits().compute_squared_radius_3_object(); + +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "Computing circumradius of facet" << std::endl; +#endif + + CGAL_precondition(!tr.is_infinite(fh)); + + const Cell_handle c = fh.first; + const int ic = fh.second; + const Cell_handle n = c->neighbor(ic); + + const Point& p1 = tr.point(c, Tr::vertex_triple_index(ic,0)); + const Point& p2 = tr.point(c, Tr::vertex_triple_index(ic,1)); + const Point& p3 = tr.point(c, Tr::vertex_triple_index(ic,2)); + + // This is not actually possible in the context of alpha wrapping, but keeping it for genericity + // and because it does not cost anything. + if(tr.is_infinite(n)) + { + CGAL_assertion(!tr.is_infinite(c)); + + Orientation ori = orientation_of_circumcenter(p1, p2, p3, + tr.point(c, 0), tr.point(c, 1), + tr.point(c, 2), tr.point(c, 3)); + if(ori == POSITIVE) + return squared_radius(p1, p2, p3); + else + return squared_radius(tr.point(c, 0), tr.point(c, 1), tr.point(c, 2), tr.point(c, 3)); + } + + if(tr.is_infinite(c)) + { + Orientation ori = orientation_of_circumcenter(p1, p2, p3, + tr.point(n, 0), tr.point(n, 1), + tr.point(n, 2), tr.point(n, 3)); + +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "Cell 'c' is infinite; Orientation: " << ori << std::endl; +#endif + + if(ori == NEGATIVE) + return squared_radius(p1, p2, p3); + else + return squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2), tr.point(n, 3)); + } + + // both c and n are finite + if(orientation_of_circumcenter(p1, p2, p3, + tr.point(c, 0), tr.point(c, 1), tr.point(c, 2), tr.point(c, 3)) != + orientation_of_circumcenter(p1, p2, p3, + tr.point(n, 0), tr.point(n, 1), tr.point(n, 2), tr.point(n, 3))) + { +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "dual crosses the face; CR: " << squared_radius(p1, p2, p3) << std::endl; +#endif + + return squared_radius(p1, p2, p3); + } + else + { + const FT cr = squared_radius(tr.point(c, 0), tr.point(c, 1), tr.point(c, 2), tr.point(c, 3)); + const FT cnr = squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2), tr.point(n, 3)); + +#ifdef CGAL_AW3_DEBUG_TRAVERSABILITY + std::cout << "dual does not cross the face; CR(c): " << cr << " CRn: " << cnr << std::endl; +#endif + + return (CGAL::min)(cr, cnr); + } +} + + } // namespace internal } // namespace Alpha_wraps_3 } // namespace CGAL diff --git a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/splitting_helper.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/splitting_helper.h index 617c76fd1047..4f72b5583ed8 100644 --- a/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/splitting_helper.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/splitting_helper.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -83,7 +83,7 @@ struct Splitter_traversal_traits : public CGAL::internal::AABB_tree::Projection_traits { using Base = CGAL::internal::AABB_tree::Projection_traits; - using Point = typename AABBTraits::Point_3; + using Point = typename AABBTraits::Point; using Primitive = typename AABBTraits::Primitive; std::unordered_set visited_data; @@ -192,7 +192,7 @@ struct AABB_tree_splitter_traits CGAL::Tag_true /*external pmaps*/, CGAL::Tag_false /*no caching*/>; - using AABB_traits = CGAL::AABB_traits; + using AABB_traits = CGAL::AABB_traits_3; using AABB_tree = CGAL::AABB_tree; }; diff --git a/Alpha_wrap_3/test/Alpha_wrap_3/alpha_wrap_validation.h b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/validation.h similarity index 97% rename from Alpha_wrap_3/test/Alpha_wrap_3/alpha_wrap_validation.h rename to Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/validation.h index c9445c9e9c50..6d9a9191d5f4 100644 --- a/Alpha_wrap_3/test/Alpha_wrap_3/alpha_wrap_validation.h +++ b/Alpha_wrap_3/include/CGAL/Alpha_wrap_3/internal/validation.h @@ -117,7 +117,7 @@ bool has_expected_Hausdorff_distance(const TriangleMesh& wrap, template bool is_valid_wrap(const TriangleMesh& wrap, - const bool check_manifoldness = true, + const bool check_manifoldness, const NamedParameters& np = parameters::default_values()) { namespace PMP = CGAL::Polygon_mesh_processing; @@ -203,6 +203,13 @@ bool is_valid_wrap(const TriangleMesh& wrap, return true; } +template +bool is_valid_wrap(const TriangleMesh& wrap, + const NamedParameters& np = parameters::default_values()) +{ + return is_valid_wrap(wrap, true /*consider manifoldness*/, np); +} + template diff --git a/Alpha_wrap_3/include/CGAL/alpha_wrap_3.h b/Alpha_wrap_3/include/CGAL/alpha_wrap_3.h index 638c5fb9fa17..8be78675422e 100644 --- a/Alpha_wrap_3/include/CGAL/alpha_wrap_3.h +++ b/Alpha_wrap_3/include/CGAL/alpha_wrap_3.h @@ -105,7 +105,7 @@ void alpha_wrap_3(const PointRange& points, using NP_helper = Point_set_processing_3_np_helper; using Geom_traits = typename NP_helper::Geom_traits; using Oracle = Alpha_wraps_3::internal::Triangle_soup_oracle; - using AW3 = Alpha_wraps_3::internal::Alpha_wrap_3; + using AW3 = Alpha_wraps_3::internal::Alpha_wrapper_3; Geom_traits gt = choose_parameter(get_parameter(in_np, internal_np::geom_traits)); @@ -254,7 +254,7 @@ void alpha_wrap_3(const TriangleMesh& tmesh, using Geom_traits = typename GetGeomTraits::type; using Oracle = Alpha_wraps_3::internal::Triangle_mesh_oracle; - using AW3 = Alpha_wraps_3::internal::Alpha_wrap_3; + using AW3 = Alpha_wraps_3::internal::Alpha_wrapper_3; Geom_traits gt = choose_parameter(get_parameter(in_np, internal_np::geom_traits)); @@ -350,7 +350,7 @@ void alpha_wrap_3(const PointRange& points, using NP_helper = Point_set_processing_3_np_helper; using Geom_traits = typename NP_helper::Geom_traits; using Oracle = Alpha_wraps_3::internal::Point_set_oracle; - using AW3 = Alpha_wraps_3::internal::Alpha_wrap_3; + using AW3 = Alpha_wraps_3::internal::Alpha_wrapper_3; Geom_traits gt = choose_parameter(get_parameter(in_np, internal_np::geom_traits)); diff --git a/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies b/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies index 7645e1ba53c5..a3f60578532c 100644 --- a/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies +++ b/Alpha_wrap_3/package_info/Alpha_wrap_3/dependencies @@ -4,6 +4,7 @@ Alpha_wrap_3 Arithmetic_kernel BGL Box_intersection_d +CGAL_Core Cartesian_kernel Circulator Distance_2 diff --git a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_cavity_initializations.cpp b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_cavity_initializations.cpp index d7567bab205b..af94eecd25ae 100644 --- a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_cavity_initializations.cpp +++ b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_cavity_initializations.cpp @@ -1,12 +1,13 @@ #define CGAL_AW3_TIMER #define CGAL_AW3_DEBUG +#define CGAL_AW3_DEBUG_MANIFOLDNESS // #define CGAL_AW3_DEBUG_INITIALIZATION #include #include #include -#include "alpha_wrap_validation.h" +#include #include @@ -27,7 +28,7 @@ void generate_random_seeds(const Oracle& oracle, Seeds& seeds, CGAL::Random& r) { - const auto bbox = CGAL::Alpha_wraps_3::internal::Alpha_wrap_3(oracle).construct_bbox(offset); + const auto bbox = CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3(oracle).construct_bbox(offset); const double sq_offset = CGAL::square(offset); while(seeds.size() < 3) @@ -69,7 +70,7 @@ void alpha_wrap_triangle_mesh(Mesh& input_mesh, Oracle oracle; oracle.add_triangle_mesh(input_mesh); - AW3::internal::Alpha_wrap_3 aw3(oracle); + AW3::internal::Alpha_wrapper_3 aw3(oracle); if(seeds.empty()) generate_random_seeds(oracle, offset, seeds, r); diff --git a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_manifoldness.cpp b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_manifoldness.cpp index 638431e30567..954fe6163dff 100644 --- a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_manifoldness.cpp +++ b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_manifoldness.cpp @@ -1,11 +1,12 @@ #define CGAL_AW3_TIMER #define CGAL_AW3_DEBUG +#define CGAL_AW3_DEBUG_MANIFOLDNESS //#define CGAL_AW3_DEBUG_STEINER_COMPUTATION //#define CGAL_AW3_DEBUG_INITIALIZATION //#define CGAL_AW3_DEBUG_QUEUE #include -#include "alpha_wrap_validation.h" +#include #include #include diff --git a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_multiple_calls.cpp b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_multiple_calls.cpp index e2abc6f1f12f..35a954f17b20 100644 --- a/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_multiple_calls.cpp +++ b/Alpha_wrap_3/test/Alpha_wrap_3/test_AW3_multiple_calls.cpp @@ -1,10 +1,11 @@ #define CGAL_AW3_TIMER #define CGAL_AW3_DEBUG +#define CGAL_AW3_DEBUG_MANIFOLDNESS #include #include -#include "alpha_wrap_validation.h" +#include #include #include @@ -52,7 +53,7 @@ void alpha_wrap_triangle_soup(Points& pr, // AW3 Oracle oracle; oracle.add_triangle_soup(pr, fr); - AW3::internal::Alpha_wrap_3 aw3(oracle); + AW3::internal::Alpha_wrapper_3 aw3(oracle); Mesh wrap; aw3(alpha, offset, wrap, CGAL::parameters::do_enforce_manifoldness(false)); diff --git a/Alpha_wrap_3/test/Alpha_wrap_3/test_alpha_wrap_3_mesh.cpp b/Alpha_wrap_3/test/Alpha_wrap_3/test_alpha_wrap_3_mesh.cpp index 470d48e40d44..6209019a7a4d 100644 --- a/Alpha_wrap_3/test/Alpha_wrap_3/test_alpha_wrap_3_mesh.cpp +++ b/Alpha_wrap_3/test/Alpha_wrap_3/test_alpha_wrap_3_mesh.cpp @@ -1,12 +1,12 @@ #define CGAL_AW3_TIMER //#define CGAL_AW3_DEBUG -//#define CGAL_AW3_DEBUG_MANIFOLDNESS +#define CGAL_AW3_DEBUG_MANIFOLDNESS //#define CGAL_AW3_DEBUG_STEINER_COMPUTATION //#define CGAL_AW3_DEBUG_INITIALIZATION //#define CGAL_AW3_DEBUG_QUEUE #include -#include "alpha_wrap_validation.h" +#include #include #include diff --git a/Apollonius_graph_2/include/CGAL/Parabola_segment_2.h b/Apollonius_graph_2/include/CGAL/Parabola_segment_2.h index 7c03ff5d5e51..20883169ba7a 100644 --- a/Apollonius_graph_2/include/CGAL/Parabola_segment_2.h +++ b/Apollonius_graph_2/include/CGAL/Parabola_segment_2.h @@ -27,7 +27,7 @@ namespace Qt { } template < class Gt > -class Parabola_segment_2 : public Parabola_2< Gt > +struct Parabola_segment_2 : public Parabola_2< Gt > { typedef CGAL::Parabola_2 Base; typedef typename Base::Site_2 Site_2; @@ -39,10 +39,10 @@ class Parabola_segment_2 : public Parabola_2< Gt > using Base::t; using Base::f; -protected: + Point_2 p1, p2; -public: + Parabola_segment_2() : Parabola_2< Gt >() {} template diff --git a/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h b/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h index b1c1b1532e83..1ac38a242340 100644 --- a/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h +++ b/Arithmetic_kernel/include/CGAL/BOOST_MP_arithmetic_kernel.h @@ -19,6 +19,10 @@ #ifdef CGAL_USE_BOOST_MP +#ifdef CGAL_USE_CORE +#include +#endif + //Currently already included in boost_mp.h //#include //#ifdef CGAL_USE_GMP @@ -26,20 +30,29 @@ //#endif // FIXME: the could be several kernels based on Boost.Multiprecision. - namespace CGAL { /** \ingroup CGAL_Arithmetic_kernel * \brief The Boost.Multiprecision set of exact number types */ + +#if !defined(CGAL_USE_CORE) || defined(CGAL_CORE_USE_GMP_BACKEND) struct BOOST_cpp_arithmetic_kernel : internal::Arithmetic_kernel_base { typedef boost::multiprecision::cpp_int Integer; typedef boost::multiprecision::cpp_rational Rational; }; +#else +typedef CORE_arithmetic_kernel BOOST_cpp_arithmetic_kernel; +#endif + #ifdef CGAL_USE_GMP +#if !defined(CGAL_USE_CORE) || !defined(CGAL_CORE_USE_GMP_BACKEND) struct BOOST_gmp_arithmetic_kernel : internal::Arithmetic_kernel_base { typedef boost::multiprecision::mpz_int Integer; typedef boost::multiprecision::mpq_rational Rational; }; +#else +typedef CORE_arithmetic_kernel BOOST_gmp_arithmetic_kernel; +#endif #endif template diff --git a/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h b/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h index c619f418de08..67586065fa5f 100644 --- a/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h +++ b/Arithmetic_kernel/include/CGAL/CORE_arithmetic_kernel.h @@ -55,14 +55,6 @@ class CORE_arithmetic_kernel : public internal::Arithmetic_kernel_base { }; -template <> -struct Get_arithmetic_kernel{ - typedef CORE_arithmetic_kernel Arithmetic_kernel; -}; -template <> -struct Get_arithmetic_kernel{ - typedef CORE_arithmetic_kernel Arithmetic_kernel; -}; template <> struct Get_arithmetic_kernel{ typedef CORE_arithmetic_kernel Arithmetic_kernel; @@ -74,6 +66,8 @@ struct Get_arithmetic_kernel{ } //namespace CGAL +#include + #endif // CGAL_USE_CORE #endif // CGAL_CORE_ARITHMETIC_KERNEL_H diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementTypes.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementTypes.h index 417ffbd3fd07..ec9e20b6c985 100644 --- a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementTypes.h +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/ArrangementTypes.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/IntersectCurves.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/IntersectCurves.cpp index 5e98113dabc5..2a97cf191936 100644 --- a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/IntersectCurves.cpp +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/IntersectCurves.cpp @@ -11,7 +11,7 @@ #include "IntersectCurves.h" #include "ArrangementTypes.h" -#include +#include template diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp index df21f6699ab9..b6bd521f6224 100644 --- a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2/Utils/Utils.cpp @@ -202,7 +202,7 @@ operator()(const Point_2& p, const X_monotone_curve_2& curve) const { auto points = painterOstream.getPointsList(curve); QPoint p_viewport = - view->mapFromScene(QPointF{p.x().doubleValue(), p.y().doubleValue()}); + view->mapFromScene(QPointF{CGAL::to_double(p.x()), CGAL::to_double(p.y())}); double min_dist = (std::numeric_limits::max)(); for (auto& vec : points) { diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.cpp new file mode 100644 index 000000000000..05c60f9dbb48 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.cpp @@ -0,0 +1,1384 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Aos.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include "Aos_defs.h" +#include "arr_print.h" +#include "Tools.h" + +using json = nlohmann::ordered_json; + +namespace { + // use this traits everytime you construct an arrangment! + static Geom_traits s_traits; + + // Extended DCEL & Arrangement + struct Flag { + bool v; + Flag() : v{ false } {} + Flag(bool init) : v{ init } {} + }; + + // EXTENDED AOS for analysing the arrangement + using Ext_dcel = CGAL::Arr_extended_dcel; + using Ext_topol_traits = + CGAL::Arr_spherical_topology_traits_2; + using Ext_aos = CGAL::Arrangement_on_surface_2; + using Dir3 = Kernel::Direction_3; + using Approximate_point_2 = Geom_traits::Approximate_point_2; + using Approximate_number_type = Geom_traits::Approximate_number_type; + using Approximate_kernel = Geom_traits::Approximate_kernel; + using Approximate_Vector_3 = CGAL::Vector_3; + using Approximate_Direction_3 = Approximate_kernel::Direction_3; + using Direction_3 = Kernel::Direction_3; + + //--------------------------------------------------------------------------- + // below are the helper functions used to construct the arcs from KML data + // TODO: Revisit handling of INNER & OUTER boundaries + using Curves = std::vector; + + // get curves for the given kml placemark + // NOTE: this is defined here to keep the definitions local to this cpp file + Curves get_arcs(const Kml::Placemark& placemark) { + //Geom_traits traits; + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + std::vector xcvs; + for (const auto& polygon : placemark.polygons) { + // colect all rings into a single list (FOR NOW!!!) + // TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!! + Kml::LinearRings linear_rings; + linear_rings.push_back(polygon.outer_boundary); + for (const auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(inner_boundary); + + + // convert the nodes to points on unit-sphere + for (const auto& lring : linear_rings) { + std::vector sphere_points; + for (const auto& node : lring.nodes) { + const auto p = node.get_coords_3d(); + Approximate_Vector_3 v(p.x, p.y, p.z); + sphere_points.push_back(v); + } + + // add geodesic arcs for the current LinearRing + std::size_t num_points = sphere_points.size(); + for (std::size_t i = 0; i < num_points - 1; ++i) { + const auto p1 = sphere_points[i]; + const auto p2 = sphere_points[i + 1]; + xcvs.push_back(ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()), + ctr_p(p2.x(), p2.y(), p2.z()))); + } + } + } + + return xcvs; + } + + + // this one is used by the Aos::check and Aos::ext_check functions + std::size_t num_counted_nodes = 0; + std::size_t num_counted_arcs = 0; + std::size_t num_counted_polygons = 0; + std::map vertex_node_map; + + template + Curves get_arcs(const Kml::Placemarks& placemarks, Arr_type& arr) { + //Geom_traits traits; + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + num_counted_nodes = 0; + num_counted_arcs = 0; + num_counted_polygons = 0; + std::vector xcvs; + for (const auto& pm : placemarks) { + for (const auto& polygon : pm.polygons) { + ++num_counted_polygons; + + // colect all rings into a single list (FOR NOW!!!) + // TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!! + Kml::LinearRings linear_rings; + linear_rings.push_back(polygon.outer_boundary); + for (const auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(inner_boundary); + + // loop on outer and inner boundaries + for (const auto& lring : linear_rings) { + // convert the nodes to points on unit-sphere + std::vector sphere_points; + for (const auto& node : lring.nodes) { + ++num_counted_nodes; + const auto p = node.get_coords_3d(); + Approximate_Vector_3 v(p.x, p.y, p.z); + sphere_points.push_back(v); + auto vh CGAL_UNUSED = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z)); + if constexpr (std::is_same::value) + { + vertex_node_map.insert(std::make_pair(vh, node)); + } + } + + // add curves + std::size_t num_points = sphere_points.size(); + for (std::size_t i = 0; i < num_points - 1; ++i) { + ++num_counted_arcs; + const auto p1 = sphere_points[i]; + const auto p2 = sphere_points[i + 1]; + auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()), + ctr_p(p2.x(), p2.y(), p2.z())); + xcvs.push_back(xcv); + } + } + } + } + return xcvs; + } + + + Aos::Approx_arc get_approx_curve(Curve xcv, double error) { + //Geom_traits traits; + auto approx = s_traits.approximate_2_object(); + std::vector approx_curve; + { + std::vector v; + approx(xcv, error, std::back_insert_iterator(v)); + + for (const auto& p : v) { + const QVector3D arc_point(p.dx(), p.dy(), p.dz()); + approx_curve.push_back(arc_point); + } + } + + return approx_curve; + } + Aos::Approx_arcs get_approx_curves(std::vector& xcvs, double error) { + Aos::Approx_arcs approx_curves; + for (const auto& xcv : xcvs) { + auto approx_curve = get_approx_curve(xcv, error); + approx_curves.push_back(std::move(approx_curve)); + } + + return approx_curves; + } +} + +Aos::Approx_arc Aos::get_approx_identification_curve(double error) { + //Geom_traits traits; + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + // identification curve (meridian pierced by NEGATIVE Y-AXIS) + auto xcv = ctr_cv(ctr_p(0, 0, -1), ctr_p(0, 0, 1), Dir3(0, 1, 0)); + + auto approx = s_traits.approximate_2_object(); + Approx_arc approx_arc; + { + std::vector v; + approx(xcv, error, std::back_insert_iterator(v)); + for (const auto& p : v) { + const QVector3D arc_point(p.dx(), p.dy(), p.dz()); + approx_arc.push_back(arc_point); + } + } + + return approx_arc; +} + +//! \brief +Aos::Approx_arcs Aos::get_approx_arcs(double error) { + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + std::vector xcvs; + xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0))); + xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 0, 1))); + xcvs.push_back(ctr_cv(ctr_p(0, 1, 0), ctr_p(0, 0, 1))); + //xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0), Dir3(0, 0, -1))); + //xcvs.push_back(ctr_cv(Dir3(0, 0, -1))); + + auto approx_arcs = get_approx_curves(xcvs, error); + return approx_arcs; +} + +//! \brief +auto Aos::get_approx_arcs(const Kml::Placemark& placemark, double error) -> + Approx_arcs { + auto xcvs = get_arcs(placemark); + auto arcs = ::get_approx_curves(xcvs, error); + return arcs; +} + +//! \brief +void Aos::check(const Kml::Placemarks& placemarks) { + //Geom_traits traits; + Arrangement arr(&s_traits); + + auto xcvs = get_arcs(placemarks, arr); + std::cout << "-------------------------------\n"; + std::cout << "num arr vertices (before adding arcs) = " << + arr.number_of_vertices() << std::endl; + // add arcs + for (auto xcv : xcvs) + CGAL::insert(arr, xcv); + + std::cout << "-------------------------------\n"; + std::cout << "num nodes = " << num_counted_nodes << std::endl; + std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num counted arcs = " << num_counted_arcs << std::endl; + std::cout << "num arr edges = " << arr.number_of_edges() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num polygons = " << num_counted_polygons << std::endl; + std::cout << "num arr faces = " << arr.number_of_faces() << std::endl; +} + +//! \brief +std::vector Aos::ext_check(const Kml::Placemarks& placemarks) { + // Construct the arrangement from 12 geodesic arcs. + Ext_aos arr(&s_traits); + + std::cout << "-------------------------------\n"; + std::cout << "** num arr FACES (before adding arcs) = " << + arr.number_of_faces() << std::endl; + + auto xcvs = get_arcs(placemarks, arr); + + // MARK all vertices as true + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) + vit->set_data(Flag(true)); + + std::cout << "-------------------------------\n"; + std::cout << "num arr vertices (before adding arcs) = " << + arr.number_of_vertices() << std::endl; + + // add arcs + for (auto& xcv : xcvs) CGAL::insert(arr, xcv); + + // extract all vertices that are ADDED when inserting the arcs! + std::size_t num_created_vertices = 0; + std::vector created_vertices; + auto approx = s_traits.approximate_2_object(); + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) { + if (vit->data().v == false) { + std::cout << "-------------------------------------\n"; + std::cout << vit->point() << std::endl; + + if (2 == vit->degree()) {} //continue; + + if (1 == vit->degree()) { + auto p = vit->point(); + // auto p2 = p.location(); + std::cout << " deg-1 vertex = " << p << std::endl; + std::cout << " deg-1 vertex: " << std::boolalpha + << vit->incident_halfedges()->target()->data().v << std::endl; + } + + + ++num_created_vertices; + auto p = vit->point(); + auto ap = approx(p); + QVector3D new_vertex(ap.dx(), ap.dy(), ap.dz()); + new_vertex.normalize(); + std::cout << new_vertex << std::endl; + std::cout << "degree = " << vit->degree() << std::endl; + + created_vertices.push_back(new_vertex); + + // find the arcs that are adjacent to the vertex of degree 4 + if (4 == vit->degree()) { + std::cout << "**************************\n DEGREE 4 VERTEX: \n"; + const auto first = vit->incident_halfedges(); + auto curr = first; + do { + auto tvh = curr->twin()->target(); + //std::cout << std::boolalpha << svh->data().v << " - " + // << tvh->data().v << std::endl; + auto it = vertex_node_map.find(tvh); + if (it != vertex_node_map.end()) + std::cout << std::setprecision(16) << it->second << std::endl; + else + std::cout << "NOT FOUND!!\n"; + } while (++curr != first); + } + + std::cout << "\n"; + } + } + Kml::Node n{ 180.0, -84.71338 }; + std::cout << "Node itself = " << n.get_coords_3d() << std::endl; + std::cout << "*** num created vertices = " << num_created_vertices + << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num nodes = " << num_counted_nodes << std::endl; + std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num counted arcs = " << num_counted_arcs << std::endl; + std::cout << "num arr edges = " << arr.number_of_edges() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num polygons = " << num_counted_polygons << std::endl; + std::cout << "num arr faces = " << arr.number_of_faces() << std::endl; + + return created_vertices; +} + +//! \brief +std::vector Aos::ext_check_id_based(Kml::Placemarks& placemarks) { + // Construct the arrangement from 12 geodesic arcs. + Ext_aos arr(&s_traits); + + auto nodes = Kml::generate_ids(placemarks); + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + std::size_t num_counted_arcs = 0; + std::size_t num_counted_polygons = 0; + // + std::vector points; + std::vector vertices; + for (const auto& node : nodes) { + auto n = node.get_coords_3d(); + auto p = ctr_p(n.x, n.y, n.z); + auto v = CGAL::insert_point(arr, p); + points.push_back(p); + vertices.push_back(v); + //arr.insert_at_vertices(Segment(p, p), v, v); + } + std::cout << "num nodes = " << nodes.size() << std::endl; + std::cout << "num points = " << points.size() << std::endl; + // MARK all vertices as true + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) + vit->set_data(Flag(true)); + + + for (auto& placemark : placemarks) { + for (auto& polygon : placemark.polygons) { + ++num_counted_polygons; + + // TO DO : ADD the outer boundaries! + auto& ids = polygon.outer_boundary.ids; + std::size_t num_nodes = ids.size(); + for (std::size_t i = 0; i < num_nodes - 1; ++i) { + ++num_counted_arcs; + const auto nid1 = ids[i]; + const auto nid2 = ids[i + 1]; + auto p1 = points[nid1]; + auto p2 = points[nid2]; + CGAL::insert(arr, ctr_cv(p1, p2)); + } + } + } + + std::cout << "-------------------------------\n"; + std::cout << "num arr vertices (before adding arcs) = " << + arr.number_of_vertices() << std::endl; + + // extract all vertices that are ADDED when inserting the arcs! + std::size_t num_created_vertices = 0; + std::vector created_vertices; + auto approx = s_traits.approximate_2_object(); + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) { + // auto& d = vit->data(); + if (vit->data().v == false) { + std::cout << "-------------------------------------\n"; + std::cout << vit->point() << std::endl; + + if (2 == vit->degree()) {} //continue; + + if (1 == vit->degree()) { + auto p = vit->point(); + // auto p2 = p.location(); + std::cout << "deg-1 vertex = " << p << std::endl; + std::cout << "deg-1 vertex: " << std::boolalpha + << vit->incident_halfedges()->target()->data().v << std::endl; + } + + + ++num_created_vertices; + auto p = vit->point(); + auto ap = approx(p); + QVector3D new_vertex(ap.dx(), ap.dy(), ap.dz()); + new_vertex.normalize(); + std::cout << new_vertex << std::endl; + std::cout << "degree = " << vit->degree() << std::endl; + + created_vertices.push_back(new_vertex); + + //// find the arcs that are adjacent to this vertex + //const auto first = vit->incident_halfedges(); + //auto curr = first; + //do { + + //} while (++curr != first); + std::cout << std::endl; + } + } + std::cout << "*** num created vertices = " << num_created_vertices + << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num nodes = " << nodes.size() << std::endl; + std::cout << "num arr vertices = " << arr.number_of_vertices() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num counted arcs = " << num_counted_arcs << std::endl; + std::cout << "num arr edges = " << arr.number_of_edges() << std::endl; + + std::cout << "-------------------------------\n"; + std::cout << "num polygons = " << num_counted_polygons << std::endl; + std::cout << "num arr faces = " << arr.number_of_faces() << std::endl; + + return created_vertices; +} + +//! brief +auto Aos::find_new_faces(Kml::Placemarks& placemarks) -> Approx_arcs { + //Geom_traits traits; + Ext_aos arr(&s_traits); + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + auto fh = arr.faces_begin(); + fh->data().v = true; + std::cout << "num faces = " << arr.number_of_faces() << std::endl; + + auto nodes = Kml::generate_ids(placemarks); + + //------------------------------------------------------------------------- + // define a set of vertex-handles: use this to check if the face is + // obtained from the polygon definition, or if it is an additional face + using Vertex_handle = Ext_aos::Vertex_handle; + std::map vertex_id_map; + std::set> all_polygon_node_ids; + + num_counted_nodes = 0; + num_counted_arcs = 0; + num_counted_polygons = 0; + std::vector xcvs; + for (auto& pm : placemarks) { + std::cout << pm.name << std::endl; + for (auto& polygon : pm.polygons) { + ++num_counted_polygons; + + // colect all rings into a single list (FOR NOW!!!) + // TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!! + auto linear_rings = polygon.get_all_boundaries(); + + auto* lring = &polygon.outer_boundary; + { + std::set polygon_node_ids; + + // convert the nodes to points on unit-sphere + std::vector sphere_points; + //for (const auto& node : lring->nodes) + //std::cout << " NUM POLYGON-NODES SIZE = " + // << lring->ids.size() << std::endl; + for (std::size_t i = 0; i < lring->ids.size(); ++i) { + ++num_counted_nodes; + const auto id = lring->ids[i]; + const auto& node = lring->nodes[i]; + const auto p = node.get_coords_3d(); + Approximate_Vector_3 v(p.x, p.y, p.z); + sphere_points.push_back(v); + auto vh = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z)); + polygon_node_ids.insert(id); + vertex_id_map[vh] = id; + vh->data().v = true; + } + //std::cout << " POLYGON-NODES SET SIZE = " + // << polygon_node_ids.size() << std::endl; + if (lring->ids.size() != (1 + polygon_node_ids.size())) + std::cout << "*** ASSERTION ERROR!!!!\n"; + + all_polygon_node_ids.insert(std::move(polygon_node_ids)); + + // add curves + auto num_points = sphere_points.size(); + for (std::size_t i = 0; i < num_points - 1; ++i) { + ++num_counted_arcs; + const auto p1 = sphere_points[i]; + const auto p2 = sphere_points[i + 1]; + auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()), + ctr_p(p2.x(), p2.y(), p2.z())); + CGAL::insert(arr, xcv); + } + } + } + } + + + // mark all faces as TRUE (= as existing faces) + std::size_t num_not_found = 0; + std::vector new_face_arcs; + for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh) { + // skip the spherical face + std::cout << "num outer_ccbs = " << fh->number_of_outer_ccbs() << std::endl; + if (fh->number_of_outer_ccbs() == 0) continue; + + // construct the set of all node-ids for the current face + std::set face_node_ids_set; + std::vector face_node_ids; + std::vector face_arcs; + auto first = fh->outer_ccb(); + auto curr = first; + do { + auto vh = curr->source(); + // skip if the vertex is due to intersection with the identification curve + if ((vh->data().v == false) && (vh->degree() == 2)) continue; + + auto id = vertex_id_map[vh]; + face_node_ids_set.insert(id); + + face_arcs.push_back(ctr_cv(curr->source()->point(), + curr->target()->point())); + } while (++curr != first); + //std::cout << "counted vertices = " << num_vertices << std::endl; + //std::cout << "vertices in the set = " << polygon_node_ids.size() + // << std::endl; + + auto it = all_polygon_node_ids.find(face_node_ids_set); + if (it == all_polygon_node_ids.cend()) { + std::cout << "NOT FOUND!!!\n"; + std::cout << "num nodes = " << face_node_ids_set.size() << std::endl; + ++num_not_found; + new_face_arcs.insert(new_face_arcs.end(), face_arcs.begin(), + face_arcs.end()); + } + } + std::cout << "num not found = " << num_not_found << std::endl; + + auto approx_arcs = get_approx_curves(new_face_arcs, 0.001); + return approx_arcs; +} + +//! \brief +void Aos::save_arr(Kml::Placemarks& placemarks, const std::string& file_name) { +#ifndef USE_EPIC + //Geom_traits traits; + Ext_aos arr(&s_traits); + auto ctr_p = s_traits.construct_point_2_object(); + auto ctr_cv = s_traits.construct_curve_2_object(); + + auto fh = arr.faces_begin(); + fh->data().v = true; + std::cout << "num faces = " << arr.number_of_faces() << std::endl; + + auto nodes = Kml::generate_ids(placemarks); + + //------------------------------------------------------------------------- + // define a set of vertex-handles: use this to check if the face is + // obtained from the polygon definition, or if it is an additional face + using Vertex_handle = Ext_aos::Vertex_handle; + using Face_handle = Ext_aos::Face_handle; + std::map vertex_id_map; + std::map, std::string> all_polygon_node_ids_map; + + // map to associate the created faces with the country names + // CAUTION: the newly created faces + + num_counted_nodes = 0; + num_counted_arcs = 0; + num_counted_polygons = 0; + std::vector xcvs; + for (auto& pm : placemarks) { + std::cout << pm.name << std::endl; + for (auto& polygon : pm.polygons) { + ++num_counted_polygons; + + auto* lring = &polygon.outer_boundary; + { + // auto num_faces_before = arr.number_of_faces(); + std::set polygon_node_ids; + + // convert the nodes to points on unit-sphere + std::vector sphere_points; + //for (const auto& node : lring->nodes) + //std::cout << " NUM POLYGON-NODES SIZE = " << lring->ids.size() + // << std::endl; + for (std::size_t i = 0; i < lring->ids.size(); ++i) { + ++num_counted_nodes; + const auto id = lring->ids[i]; + const auto& node = lring->nodes[i]; + const auto p = node.get_coords_3d(); + Approximate_Vector_3 v(p.x, p.y, p.z); + sphere_points.push_back(v); + auto vh = CGAL::insert_point(arr, ctr_p(p.x, p.y, p.z)); + polygon_node_ids.insert(id); + vertex_id_map[vh] = id; + vh->data().v = true; + } + //std::cout << " POLYGON-NODES SET SIZE = " + // << polygon_node_ids.size() << std::endl; + if (lring->ids.size() != (1 + polygon_node_ids.size())) + std::cout << "*** ASSERTION ERROR!!!!\n"; + + all_polygon_node_ids_map.insert(std::make_pair( + std::move(polygon_node_ids), pm.name)); + + // add curves + std::size_t num_points = sphere_points.size(); + for (std::size_t i = 0; i < num_points - 1; ++i) { + ++num_counted_arcs; + const auto p1 = sphere_points[i]; + const auto p2 = sphere_points[i + 1]; + auto xcv = ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()), + ctr_p(p2.x(), p2.y(), p2.z())); + CGAL::insert(arr, xcv); + } + } + } + } + + std::cout << "*** arr.number_of_faces = " << arr.number_of_faces() + << std::endl; + std::cout << "*** arr.number_of_halfedges = " << arr.number_of_halfedges() + << std::endl; + std::cout << "*** arr.number_of_vertices = " << arr.number_of_vertices() + << std::endl; + + // DEFINE JSON OBJECT + json js; + auto& js_points = js["points"] = json::array(); + + //////////////////////////////////////////////////////////////////////////// + // POINTS + // define a map from each vertex to its position in the arrangement + //auto get_num_denum + using FT = typename Kernel::FT; + //using json = nlohmann::ordered_json; + FT ft(0); + auto ex = ft.exact(); + CGAL::Rational_traits rt; + typename CGAL::Algebraic_structure_traits::Simplify simplify; + + auto set_num_denum = [&](decltype(ex)& x, json& ratx) { + simplify(x); + std::stringstream ss_x_num; + CGAL::IO::set_ascii_mode(ss_x_num); + ss_x_num << rt.numerator(x); + std::string xnum; + ss_x_num >> xnum; + ratx["num"] = xnum; + + std::stringstream ss_x_den; + CGAL::IO::set_ascii_mode(ss_x_den); + ss_x_den << rt.denominator(x); + std::string xden; + ss_x_den >> xden; + ratx["den"] = xden; + }; + + using Point_ = std::decay_tpoint())>; + std::map point_pos_map; + std::vector points; + std::map vertex_pos_map; + for (auto vh = arr.vertices_begin(); vh != arr.vertices_end(); ++vh) { + // add the vertex if not found in the map + auto it = vertex_pos_map.find(vh); + if (it == vertex_pos_map.end()) { + std::size_t new_vh_pos = vertex_pos_map.size(); + vertex_pos_map[vh] = new_vh_pos; + + // write the vertex-data to JSON object + auto& p = vh->point(); + points.push_back(p); + point_pos_map[&p] = new_vh_pos; + auto dx = p.dx().exact(); + auto dy = p.dy().exact(); + auto dz = p.dz().exact(); + + json jv; + jv["location"] = p.location(); + set_num_denum(dx, jv["dx"]); + set_num_denum(dy, jv["dy"]); + set_num_denum(dz, jv["dz"]); + js_points.push_back(std::move(jv)); + } + } + + //////////////////////////////////////////////////////////////////////////// + // CURVES + // define a map from each curve to its position in the arrangment + auto& js_curves = js["curves"] = json::array(); + using Ext_curve = Ext_aos::X_monotone_curve_2; + std::map curve_pos_map; + std::size_t num_edges = 0; + for (auto eh = arr.edges_begin(); eh != arr.edges_end(); ++eh) { + ++num_edges; + auto& xcv = eh->curve(); + auto it = curve_pos_map.find(&xcv); + if (it == curve_pos_map.end()) { + std::size_t new_xcv_pos = curve_pos_map.size(); + curve_pos_map[&xcv] = new_xcv_pos; + + json je; + auto& sv = xcv.source(); + auto& tv = xcv.target(); + auto svit = std::find(points.begin(), points.end(), sv); + auto tvit = std::find(points.begin(), points.end(), tv); + auto svp = std::distance(points.begin(), svit); + auto tvp = std::distance(points.begin(), tvit); + //std::cout << "svp= " << point_pos_map[(void*)&sv] << std::endl; + je["source"] = svp; + je["target"] = tvp; + auto& je_normal = je["normal"]; + + // write the vertex-data to JSON object + auto& n = xcv.normal(); + auto dx = n.dx().exact(); + auto dy = n.dy().exact(); + auto dz = n.dz().exact(); + set_num_denum(dx, je_normal["dx"]); + set_num_denum(dy, je_normal["dy"]); + set_num_denum(dz, je_normal["dz"]); + + je["is_vertical"] = xcv.is_vertical(); + je["is_directed_right"] = xcv.is_directed_right(); + je["is_full"] = xcv.is_full(); + + js_curves.push_back(std::move(je)); + } + } + std::cout << "num edges = " << num_edges << std::endl; + std::cout << "curve map size = " << curve_pos_map.size() << std::endl; + std::cout << "js_curves size = " << js_curves.size() << std::endl; + + //////////////////////////////////////////////////////////////////////////// + // VERTICES + // there is a one-to-one corresponce between vertices and points + auto& js_vertices = js["vertices"] = json::array(); + for (auto vh = arr.vertices_begin(); vh != arr.vertices_end(); ++vh) { + json js_vertex; + auto vpos = vertex_pos_map[vh]; + js_vertex["point"] = vpos; + js_vertices.push_back(std::move(js_vertex)); + } + + //////////////////////////////////////////////////////////////////////////// + // EDGES + num_edges = 0; + auto& js_edges = js["edges"] = json::array(); + std::map halfedge_pos_map; + for (auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit) { + auto& edge = *eit; + auto& xcv = edge.curve(); + auto xcvp = curve_pos_map[&xcv]; + json js_edge; + js_edge["curve"] = xcvp; + js_edge["direction"] = edge.direction(); + js_edge["source"] = vertex_pos_map[edge.source()]; + js_edge["target"] = vertex_pos_map[edge.target()]; + js_edges.push_back(std::move(js_edge)); + ++num_edges; + + // add the halfedge indices to the map + std::size_t new_halfedge_index = halfedge_pos_map.size(); + auto& twin = *edge.twin(); + halfedge_pos_map[&edge] = new_halfedge_index; + halfedge_pos_map[&twin] = new_halfedge_index + 1; + } + std::cout << "EDGE CHECKS:\n"; + std::cout << " *** num edges = " << num_edges << std::endl; + std::cout << " *** js_edges size = " << js_edges.size() << std::endl; + + + //////////////////////////////////////////////////////////////////////////// + // FACES + // CONDITION DATA: Caspian Sea needs to be defined + num_edges = 0; + std::size_t num_not_found = 0; + std::map face_name_map; + for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh) { + // skip the spherical face + std::cout << "num outer_ccbs = " << fh->number_of_outer_ccbs() << std::endl; + if (fh->number_of_outer_ccbs() == 0) continue; + + // construct the set of all node-ids for the current face + std::set face_node_ids_set; + std::vector face_node_ids; + auto first = fh->outer_ccb(); + auto curr = first; + do { + auto vh = curr->source(); + // skip if the vertex is due to intersection with the identification curve + if ((vh->data().v == false) && (vh->degree() == 2)) continue; + + auto id = vertex_id_map[vh]; + face_node_ids_set.insert(id); + } while (++curr != first); + + std::string name; + auto it = all_polygon_node_ids_map.find(face_node_ids_set); + if (it == all_polygon_node_ids_map.cend()) + { + std::cout << "NOT FOUND!!!\n"; + std::cout << "num nodes = " << face_node_ids_set.size() << std::endl; + ++num_not_found; + name = "Caspian Sea"; + } + else { + // ++num_found; + name = it->second; + } + face_name_map[fh] = name; + } + std::cout << "num not found = " << num_not_found << std::endl; + + + // RECORD FACES + json& js_faces = js["faces"] = json::array(); + auto get_ccb_json = [&](Ext_aos::Ccb_halfedge_circulator first) { + json js_edges; + auto& ccb_edge_indices = js_edges["halfedges"] = json::array(); + auto curr = first; + do { + auto& he = *curr; + auto it = halfedge_pos_map.find(&he); + if (it == halfedge_pos_map.end()) + { + std::cout << "ASSERTION ERROR!!!" << std::endl; + } + + auto edge_pos = it->second; + ccb_edge_indices.push_back(edge_pos); + } while (++curr != first); + + return js_edges; + }; + + std::size_t total_num_half_edges = 0; + for (auto fh = arr.faces_begin(); fh != arr.faces_end(); ++fh) { + //// skip the spherical face + //if (fh->number_of_outer_ccbs() == 0) + // continue; + + // json object for the current face + json js_face; + auto face_name = face_name_map[fh]; + js_face["name"] = face_name; + js_face["is_unbounded"] = false; + js_face["is_valid"] = true; + + // at this point we are sure that we have at least 1 outer-ccb + auto& js_outer_ccbs = js_face["outer_ccbs"] = json::array(); + for (auto ccb = fh->outer_ccbs_begin(); ccb != fh->outer_ccbs_end(); ++ccb) + { + auto js_ccb = get_ccb_json(*ccb); + js_outer_ccbs.push_back(std::move(js_ccb)); + } + + // INNER CCBS + if (fh->number_of_inner_ccbs() > 0) { + auto& js_inner_ccbs = js_face["inner_ccbs"] = json::array(); + for (auto ccb = fh->inner_ccbs_begin(); ccb != fh->inner_ccbs_end(); + ++ccb) { + auto js_ccb = get_ccb_json(*ccb); + js_inner_ccbs.push_back(std::move(js_ccb)); + } + } + + js_faces.push_back(std::move(js_face)); + } + std::cout << "total num half-edges = " << total_num_half_edges << std::endl; + + // save the arrangment + std::ofstream ofile(file_name); + ofile << js.dump(2); + ofile.close(); +#endif +} + +namespace { + enum Error_id { + FILE_NOT_FOUND, + ILLEGAL_EXTENSION, + UNABLE_TO_OPEN, + FILE_IS_EMPTY, + INVALID_INITIAL_POLYGON, + UNSUPPORTED, + INVALID_OUTPUT_FILE, + ILLEGAL_SOLUTION_FILE + }; + + struct Illegal_input : public std::logic_error { + Illegal_input(Error_id /* err */, const std::string& msg, + const std::string& filename) : + std::logic_error(std::string(msg).append(" (").append(filename). + append(")!")) + {} + + Illegal_input(Error_id /* err */, const std::string& msg) : + std::logic_error(std::string(msg).append("!")) + {} + }; + + struct Input_file_missing_error : public std::logic_error { + Input_file_missing_error(std::string& str) : std::logic_error(str) {} + }; + + // + struct country { + //! Constructor + country(std::string& name) : m_name(std::move(name)) {} + + std::string m_name; + }; + + /*! Read a json file. + */ + bool read_json(const std::string& filename, nlohmann::json& data) { + using json = nlohmann::json; + std::ifstream infile(filename); + if (!infile.is_open()) { + throw Illegal_input(UNABLE_TO_OPEN, "Cannot open file", filename); + return false; + } + data = json::parse(infile); + infile.close(); + if (data.empty()) { + throw Illegal_input(FILE_IS_EMPTY, "File is empty", filename); + return false; + } + return true; + } + + /*! + */ + template + FT to_ft(const nlohmann::json& js_ft) { + using Exact_type = typename FT::Exact_type; + const std::string& js_num = js_ft["num"]; + const std::string& js_den = js_ft["den"]; + std::string str = js_num + "/" + js_den; + Exact_type eft(str); + return FT(eft); + } + + template + bool read_arrangement(const std::string& filename, Arrangement_& arr) { + using Arrangement = Arrangement_; + using Kernel = Kernel_; + + using json = nlohmann::json; + json data; + auto rc = read_json(filename, data); + if (! rc) return false; + + // points + auto it = data.find("points"); + if (it == data.end()) { + std::cerr << "The points item is missing " << " (" << filename << ")\n"; + return false; + } + const auto& js_points = it.value(); + + // curves + it = data.find("curves"); + if (it == data.end()) { + std::cerr << "The curves item is missing " << " (" << filename << ")\n"; + return false; + } + const auto& js_curves = it.value(); + + // vertices + it = data.find("vertices"); + if (it == data.end()) { + std::cerr << "The vertices item is missing " << " (" << filename << ")\n"; + return false; + } + const auto& js_vertices = it.value(); + + // edges + it = data.find("edges"); + if (it == data.end()) { + std::cerr << "The edges item is missing " << " (" << filename << ")\n"; + return false; + } + const auto& js_edges = it.value(); + + // faces + it = data.find("faces"); + if (it == data.end()) { + std::cerr << "The faces item is missing " << " (" << filename << ")\n"; + return false; + } + const auto& js_faces = it.value(); + + const std::size_t num_points = js_points.size(); + const std::size_t num_curves = js_curves.size(); + const std::size_t num_vertices = js_vertices.size(); + const std::size_t num_edges = js_edges.size(); + const std::size_t num_faces = js_faces.size(); + const std::size_t num_halfedges = num_edges * 2; + + if (num_points < num_vertices) { + std::cerr << "The no. of points (" << num_points + << ") is smaller than the no. of vertices (" << num_vertices + << ")\n"; + return false; + } + + if (num_curves < num_edges) { + std::cerr << "The no. of curves (" << num_curves + << ") is smaller than the no. of edge (" << num_edges << ")\n"; + return false; + } + + std::cout << "# points: " << num_points << std::endl; + std::cout << "# curves: " << num_curves << std::endl; + std::cout << "# vertices: " << num_vertices << std::endl; + std::cout << "# halfedges: " << num_halfedges << std::endl; + std::cout << "# faces: " << num_faces << std::endl; + + using Point = typename Arrangement::Point_2; + using X_monotone_curve = typename Arrangement::X_monotone_curve_2; + using FT = typename Kernel::FT; + // using Exact_type = typename FT::Exact_type; + + std::vector points; + points.reserve(num_points); + for (const auto& js_pnt : js_points) { + using Direction_3 = typename Kernel::Direction_3; + using Location = typename Point::Location_type; + auto location = static_cast(js_pnt["location"]); + auto dx = to_ft(js_pnt["dx"]); + auto dy = to_ft(js_pnt["dy"]); + auto dz = to_ft(js_pnt["dz"]); + Direction_3 dir(dx, dy, dz); + Point pnt(dir, location); + points.push_back(pnt); + } + + std::vector xcurves; + xcurves.reserve(num_curves); + for (const auto& js_xcv : js_curves) { + using Direction_3 = typename Kernel::Direction_3; + std::size_t src_id = js_xcv["source"]; + std::size_t trg_id = js_xcv["target"]; + const auto& js_normal = js_xcv["normal"]; + auto dx = to_ft(js_normal["dx"]); + auto dy = to_ft(js_normal["dy"]); + auto dz = to_ft(js_normal["dz"]); + Direction_3 normal(dx, dy, dz); + bool is_vert = js_xcv["is_vertical"]; + bool is_directed_right = js_xcv["is_directed_right"]; + bool is_full = js_xcv["is_full"]; + const auto& src = points[src_id]; + const auto& trg = points[trg_id]; + X_monotone_curve xcv(src, trg, normal, is_vert, is_directed_right, is_full); + xcurves.push_back(xcv); + } + + using Arr_accessor = CGAL::Arr_accessor; + Arr_accessor arr_access(arr); + arr_access.clear_all(); + + // Vertices + using DVertex = typename Arr_accessor::Dcel_vertex; + std::vector vertices(num_vertices); + size_t k = 0; + for (const auto& js_vertex : js_vertices) { + std::size_t point_id = js_vertex["point"]; + const auto& point = points[point_id]; + CGAL::Arr_parameter_space ps_x, ps_y; + switch (point.location()) { + case Point::NO_BOUNDARY_LOC: ps_x = ps_y = CGAL::INTERIOR; break; + case Point::MIN_BOUNDARY_LOC: + ps_x = CGAL::INTERIOR; + ps_y = CGAL::ARR_BOTTOM_BOUNDARY; + break; + case Point::MID_BOUNDARY_LOC: + ps_x = CGAL::LEFT_BOUNDARY; + ps_y = CGAL::INTERIOR; + break; + case Point::MAX_BOUNDARY_LOC: + ps_x = CGAL::INTERIOR; + ps_y = CGAL::ARR_TOP_BOUNDARY; + break; + } + vertices[k++] = arr_access.new_vertex(&point, ps_x, ps_y); + } + + // Halfedges + using DHalfedge = typename Arr_accessor::Dcel_halfedge; + std::vector halfedges(num_halfedges); + k = 0; + for (const auto& js_edge : js_edges) { + std::size_t source_id = js_edge["source"]; + std::size_t target_id = js_edge["target"]; + std::size_t curve_id = js_edge["curve"]; + auto direction = js_edge["direction"]; + DVertex* src_v = vertices[source_id]; + DVertex* trg_v = vertices[target_id]; + const auto& curve = xcurves[curve_id]; + DHalfedge* new_he = arr_access.new_edge(&curve); + trg_v->set_halfedge(new_he); + new_he->set_vertex(trg_v); + src_v->set_halfedge(new_he->opposite()); + new_he->opposite()->set_vertex(src_v); + new_he->set_direction(static_cast(direction)); + halfedges[k++] = new_he; + halfedges[k++] = new_he->opposite(); + } + + // Faces + using DFace = typename Arr_accessor::Dcel_face; + const bool is_unbounded(false); + const bool is_valid(true); + for (const auto& js_face : js_faces) { + DFace* new_f = arr_access.new_face(); + + new_f->set_unbounded(is_unbounded); + new_f->set_fictitious(!is_valid); + new_f->set_data(js_face["name"]); + // Read the outer CCBs of the face. + auto oit = js_face.find("outer_ccbs"); + if (oit != js_face.end()) { + const auto& js_outer_ccbs = *oit; + for (const auto& js_ccb : js_outer_ccbs) { + // Allocate a new outer CCB record and set its incident face. + auto* new_occb = arr_access.new_outer_ccb(); + new_occb->set_face(new_f); + + // Read the current outer CCB. + auto bit = js_ccb.find("halfedges"); + if (bit == js_ccb.end()) { + std::cerr << "The halfedges item is missing " << " (" << filename + << ")\n"; + return false; + } + + const auto& js_halfedges = *bit; + auto hit = js_halfedges.begin(); + std::size_t first_idx = *hit; + DHalfedge* first_he = halfedges[first_idx]; + first_he->set_outer_ccb(new_occb); + DHalfedge* prev_he = first_he; + for (++hit; hit != js_halfedges.end(); ++hit) { + std::size_t curr_idx = *hit; + auto curr_he = halfedges[curr_idx]; + prev_he->set_next(curr_he); // connect + curr_he->set_outer_ccb(new_occb);// set the CCB + prev_he = curr_he; + } + prev_he->set_next(first_he);// close the loop + new_f->add_outer_ccb(new_occb, first_he); + } + } + + // Read the inner CCBs of the face. + auto iit = js_face.find("inner_ccbs"); + if (iit != js_face.end()) { + const auto& js_inner_ccbs = *iit; + for (const auto& js_ccb : js_inner_ccbs) { + // Allocate a new inner CCB record and set its incident face. + auto* new_iccb = arr_access.new_inner_ccb(); + new_iccb->set_face(new_f); + + // Read the current inner CCB. + auto bit = js_ccb.find("halfedges"); + if (bit == js_ccb.end()) { + std::cerr << "The halfedges item is missing " << " (" << filename + << ")\n"; + return false; + } + + const auto& js_halfedges = *bit; + auto hit = js_halfedges.begin(); + std::size_t first_idx = *hit; + DHalfedge* first_he = halfedges[first_idx]; + first_he->set_inner_ccb(new_iccb); + DHalfedge* prev_he = first_he; + for (++hit; hit != js_halfedges.end(); ++hit) { + std::size_t curr_idx = *hit; + auto curr_he = halfedges[curr_idx]; + prev_he->set_next(curr_he); // connect + curr_he->set_inner_ccb(new_iccb); // set the CCB + prev_he = curr_he; + } + prev_he->set_next(first_he); // close the loop + new_f->add_inner_ccb(new_iccb, first_he); + } + } + } + + return true; + } +} + +namespace { + + /*! + */ + template + Aos::Arr_handle get_handle(T* arr) { + return Aos::Arr_handle(arr, [](void* ptr) { + // std::cout << "DELETING THE ARRANGEMENT WITH SHARED_PTR DELETER!!\n"; + delete reinterpret_cast(ptr); + }); + } +} + +/*! + */ +Aos::Arr_handle Aos::load_arr(const std::string& file_name) { + auto* arr = new Countries_arr(&s_traits); + auto arrh = get_handle(arr); + auto rc = read_arrangement(file_name, *arr); + if (! rc) return nullptr; + return arrh; +} + +Aos::Arr_handle Aos::construct(Kml::Placemarks& placemarks) { + auto* arr = new Arrangement(&s_traits); + auto arrh = get_handle(arr); + auto xcvs = get_arcs(placemarks, *arr); + for (auto& xcv : xcvs) CGAL::insert(*arr, xcv); + return arrh; +} + +Aos::Country_color_map Aos::get_color_mapping(Arr_handle arrh) { + auto& arr = *reinterpret_cast(arrh.get()); + + // group the faces by their country name, + std::vector all_countries; + using Face_ = Countries_arr::Face_handle::value_type; + std::map> country_faces_map; + for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) { + auto& face = *fit; + const auto& country_name = fit->data(); + // skipping spherical-face + if (country_name.empty()) continue; + country_faces_map[country_name].push_back(&face); + all_countries.push_back(country_name); + } + + // prepare a map of neighboring countries + std::map> country_neighbors_map; + for (auto& [country_name, faces] : country_faces_map) { + // loop on all of the faces of the current country + for (auto* face : faces) { + auto first = face->outer_ccb(); + auto curr = first; + do { + const auto& neighbor_country_name = curr->twin()->face()->data(); + + // skip the spherical face + if (neighbor_country_name.empty()) continue; + + country_neighbors_map[country_name].insert(neighbor_country_name); + } while (++curr != first); + } + } + + // find a color index for each country by looking at its neighbors + Country_color_map result; + for (const auto& country_name : all_countries) { + // first: find a free color index + bool color_used[5] = { false, false, false, false, false }; + auto& neighbor_set = country_neighbors_map[country_name]; + for (auto& neighbor : neighbor_set) { + auto it = result.find(neighbor); + // if there is a country in the map, then it must have been assigned one! + if (it != result.end()) { + auto used_color_index = it->second; + color_used[used_color_index] = true; + } + } + + // find the first color index not used + bool found = false; + for (std::size_t i = 0; i < 5; ++i) { + if (! color_used[i]) { + found = true; + result[country_name] = i; + } + } + // assertion check!!! + if(! found) std::cout << "*** ASSERTION ERROR: NO INDEX FOUND!!!\n"; + } + + return result; +} + +std::string Aos::locate_country(Arr_handle arrh, const QVector3D& point) { + using Naive_pl = CGAL::Arr_naive_point_location; + + auto& arr = *reinterpret_cast(arrh.get()); + auto ctr_point = arr.geometry_traits()->construct_point_2_object(); + auto query_point = ctr_point(point.x(), point.y(), point.z()); + + Naive_pl npl(arr); + auto obj = npl.locate(query_point); + + using Arrangement_2 = Countries_arr; + using Vertex_const_handle = typename Arrangement_2::Vertex_const_handle; + using Halfedge_const_handle = typename Arrangement_2::Halfedge_const_handle; + using Face_const_handle = typename Arrangement_2::Face_const_handle; + std::string country_name = ""; + if (auto f = std::get_if(&obj)) { // located inside a face + //std::cout << "*** QUERY: FACE\n"; + country_name = f->ptr()->data(); + } + else if (auto e = std::get_if(&obj)) { + // located on an edge: return one of the incident face arbitrarily + //std::cout << "*** QUERY: EDGE\n"; + country_name = (*e)->face()->data(); + } + else if (auto v = std::get_if(&obj)) { + // located on a vertex + if ((*v)->is_isolated()) { + //std::cout << "*** QUERY: ISOLATED VERTEX\n"; + country_name = (*v)->face()->data(); + } + else { + //std::cout << "*** QUERY: VERTEX\n"; + country_name = (*v)->incident_halfedges()->face()->data(); + } + } + else CGAL_error_msg("Invalid object."); + + return country_name; +} + +Aos::Approx_arcs +Aos::get_approx_arcs_from_faces_edges(Arr_handle arrh, double error) { + auto& arr = *reinterpret_cast(arrh.get()); + auto ctr_cv = s_traits.construct_curve_2_object(); + Curves xcvs; + for (auto eit = arr.halfedges_begin(); eit != arr.halfedges_end(); ++eit) { + auto& s = eit->curve(); + xcvs.push_back( ctr_cv(s.source(), s.target()) ); + } + + auto arcs = ::get_approx_curves(xcvs, error); + return arcs; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.h new file mode 100644 index 000000000000..af4b9c2d2ff0 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos.h @@ -0,0 +1,84 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef AOS_H +#define AOS_H + +#include +#include +#include +#include + +#include "Kml_reader.h" + +class Aos { +public: + using Approx_arc = std::vector; + using Approx_arcs = std::vector; + //using Arr_handle = void*; + using Arr_handle = std::shared_ptr; + + static Approx_arc get_approx_identification_curve(double error); + + // this constructs some sample arcs manually (used to check the visual output) + static Approx_arcs get_approx_arcs(double error); + + // this is used to construct approximate arcs from KML-Placemark data + static Approx_arcs get_approx_arcs(const Kml::Placemark& placemark, + double error); + + static void get_curves(); + + static void check(const Kml::Placemarks& placemarks); + + // Extended check: here we use the extended version of the DCEL to understand + // what is going on with the additional vertices and faces. + // INPUT: GIS data + // OUTPUT: vertices created during arrangement construction + static std::vector ext_check(const Kml::Placemarks& placemarks); + + // Same as above function but works by identifying the duplicate nodes + static std::vector ext_check_id_based(Kml::Placemarks& placemarks); + + // This function is used to find the newly-created faces during arrangement + // construction. It uses the extended DCEL structure (see: above 2 functions). + // NOTE: The need for this function arises due to the following observation: + // when constructing the arrangement from our data-set I observed a newly + // created face which is not defined explicitly in the data-set. My intiution + // tells me that this is the Caspian sea, because it is the only land-locked + // polygon whose boundaries are not defined in the data-set and its boundaries + // are defined indirecly by its surrounding countries. + static Approx_arcs find_new_faces(Kml::Placemarks& placemarks); + + // save the arrangement created with EPEC + static void save_arr(Kml::Placemarks& placemarks, + const std::string& file_name); + + // loads the arrangement created by the save_arr function + // NOTE: This one loads the arrangement as "Country_arr" type + static Arr_handle load_arr(const std::string& file_name); + + static Arr_handle construct(Kml::Placemarks& placemarks); + //static std::vector get_triangles(Arr_handle arrh); + + //using Country_triangles_map = std::map>; + //static Country_triangles_map get_triangles_by_country(Arr_handle arrh); + + using Country_color_map = std::map; + static Country_color_map get_color_mapping(Arr_handle arrh); + + static std::string locate_country(Arr_handle arrh, const QVector3D& point); + + // this will get the approximate arcs of face-edges from the arrangement + // NOTE: this is similar to "get_approx_arcs(KML::Placemarks&, double)" above! + static Approx_arcs get_approx_arcs_from_faces_edges(Arr_handle arrh, + double error); +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_defs.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_defs.h new file mode 100644 index 000000000000..5332eb1e8569 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_defs.h @@ -0,0 +1,47 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef AOS_DEFS_H +#define AOS_DEFS_H + +#include +#include +#include +#include +#include +#include +#include + +//#define USE_EPIC + +#ifdef USE_EPIC +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +#else +using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; +#endif + +using Geom_traits = CGAL::Arr_geodesic_arc_on_sphere_traits_2; +using Point = Geom_traits::Point_2; +using Curve = Geom_traits::Curve_2; +using Topol_traits = CGAL::Arr_spherical_topology_traits_2; +using Arrangement = CGAL::Arrangement_on_surface_2; + +// the following is from "arr_inexact_construction_segments.h": +using Segment = Geom_traits::X_monotone_curve_2; +using Vertex_handle = Arrangement::Vertex_handle; + +// COUNTRIES AOS for grouping the faces by the country name +using Countries_dcel = CGAL::Arr_face_extended_dcel; +using Countries_topol_traits = + CGAL::Arr_spherical_topology_traits_2; +using Countries_arr = + CGAL::Arrangement_on_surface_2; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.cpp new file mode 100644 index 000000000000..0fb46b142aeb --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.cpp @@ -0,0 +1,318 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +// #include +#include +#include +#include +#include + +#include "Aos_defs.h" +#include "Aos_triangulator.h" + +using json = nlohmann::ordered_json; + +namespace { + + // use this traits everytime you construct an arrangment! + static Geom_traits s_traits; + using Dir3 = Kernel::Direction_3; + using Approximate_number_type = Geom_traits::Approximate_number_type; + using Approximate_kernel = Geom_traits::Approximate_kernel; + using Approximate_Vector_3 = CGAL::Vector_3; + using Approximate_Direction_3 = Approximate_kernel::Direction_3; + using Direction_3 = Kernel::Direction_3; +} + +//! \brief +std::vector Aos_triangulator::get_all(Aos::Arr_handle arrh) { + using K = CGAL::Exact_predicates_inexact_constructions_kernel; + using Vb = CGAL::Triangulation_vertex_base_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using TDS = CGAL::Triangulation_data_structure_2; + using Itag = CGAL::Exact_predicates_tag; + using CDT = CGAL::Constrained_Delaunay_triangulation_2; + using Face_handle = CDT::Face_handle; + using Polygon_2 = CGAL::Polygon_2; + + auto& arr = *reinterpret_cast(arrh.get()); + + //Geom_traits traits; + auto approx = s_traits.approximate_2_object(); + + std::vector> all_faces; + // loop on all faces of the arrangement + for (auto fh : arr.face_handles()) { + // skip any face with no OUTER-CCB + if (0 == fh->number_of_outer_ccbs()) continue; + + // COMPUTE THE CENTROID OF ALL FACE-POINTS + std::vector face_points; + + // loop on the egdes of the current outer-ccb + auto first = fh->outer_ccb(); + auto curr = first; + do { + auto ap = approx(curr->source()->point()); + QVector3D p(ap.dx(), ap.dy(), ap.dz()); + p.normalize(); + face_points.push_back(p); + } while (++curr != first); + + all_faces.push_back(std::move(face_points)); + } + + // RESULTING TRIANGLE POINTS (every 3 point => triangle) + std::vector triangles; + + std::cout << "triangulating individual faces\n"; + + // loop on all approximated faces + for (auto& face_points : all_faces) { + std::cout << "num face points = " << face_points.size() << std::endl; + // no need to triangulate if the number of points is 3 + if (face_points.size() == 3) { + triangles.insert(triangles.end(), face_points.begin(), face_points.end()); + continue; + } + + // find the centroid of all face-points + QVector3D centroid(0, 0, 0); + for (const auto& fp : face_points) centroid += fp; + centroid /= face_points.size(); + centroid.normalize(); + auto normal = centroid; + + K::Point_3 plane_origin(centroid.x(), centroid.y(), centroid.z()); + K::Vector_3 plane_normal(normal.x(), normal.y(), normal.z()); + K::Plane_3 plane(plane_origin, plane_normal); + + Polygon_2 polygon; + + // project all points onto the plane + K::Point_3 origin(0, 0, 0); + for (const auto& fp : face_points) { + // define a ray through the origin and the current point + K::Point_3 current_point(fp.x(), fp.y(), fp.z()); + K::Ray_3 ray(origin, current_point); + + auto intersection = CGAL::intersection(plane, ray); + if (!intersection.has_value()) + std::cout << "INTERSECTION ASSERTION ERROR!!!\n"; + auto ip = std::get(intersection.value()); + auto ip2 = plane.to_2d(ip); + + // add this to the polygon constraint + polygon.push_back(ip2); + } + + CDT cdt; + cdt.insert_constraint(polygon.vertices_begin(), polygon.vertices_end(), + true); + + std::unordered_map in_domain_map; + boost::associative_property_map< std::unordered_map> + in_domain(in_domain_map); + + //Mark facets that are inside the domain bounded by the polygon + CGAL::mark_domain_in_triangulation(cdt, in_domain); + + // loop on all the triangles ("faces" in triangulation doc) + for (Face_handle f : cdt.finite_face_handles()) { + // if the current triangles is not inside the polygon -> skip it + if (! get(in_domain, f)) continue; + + for (int i = 0; i < 3; ++i) { + auto tp = f->vertex(i)->point(); + auto tp3 = plane.to_3d(tp); + QVector3D p3(tp3.x(), tp3.y(), tp3.z()); + p3.normalize(); + triangles.push_back(p3); + } + } + } + + return triangles; +} + +//! \brief +Country_triangles_map +Aos_triangulator::get_by_country(Aos::Arr_handle arrh, float error, + std::size_t num_uniform_points) { + + using K = CGAL::Exact_predicates_inexact_constructions_kernel; + using Pnt_2 = typename K::Point_2; + using Pnt_3 = typename K::Point_3; + using Vb = CGAL::Triangulation_vertex_base_2; + using Fb = CGAL::Constrained_triangulation_face_base_2; + using TDS = CGAL::Triangulation_data_structure_2; + using Itag = CGAL::Exact_predicates_tag; + using CDT = CGAL::Constrained_Delaunay_triangulation_2; + using Face_handle = CDT::Face_handle; + using Approximate_point_2 = Geom_traits::Approximate_point_2; + using Aos = Countries_arr; + using Aos_pnt = Aos::Point_2; + + auto& arr = *reinterpret_cast(arrh.get()); + auto approx = s_traits.approximate_2_object(); + + // group the faces by their country name + using Face_const_handle = Countries_arr::Face_const_handle; + std::map> country_faces_map; + for (auto fh : arr.face_handles()) { + const auto& country_name = fh->data(); + // skipping spherical-face + if (country_name.empty()) continue; + country_faces_map[country_name].push_back(fh); + } + + // Generate random point uniformly distributed + std::vector rps; + rps.reserve(num_uniform_points); + CGAL::Random_points_in_sphere_3 g(1.0); + std::copy_n(g, rps.capacity(), std::back_inserter(rps)); + std::map> face_random_points; + using Point_location_result = CGAL::Arr_point_location_result; + using Query_result = std::pair; + std::vector rqs(rps.size()); + auto ctr_p = arr.geometry_traits()->construct_point_2_object(); + std::transform(rps.begin(), rps.end(), rqs.begin(), + [&](const Pnt_3& rp) -> Aos_pnt { + return ctr_p(rp.x(), rp.y(), rp.z()); + }); + std::list results; + CGAL::locate(arr, rqs.begin(), rqs.end(), std::back_inserter(results)); + for (auto& [q, res] : results) { + const Aos::Face_const_handle* f; + if ((f = std::get_if(&res))) { + auto x = CGAL::to_double(q.dx()); + auto y = CGAL::to_double(q.dy()); + auto z = CGAL::to_double(q.dz()); + face_random_points[*f].push_back(Pnt_3(x, y, z)); + } + } + + // Triangulate the faces + Country_triangles_map result; + for (auto& [country_name, fhs] : country_faces_map) { + // std::cout << "processing country " << country_name << std::endl; + auto& triangles = result[country_name]; + // CONVERT the face-points to QVector3D + for (auto fh : fhs) { + // skip any face with no OUTER-CCB + if (0 == fh->number_of_outer_ccbs()) continue; + + std::vector face_points; + // Loop on the egdes of the current outer-ccb + auto first = fh->outer_ccb(); + auto curr = first; + do { + std::vector apx_points; + const auto& xcv = curr->curve(); + approx(xcv, error, std::back_insert_iterator(apx_points), + curr->direction() == CGAL::ARR_LEFT_TO_RIGHT); + for (auto it = apx_points.begin(); it != apx_points.end()-1; ++it) { + const auto& apx_p = *it; + const QVector3D p(apx_p.dx(), apx_p.dy(), apx_p.dz()); + face_points.push_back(p); + } + } while (++curr != first); + + // no need to triangulate if the number of points is 3 + if (face_points.size() == 3) { + triangles.insert(triangles.end(), face_points.begin(), + face_points.end()); + continue; + } + + // Calculate the centroid of all face-points + QVector3D centroid(0, 0, 0); + for (const auto& fp : face_points) centroid += fp; + centroid /= face_points.size(); + centroid.normalize(); + auto normal = centroid; + + Pnt_3 plane_origin(centroid.x(), centroid.y(), centroid.z()); + K::Vector_3 plane_normal(normal.x(), normal.y(), normal.z()); + K::Plane_3 plane(plane_origin, plane_normal); + + // Project all points onto the plane + std::vector planar_points(face_points.size()); + std::transform(face_points.begin(), face_points.end(), + planar_points.begin(), + [&](const QVector3D& fp) -> Pnt_2 { + // define a ray through the origin and the current point + Pnt_3 p3(fp.x(), fp.y(), fp.z()); + K::Ray_3 ray(CGAL::ORIGIN, p3); + auto intersection = CGAL::intersection(plane, ray); + if (! intersection.has_value()) + std::cout << "INTERSECTION ASSERTION ERROR!!!\n"; + auto ip = std::get(intersection.value()); + return plane.to_2d(ip); + }); + CDT cdt; + + // Insert points uniformly distributed into the triangulation + auto it = face_random_points.find(fh); + if (it != face_random_points.end()) { + const auto& points = it->second; + for (const auto& p3 : points) { + K::Ray_3 ray(CGAL::ORIGIN, p3); + auto intersection = CGAL::intersection(plane, ray); + if (! intersection.has_value()) + std::cout << "INTERSECTION ASSERTION ERROR!!!\n"; + auto ip = std::get(intersection.value()); + auto p2 = plane.to_2d(ip); + cdt.insert(p2); + } + } + + // Insert the constraints into the triangulation + cdt.insert_constraint(planar_points.begin(), planar_points.end(), true); + + // Mark facets that are inside the domain bounded by the polygon + std::unordered_map in_domain_map; + boost::associative_property_map< std::unordered_map> + in_domain(in_domain_map); + CGAL::mark_domain_in_triangulation(cdt, in_domain); + + // Loop on all the triangles ("faces" in triangulation doc) + for (Face_handle f : cdt.finite_face_handles()) { + // If the current triangles is not inside the polygon -> skip it + if (! get(in_domain, f)) continue; + + for (int i = 0; i < 3; ++i) { + auto tp = f->vertex(i)->point(); + auto tp3 = plane.to_3d(tp); + QVector3D p3(tp3.x(), tp3.y(), tp3.z()); + p3.normalize(); + triangles.push_back(p3); + } + } + } + } + + return result; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.h new file mode 100644 index 000000000000..4d291b43e1ed --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Aos_triangulator.h @@ -0,0 +1,31 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef AOS_TRIANGULATOR_H +#define AOS_TRIANGULATOR_H + +#include +#include + +#include + +#include "Aos.h" + +using Country_triangles_map = std::map>; + +class Aos_triangulator { +public: + static std::vector get_all(Aos::Arr_handle arrh); + + static Country_triangles_map get_by_country(Aos::Arr_handle arrh, + float error, + std::size_t num_uniform_points); +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/CMakeLists.txt b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/CMakeLists.txt new file mode 100644 index 000000000000..2a0f34d82566 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/CMakeLists.txt @@ -0,0 +1,128 @@ +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.23) +project(Arrangement_on_surface_2_earth_Demo) + +if(NOT POLICY CMP0070 AND POLICY CMP0053) + # Only set CMP0053 to OLD with CMake<3.10, otherwise there is a warning. + cmake_policy(SET CMP0053 OLD) +endif() + +if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) +endif() + +find_package(Qt6 QUIET COMPONENTS Core Gui OpenGL OpenGLWidgets Widgets Xml) +find_package(CGAL COMPONENTS Qt6) +find_package(nlohmann_json QUIET 3.9) + +if (NOT CGAL_FOUND OR NOT CGAL_Qt6_FOUND OR NOT Qt6_FOUND OR NOT Boost_FOUND OR NOT nlohmann_json_FOUND) + if (NOT CGAL_FOUND) + set(MISSING_DEPS "the CGAL library, ${MISSING_DEPS}") + endif() + if (NOT CGAL_Qt6_FOUND) + set(MISSING_DEPS "the CGAL Qt6 component, ${MISSING_DEPS}") + endif() + if (NOT Qt6_FOUND) + set(MISSING_DEPS "the Qt6 library, ${MISSING_DEPS}") + endif() + if (NOT Boost_FOUND) + set(MISSING_DEPS "the Boost library, ${MISSING_DEPS}") + endif() + if (NOT nlohmann_json_FOUND) + set(MISSING_DEPS "JSON for Modern C++ 3.9+ (know as nlohmann_json), ${MISSING_DEPS}") + endif() + + message(STATUS "NOTICE: This project requires ${MISSING_DEPS} and will not be compiled.") + return() +endif() + + + +add_definitions(-DQT_NO_VERSION_TAGGING) + +# AOS +file(GLOB source_files_aos Aos.h Aos.cpp Aos_defs.h + Aos_triangulator.h Aos_triangulator.cpp) +source_group("Aos" FILES ${source_files_aos}) + +# GIS +file(GLOB source_files_gis Kml_reader.h Kml_reader.cpp) +source_group("GIS" FILES ${source_files_gis}) + +# GRAPHICS +file(GLOB source_files_graphics Camera.h Camera.cpp + Shader_program.h Shader_program.cpp) +source_group("Graphics" FILES ${source_files_graphics}) + +# GRAPHICS-GEOMETRY (Graphics-related) +file(GLOB source_files_graphics_geometry + Line_strips.h Line_strips.cpp + Single_vertex.h Single_vertex.cpp + Sphere.h Sphere.cpp + Triangles.h Triangles.cpp + Vertices.h Vertices.cpp + World_coordinate_axes.h World_coordinate_axes.cpp) +source_group("Graphics_Geometry" FILES ${source_files_graphics_geometry}) + +# GUI +file(GLOB source_files_gui + Camera_manip.h Camera_manip.cpp + Camera_manip_rot.h Camera_manip_rot.cpp + Camera_manip_rot_bpa.h Camera_manip_rot_bpa.cpp + Camera_manip_zoom.h Camera_manip_zoom.cpp + GUI_country_pick_handler.h GUI_country_pick_handler.cpp + GUI_event_handler.h GUI_event_handler.cpp) +source_group("GUI" FILES ${source_files_gui}) + +#SOURCE FILES (NOT CATEGORIZED YET) +file(GLOB source_files Common_defs.h earth.cpp Timer.h + Main_widget.h Main_widget.cpp + Message_manager.h Message_manager.cpp + Tools.h Tools.cpp + Verification.h Verification.cpp) +source_group("Source Files" FILES ${source_files}) + +set(CMAKE_AUTOMOC ON) + +qt_add_executable(earth + ${source_files_aos} + ${source_files_gis} + ${source_files_graphics} + ${source_files_graphics_geometry} + ${source_files_gui} + ${source_files} +) + +set_target_properties(earth PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +if (MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE") +endif (MSVC) + +target_link_libraries(earth PRIVATE + Qt6::Core + Qt6::Gui + Qt6::OpenGL + Qt6::OpenGLWidgets + Qt6::Widgets + Qt6::Xml + CGAL::CGAL + CGAL::Data + nlohmann_json::nlohmann_json) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/shaders + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +# install(TARGETS earth +# RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" +# BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" +# LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +# ) + +add_to_cached_list(CGAL_EXECUTABLE_TARGETS earth) +include(${CGAL_MODULES_DIR}/CGAL_add_test.cmake) +cgal_add_compilation_test(earth) diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.cpp new file mode 100644 index 000000000000..0b4b6fa40f19 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.cpp @@ -0,0 +1,101 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Camera.h" + +//! \brief +Camera::Camera() : + m_ux(1, 0, 0), + m_uy(0, 1, 0), + m_uz(0, 0, 1) +{} + +//! \brief +void Camera::perspective(qreal fov, qreal aspect, qreal z_near, qreal z_far) { + m_z_near = z_near; + m_z_far = z_far; + + m_projection.setToIdentity(); + m_projection.perspective(fov, aspect, z_near, z_far); +} + +//! \brief +QMatrix4x4 Camera::get_view_matrix() const { + QMatrix4x4 view; + const QVector3D center = m_pos - m_uz; + view.lookAt(m_pos, center, m_uy); + return view; +} + +//! \brief +void Camera::rotate_from_init_config(float theta, float phi) { + // TO-DO: use the following logic to eliminate the QT-deprecation warnings! + //QMatrix4x4 rot; + //rot.rotate(theta, m_ux); + //auto pos = m_pos.toVector4D(); pos.setW(1); + //auto uy = m_uy.toVector4D(); uy.setW(0); + //auto uz = m_uz.toVector4D(); uz.setW(0); + + //pos = pos * rot; + //uy = uy * rot; + //uz = uz * rot; + + //m_pos = pos.toVector3D(); + //m_uy = uy.toVector3D(); + //m_uz = uz.toVector3D(); + + QMatrix4x4 r1; + QVector3D ey(0, 1, 0); + r1.rotate(theta, ey); + + // rx = rotated x axis + auto rx = r1.map(QVector3D(1, 0, 0)); + QMatrix4x4 r2; + r2.rotate(phi, rx); + + // total rotation: + auto r = r2 * r1; + + const auto dist_cam_to_origin = m_pos.length(); + m_pos = r.map(QVector3D(0, 0, dist_cam_to_origin)); + m_ux = r.map(QVector3D(1, 0, 0)); // should be the same as rx (sanity check?) + m_uy = r.map(QVector3D(0, 1, 0)); + m_uz = r.map(QVector3D(0, 0, 1)); +} + +//! \brief +void Camera::rotate(QMatrix4x4 rot) { + m_pos = rot.map(m_pos); + m_ux = rot.map(m_ux); + m_uy = rot.map(m_uy); + m_uz = rot.map(m_uz); +} + +//! \brief +void Camera::save_config() { + m_saved_pos = m_pos; + m_saved_ux = m_ux; + m_saved_uy = m_uy; + m_saved_uz = m_uz; +} + +//! \brief +void Camera::rotate_from_saved_config(QMatrix4x4 rot) { + m_pos = rot.map(m_saved_pos); + m_ux = rot.map(m_saved_ux); + m_uy = rot.map(m_saved_uy); + m_uz = rot.map(m_saved_uz); +} + +//! \brief +void Camera::move_forward(float distance) { + // recall that in OpenGL camera model, camera's z-axis points always + // out of the screen (towards the user). + m_pos -= distance * m_uz; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.h new file mode 100644 index 000000000000..9d166a794b05 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera.h @@ -0,0 +1,58 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include + +class Camera { +public: + Camera(); + + void set_pos(const QVector3D& pos) { m_pos = pos; } + void set_pos(float x, float y, float z) { m_pos = QVector3D(x,y,z); } + const QVector3D& get_pos() const { return m_pos; } + + void perspective(qreal fov, qreal aspect_ratio, qreal z_near, qreal z_far); + + qreal get_z_near() const { return m_z_near; } + QMatrix4x4 get_view_matrix() const; + QMatrix4x4 get_projection_matrix() const { return m_projection; } + + // theta: angle around y-axis + // phi: angle from the xz-plane (= rotated x-axis after the above rotation) + void rotate_from_init_config(float theta, float phi); + void rotate(QMatrix4x4 rot); + + // save config & rotate from saved config (move to separate class?) + void save_config(); + void rotate_from_saved_config(QMatrix4x4 rot); + + // move the camera forward around its own z-axis + void move_forward(float distance); + +private: + QVector3D m_pos; + QVector3D m_ux; + QVector3D m_uy; + QVector3D m_uz; + + QVector3D m_saved_pos; + QVector3D m_saved_ux; + QVector3D m_saved_uy; + QVector3D m_saved_uz; + + qreal m_z_near, m_z_far; + + QMatrix4x4 m_projection; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.cpp new file mode 100644 index 000000000000..ef904a969cae --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.cpp @@ -0,0 +1,12 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University(Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Camera_manip.h" + +Camera_manip::Camera_manip(Camera& camera) : m_camera(camera) {} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.h new file mode 100644 index 000000000000..d04f087f8fa6 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip.h @@ -0,0 +1,28 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef CAMERA_MANIP_H +#define CAMERA_MANIP_H + +#include +#include + +#include "Camera.h" +#include "GUI_event_handler.h" + +class Camera_manip : public GUI_event_handler { +public: + Camera_manip(Camera& camera); + +protected: + Camera& m_camera; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.cpp new file mode 100644 index 000000000000..7750fda45299 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.cpp @@ -0,0 +1,21 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Camera_manip_rot.h" + +Camera_manip_rot::Camera_manip_rot(Camera& camera) : Camera_manip(camera) {} + +void Camera_manip_rot::mouse_move_event(QMouseEvent* /* e */) { + if (m_left_mouse_button_down) { + const float rotation_scale_factor = 0.1f; + m_theta += rotation_scale_factor * m_diff.x(); + m_phi += rotation_scale_factor * m_diff.y(); + m_camera.rotate_from_init_config(-m_theta, -m_phi); + } +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.h new file mode 100644 index 000000000000..30f268507c9a --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot.h @@ -0,0 +1,30 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef CAMERA_MANIP_ROT_H +#define CAMERA_MANIP_ROT_H + +#include +#include + +#include "Camera_manip.h" + +class Camera_manip_rot : public Camera_manip { +public: + Camera_manip_rot(Camera& camera); + +protected: + virtual void mouse_move_event(QMouseEvent* e) override; + +private: + float m_theta = 0; + float m_phi = 0; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.cpp new file mode 100644 index 000000000000..8d6a22cd7acd --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.cpp @@ -0,0 +1,61 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Camera_manip_rot_bpa.h" + +//! \brief +Camera_manip_rot_bpa::Camera_manip_rot_bpa(Camera& camera) : + Camera_manip(camera) +{} + +//! \brief +void Camera_manip_rot_bpa::mouse_press_event(QMouseEvent* /* e */) { + // for the backprojected diff-vector method: + if (m_left_mouse_button_down) m_camera.save_config(); +} + +//! \brief +void Camera_manip_rot_bpa::mouse_move_event(QMouseEvent* /* e */) { + const float rotation_scale_factor = 0.1f; + + // ROTATION AROUND AN AXIS ORTHOGONAL TO THE BACKPROJECTED DIF-VECTOR + //QVector3D p0(m_last_mouse_pos.x(), m_vp_height - m_last_mouse_pos.y(), 0); + QVector3D p0(m_mouse_press_pos.x(), m_vp_height - m_mouse_press_pos.y(), 0); + QVector3D p1(m_current_mouse_pos.x(), m_vp_height - m_current_mouse_pos.y(), 0); + auto dp = p1 - p0; // difference vector in OpenGL window coords. + QVector3D rdp(-dp.y(), dp.x(), 0); // rotate diff-vector CCW by 90-deg + QVector3D rp = p0 + rdp; // r1 rotated CCW by 90 deg + + QMatrix4x4 model; // this is different from Sphere's model matrix!!! + auto proj = m_camera.get_projection_matrix(); + auto view = m_camera.get_view_matrix(); + auto model_view = view * model; + QRect viewport(0, 0, m_vp_width, m_vp_height); + auto wp0 = p0.unproject(model_view, proj, viewport); + auto wrp = rp.unproject(model_view, proj, viewport); + + // rotation axis & angle + auto rot_axis = wrp - wp0; + rot_axis.normalize(); + const auto rot_angle = rotation_scale_factor * dp.length(); + + QMatrix4x4 rot_matrix; + rot_matrix.rotate(-rot_angle, rot_axis); + + m_camera.rotate_from_saved_config(rot_matrix); +} + +//! \brief +void Camera_manip_rot_bpa::mouse_release_event(QMouseEvent* /* e */) {} + +//! \brief +void Camera_manip_rot_bpa::resize(int w, int h) { + m_vp_width = w; + m_vp_height = h; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.h new file mode 100644 index 000000000000..1867e6a74ba9 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_rot_bpa.h @@ -0,0 +1,32 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef CAMERA_MANIP_ROT_BPA_H +#define CAMERA_MANIP_ROT_BPA_H + +#include +#include + +#include "Camera_manip.h" + +class Camera_manip_rot_bpa : public Camera_manip { +public: + Camera_manip_rot_bpa(Camera& camera); + +protected: + virtual void mouse_press_event(QMouseEvent* e) override; + virtual void mouse_move_event(QMouseEvent* e) override; + virtual void mouse_release_event(QMouseEvent* e) override; + virtual void resize(int w, int h) override; + +private: + int m_vp_width, m_vp_height; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.cpp new file mode 100644 index 000000000000..724ce32f1348 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.cpp @@ -0,0 +1,29 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Camera_manip_zoom.h" + +#include "Message_manager.h" + +Camera_manip_zoom::Camera_manip_zoom(Camera& camera) : Camera_manip(camera) {} + +//! \brief +void Camera_manip_zoom::mouse_move_event(QMouseEvent* /* e */) { + if (m_middle_mouse_button_down) { + const float zoom_scale_factor = 0.01f; + const auto distance = zoom_scale_factor * m_diff.y(); + m_camera.move_forward(distance); + } +} + +//! \brief +void Camera_manip_zoom::mouse_release_event(QMouseEvent* e) { + if (e->button() == Qt::MiddleButton) + Message_manager::notify_all("zoom_changed"); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.h new file mode 100644 index 000000000000..3ec15895a8aa --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Camera_manip_zoom.h @@ -0,0 +1,27 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef CAMERA_MANIP_ZOOM_H +#define CAMERA_MANIP_ZOOM_H + +#include +#include + +#include "Camera_manip.h" + +class Camera_manip_zoom : public Camera_manip { +public: + Camera_manip_zoom(Camera& camera); + +protected: + virtual void mouse_move_event(QMouseEvent* e) override; + virtual void mouse_release_event(QMouseEvent* e) override; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Common_defs.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Common_defs.h new file mode 100644 index 000000000000..42533e6434c5 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Common_defs.h @@ -0,0 +1,18 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef COMMON_DEFS_H +#define COMMON_DEFS_H + +#include +//#include + +using OpenGLFunctionsBase = QOpenGLFunctions_3_3_Core; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.cpp new file mode 100644 index 000000000000..0f9e9b7c7962 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.cpp @@ -0,0 +1,108 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "GUI_country_pick_handler.h" + +//#include + +//! \brief +GUI_country_pick_handler::GUI_country_pick_handler(Main_widget& main_widget) : + m_main_widget(main_widget), + m_camera(main_widget.get_camera()) +{} + +//! \brief +void GUI_country_pick_handler::mouse_press_event(QMouseEvent* e) { + // handle country selection + if (e->button() == Qt::RightButton) { + auto p = e->pos(); + QVector3D sp0(p.x(), m_vp_height - p.y(), 0); + QVector3D sp1(p.x(), m_vp_height - p.y(), 1); + + auto proj = m_camera.get_projection_matrix(); + auto view = m_camera.get_view_matrix(); + auto model_view = view * m_main_widget.get_model_matrix(); + QRect viewport(0, 0, m_vp_width, m_vp_height); + auto wp0 = sp0.unproject(model_view, proj, viewport); + auto wp1 = sp1.unproject(model_view, proj, viewport); + + // ASSERTION!!! + m_main_widget.set_mouse_pos(wp0); + + // define a ray from the camera pos to the world-point + //auto o = m_camera.get_pos(); + //auto u = wp - o; + auto o = wp0; + auto u = wp1 - wp0; + + // solve the quadratic equation to check for intersection of ray with sphere + auto a = QVector3D::dotProduct(u, u); + auto b = 2 * QVector3D::dotProduct(u, o); + auto c = QVector3D::dotProduct(o, o) - 1; + auto d = b * b - 4 * a * c; + + float ti = -1; + if (abs(d) < std::numeric_limits::epsilon()) { + // single intersection + ti = -b / (2 * a); + } + else { + if (d < 0) { + // no intersection + return; + } + else { + // two intersections + auto sd = sqrt(d); + auto t1 = (-b - sd) / (2 * a); + auto t2 = (-b + sd) / (2 * a); + if (t1 > 0 && t2 > 0) ti = std::min(t1, t2); + else if (t1 > 0) ti = t1; + else ti = t2; + } + } + + //m_mouse_pos = o + ti * u; + auto pos = o + ti * u; + m_main_widget.set_mouse_pos(pos); + static std::string prev_picked_country; + auto& arrh = m_main_widget.get_arr_handle(); + auto picked_country = Aos::locate_country(arrh, pos); + + m_main_widget.hightlight_country(picked_country); + // if (!prev_picked_country.empty()) + // { + // // dim the previous country color + // auto& prev_country = m_country_triangles[prev_picked_country]; + // auto color = prev_country->get_color(); + // color *= s_dimming_factor; + // color.setW(1); + // prev_country->set_color(color); + // } + + // if (!picked_country.empty()) + // { + // // highlight the current country color + // auto& curr_country = m_country_triangles[picked_country]; + // auto color = curr_country->get_color(); + // color /= s_dimming_factor; + // color.setW(1); + // curr_country->set_color(color); + // qDebug() << "SELECTED COUNTRY: " << picked_country; + // } + + // prev_picked_country = picked_country; + } +} + +//! \brief +void GUI_country_pick_handler::resize(int w, int h) { + m_vp_width = w; + m_vp_height = h; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.h new file mode 100644 index 000000000000..08fe585289e5 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_country_pick_handler.h @@ -0,0 +1,34 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef GUI_COUNTRY_PICK_HANDLER_H +#define GUI_COUNTRY_PICK_HANDLER_H + +#include +#include + +#include "GUI_event_handler.h" +#include "Main_widget.h" + +class GUI_country_pick_handler : public GUI_event_handler { +public: + GUI_country_pick_handler(Main_widget& main_widget); + +protected: + virtual void mouse_press_event(QMouseEvent* e) override; + virtual void resize(int w, int h) override; + + Main_widget& m_main_widget; + Camera& m_camera; + int m_vp_width; + int m_vp_height; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.cpp new file mode 100644 index 000000000000..848ea3d2ceeb --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.cpp @@ -0,0 +1,57 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "GUI_event_handler.h" + +//! \brief +void +GUI_event_handler::set_mouse_button_pressed_flag(QMouseEvent* e, bool flag) { + switch (e->button()) { + case Qt::LeftButton: + m_left_mouse_button_down = flag; + break; + + case Qt::MiddleButton: + m_middle_mouse_button_down = flag; + break; + + default: break; + } +} + +//! \brief +void GUI_event_handler::mousePressEvent(QMouseEvent* e) { + set_mouse_button_pressed_flag(e, true); + m_mouse_press_pos = m_last_mouse_pos = QVector2D(e->position()); + + // call the function overridden by the derived class + mouse_press_event(e); +} + +//! \brief +void GUI_event_handler::mouseMoveEvent(QMouseEvent* e) { + m_current_mouse_pos = QVector2D(e->position()); + m_diff = m_current_mouse_pos - m_last_mouse_pos; + + // call the function overridden by the derived class + mouse_move_event(e); + + m_last_mouse_pos = m_current_mouse_pos; +} + +//! \brief +void GUI_event_handler::mouseReleaseEvent(QMouseEvent* e) { + set_mouse_button_pressed_flag(e, false); + + // call the function overridden by the derived class + mouse_release_event(e); +} + +//! \brief +void GUI_event_handler::resizeGL(int w, int h) { resize(w, h); } diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.h new file mode 100644 index 000000000000..d29dce172f1a --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/GUI_event_handler.h @@ -0,0 +1,42 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef GUI_EVENT_HANDLER_H +#define GUI_EVENT_HANDLER_H + +#include +#include + +class GUI_event_handler { +public: + virtual ~GUI_event_handler() {}; + + void mousePressEvent(QMouseEvent* e); + void mouseMoveEvent(QMouseEvent* e); + void mouseReleaseEvent(QMouseEvent* e); + void resizeGL(int w, int h); + +protected: + void set_mouse_button_pressed_flag(QMouseEvent* e, bool flag); + + virtual void mouse_press_event(QMouseEvent* /* e */) {} + virtual void mouse_move_event(QMouseEvent* /* e */) {} + virtual void mouse_release_event(QMouseEvent* /* e */) {} + virtual void resize(int /* w */, int /* h */) {} + + bool m_left_mouse_button_down = false; + bool m_middle_mouse_button_down = false; + QVector2D m_current_mouse_pos; + QVector2D m_last_mouse_pos; + QVector2D m_mouse_press_pos; + QVector2D m_diff; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.cpp new file mode 100644 index 000000000000..83de65cb955c --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.cpp @@ -0,0 +1,143 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "arr_print.h" +#include "Geodesic_arcs.h" + +using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; +using Geom_traits = CGAL::Arr_geodesic_arc_on_sphere_traits_2; +using Point = Geom_traits::Point_2; +using Curve = Geom_traits::Curve_2; +using Topol_traits = CGAL::Arr_spherical_topology_traits_2; +using Arrangement = CGAL::Arrangement_on_surface_2; + +using Dir3 = Kernel::Direction_3; +std::ostream& operator << (std::ostream& os, const Dir3& d) { + os << d.dx() << ", " << d.dy() << ", " << d.dz(); + return os; +} + +using Approximate_point_2 = Geom_traits::Approximate_point_2; +std::ostream& operator << (std::ostream& os, const Approximate_point_2& d) { + os << d.dx() << ", " << d.dy() << ", " << d.dz(); + return os; +} + +using Approximate_number_type = Geom_traits::Approximate_number_type; +using Approximate_kernel = Geom_traits::Approximate_kernel; +using Approximate_Vector_3 = CGAL::Vector_3; +using Approximate_Direction_3 = Approximate_kernel::Direction_3; +using Direction_3 = Kernel::Direction_3; + +std::ostream& operator << (std::ostream& os, const Approximate_Vector_3& v) { + os << v.x() << ", " << v.y() << ", " << v.z(); + //os << v.hx() << ", " << v.hy() << ", " << v.hz() << ", " << v.hw(); + return os; +} + +auto Geodesic_arcs::get_approx_arcs(double error) -> Approx_arcs { + // Construct the arrangement from 12 geodesic arcs. + Geom_traits traits; + Arrangement arr(&traits); + + auto ctr_p = traits.construct_point_2_object(); + auto ctr_cv = traits.construct_curve_2_object(); + + std::vector xcvs; + xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0))); + xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 0, 1))); + xcvs.push_back(ctr_cv(ctr_p(0, 1, 0), ctr_p(0, 0, 1))); + //xcvs.push_back(ctr_cv(ctr_p(1, 0, 0), ctr_p(0, 1, 0), Dir3(0, 0, -1))); + //xcvs.push_back(ctr_cv(Dir3(0, 0, -1))); + + auto approx = traits.approximate_2_object(); + + std::vector> arcs; + for (const auto& xcv : xcvs) { + std::vector v; + auto oi2 = approx(xcv, error, std::back_insert_iterator(v)); + + std::vector arc_points; + for (const auto& p : v) { + const QVector3D arc_point(p.dx(), p.dy(), p.dz()); + arc_points.push_back(arc_point); + } + arcs.push_back(std::move(arc_points)); + } + //std::cout << "offset count = " << m_arc_offsets.size() << std::endl; + + return arcs; +} + +auto Geodesic_arcs::get_approx_arcs(const Kml::Placemark& + placemark, double error) -> Approx_arcs { + Geom_traits traits; + auto ctr_p = traits.construct_point_2_object(); + auto ctr_cv = traits.construct_curve_2_object(); + + std::vector xcvs; + for (const auto& polygon : placemark.polygons) { + // colect all rings into a single list (FOR NOW!!!) + // TO-DO: PROCESS OUTER & INNER BOUNDARIES SEPARATELY!!! + Kml::LinearRings linear_rings; + linear_rings.push_back(polygon.outer_boundary); + for (const auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(inner_boundary); + + + // convert the nodes to points on unit-sphere + for (const auto& lring : linear_rings) { + std::vector sphere_points; + for (const auto& node : lring.nodes) { + const auto p = node.get_coords_3d(); + Approximate_Vector_3 v(p.x, p.y, p.z); + sphere_points.push_back(v); + } + + // add geodesic arcs for the current LinearRing + int num_points = sphere_points.size(); + for (int i = 0; i < num_points - 1; ++i) { + const auto p1 = sphere_points[i]; + const auto p2 = sphere_points[i + 1]; + xcvs.push_back(ctr_cv(ctr_p(p1.x(), p1.y(), p1.z()), + ctr_p(p2.x(), p2.y(), p2.z()))); + } + } + } + + auto approx = traits.approximate_2_object(); + std::vector> arcs; + for (const auto& xcv : xcvs) { + std::vector v; + auto oi2 = approx(xcv, error, std::back_insert_iterator(v)); + + std::vector arc_points; + for (const auto& p : v) { + const QVector3D arc_point(p.dx(), p.dy(), p.dz()); + arc_points.push_back(arc_point); + } + arcs.push_back(std::move(arc_points)); + } + //std::cout << "offset count = " << m_arc_offsets.size() << std::endl; + + return arcs; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.h new file mode 100644 index 000000000000..7e8a3a13849d --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Geodesic_arcs.h @@ -0,0 +1,30 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef GEODESIC_ARCS_H +#define GEODESIC_ARCS_H + +#include +#include + +#include "Kml_reader.h" + +class Geodesic_arcs { +public: + using Approx_arcs = std::vector>; + + Approx_arcs get_approx_arcs(double error); + + // generate approximate arcs from KML data + Approx_arcs get_approx_arcs(const Kml::Placemark& placemark, double error); + Approx_arcs get_approx_arcs(const Kml::Placemarks& placemarks, double error); +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.cpp new file mode 100644 index 000000000000..8ec7d23ea76b --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.cpp @@ -0,0 +1,404 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include +#include +#include +#include + +#include +#include +#include + +#include "Kml_reader.h" + +//! \brief +double Kml::Node::distance_to(const Node& r) const { + const auto dx = lon - r.lon; + const auto dy = lat - r.lat; + return sqrt(dx * dx + dy * dy); +} + +//! \brief +bool Kml::Node::operator == (const Node& r) const +{ return (lon == r.lon) && (lat == r.lat); } + +//! \brief +Kml::Vec3d Kml::Node::get_coords_3d(const double r) const { + const long double phi = qDegreesToRadians(lat); + const long double theta = qDegreesToRadians(lon); + const auto z = r * std::sin(phi); + const auto rxy = r * std::cos(phi); + const auto x = rxy * std::cos(theta); + const auto y = rxy * std::sin(theta); + + return Vec3d{ (double)x, (double)y, (double)z }; +} + +//! \brief +QVector3D Kml::Node::get_coords_3f(const double r) const { + const auto v = get_coords_3d(r); + return QVector3D(v.x, v.y, v.z); +} + +//! \brief +std::ostream& operator << (std::ostream& os, const Kml::Node& n) { + os << n.lon << ", " << n.lat; + return os; +} + +//! \brief +std::size_t Kml::get_number_of_polygons(Placemarks& placemarks) { + std::size_t total_number_of_polygons = 0; + for (auto& placemark : placemarks) + total_number_of_polygons += placemark.polygons.size(); + return total_number_of_polygons; +} + +//! \brief +Kml::Placemarks Kml::read(const std::string& file_name) { + LinearRing lring; + Polygon polygon; + Placemark placemark; + Placemarks placemarks; + + QFile file(file_name.c_str()); + if (file.open(QIODevice::ReadOnly)) { + QXmlStreamReader xmlReader; + xmlReader.setDevice(&file); + + xmlReader.readNext(); + + // Reading from the file + while (!xmlReader.isEndDocument()) { + QString name = xmlReader.name().toString(); + + if (xmlReader.isStartElement()) { + if (name == "Placemark") { + placemark = Placemark{}; + } + else if (name == "Polygon") { + polygon = Polygon{}; + } + else if (name == "LinearRing") { + lring = LinearRing{}; + } + else if (name == "coordinates") { + xmlReader.readNext(); + auto str = xmlReader.text().toString(); + auto node_strs = str.split(" "); + for (const auto& node_str : node_strs) { + if (node_str.isEmpty()) continue; + + auto coord_strs = node_str.split(","); + const auto lon = coord_strs[0].toDouble(); + const auto lat = coord_strs[1].toDouble(); + lring.nodes.push_back(Node{ lon, lat }); + } + } + else if (name == "SimpleData") { + auto attributes = xmlReader.attributes(); + auto attr_name = attributes[0].name().toString(); + auto attr_value = attributes[0].value().toString(); + if ((attr_name == "name") && (attr_value == "ADMIN")) + { + xmlReader.readNext(); + placemark.name = xmlReader.text().toString().toStdString();; + } + } + } + else if (xmlReader.isEndElement()) { + if (name == "Placemark") + { + placemarks.push_back(std::move(placemark)); + } + else if (name == "Polygon") { + placemark.polygons.push_back(std::move(polygon)); + } + else if (name == "outerBoundaryIs") { + polygon.outer_boundary = std::move(lring); + } + else if (name == "innerBoundaryIs") { + polygon.inner_boundaries.push_back(std::move(lring)); + } + else if (name == "LinearRing") { + // LinearRing is moved to the correct locations via other tags above + assert(*lring.nodes.begin() == *(--lring.nodes.end())); + } + else if (name == "coordinates") { + // no need to do anything here: the coordinates are read above! + } + } + + xmlReader.readNext(); + } + + if (xmlReader.hasError()) { + std::cout << "XML error: " << xmlReader.errorString().data() << std::endl; + } + } + + return placemarks; +} + +//! \brief +Kml::Nodes Kml::get_duplicates(const Placemarks& placemarks) { + // collect all nodes into a single vector + std::size_t polygon_count = 0; + std::vector nodes; + for (const auto& pm : placemarks) { + for (const auto& polygon : pm.polygons) { + ++polygon_count; + + Kml::LinearRings linear_rings; + linear_rings.push_back(polygon.outer_boundary); + for (const auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(inner_boundary); + + for(const auto& lring : linear_rings) { + for (const auto& node : lring.nodes) nodes.push_back(node); + } + } + } + qDebug() << "polygon count = " << polygon_count; + + auto count = nodes.size(); + std::vector num_duplicates(count, 0); + qDebug() << "node count (with duplicates) = " << count; + std::size_t dup_count = 0; + + // this keeps track of how many nodes there are with certain dup-count + std::unordered_map dup_count_map; + + Nodes duplicate_nodes; + for (std::size_t i = 0; i < count; ++i) { + // if the current node has been detected as duplicate skip it + if (num_duplicates[i] > 0) continue; + + const auto& curr_node = nodes[i]; + std::vector curr_dup; // current set of duplicates + for (std::size_t j = i + 1; j < count; ++j) { + if (curr_node == nodes[j]) curr_dup.push_back(j); + } + + // if duplicates found + if (!curr_dup.empty()) { + ++dup_count; + std::size_t num_dup = curr_dup.size() + 1; // +1 for the i'th node + num_duplicates[i] = num_dup; + for (const auto di : curr_dup) num_duplicates[di] = num_dup; + + duplicate_nodes.push_back(curr_node); + dup_count_map[num_dup]++; + } + } + qDebug() << "dup count = " << dup_count; + for (const auto& p : dup_count_map) { + const auto dup_count = p.first; + const auto num_nodes_with_this_dup_count = p.second; + qDebug() << dup_count << ": " << num_nodes_with_this_dup_count; + } + + return duplicate_nodes; +} + +//! \brief +Kml::Nodes Kml::generate_ids(Placemarks& placemarks) { + // collect all nodes into a single vector + // std::size_t polygon_count = 0; + std::vector nodes; + for (auto& pm : placemarks) { + for (auto& polygon : pm.polygons) { + // polygon_count++; + + std::vector linear_rings; + linear_rings.push_back(&polygon.outer_boundary); + for (auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(&inner_boundary); + + for (auto* lring : linear_rings) { + for (const auto& node : lring->nodes) { + // check if the node is in the nodes + auto it = std::find(nodes.begin(), nodes.end(), node); + if (nodes.end() == it) { + // insert new node + nodes.push_back(node); + const std::size_t node_id = nodes.size() - 1; + lring->ids.push_back(node_id); + } + else { + // get the existing node + const std::size_t node_id = std::distance(nodes.begin(), it); + lring->ids.push_back(node_id); + assert(nodes[node_id] == node); + } + } + + assert(lring->nodes.size() == lring->ids.size()); + for (std::size_t i = 0; i < lring->nodes.size(); ++i) + assert(lring->nodes[i] == nodes[lring->ids[i]]); + } + } + } + + return nodes; +} + +//! \breif +Kml::Nodes Kml::generate_ids_approx(Placemarks& placemarks, const double eps) { + // collect all nodes into a single vector + // std::size_t polygon_count = 0; + std::vector nodes; + for (auto& pm : placemarks) { + for (auto& polygon : pm.polygons) { + // ++polygon_count; + + std::vector linear_rings; + linear_rings.push_back(&polygon.outer_boundary); + for (auto& inner_boundary : polygon.inner_boundaries) + linear_rings.push_back(&inner_boundary); + + for (auto* lring : linear_rings) { + lring->ids.clear(); + + for (const auto& node : lring->nodes) { + // check if there is a node sufficiently close to the current one + auto node_index = std::numeric_limits::max(); + for (std::size_t i = 0; i < nodes.size(); ++i) { + const auto dist = node.distance_to(nodes[i]); + if (dist < eps) { + node_index = i; + break; + } + } + + if (node_index == std::numeric_limits::max()) { + // insert new node + nodes.push_back(node); + const auto node_id = nodes.size() - 1; + lring->ids.push_back(node_id); + } + else { + // get the existing node + const auto node_id = node_index; + lring->ids.push_back(node_id); + } + + auto it = std::unique(lring->ids.begin(), lring->ids.end()); + std::vector new_ids(lring->ids.begin(), it); + if (new_ids.size() < lring->ids.size()) + std::cout << "** REDUCED!\n"; + lring->ids = std::move(new_ids); + } + } + } + } + + // find the pair of closest nodes + double min_dist = std::numeric_limits::max(); + std::size_t ni1 = 0; + std::size_t ni2 = 0; + std::size_t num_nodes = nodes.size(); + for (std::size_t i = 0; i < num_nodes - 1; ++i) { + for (std::size_t j = i + 1; j < num_nodes; ++j) { + const auto dist = nodes[i].distance_to(nodes[j]); + if (min_dist > dist) { + min_dist = dist; + ni1 = i; + ni2 = j; + } + } + } + std::cout << "min dist = " << min_dist << std::endl; + std::cout << "node 1 = " << nodes[ni1] << std::endl; + std::cout << "node 2 = " << nodes[ni2] << std::endl; + std::cout << "node 1 = " << nodes[ni1].get_coords_3d() << std::endl; + std::cout << "node 2 = " << nodes[ni2].get_coords_3d() << std::endl; + + return nodes; +} + +//! \brief +Kml::Nodes Kml::Polygon::get_all_nodes() const { + Nodes all_nodes; + auto source_first = outer_boundary.nodes.begin(); + auto source_last = outer_boundary.nodes.end(); + all_nodes.insert(all_nodes.begin(), source_first, source_last); + + for (const auto& inner_boundary : inner_boundaries) { + auto source_first = inner_boundary.nodes.begin(); + auto source_last = inner_boundary.nodes.end(); + all_nodes.insert(all_nodes.begin(), source_first, source_last); + } + + return all_nodes; +} + +//! \brief +std::vector Kml::Polygon::get_all_boundaries() { + std::vector linear_rings; + linear_rings.push_back(&outer_boundary); + for (auto& inner_boundary : inner_boundaries) + linear_rings.push_back(&inner_boundary); + + return linear_rings; +} + +//! \brief +Kml::Nodes Kml::Placemark::get_all_nodes() const { + Nodes all_nodes; + for (const auto& polygon : polygons) { + auto polygon_nodes = polygon.get_all_nodes(); + auto first = std::make_move_iterator(polygon_nodes.begin()); + auto last = std::make_move_iterator(polygon_nodes.end()); + all_nodes.insert(all_nodes.end(), first, last); + } + + return all_nodes; +} + +//! \brief +std::size_t Kml::Placemark::get_all_nodes_count() const { + std::size_t num_nodes = 0; + for (const auto& polygon : polygons) { + auto polygon_nodes = polygon.get_all_nodes(); + num_nodes += polygon_nodes.size(); + } + return num_nodes; +} + +//! \brief +Kml::Arcs Kml::LinearRing::get_arcs() const { + Arcs arcs; + const auto num_nodes = nodes.size(); + for (std::size_t i = 0; i < num_nodes - 1; ++i) { + const auto from = nodes[i]; + const auto to = nodes[i + 1]; + arcs.push_back(Arc{ from, to }); + } + return arcs; +} + +//! \brief +void Kml::LinearRing::get_arcs(Arcs& arcs) const { + auto a = get_arcs(); + arcs.insert(arcs.end(), a.begin(), a.end()); +} + +Kml::Arcs Kml::Placemark::get_all_arcs() const { + Arcs all_arcs; + + for (const auto& polygon : polygons) { + polygon.outer_boundary.get_arcs(all_arcs); + for (const auto& inner_boundary : polygon.inner_boundaries) + inner_boundary.get_arcs(all_arcs); + } + + return all_arcs; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.h new file mode 100644 index 000000000000..dbeaa5eeceda --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Kml_reader.h @@ -0,0 +1,100 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef KML_READER_H +#define KML_READER_H + +#include +#include +#include + +#include + +class Kml { +public: + // double precision 3D-point (QVector3D has float coordinates) + struct Vec3d { + double x, y, z; + + friend std::ostream& operator << (std::ostream& os, const Vec3d& v) { + os << v.x << ", " << v.y << ", " << v.z; + return os; + } + }; + + struct Node { + double lon, lat; + + Node() : lon(-1111), lat(-1111) {}; + Node(double longitude, double latitude) : lon(longitude), lat(latitude) {}; + + double distance_to(const Node& r) const; + + bool operator == (const Node& r) const; + Vec3d get_coords_3d(const double r = 1.0) const; + QVector3D get_coords_3f(const double r=1.0) const; + friend std::ostream& operator << (std::ostream& os, const Node& n); + }; + using Nodes = std::vector; + + struct Arc { + Node from, to; + }; + + using Arcs = std::vector; + + struct LinearRing { + std::vector nodes; + std::vector ids; + + Arcs get_arcs() const; + void get_arcs(Arcs& arcs) const; + }; + using LinearRings = std::vector; + + struct Polygon { + LinearRing outer_boundary; + LinearRings inner_boundaries; + + // when collecting nodes start from the outer boundary and then get nodes + // from individual inner boundaries in the order + Nodes get_all_nodes() const; + + std::vector get_all_boundaries(); + }; + + struct Placemark { + std::vector polygons; + std::string name; + + // collects all nodes from all polygons + Nodes get_all_nodes() const; + std::size_t get_all_nodes_count() const; + + Arcs get_all_arcs() const; + }; + + using Placemarks = std::vector; + + static std::size_t get_number_of_polygons(Placemarks& placemarks); + + static Placemarks read(const std::string& file_name); + + static Nodes get_duplicates(const Placemarks& placemarks); + + + // Outputs all used nodes without duplications! + // NOTE: this function modifies Placemarks data-structure! + static Nodes generate_ids(Placemarks& placemarks); + + // same as above but by collapsing close-by nodes based on distance bound + static Nodes generate_ids_approx(Placemarks& placemarks, const double eps); +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.cpp new file mode 100644 index 000000000000..93ab6cd55966 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.cpp @@ -0,0 +1,112 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include + +#include "Line_strips.h" + +//! \brief +Line_strips::Line_strips(std::vector& line_strip_points) { + initializeOpenGLFunctions(); + + std::vector vertex_data; + m_offsets.push_back(0); + for (const auto& p : line_strip_points) + vertex_data.push_back(p); + + const auto end_of_current_arc_points = static_cast(vertex_data.size()); + m_offsets.push_back(end_of_current_arc_points); + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 0; + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//! \brief +Line_strips::Line_strips(std::vector>& arcs) { + initializeOpenGLFunctions(); + + std::vector vertex_data; + m_offsets.push_back(0); + for (const auto& arc : arcs) { + for(const auto& p : arc) vertex_data.push_back(p); + + const auto end_of_current_arc_points = static_cast(vertex_data.size()); + m_offsets.push_back(end_of_current_arc_points); + } + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, + vertex_buffer_size, + vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 0; + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//! \brief +std::size_t Line_strips::get_num_line_strips() const +{ return m_offsets.size() - 1; } + +//! \brief +void Line_strips::draw(int line_strip_index) { + glBindVertexArray(m_vao); + const auto first = m_offsets[line_strip_index]; + const auto count = m_offsets[line_strip_index + 1] - first; + glDrawArrays(GL_LINE_STRIP, first, count); + glBindVertexArray(0); +} + + +//! \brief +void Line_strips::draw() { + glBindVertexArray(m_vao); + for (std::size_t i = 1; i < m_offsets.size(); i++) { + const auto first = m_offsets[i - 1]; + const auto count = m_offsets[i] - first; + glDrawArrays(GL_LINE_STRIP, first, count); + } + glBindVertexArray(0); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.h new file mode 100644 index 000000000000..a4b0d996adc6 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Line_strips.h @@ -0,0 +1,35 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef LINE_STRIPS_H +#define LINE_STRIPS_H + +#include +#include + +#include "Common_defs.h" + +class Line_strips : protected OpenGLFunctionsBase { +public: + Line_strips(std::vector& line_strip_points); + Line_strips(std::vector>& arcs); + + std::size_t get_num_line_strips() const; + void draw(int line_strip_index); + + void draw(); + +private: + GLuint m_vao; + GLuint m_vbo; + std::vector m_offsets; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.cpp new file mode 100644 index 000000000000..4d711d661283 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.cpp @@ -0,0 +1,394 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Main_widget.h" + +#include +#include +#include + +#include + +#include "Aos.h" +#include "Aos_triangulator.h" +#include "Camera_manip_rot.h" +#include "Camera_manip_rot_bpa.h" +#include "Camera_manip_zoom.h" +#include "GUI_country_pick_handler.h" +#include "Kml_reader.h" +#include "Message_manager.h" +#include "Timer.h" +#include "Tools.h" +#include "Verification.h" + +//! \brief +Main_widget::Main_widget(const QString& file_name) : m_file_name(file_name) {} + +//! \brief +Main_widget::~Main_widget() { + // Make sure the context is current when deleting the texture and the buffers. + makeCurrent(); + doneCurrent(); +} + +//! \brief +void Main_widget::set_mouse_pos(const QVector3D mouse_pos) +{ m_gr_mouse_vertex->set_pos(mouse_pos); } + +//! \brief +void Main_widget::hightlight_country(const std::string& country_name) { + static std::string prev_picked_country; + + if (! prev_picked_country.empty()) { + // dim the previous country color + auto& prev_country = m_gr_country_triangles[prev_picked_country]; + auto color = prev_country->get_color(); + color *= m_dimming_factor; + color.setW(1); + prev_country->set_color(color); + } + + if (! country_name.empty()) { + // highlight the current country color + auto& curr_country = m_gr_country_triangles[country_name]; + auto color = curr_country->get_color(); + color /= m_dimming_factor; + color.setW(1); + curr_country->set_color(color); + qDebug() << "SELECTED COUNTRY: " << country_name.c_str(); + } + + prev_picked_country = country_name; +} + +//! \brief +void Main_widget::mousePressEvent(QMouseEvent* e) { + // forward the event to the camera manipulators + m_camera_manip_rot->mousePressEvent(e); + m_camera_manip_zoom->mousePressEvent(e); + m_pick_handler->mousePressEvent(e); + update(); +} + +//! \brief +void Main_widget::mouseMoveEvent(QMouseEvent* e) { + // forward the event to the camera manipulator + m_camera_manip_rot->mouseMoveEvent(e); + m_camera_manip_zoom->mouseMoveEvent(e); + m_pick_handler->mouseMoveEvent(e); + update(); +} + +//! \brief +void Main_widget::mouseReleaseEvent(QMouseEvent* e) { + // forward the event to the camera manipulator + m_camera_manip_rot->mouseReleaseEvent(e); + m_camera_manip_zoom->mouseReleaseEvent(e); + m_pick_handler->mouseReleaseEvent(e); + update(); +} + +//! \brief +// void Main_widget::timerEvent(QTimerEvent* event) { update(); } + +//! \brief +void Main_widget::keyPressEvent(QKeyEvent* /* event */) {} + +//! \brief +void Main_widget::initializeGL() { + m_pick_handler = std::make_unique(*this); + + QVector3D initial_mouse_pos(0, -1, 0); + m_gr_mouse_vertex = std::make_unique(initial_mouse_pos); + + initializeOpenGLFunctions(); + init_camera(); + init_geometry(); + init_shader_programs(); + + m_current_approx_error = 0.001f; + m_num_uniform_points = 4096; + + qDebug() << "loading arrangement.."; + m_arrh = Aos::load_arr(m_file_name.toStdString()); + if (m_arrh == nullptr) { + std::string msg("Error: failed to load file "); + msg += m_file_name.toStdString(); + throw std::runtime_error(msg); + return; + } + + init_country_borders(m_current_approx_error); + + qDebug() << "generating triangles.."; + //auto triangle_points = Aos::get_triangles(arrh); + //auto triangle_points = Aos_triangulator::get_all(arrh); + //auto country_triangles_map = Aos::get_triangles_by_country(m_arrh); + auto country_triangles_map = + Aos_triangulator::get_by_country(m_arrh, m_current_approx_error, + m_num_uniform_points); + //auto color_map = Aos::get_color_mapping(m_arrh); + //qDebug() << "color map size = " << color_map.size(); + qDebug() << "num countries = " << country_triangles_map.size(); + auto rndm = [] {return rand() / double(RAND_MAX); }; + for (auto& [country_name, triangle_points] : country_triangles_map) { + auto country_triangles = std::make_unique(triangle_points); + auto color = QVector4D(rndm(), rndm(), rndm(), 1); + auto m = std::max(color.x(), std::max(color.y(), color.z())); + color /= m; + color *= m_dimming_factor; + color.setW(1); + country_triangles->set_color(color); + //country_triangles->set_color(colors[color_map[country_name]]); + m_gr_country_triangles.emplace(country_name, std::move(country_triangles)); + } + country_triangles_map.clear(); + + //qDebug() << "num triangles = " << triangle_points.size() / 3; + //m_gr_all_triangles = std::make_unique(triangle_points); + + glClearColor(0, 0, 0, 1); + glEnable(GL_DEPTH_TEST); // Enable depth buffer + glEnable(GL_CULL_FACE); // Enable back face culling + + // Use QBasicTimer because its faster than QTimer + // m_timer.start(12, this); +} + +//! \brief +void Main_widget::init_camera() { + m_camera.set_pos(0, 0, 3); + m_camera_manip_rot = std::make_unique(m_camera); + //m_camera_manip_rot = std::make_unique(m_camera); + m_camera_manip_zoom = std::make_unique(m_camera); + + // this makes z-axes point upwards! + m_model.rotate(-90, 1, 0, 0); + + // register the zoom-changed function + Message_manager::add("zoom_changed", + [&] { + qDebug() << "ZOOM CHANGED!!!"; + //const auto error = compute_backprojected_error(0.5); + //qDebug() << "new error = " << error; + m_update_approx_error = true; + //qDebug() << "re-initializing the country borders.."; + //init_country_borders(error); + }); +} + +//! \brief +void Main_widget::init_geometry() { + // SPHERE + std::size_t num_slices = 64; + std::size_t num_stacks = 64; + float r = 1.0f; + m_gr_sphere = std::make_unique(num_slices, num_stacks, r); + const float c = 0.8f; + m_gr_sphere->set_color(c, c, c, 1); + + // IDENTIFICATION CURVE + const double error = 0.001; + auto approx_ident_curve = Aos::get_approx_identification_curve(error); + m_gr_identification_curve = std::make_unique(approx_ident_curve); + + const float axes_length = 2; + m_gr_world_coord_axes = std::make_unique(axes_length); +} + +//! \brief +void Main_widget::init_shader_programs() { + Shader_program::set_shader_path("shaders/"); + m_sp_smooth.init_with_vs_fs("smooth");; + m_sp_per_vertex_color.init_with_vs_fs("per_vertex_color"); + m_sp_arc.init_with_vs_fs("arc"); +} + +//! \brief +void Main_widget::init_country_borders(float error) { + // this part does the same as the code below but using arrangement! + // NOTE: the old code interferes with some logic (NEEDS REFACTORING!!!) + m_gr_all_country_borders.reset(nullptr); + qDebug() << "approximating the arcs of each edge of all faces.."; + auto all_approx_arcs = Aos::get_approx_arcs_from_faces_edges(m_arrh, error); + m_gr_all_country_borders = std::make_unique(all_approx_arcs); +} + +//! \brief +float Main_widget::compute_backprojected_error(float pixel_error) { + // compute the back-projected error + QRect vp(0, 0, m_vp_width, m_vp_height); + auto proj = m_camera.get_projection_matrix(); + auto view = m_camera.get_view_matrix(); + QMatrix4x4 model; + auto model_view = view * model; + + QVector3D p0(m_vp_width / 2, m_vp_height / 2, 0); + QVector3D p1(p0.x() + pixel_error, p0.y(), 0); + auto wp0 = p0.unproject(model_view, proj, vp); + auto wp1 = p1.unproject(model_view, proj, vp); + const float z_near = m_camera.get_z_near(); + const float r = 1.f; // sphere radius + const QVector3D origin(0, 0, 0); + const float dist_to_cam = m_camera.get_pos().distanceToPoint(origin); + + float d = dist_to_cam - r; + float err = wp0.distanceToPoint(wp1) * (d / z_near); + //find_minimum_projected_error_on_sphere(err); + return err; +} + +//! \brief +void Main_widget::resizeGL(int w, int h) { + m_camera_manip_rot->resizeGL(w, h); + m_pick_handler->resizeGL(w, h); + + m_vp_width = w; + m_vp_height = h; + + // Reset projection + qreal aspect = qreal(w) / qreal(h ? h : 1); + const qreal z_near = 0.1, z_far = 100.0, fov = 45.0; + m_camera.perspective(fov, aspect, z_near, z_far); + + // signal to look into the approximation error + m_update_approx_error = true; +} + +//! \brief +template +void draw_safe(T& ptr) { if (ptr) ptr->draw(); } + +//! \brief +void Main_widget::paintGL() { + if (m_update_approx_error) { + const auto error = compute_backprojected_error(0.5); + qDebug() << "current error = " << m_current_approx_error; + qDebug() << "new approx error = " << error; + if (error < m_current_approx_error) { + init_country_borders(error); + m_current_approx_error = error; + auto ratio = static_cast(m_current_approx_error) / error; + m_num_uniform_points *= ratio*ratio; + std::cout << "num uniform points = " << m_num_uniform_points + << std::endl; + } + m_update_approx_error = false; + } + + const auto view = m_camera.get_view_matrix(); + const auto projection = m_camera.get_projection_matrix(); + const auto model_view = view * m_model; + const auto mvp = projection * model_view; + const auto normal_matrix = model_view.normalMatrix(); + + // compute the cutting plane + // remember that we are passing the local vertex positions of the sphere + // between the vertex and fragment shader stages, so we need to convert + // the camera-pos in world coords to sphere's local coords! + auto c = m_model.inverted().map(m_camera.get_pos()); + const auto d = c.length(); + const auto r = 1.0f; + const auto sin_alpha = r / d; + const auto n = (c / d); // plane unit normal vector + const auto cos_beta = sin_alpha; + const auto p = (r * cos_beta) * n; + QVector4D plane(n.x(), n.y(), n.z(), -QVector3D::dotProduct(p, n)); + + // Clear color and depth buffer + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // SMOTH RENDERING + { + glEnable(GL_DEPTH_TEST); + + auto& sp = m_sp_smooth; + sp.use(); + + // SPHERE + { + sp.set_uniform("u_mvp", mvp); + sp.set_uniform("u_normal_matrix", normal_matrix); + auto sphere_color = QVector4D(167, 205, 242, 255) / 255; + sp.set_uniform("u_color", sphere_color); + sp.set_uniform("u_plane", QVector4D(0, 0, 0, 0)); + //sp.set_uniform("u_color", m_sphere->get_color()); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + m_gr_sphere->draw(); + } + + // DRAW SOLID FACES + { + glDisable(GL_DEPTH_TEST); + //auto face_color = QVector4D(241, 141, 0, 255) / 255; + //sp.set_uniform("u_color", face_color); + sp.set_uniform("u_plane", plane); + //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + //m_gr_all_triangles->draw(); + for (auto& [country_name, country] : m_gr_country_triangles) + { + sp.set_uniform("u_color", country->get_color()); + country->draw(); + } + + //sp.set_uniform("u_color", QVector4D(0,0,0,1)); + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + //m_gr_all_triangles->draw(); + } + + sp.unuse(); + } + + // WORLD COORDINATE AXES + { + auto& sp = m_sp_per_vertex_color; + sp.use(); + sp.set_uniform("u_mvp", mvp); + + glEnable(GL_DEPTH_TEST); + m_gr_world_coord_axes->draw(); + + sp.unuse(); + } + + // VERTICES & GEODESIC ARCS + { + glDisable(GL_DEPTH_TEST); + + auto& sp = m_sp_arc; + sp.use(); + sp.set_uniform("u_mvp", mvp); + + // const QVector4D arc_color(0, 0.5, 1, 1); + glLineWidth(5); + sp.set_uniform("u_plane", plane); + + // IDENTIFICATION CURVE + sp.set_uniform("u_color", QVector4D(0, 1, 1, 1)); + m_gr_identification_curve->draw(); + + // draw all countries' borders + float a = 0.0; + sp.set_uniform("u_color", QVector4D(a, a, a, 1)); + m_gr_all_country_borders->draw(); + + // MOUSE VERTEX + { + glPointSize(5); + sp.set_uniform("u_color", QVector4D(1, 0, 0, 1)); + //auto pos = m_mouse_vertex->get_pos(); + //pos.setX(pos.x() + 0.01); + //m_mouse_vertex->set_pos(pos); + //m_gr_mouse_vertex->set_pos(m_mouse_pos); + draw_safe(m_gr_mouse_vertex); + } + + sp.unuse(); + } +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.h new file mode 100644 index 000000000000..6d70270a5739 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget.h @@ -0,0 +1,139 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef MAIN_WIDGET_H +#define MAIN_WIDGET_H + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "Aos.h" +#include "Camera.h" +#include "Camera_manip.h" +#include "Common_defs.h" +#include "GUI_event_handler.h" +#include "Kml_reader.h" +#include "Line_strips.h" +#include "Shader_program.h" +#include "Single_vertex.h" +#include "Sphere.h" +#include "Triangles.h" +#include "Vertices.h" +#include "World_coordinate_axes.h" + +class Main_widget : public QOpenGLWidget, protected OpenGLFunctionsBase { + Q_OBJECT + +public: + using QOpenGLWidget::QOpenGLWidget; + + Main_widget(const QString& file_name); + ~Main_widget(); + + auto& get_camera() { return m_camera; } + auto& get_model_matrix() { return m_model; } + auto& get_arr_handle() { return m_arrh; } + + void set_mouse_pos(const QVector3D mouse_pos); + + void hightlight_country(const std::string& country_name); + +protected: + void mousePressEvent(QMouseEvent* e) override; + void mouseMoveEvent(QMouseEvent* e) override; + void mouseReleaseEvent(QMouseEvent* e) override; + // void timerEvent(QTimerEvent* e) override; + void keyPressEvent(QKeyEvent* event) override; + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + + void init_camera(); + void init_geometry(); + void init_shader_programs(); + + void init_country_borders(float error); + + // This is called when the required approximation of the arcs is below the + // currently required one defined by the zoom level and window size. If you + // zoom-in or increase the window-size this can be called. But once a minimum + // approximation error is needed, it will stay there until futher change. + // SEE the definition of "m_current_approx_error" member variable below! + float compute_backprojected_error(float pixel_error); + +private: + // COUNTRY ARRANGEMENT SPECIFIC DATA + QString m_file_name; + Aos::Arr_handle m_arrh; + std::unique_ptr m_gr_all_country_borders; + + // used when dimming / highlighting selected countries + const float m_dimming_factor = 0.4f; + + // GUI: event handler for picking with right mouse button + std::unique_ptr m_pick_handler; + + // These are used to highlight the picked position by right-mouse click + std::unique_ptr m_gr_mouse_vertex; + + + // TRIANGLES for rendering the countries in solid + std::unique_ptr m_gr_all_triangles; + std::map> m_gr_country_triangles; + + + // ------------------------------- + // --> COMMON SETUP FOR ALL SCENES + + // Basic objects in the scene + std::unique_ptr m_gr_sphere; + std::unique_ptr m_gr_world_coord_axes; + std::unique_ptr m_gr_identification_curve; + + // Shaders + Shader_program m_sp_smooth; + Shader_program m_sp_per_vertex_color; + Shader_program m_sp_arc; + + // Camera & controls + Camera m_camera; + std::unique_ptr m_camera_manip_rot; + std::unique_ptr m_camera_manip_zoom; + QMatrix4x4 m_model; + + // view-port + int m_vp_width = 0; + int m_vp_height = 0; + + // After zooming in or making the viewport larger, the approximation-error + // needs to be updated and checked against the old value. If a lower approxi- + // mation error is needed the necessary graphics-side updates need to be made + // INSIDE the paintGL (or whereever the OpenGL context is active)! + bool m_update_approx_error = false; + float m_current_approx_error; + std::size_t m_num_uniform_points; + + // Timer for continuous screen-updates + // QBasicTimer m_timer; + + // <-- COMMON SETUP FOR ALL SCENES + // ------------------------------- +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget_old.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget_old.h new file mode 100644 index 000000000000..98197d4fd426 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Main_widget_old.h @@ -0,0 +1,150 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef MAIN_WIDGET_H +#define MAIN_WIDGET_H + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "Aos.h" +#include "Camera.h" +#include "Camera_manip.h" +#include "Common_defs.h" +#include "GUI_event_handler.h" +#include "Kml_reader.h" +#include "Line_strips.h" +#include "Shader_program.h" +#include "SingleVertex.h" +#include "Sphere.h" +#include "Triangles.h" +#include "Vertices.h" +#include "World_coordinate_axes.h" + +class Main_widget : public QOpenGLWidget, protected OpenGLFunctionsBase { + Q_OBJECT + +public: + using QOpenGLWidget::QOpenGLWidget; + ~Main_widget(); + + auto& get_camera() { return m_camera; } + auto& get_model_matrix() { return m_model; } + auto& get_arr_handle() { return m_arrh; } + + void set_mouse_pos(const QVector3D mouse_pos) { m_mouse_pos = mouse_pos; } + + void hightlight_country(const std::string& country_name); + +protected: + void mousePressEvent(QMouseEvent* e) override; + void mouseMoveEvent(QMouseEvent* e) override; + void mouseReleaseEvent(QMouseEvent* e) override; + void timerEvent(QTimerEvent* e) override; + void keyPressEvent(QKeyEvent* event) override; + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + + void init_camera(); + void init_geometry(); + void init_shader_programs(); + + void init_country_borders(float error); + void init_country_selection(); + + void handle_country_picking(QMouseEvent* e); + + // This is called when the required approximation of the arcs is below the + // currently required one defined by the zoom level and window size. If you + // zoom-in or increase the window-size this can be called. But once a minimum + // approximation error is needed, it will stay there until futher change. + // SEE the definition of "m_current_approx_error" member variable below! + float compute_backprojected_error(float pixel_error); + + + // init problematic vertices: these are the vertices incident to deg-4 vertex + void init_problematic_nodes(); + +private: + // ARRANGEMENT + Aos::Arr_handle m_arrh; + std::unique_ptr m_gr_all_approx_arcs; + + // GUI: event handler for picking with right mouse button + std::unique_ptr m_pick_handler; + + // Objects in the scene + std::unique_ptr m_sphere; + std::unique_ptr m_world_coord_axes; + std::unique_ptr m_geodesic_arcs; + std::unique_ptr m_vertices, m_problematic_vertices; + std::unique_ptr m_identification_curve; + + // New faces not in the KML-file but created during arr-construction. + // This is used to identify the Caspian Sea! + std::unique_ptr m_new_faces; + + // These are used to highlight the picked position by right-mouse click + QVector3D m_mouse_pos; + std::unique_ptr m_mouse_vertex; + + // COUNTRY DATA + Kml::Placemarks m_countries; + std::vector m_country_names; + std::vector> m_country_borders; + + // boundary-arcs by country + int m_selected_country_index; + int m_selected_arc_index; + Kml::Nodes m_selected_country_nodes; + Kml::Arcs m_selected_country_arcs; + Kml::Placemark* m_selected_country; + + // TRIANGLES for rendering the countries in solid + std::unique_ptr m_all_triangles; + std::map> m_country_triangles; + + // Shaders + Shader_program m_sp_smooth; + Shader_program m_sp_per_vertex_color; + Shader_program m_sp_arc; + + // Camera & controls + Camera m_camera; + std::unique_ptr m_camera_manip_rot; + std::unique_ptr m_camera_manip_zoom; + QMatrix4x4 m_model; + + // view-port + int m_vp_width = 0; + int m_vp_height = 0; + + // After zooming in or making the viewport larger, the approximation-error + // needs to be updated and checked against the old value. If a lower approxi- + // mation error is needed the necessary graphics-side updates need to be made + // INSIDE the paintGL (or whereever the OpenGL context is active)! + bool m_update_approx_error = false; + float m_current_approx_error; + + // Timer for continuous screen-updates + QBasicTimer m_timer; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.cpp new file mode 100644 index 000000000000..69ddf0cdf027 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.cpp @@ -0,0 +1,25 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Message_manager.h" + +std::map +Message_manager::s_message_map; + +void Message_manager::add(const std::string& msg_name, + std::function callback) +{ s_message_map[msg_name].push_back(callback); } + +void Message_manager::notify_all(const std::string& msg_name) { + auto it = s_message_map.find(msg_name); + if (s_message_map.cend() != it) { + auto& callbacks = it->second; + for (auto& cb : callbacks) cb(); + } +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.h new file mode 100644 index 000000000000..f2cb59fc1eeb --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Message_manager.h @@ -0,0 +1,29 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef MESSAGE_MANAGER_H +#define MESSAGE_MANAGER_H + +#include +#include +#include +#include + +class Message_manager { +public: + static void add(const std::string& msg_name, std::function callback); + static void notify_all(const std::string& msg_name); + +protected: + using Callbacks = std::vector>; + static std::map s_message_map; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.cpp new file mode 100644 index 000000000000..c52d2db408d5 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.cpp @@ -0,0 +1,172 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include + +#include "Tools.h" +#include "Shader_program.h" + +std::string Shader_program::s_shader_path; + +//! \brief +void Shader_program::set_shader_path(const char* path) +{ s_shader_path = std::string(path); } + +//! \brief +bool Shader_program::init() { + initializeOpenGLFunctions(); + + m_program = glCreateProgram(); + if (! m_program) { + std::cout << "error creating shader program!\n"; + return false; + } + + return true; +} + +//! \brief +bool Shader_program::init(const char* vs, const char* gs, const char* fs) { + if (! init()) return false; + + add_shader_from_file(vs, GL_VERTEX_SHADER); + add_shader_from_file(gs, GL_GEOMETRY_SHADER); + add_shader_from_file(fs, GL_FRAGMENT_SHADER); + + link(); + validate(); + + return true; +} + +//! \brief +bool Shader_program::init(const std::string& vs, + const std::string& gs, + const std::string& fs) +{ return init(vs.c_str(), gs.c_str(), fs.c_str()); } + +//! \brief +bool Shader_program::init_with_vs_fs(const char* shader_file_prefix) { + std::string prefix(shader_file_prefix); + std::string vs = s_shader_path + prefix + "_vs.glsl"; + std::string fs = s_shader_path + prefix + "_fs.glsl"; + return init(vs, "", fs); +} + +//! \brief +void Shader_program::add_shader(const char* shader_code, GLenum shader_type) { + GLuint the_shader = glCreateShader(shader_type); + + const GLchar* the_code[] = { shader_code }; + GLint code_length[] = { static_cast(strlen(shader_code)) }; + + glShaderSource(the_shader, 1, the_code, code_length); + glCompileShader(the_shader); + + + GLint result = 0; + GLchar elog[1024] = { 0 }; + glGetShaderiv(the_shader, GL_COMPILE_STATUS, &result); + if (! result) { + std::string shader_type_name; + switch (shader_type) { + case GL_VERTEX_SHADER: shader_type_name = "VERTEX"; break; + case GL_GEOMETRY_SHADER: shader_type_name = "GEOMETRY"; break; + case GL_FRAGMENT_SHADER: shader_type_name = "FRAGMENT"; break; + } + glGetShaderInfoLog(the_shader, sizeof(elog), NULL, elog); + std::cout << "! error compiling the " << shader_type_name << + " shader:\n" << elog << std::endl; + return; + } + + glAttachShader(m_program, the_shader); +} + +//! \brief +void Shader_program::add_shader_from_file(const char* shader_file, + GLenum shader_type) { + if (strlen(shader_file) == 0) return; + + auto src = read_file(shader_file); + add_shader(src.c_str(), shader_type); +} + +//! \brief +bool Shader_program::link() { + GLint result = 0; + GLchar elog[1024] = { 0 }; + + glLinkProgram(m_program); + glGetProgramiv(m_program, GL_LINK_STATUS, &result); + if (! result) { + glGetProgramInfoLog(m_program, sizeof(elog), NULL, elog); + std::cout << "! error linking program:\n" << elog << std::endl; + return false; + } + return true; +} + +//! \brief +bool Shader_program::validate() { + GLint result = 0; + GLchar elog[1024] = { 0 }; + + glValidateProgram(m_program); + glGetProgramiv(m_program, GL_VALIDATE_STATUS, &result); + if (!result) + { + glGetProgramInfoLog(m_program, sizeof(elog), NULL, elog); + std::cout << "! error validating program:\n" << elog << std::endl; + return false; + } + return true; +} + +//! \brief +GLint Shader_program::get_uniform_location(const GLchar* name) { + const auto uniform_loc = glGetUniformLocation(m_program, name); + return uniform_loc; +} + +//! \brief +void Shader_program::use() { glUseProgram(m_program); } + +//! \brief +void Shader_program::unuse() { glUseProgram(0); } + +//! \brief +void Shader_program::set_uniform(GLint uniform_loc, const QMatrix4x4& m) +{ glUniformMatrix4fv(uniform_loc, 1, GL_FALSE, m.data()); } + +//! \brief +void Shader_program::set_uniform(const GLchar* name, const QMatrix4x4& m) { + const auto uniform_loc = get_uniform_location(name); + set_uniform(uniform_loc, m); +} + +//! \brief +void Shader_program::set_uniform(GLint uniform_loc, const QMatrix3x3& m) +{ glUniformMatrix3fv(uniform_loc, 1, GL_FALSE, m.data()); } + +//! \brief +void Shader_program::set_uniform(const GLchar* name, const QMatrix3x3& m) { + const auto uniform_loc = get_uniform_location(name); + set_uniform(uniform_loc, m); +} + +//! \brief +void Shader_program::set_uniform(GLint uniform_loc, const QVector4D& v) +{ glUniform4f(uniform_loc, v.x(), v.y(), v.z(), v.w()); } + +//! \brief +void Shader_program::set_uniform(const GLchar* name, const QVector4D& v) { + const auto uniform_loc = get_uniform_location(name); + set_uniform(uniform_loc, v); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.h new file mode 100644 index 000000000000..1c0c4d06061e --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Shader_program.h @@ -0,0 +1,57 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef SHADER_PROGRAM_H +#define SHADER_PROGRAM_H + +#include +#include + +#include "Common_defs.h" + +class Shader_program : protected OpenGLFunctionsBase { +public: + static void set_shader_path(const char* path); + + bool init(); + bool init(const char* vs, const char* gs, const char* fs); + bool init(const std::string& vs, const std::string& gs, + const std::string& fs); + + /*! Initialize with just the vertex and fragment shaders + */ + bool init_with_vs_fs(const char* shader_file_prefix); + + void add_shader(const char* shader_code, GLenum shader_type); + void add_shader_from_file(const char* shader_file, GLenum shader_type); + + bool link(); + bool validate(); + + GLint get_uniform_location(const GLchar* name); + + void use(); + void unuse(); + + void set_uniform(GLint uniform_loc, const QMatrix4x4& m); + void set_uniform(const GLchar* name, const QMatrix4x4& m); + + void set_uniform(GLint uniform_loc, const QMatrix3x3& m); + void set_uniform(const GLchar* name, const QMatrix3x3& m); + + void set_uniform(GLint uniform_loc, const QVector4D& v); + void set_uniform(const GLchar* name, const QVector4D& v); + +private: + GLuint m_program; + static std::string s_shader_path; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.cpp new file mode 100644 index 000000000000..ce94629bbfaf --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.cpp @@ -0,0 +1,72 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Single_vertex.h" + +//!\brief +Single_vertex::Single_vertex(const QVector3D& pos) { + initializeOpenGLFunctions(); + + m_pos = pos; + m_visible = true; + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(m_pos); + auto vertex_buffer_data = reinterpret_cast(&m_pos); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_DYNAMIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 0; + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//!\brief +void Single_vertex::set_visible(bool flag) { m_visible = flag; } + +//!\brief +void Single_vertex::set_pos(const QVector3D& pos) { + m_pos = pos; + m_update = true; +} + +//!\brief +const QVector3D& Single_vertex::get_pos() const { return m_pos; } + +//!\brief +void Single_vertex::draw() { + if (m_visible) { + if (m_update) { + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(m_pos); + auto vertex_buffer_data = reinterpret_cast(&m_pos); + auto offset = 0; + glBufferSubData(GL_ARRAY_BUFFER, offset, vertex_buffer_size, + vertex_buffer_data); + m_update = false; + } + + glBindVertexArray(m_vao); + glDrawArrays(GL_POINTS, 0, 1); + glBindVertexArray(0); + } +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.h new file mode 100644 index 000000000000..078b697e38ff --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Single_vertex.h @@ -0,0 +1,36 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef SINGLE_VERTEX_H +#define SINGLE_VERTEX_H + +#include +#include +#include "Common_defs.h" + +class Single_vertex : protected OpenGLFunctionsBase { +public: + Single_vertex(const QVector3D& pos); + + void set_visible(bool flag); + void set_pos(const QVector3D& pos); + const QVector3D& get_pos() const; + + void draw(); + +private: + bool m_visible; + bool m_update = true; // flag to update the VBO (set_pos sets this) + GLuint m_vao; + GLuint m_vbo; + QVector3D m_pos; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.cpp new file mode 100644 index 000000000000..8a24ae4c6b80 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.cpp @@ -0,0 +1,178 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include +#include + +#include + +#include "Sphere.h" + +//! \brief +Sphere::Sphere(std::size_t num_slices, std::size_t num_stacks, float r) { + initializeOpenGLFunctions(); + + num_stacks = std::max(2, num_stacks); + std::vector vertices, normals; + + // NORTH POLE + vertices.push_back(QVector3D(0, 0, r)); + normals.push_back(QVector3D(0, 0, 1)); + + // SOUTH POLE + vertices.push_back(QVector3D(0, 0, -r)); + normals.push_back(QVector3D(0, 0, -1)); + auto starting_index_of_middle_vertices = vertices.size(); + + for (std::size_t j = 1; j < num_stacks; ++j) { + // Calculate the latitude (vertical angle) for the current stack + float lat = M_PI * j / num_stacks; + float rxy = r * std::sin(lat); + float z = r * std::cos(lat); + + for (std::size_t i = 0; i < num_slices; ++i) { + // Calculate the longitude (horizontal angle) for the current slice + float lon = 2 * M_PI * i / num_slices; + + // Convert spherical coordinates to Cartesian coordinates + float x = rxy * std::cos(lon); + float y = rxy * std::sin(lon); + + auto p = QVector3D(x, y, z); + auto n = p / p.length(); + vertices.push_back(p); + normals.push_back(n); + } + } + + // strided vertex-data + std::vector vertex_data; + for (std::size_t i = 0; i < vertices.size(); ++i) { + vertex_data.push_back(vertices[i]); + vertex_data.push_back(normals[i]); + } + + // add the indices for all triangles + std::vector indices; + + // NORTH CAP + const GLuint north_vertex_index = 0; + const auto north_cap_vertex_index_start = starting_index_of_middle_vertices; + for (std::size_t i = 0; i < num_slices; ++i) { + indices.push_back(north_vertex_index); + indices.push_back(static_cast(north_cap_vertex_index_start + i)); + indices.push_back(static_cast(north_cap_vertex_index_start + (i + 1) % num_slices)); + } + + // 0 = NORTH VERTEX + // 1 = SOUTH VERTEX + // [2, 2 + (numSlices-1)] = bottom vertices of the stack #1 + // [2+numSlices, 2 + (2*numSlices - 1)] = bottom vertices of the stack #2 + // ... + // [2+(k-1)*numSlices, 2 + (k*numSlices -1)] = bottom vertices of the stack #k + // .. + // [2+(numStacks-1)*numSlices, 2+(numStacks*numSlices-1)] = bottom vertices of + // the last stack (# numStacks) + + // SOUTH CAP + const GLuint south_vertex_index = 1; + const std::size_t south_cap_index_start = starting_index_of_middle_vertices + + (num_stacks - 2) * num_slices; + for (std::size_t i = 0; i < num_slices; ++i) { + const auto vi0 = south_vertex_index; + const auto vi1 = static_cast(south_cap_index_start + i); + const auto vi2 = static_cast(south_cap_index_start + (i + 1) % num_slices); + indices.push_back(vi2); + indices.push_back(vi1); + indices.push_back(vi0); + } + + // MIDDLE TRIANGLES + for (std::size_t k = 0; k < num_stacks - 2; ++k) { + const std::size_t stack_start_index = + starting_index_of_middle_vertices + k * num_slices; + const std::size_t next_stack_start_index = stack_start_index + num_slices; + for (std::size_t i = 0; i < num_slices; ++i) { + // check why the following code snippet does not work (winding order?) + //std::size_t vi0 = stackStartIndex + i; + //std::size_t vi1 = nextStackStartIndex + i; + //std::size_t vi2 = nextStackStartIndex + (i + 1) % numSlices; + //std::size_t vi3 = stackStartIndex + (i + 1) % numSlices; + auto vi0 = static_cast(stack_start_index + i); + auto vi1 = static_cast(stack_start_index + (i + 1) % num_slices); + auto vi2 = static_cast(next_stack_start_index + i); + auto vi3 = static_cast(next_stack_start_index + (i + 1) % num_slices); + + indices.push_back(vi0); + indices.push_back(vi2); + indices.push_back(vi1); + // + indices.push_back(vi2); + indices.push_back(vi3); + indices.push_back(vi1); + } + } + m_num_indices = static_cast(indices.size()); + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Index buffer + glGenBuffers(1, &m_ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo); + auto indices_size = sizeof(GLuint) * indices.size(); + auto indices_data = reinterpret_cast(indices.data()); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size, indices_data, + GL_STATIC_DRAW); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 6 * sizeof(float); + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + // Normal Vertex-Attribute + GLint normal_attrib_index = 1; + auto* normal_offset = reinterpret_cast(3 * sizeof(float)); + glVertexAttribPointer(normal_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + normal_offset); + glEnableVertexAttribArray(normal_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Note: calling this before glBindVertexArray(0) results in no output! + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +//! \brief +void Sphere::set_color(float r, float g, float b, float a) +{ m_color = QVector4D(r, g, b, a); } + +//! \brief +void Sphere::draw() { + // DRAW TRIANGLES + glBindVertexArray(m_vao); + + //glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); + glDrawElements(GL_TRIANGLES, m_num_indices, GL_UNSIGNED_INT, 0); + + glBindVertexArray(0); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.h new file mode 100644 index 000000000000..983a40a89a0d --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Sphere.h @@ -0,0 +1,34 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef SPHERE_H +#define SPHERE_H + +#include "Common_defs.h" +#include + +class Sphere : protected OpenGLFunctionsBase { +public: + Sphere(std::size_t num_slices, std::size_t num_stacks, float r); + + void set_color(float r, float g, float b, float a); + const QVector4D& get_color() { return m_color; } + + void draw(); + +private: + GLuint m_vao; + GLuint m_vbo; + GLuint m_ibo; + GLuint m_num_indices; + QVector4D m_color; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Timer.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Timer.h new file mode 100644 index 000000000000..1c0606be591d --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Timer.h @@ -0,0 +1,50 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef TIMER_H +#define TIMER_H + +#include +#include +#include + +class Timer { +public: + Timer() { reset(); } + + void reset() { m_Start = std::chrono::high_resolution_clock::now(); } + + double elapsed() { + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - m_Start).count() * + 0.001 * 0.001 * 0.001; + } + + double elapsed_millis() { return elapsed() * 1000.0f; } + +private: + std::chrono::time_point m_Start; +}; + +class ScopedTimer { +public: + ScopedTimer(const std::string& name) : m_name(name) {} + + ~ScopedTimer() { + double time = m_timer.elapsed_millis(); + std::cout << "[TIMER] " << m_name << " - " << time << "ms\n"; + } + +private: + std::string m_name; + Timer m_timer; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.cpp new file mode 100644 index 000000000000..c2173dd20d29 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.cpp @@ -0,0 +1,51 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Tools.h" + +#include +#include +#include + +//! \brief +std::string read_file(const std::string& file_name) { + const auto flags = std::ios::in | std::ios::binary | std::ios::ate; + std::ifstream ifs(file_name.c_str(), flags); + + if (! ifs.is_open()) { + std::cout << "could not open file: " << file_name << std::endl; + return ""; + } + + std::ifstream::pos_type file_size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector bytes(file_size); + ifs.read(&bytes[0], file_size); + + return std::string(&bytes[0], file_size); +} + +//! \brief +std::ostream& operator << (std::ostream& os, const QVector2D& v) { + os << v.x() << ", " << v.y(); + return os; +} + +//! \brief +std::ostream& operator << (std::ostream& os, const QVector3D& v) { + os << v.x() << ", " << v.y() << ", " << v.z(); + return os; +} + +//! \brief +std::ostream& operator << (std::ostream& os, const QVector4D& v) { + os << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w(); + return os; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.h new file mode 100644 index 000000000000..5b7c1362149f --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Tools.h @@ -0,0 +1,25 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef TOOLS_H +#define TOOLS_H + +#include + +#include +#include + +std::string read_file(const std::string& file_name); + +std::ostream& operator << (std::ostream& os, const QVector2D& v); +std::ostream& operator << (std::ostream& os, const QVector3D& v); +std::ostream& operator << (std::ostream& os, const QVector4D& v); + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.cpp new file mode 100644 index 000000000000..39eb89a7a5ba --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.cpp @@ -0,0 +1,80 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Triangles.h" + +#include + +//! \brief +Triangles::Triangles(std::vector& vertices) { + initializeOpenGLFunctions(); + + // computer the normals of each vertex + std::vector normals; + for(const auto& p : vertices) { + auto n = p; + n.normalize(); + normals.push_back(n); + } + + // std::size_t num_triangles = vertices.size() / 3; + + // strided vertex-data + std::vector vertex_data; + for (std::size_t i = 0; i < vertices.size(); ++i) { + vertex_data.push_back(vertices[i]); + vertex_data.push_back(normals[i]); + } + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + m_num_vertices = static_cast(vertices.size()); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 6 * sizeof(float); + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + // Normal Vertex-Attribute + GLint normal_attrib_index = 1; + auto* normal_offset = reinterpret_cast(3 * sizeof(float)); + glVertexAttribPointer(normal_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + normal_offset); + glEnableVertexAttribArray(normal_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//! \brief +void Triangles::set_color(const QVector4D& rgba) +{ m_color = rgba; } + +//! \brief +const QVector4D& Triangles::get_color() const { return m_color; } + +//! \brief +void Triangles::draw() { + // DRAW TRIANGLES + glBindVertexArray(m_vao); + glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); + glBindVertexArray(0); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.h new file mode 100644 index 000000000000..ce24fc55d6ae --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Triangles.h @@ -0,0 +1,38 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef TRIANGLES_H +#define TRIANGLES_H + +#include +#include +#include "Common_defs.h" + +class Triangles : protected OpenGLFunctionsBase { +public: + // IMPORTANT: we assume that the triangles are on the sphere! + // this means that all vertex-normals are actually the normal vector on the + // sphere at the point of projection of the current vertex on the sphere. + Triangles(std::vector& vertices); + + int get_num_triangles() const; + void set_color(const QVector4D& rgba); + const QVector4D& get_color() const; + + void draw(); + +private: + GLuint m_vao; + GLuint m_vbo; + GLsizei m_num_vertices; + QVector4D m_color; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.cpp new file mode 100644 index 000000000000..705f082eed61 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.cpp @@ -0,0 +1,123 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Verification.h" + +#include + +#include "Kml_reader.h" + +void Verification::find_minimum_projected_error_on_sphere(float we, + Camera& cam, + int vp_width, + int vp_height) { + QRect vp(0, 0, vp_width, vp_height); + auto proj = cam.get_projection_matrix(); + auto view = cam.get_view_matrix(); + QMatrix4x4 model; + auto model_view = view * model; + + float max_err = 0; + float max_theta = -1; + float max_phi = -1; + + int num_divs = 200; + const float dtheta = M_PI_2 / num_divs; + const float dphi = M_PI_2 / num_divs; + + const float r1 = 1.f; + const float r2 = r1 - we; + for (int i = 0; i <= num_divs; ++i) { + const float theta = dtheta * i; + const float cos_theta = std::cos(theta); + const float sin_theta = std::sin(theta); + + for (int j = 0; j <= num_divs; ++j) { + QVector3D p1, p2; + const float phi = dphi * j; + const float cos_phi = std::cos(phi); + const float sin_phi = std::sin(phi); + + // p1 + const float r1xz = r1 * sin_phi; + p1.setY(r1 * cos_phi); + p1.setX(r1xz * cos_theta); + p1.setZ(r1xz * sin_theta); + + // p2 + const float r2xz = r2 * sin_phi; + p2.setY(r2 * cos_phi); + p2.setX(r2xz * cos_theta); + p2.setZ(r2xz * sin_theta); + + auto wp1 = p1.project(model_view, proj, vp); + auto wp2 = p2.project(model_view, proj, vp); + + const auto pe = wp1.distanceToPoint(wp2); + if (max_err < pe) { + max_err = pe; + max_theta = theta; + max_phi = phi; + } + } + } + + std::cout << "max err = " << max_err << std::endl; + std::cout << "max phi = " << max_phi * 180 / M_PI << std::endl; + std::cout << "max theta = " << max_theta * 180 / M_PI << std::endl; + + auto wp1 = QVector3D(0, r1, 0).project(model_view, proj, vp); + auto wp2 = QVector3D(0, r2, 0).project(model_view, proj, vp); + auto pe = wp1.distanceToPoint(wp2); + std::cout << "polar err = " << pe << std::endl; + + wp1 = QVector3D(r1, 0, 0).project(model_view, proj, vp); + wp2 = QVector3D(r2, 0, 0).project(model_view, proj, vp); + pe = wp1.distanceToPoint(wp2); + std::cout << "x-axis err = " << pe << std::endl; + + wp1 = QVector3D(0, 0, 1).project(model_view, proj, vp); + wp2 = QVector3D(we, 0, 1).project(model_view, proj, vp); + pe = wp1.distanceToPoint(wp2); + std::cout << "nearest proj err = " << pe << std::endl; + + wp1 = QVector3D(0, 0, -1).project(model_view, proj, vp); + wp2 = QVector3D(we, 0, -1).project(model_view, proj, vp); + pe = wp1.distanceToPoint(wp2); + std::cout << "farthest proj err = " << pe << std::endl; + + // project the origin on the screen (to check if it projects to the mid-vp) + //std::cout << QVector3D(0, 0, 0).project(model_view, proj, vp) << std::endl; +} + +//! \brief +void Verification::verify_antarctica_node_is_redundant() { + Kml::Node n1(178.277211542064, -84.4725179992025), + n2(180.0, -84.71338), + n3(-179.942499356179, -84.7214433735525); + + // 1) check if it is collinear with its neighboring nodes: + // all of the vectors in 3D must lie in the same plane + auto v1 = n1.get_coords_3f(); + auto v2 = n2.get_coords_3f(); + auto v3 = n3.get_coords_3f(); + auto n = QVector3D::crossProduct(v1, v3); + n.normalize(); + std::cout << "** DOT PRODUCT = " << QVector3D::dotProduct(n, v2) << std::endl; + + // 2) check if it is between its neighbors (check if r,s > 0) + auto det = [](float ax, float ay, float bx, float by) + { return ax * by - ay * bx; }; + auto D = det(v1.x(), v1.y(), v3.x(), v3.y()); + auto Dr = det(v2.x(), v2.y(), v3.x(), v3.y()); + auto Ds = det(v1.x(), v1.y(), v2.x(), v2.y()); + auto r = Dr / D; + auto s = Ds / D; + std::cout << "r = " << r << "\ns=" << s << std::endl; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.h new file mode 100644 index 000000000000..263d1314cdfc --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Verification.h @@ -0,0 +1,32 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef VERIFICATION_H +#define VERIFICATION_H + +#include +#include + +#include "Camera.h" + +// This class contains code to verify or compute certain hypotheses +// +class Verification { +public: + // Use this to find the approximate of the true minimum projected error. + // we are ot using this complicated method, but provide it for completeness. + static void find_minimum_projected_error_on_sphere(float we, Camera& cam, + int vp_width, + int vp_height); + + // verify that the node (180.0, -84.71338) in Antarctica is redundant + static void verify_antarctica_node_is_redundant(); +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.cpp new file mode 100644 index 000000000000..dc64111bf93e --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.cpp @@ -0,0 +1,48 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include "Vertices.h" + +//! brief +Vertices::Vertices(const std::vector& vertices) { + initializeOpenGLFunctions(); + + auto& vertex_data = vertices; + m_num_indices = static_cast(vertices.size()); + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 0; + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//! brief +void Vertices::draw() { + glBindVertexArray(m_vao); + glDrawArrays(GL_POINTS, 0, m_num_indices); + glBindVertexArray(0); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.h new file mode 100644 index 000000000000..1ec741f3688b --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/Vertices.h @@ -0,0 +1,31 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef VERTICES_H +#define VERTICES_H + +#include +#include + +#include "Common_defs.h" + +class Vertices : protected OpenGLFunctionsBase { +public: + Vertices(const std::vector& vertices); + + void draw(); + +private: + GLuint m_vao; + GLuint m_vbo; + GLsizei m_num_indices; + std::vector m_offsets; +}; + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.cpp new file mode 100644 index 000000000000..baea14f53bf9 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.cpp @@ -0,0 +1,70 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#include + +#include + +#include "World_coordinate_axes.h" + +//! \brief +World_coord_axes::World_coord_axes(float length) { + initializeOpenGLFunctions(); + + const auto a = length; + const float c = 0.0f; + std::vector vertex_data { + QVector3D(0, 0, 0), QVector3D(1, 0, 0), + QVector3D(a, 0, 0), QVector3D(1, 0, 0), + + QVector3D(0, 0, 0), QVector3D(0, 1, 0), + QVector3D(0, a, 0), QVector3D(0, 1, 0), + + QVector3D(0, 0, 0), QVector3D(c, c, 1), + QVector3D(0, 0, a), QVector3D(c, c, 1) + }; + + // DEFINE OPENGL BUFFERS + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // Vertex Buffer + glGenBuffers(1, &m_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + auto vertex_buffer_size = sizeof(QVector3D) * vertex_data.size(); + auto vertex_buffer_data = reinterpret_cast(vertex_data.data()); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer_data, + GL_STATIC_DRAW); + + // Position Vertex-Attribute + GLint position_attrib_index = 0; + const void* position_offset = 0; + GLsizei stride = 6 * sizeof(float); + glVertexAttribPointer(position_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + position_offset); + glEnableVertexAttribArray(position_attrib_index); + + // Color Vertex-Attribute + GLint color_attrib_index = 1; + auto* color_offset = reinterpret_cast(3 * sizeof(float)); + glVertexAttribPointer(color_attrib_index, 3, GL_FLOAT, GL_FALSE, stride, + color_offset); + glEnableVertexAttribArray(color_attrib_index); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +//! \brief +void World_coord_axes::draw() { + glBindVertexArray(m_vao); + const GLsizei count = 2 * 3; // = 2 * number of lines + glDrawArrays(GL_LINES, 0, count); + glBindVertexArray(0); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.h new file mode 100644 index 000000000000..53d50109d905 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/World_coordinate_axes.h @@ -0,0 +1,29 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas + +#ifndef WORLD_COORD_AXES_H +#define WORLD_COORD_AXES_H + +#include + +#include "Common_defs.h" + +class World_coord_axes : protected OpenGLFunctionsBase { +public: + World_coord_axes(float length); + + void draw(); + +private: + GLuint m_vao; + GLuint m_vbo; +}; + + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/arr_print.h b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/arr_print.h new file mode 100644 index 000000000000..611bb8db15aa --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/arr_print.h @@ -0,0 +1,124 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas +// : Efi Fogel + +#ifndef PRINT_ARR_H +#define PRINT_ARR_H + +/*! Print all neighboring vertices to a given arrangement vertex. + */ +template +void print_neighboring_vertices(typename Arrangement::Vertex_const_handle v) { + if (v->is_isolated()) { + std::cout << "The vertex (" << v->point() << ") is isolated\n"; + return; + } + + std::cout << "The neighbors of the vertex (" << v->point() << ") are:"; + typename Arrangement::Halfedge_around_vertex_const_circulator first, curr; + first = curr = v->incident_halfedges(); + do std::cout << " (" << curr->source()->point() << ")"; + while (++curr != first); + std::cout << std::endl; +} + +/*! Print all vertices (points) and edges (curves) along a connected component + * boundary. + */ +template +void print_ccb(typename Arrangement::Ccb_halfedge_const_circulator circ) { + std::cout << "(" << circ->source()->point() << ")"; + typename Arrangement::Ccb_halfedge_const_circulator curr = circ; + do { + typename Arrangement::Halfedge_const_handle e = curr; + std::cout << " [" << e->curve() << "] " + << "(" << e->target()->point() << ")"; + } while (++curr != circ); + std::cout << std::endl; +} + +/*! Print the boundary description of an arrangement face. + */ +template +void print_face(typename Arrangement::Face_const_handle f) { + // Print the outer boundary. + if (f->is_unbounded()) std::cout << "Unbounded face.\n"; + else { + std::cout << "Outer boundary: "; + print_ccb(f->outer_ccb()); + } + + // Print the boundary of each of the holes. + size_t index = 1; + for (auto hole = f->holes_begin(); hole != f->holes_end(); ++hole, ++index) { + std::cout << " Hole #" << index << ": "; + // The following statement pacifies msvc. + typename Arrangement::Ccb_halfedge_const_circulator circ = *hole; + print_ccb(circ); + } + + // Print the isolated vertices. + index = 1; + for (auto iv = f->isolated_vertices_begin(); + iv != f->isolated_vertices_end(); ++iv, ++index) + { + std::cout << " Isolated vertex #" << index << ": " + << "(" << iv->point() << ")" << std::endl; + } +} + +/*! Print the given arrangement. + */ +template +void print_arrangement(const Arrangement& arr) { + CGAL_precondition(arr.is_valid()); + + // Print the arrangement vertices. + std::cout << arr.number_of_vertices() << " vertices:\n"; + for (auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) { + std::cout << "(" << vit->point() << ")"; + if (vit->is_isolated()) std::cout << " - Isolated.\n"; + else std::cout << " - degree " << vit->degree() << std::endl; + } + + // Print the arrangement edges. + std::cout << arr.number_of_edges() << " edges:\n"; + for (auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit) + std::cout << "[" << eit->curve() << "]\n"; + + // Print the arrangement faces. + std::cout << arr.number_of_faces() << " faces:\n"; + for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) + print_face(fit); +} + +/*! Print the size of the given arrangement. + */ +template +void print_arrangement_size(const Arrangement& arr) { + std::cout << "The arrangement size:\n" + << " |V| = " << arr.number_of_vertices() + << ", |E| = " << arr.number_of_edges() + << ", |F| = " << arr.number_of_faces() << std::endl; +} + +/*! Print the size of the given unbounded arrangement. + */ +template +void print_unbounded_arrangement_size(const Arrangement& arr) { + std::cout << "The arrangement size:\n" + << " |V| = " << arr.number_of_vertices() + << " (plus " << arr.number_of_vertices_at_infinity() + << " at infinity)" + << ", |E| = " << arr.number_of_edges() + << ", |F| = " << arr.number_of_faces() + << " (" << arr.number_of_unbounded_faces() << " unbounded)\n\n"; +} + +#endif diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/earth.cpp b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/earth.cpp new file mode 100644 index 000000000000..4bd86b24e987 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/earth.cpp @@ -0,0 +1,62 @@ +// Copyright(c) 2023, 2024 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s): Engin Deniz Diktas +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_OPENGL +#include "Main_widget.h" +#endif + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + auto args = QCoreApplication::arguments(); + if (args.size() > 2) { + qDebug() << "Usage: earth []"; + return(-1); + } + std::string file_name = (argc > 1) ? argv[1] : + CGAL::data_file_path("geometry_on_sphere/ne_110m_admin_0_countries.json"); + + if (!std::ifstream(file_name).good()) { + std::cerr << "Error: failed to find file " << file_name << "\n"; + return -1; + } + + QSurfaceFormat format; + format.setVersion(3, 3); + format.setProfile(QSurfaceFormat::CoreProfile); + //format.setProfile(QSurfaceFormat::CompatibilityProfile); + //format.setOptions(QSurfaceFormat::DeprecatedFunctions); + //QSurfaceFormat::setDefaultFormat(format); + format.setDepthBufferSize(24); + QSurfaceFormat::setDefaultFormat(format); + + app.setApplicationName("Earth"); + app.setApplicationVersion("0.1"); + try { +#ifndef QT_NO_OPENGL + Main_widget widget(file_name.c_str()); + widget.show(); +#else + QLabel note("OpenGL Support required"); + note.show(); +#endif + return app.exec(); + } + catch (std::exception& e) { + std::cerr << e.what() << std::endl; + std::cerr << "Try `" << argv[0] << " --help' for more information." + << std::endl; + return -1; + } + return 0; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_fs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_fs.glsl new file mode 100644 index 000000000000..56040e3d629a --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_fs.glsl @@ -0,0 +1,14 @@ + +#version 330 + +uniform vec4 u_color; +uniform vec4 u_plane; + +in vec3 v_pos; +out vec4 out_color; + +void main() { + if (dot(u_plane, vec4(v_pos, 1)) < 0) discard; + + out_color = u_color; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_vs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_vs.glsl new file mode 100644 index 000000000000..df598c870956 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/arc_vs.glsl @@ -0,0 +1,13 @@ + +#version 330 + +layout (location = 0) in vec3 a_pos; + +uniform mat4 u_mvp; + +out vec3 v_pos; + +void main() { + v_pos = a_pos; + gl_Position = u_mvp * vec4(a_pos.xyz, 1); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/geometry.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/geometry.glsl new file mode 100644 index 000000000000..97cf338e39b3 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/geometry.glsl @@ -0,0 +1,23 @@ + +#version 330 + +in vec3 vpos[]; +out vec4 vCol; + +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; + +void main() { + const vec3 lightDir = normalize(vec3(1,.5,.5)); + + // compute the normal for the current triangle + vec3 triNormal = normalize(cross(vpos[1]-vpos[0], vpos[2]-vpos[0])); + //float c = clamp(dot(lightDir,triNormal), 0, 1); + float c = abs(dot(lightDir,triNormal)); + vCol = vec4(.2, .2,0,1) + vec4(c,c,0,1); + + gl_Position = gl_in[0].gl_Position; EmitVertex(); + gl_Position = gl_in[1].gl_Position; EmitVertex(); + gl_Position = gl_in[2].gl_Position; EmitVertex(); + EndPrimitive(); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_fs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_fs.glsl new file mode 100644 index 000000000000..26924269055b --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_fs.glsl @@ -0,0 +1,9 @@ + +#version 330 + +uniform vec4 u_plane; + +in vec3 v_color; +out vec4 out_color; + +void main() {out_color = vec4(v_color, 1); } diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_vs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_vs.glsl new file mode 100644 index 000000000000..9d30f9ac49bf --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/per_vertex_color_vs.glsl @@ -0,0 +1,14 @@ + +#version 330 + +layout (location = 0) in vec3 a_pos; +layout (location = 1) in vec3 a_color; + +uniform mat4 u_mvp; + +out vec3 v_color; + +void main() { + v_color = a_color; + gl_Position = u_mvp * vec4(a_pos.xyz, 1); +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_fs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_fs.glsl new file mode 100644 index 000000000000..8b03859512de --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_fs.glsl @@ -0,0 +1,24 @@ + +#version 330 + +uniform vec4 u_color; +uniform vec4 u_plane; + +in vec3 v_pos; +in vec3 v_normal; + +out vec4 out_color; + +void main() { + if (dot(u_plane, vec4(v_pos, 1)) < 0) discard; + + const vec3 lightDir = normalize(vec3(0,0,-1)); + + //float c = clamp(dot(lightDir,triNormal), 0, 1); + vec3 n = normalize(v_normal); + float c = abs(dot(lightDir, n) ); + out_color = u_color * (vec4(.2, .2,.2,1) + 0.8 * vec4(c,c,c,1)); + + //color = vec4(1,1,0,1); + //color = vCol; +} diff --git a/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_vs.glsl b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_vs.glsl new file mode 100644 index 000000000000..b0f0205ab190 --- /dev/null +++ b/Arrangement_on_surface_2/demo/Arrangement_on_surface_2_earth/shaders/smooth_vs.glsl @@ -0,0 +1,19 @@ + +#version 330 + +layout (location = 0) in vec3 a_pos; +layout (location = 1) in vec3 a_normal; + +//out vec4 v_col; +out vec3 v_pos; +out vec3 v_normal; + +uniform mat4 u_mvp; +uniform mat3 u_normal_matrix; + +void main() { + v_pos = a_pos; + v_normal = u_normal_matrix * a_normal; + gl_Position = u_mvp * vec4(a_pos.xyz, 1); + //gl_Position = vec4(pos.xyz, 1); +} diff --git a/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/Arrangement_on_surface_2.txt b/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/Arrangement_on_surface_2.txt index 7c143578c893..5b4b9ed90515 100644 --- a/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/Arrangement_on_surface_2.txt +++ b/Arrangement_on_surface_2/doc/Arrangement_on_surface_2/Arrangement_on_surface_2.txt @@ -69,10 +69,10 @@ class Arrangement_2 { ... }; An instance of this template represents an arrangement embedded in the plane. When the template is instantiated, the `GeometryTraits` -parameter must be substituted with a type that defines a set of +parameter must be substituted by a type that defines a set of geometric-object types, such as point and curve, and a set of operations on objects of these types (see Section \ref -aos_sec-geom_traits); the `Dcel` parameter must be substituted with a +aos_sec-geom_traits); the `Dcel` parameter must be substituted by a type that represents a doubly-connected edge list (\dcel) data structure. It defines types of topological objects, such as vertices, edges, and faces, and the operations required to maintain the @@ -91,7 +91,7 @@ An instance of this template represents a two-dimensional arrangement embedded in a surface in three dimensional space. When the template is instantiated, the `GeometryTraits` parameter must be substituted as described above; the `TopologyTraits` parameter must be substituted -with a type that deals with the topology of the surface (see Section +by a type that deals with the topology of the surface (see Section \ref aos_sec-topol_traits). In particular, it maintains a representation of the arrangement graph embedded in the surface using a doubly-connected edge list (\dcel) data-structure suitable for @@ -390,7 +390,7 @@ the `Arrangement_2` class template; their description follows.
    -
  • The `Traits` template-parameter should be instantiated with a +
  • The `Traits` template-parameter should be substituted by a model of the `ArrangementBasicTraits_2` concept and optionally additional geometry traits concepts. A model of the `ArrangementBasicTraits_2` concept defines the types of @@ -415,14 +415,15 @@ the `Arrangement_2` class template; their description follows. rational functions. We exemplify the usage of these traits classes in Section \ref aos_sec-geom_traits. -
  • The `Dcel` template-parameter should be instantiated with a class +
  • The `Dcel` template-parameter should be substituted by a class that models the `ArrangementDcel` concept, which is used to represent the topological layout of the arrangement. This parameter is - substituted with `Arr_default_dcel` by default, and + substituted by `Arr_default_dcel` by default, and we use this default value in this and in the following three - sections. However, in many applications it is necessary to - extend the \dcel features. This is done by substituting the - `Dcel` parameter with a different type; see Section \ref arr_ssecex_dcel + sections. However, in many applications it is necessary to extend the + \dcel features. This is done by substituting the `Dcel` parameter with + a different type (typically, a different instance of the + `Arr_dcel<>` class template); see Section \ref arr_ssecex_dcel for further explanations and examples.
@@ -1001,7 +1002,7 @@ segments common to all examples that do not construct new geometric objects. They are kept in the header file `arr_inexact_construction_segments.h`. In these examples the `Traits` parameter of the `Arrangement_2` class template is -substituted with an instance of the +substituted by an instance of the `Arr_non_caching_segment_traits_2` class template. The `Arr_non_caching_segment_traits_2` class template is instantiated with the predefined kernel that evaluates predicates in an exact manner, @@ -1438,7 +1439,7 @@ vary according to the user choice.} for answering queries: The landmark strategy requires that the type of the attached arrangement be an instance of the `Arrangement_2` class - template, where the `Traits` parameter is substituted with a + template, where the `Traits` parameter is substituted by a geometry-traits class that models the `ArrangementLandmarkTraits_2` concept, which refines the basic `ArrangementBasicTraits_2` concept; see Section \ref aos_sssec-tr_landmarks_concept for details. Most @@ -1742,7 +1743,7 @@ query results, and insert \f$c\f$ at its proper location.\cgalFootnote{The \cgalFootnoteCode{CGAL::insert_non_intersecting_curve<>()} function template, as all other functions reviewed in this section, is parameterized by an arrangement type and a point-location type (The -latter must be substituted with a model of the +latter must be substituted by a model of the `ArrangementPointLocation_2` concept).} The insertion operation thus hardly requires any geometric operations on top of the ones needed to answer the point-location queries. Moreover, it is sufficient that the @@ -1846,7 +1847,7 @@ condition. Consider the call \link CGAL::insert<>() `insert(arr, c, pl)`\endlink, where \f$c\f$ is not necessarily \f$x\f$-monotone. In this case the type of `arr` must be an instance of the `Arrangement_2` class template, where the `Traits` -template parameter is substituted with a traits class that models the +template parameter is substituted by a traits class that models the concept `ArrangementTraits_2`, which refines the `ArrangementXMonotoneTraits_2` concept. It has to define an additional \link ArrangementTraits_2::Curve_2 `Curve_2`\endlink type, which may @@ -2267,7 +2268,7 @@ accepts the name of an input file that contains the plain-text description of the geometric objects and an output iterator for storing the newly constructed objects. When the function is instantiated, the first template parameter, namely `Type`, must be -substituted with the type of objects to read. It is assumed that an +substituted by the type of objects to read. It is assumed that an extractor operator (`>>`) that extracts objects of the given type from the input stream is available. The listing of the function template, which is defined in the file `read_objects.h`, is omitted here @@ -2704,7 +2705,7 @@ All the free functions that operate on arrangements of bounded curves of unbounded curves. For example, consider a container of linear curves that has to be inserted into an arrangement object, the type of which is an instance of the `Arrangement_2` class -template, where the `Traits` parameter is substituted with the traits +template, where the `Traits` parameter is substituted by the traits class that handles linear curves; see Section \ref arr_sssectr_linear. You can do it incrementally; namely, insert the curves one by one as follows: @@ -3485,7 +3486,7 @@ point-location strategy. The type of an arrangement associated with the landmark point-location strategy (see Section \ref arr_ssecpl) must be an instance of the `Arrangement_on_surface_2` class template, -where the `GeomTraits` parameter is substituted with a model of the +where the `GeomTraits` parameter is substituted by a model of the concept `ArrangementLandmarkTraits_2`. (Naturally, it can also model either the `ArrangementXMonotoneTraits_2` concept or the `ArrangementTraits_2` concept.) The `ArrangementLandmarkTraits_2` @@ -4541,7 +4542,7 @@ PkgBooleanSetOperations2Ref. Note that it is not a model of the computations with square root numbers, which makes it attractive for arrangements induced by line segments, circular arcs, and whole circles. When the traits class-template is instantiated, the `Kernel` -template parameter must be substituted with a geometric kernel that +template parameter must be substituted by a geometric kernel that models the `Kernel` concept. Always plug in a kernel that uses a rational number type, such as `Exact_predicates_exact_constructions_kernel`. Observe that the nested @@ -4722,20 +4723,20 @@ follows: