From ad026eb21761ed670fdf8bb3ed2f92dd692ff88b Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 24 May 2021 23:46:05 -0300 Subject: [PATCH 01/68] adding basic file structure --- .../Barycentric_coordinates_3/CMakeLists.txt | 24 +++++++++++++++++++ .../benchmark_tetrahedon_coordinates.cpp | 0 .../Barycentric_coordinates_3.txt | 0 .../doc/Barycentric_coordinates_3/Doxyfile.in | 6 +++++ .../PackageDescription.txt | 0 .../Barycentric_coordinates_3/dependencies | 0 .../Barycentric_coordinates_3/examples.txt | 6 +++++ .../Barycentric_coordinates_3/CMakeLists.txt | 24 +++++++++++++++++++ .../tetrahedon_coordinates.cpp | 0 .../include/CGAL/Barycentric_coordinates_3.h | 0 .../Barycentric_coordinates_3/copyright | 1 + .../Barycentric_coordinates_3/dependencies | 9 +++++++ .../Barycentric_coordinates_3/description.txt | 1 + .../Barycentric_coordinates_3/license.txt | 1 + .../long_description.txt | 1 + .../Barycentric_coordinates_3/maintainer | 1 + .../Barycentric_coordinates_3/CMakeLists.txt | 24 +++++++++++++++++++ .../test_tetrahedron_coordinates.cpp | 0 18 files changed, 98 insertions(+) create mode 100644 Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt create mode 100644 Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/dependencies create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/license.txt create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt create mode 100644 Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt new file mode 100644 index 000000000000..88239efcb4c0 --- /dev/null +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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.15) +project( Barycentric_coordinates_3_Benchmark ) + + +find_package(CGAL QUIET) + +if ( CGAL_FOUND ) + + # create a target per cppfile + file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + foreach(cppfile ${cppfiles}) + create_single_source_cgal_program( "${cppfile}" ) + endforeach() + +else() + + message(STATUS "This program requires the CGAL library, and will not be compiled.") + +endif() + diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in new file mode 100644 index 000000000000..1ae7018bb52e --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in @@ -0,0 +1,6 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} + +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Generalized Barycentric Coordinates" +EXTRACT_ALL = false +HIDE_UNDOC_MEMBERS = true +HIDE_UNDOC_CLASSES = true diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt new file mode 100644 index 000000000000..21f7878a7e3e --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt @@ -0,0 +1,6 @@ +/*! + +\example Barycentric_coordinates_3/tetrahedon_coordinates.cpp + + +*/ diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt new file mode 100644 index 000000000000..9a6e723b1fd6 --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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.15) +project( Barycentric_coordinates_3_Examples ) + + +find_package(CGAL QUIET) + +if ( CGAL_FOUND ) + + # create a target per cppfile + file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + foreach(cppfile ${cppfiles}) + create_single_source_cgal_program( "${cppfile}" ) + endforeach() + +else() + + message(STATUS "This program requires the CGAL library, and will not be compiled.") + +endif() + diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright new file mode 100644 index 000000000000..ad96e5727c9e --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright @@ -0,0 +1 @@ +Università della Svizzera italiana (Switzerland) and INRIA Sophia-Antipolis (France) diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/dependencies b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/dependencies new file mode 100644 index 000000000000..527389f7e8a5 --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/dependencies @@ -0,0 +1,9 @@ +Algebraic_foundations +Barycentric_coordinates_2 +Installation +Kernel_23 +Number_types +Polygon +Profiling_tools +STL_Extension +Stream_support diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt new file mode 100644 index 000000000000..ab0d977ebf04 --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt @@ -0,0 +1 @@ +2D Generalized Barycentric Coordinates for simple polygons diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/license.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/license.txt new file mode 100644 index 000000000000..8bb8efcb72b0 --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/license.txt @@ -0,0 +1 @@ +GPL (v3 or later) diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt new file mode 100644 index 000000000000..4e4413d731dc --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt @@ -0,0 +1 @@ +The package 2D Generalized Barycentric Coordinates offers an efficient and robust implementation of two-dimensional closed-form generalized barycentric coordinates defined for simple two-dimensional polygons. If coordinates with respect to multivariate scattered points instead of a polygon are required, please refer to natural neighbour coordinates from the package 2D and Surface Function Interpolation. diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer new file mode 100644 index 000000000000..c5e411db8d5f --- /dev/null +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer @@ -0,0 +1 @@ +Dmitry Anisimov or diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt new file mode 100644 index 000000000000..0e8b670ca512 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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.15) +project( Barycentric_coordinates_3_Tests ) + + +find_package(CGAL QUIET) + +if ( CGAL_FOUND ) + + # create a target per cppfile + file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + foreach(cppfile ${cppfiles}) + create_single_source_cgal_program( "${cppfile}" ) + endforeach() + +else() + + message(STATUS "This program requires the CGAL library, and will not be compiled.") + +endif() + diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp new file mode 100644 index 000000000000..e69de29bb2d1 From de57b75dcfef33f70a0d8a51b87081d3623dd5e6 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Sun, 30 May 2021 00:35:32 -0300 Subject: [PATCH 02/68] Created tetrahedron_coordinates_3 that contains the functions to calculate tetrahedra coordinates, as well as utils_3 that do the actual calculation, and one example to test the possible 3 cases of points --- .../tetrahedon_coordinates.cpp | 0 .../tetrahedron_coordinates.cpp | 53 +++++++++++ .../include/CGAL/Barycentric_coordinates_3.h | 6 ++ .../internal/utils_3.h | 75 +++++++++++++++ .../tetrahedron_coordinates_3.h | 94 +++++++++++++++++++ 5 files changed, 228 insertions(+) delete mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedon_coordinates.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp new file mode 100644 index 000000000000..43eb940adf14 --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp @@ -0,0 +1,53 @@ +#include +#include + +//Typedefs +using Kernel = CGAL::Simple_cartesian; + +using FT = Kernel::FT; +using Point_3 = Kernel::Point_3; + +int main(){ + + // Construct tetrahedron + const Point_3 p0(0.0, 0.0, 0.0); + const Point_3 p1(1.0, 0.0, 0.0); + const Point_3 p2(0.0, 1.0, 0.0); + const Point_3 p3(0.0, 0.0, 1.0); + + // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. + const int number_of_query_points = 13; + const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points + Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), Point_3(1.0/3, 1.0/3, 1.0/3), // boundary query points + Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points + }; + + // Compute tetrahedra coordinates; + std::vector coordinates; + coordinates.reserve(queries.size()*4); + + // Output all tetrahedra coordinates. + std::cout << std::endl << "tetrahedra coordinates (all queries): " << std::endl + << std::endl; + for (std::size_t i = 0; i < coordinates.size(); i += 3){ + std::cout << + coordinates[i + 0] << ", " << + coordinates[i + 1] << ", " << + coordinates[i + 2] << ", " << + coordinates[i + 3] << std::endl; + } + std::cout << std::endl; + + // Get a tuple of triangle coordinates for all query points + for(std::size_t i = 0; i < queries.size(); i++){ + const auto tuple = + CGAL::Barycentric_coordinates::triangle_coordinates_in_tuple_3(p0, p1, p2, p3, queries[i]); + + std::cout << "tetrahedra coordinates (query " << i << "): " << + std::get<0>(tuple) << " " << std::get<1>(tuple) << " " << + std::get<2>(tuple) << " " << std::get<3>(tuple) << std::endl; + } + + return EXIT_SUCCESS; + +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index e69de29bb2d1..f6d5d572cdbc 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -0,0 +1,6 @@ +#ifndef CGAL_BARYCENTRIC_COORDINATES_3_H +#define CGAL_BARYCENTRIC_COORDINATES_3_H + +#include + +#endif \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h new file mode 100644 index 000000000000..5fe27dddcdd1 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -0,0 +1,75 @@ +#ifndef CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H +#define CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H + +// STL includes +#include + +namespace CGAL{ +namespace Barycentric_coordinates{ +namespace internal{ + +// Get default values. + template + void get_default( + const std::size_t n, OutputIterator output) { + + for (std::size_t i = 0; i < n; ++i) { + *(output++) = 0; + } + } + +// Compute barycentric coordinates in the space. + template< + typename OutputIterator, + typename GeomTraits> + OutputIterator planar_coordinates_3( + const typename GeomTraits::Point_3& p0, + const typename GeomTraits::Point_3& p1, + const typename GeomTraits::Point_3& p2, + const typename GeomTraits::Point_3& p3, + const typename GeomTraits::Point_3& query, + OutputIterator coordinates, + const GeomTraits& traits) { + + // Number type. + using FT = typename GeomTraits::FT; + + // Functions. + const auto volume_3 = traits.compute_volume_3_object(); + const FT total_volume = volume_3(p0, p1, p2, p3); + + CGAL_precondition(total_volume != FT(0)); + if (total_volume == FT(0)) { + get_default(4, coordinates); + return coordinates; + } + + // Compute some related sub-volumes. + const FT V1 = volume_3(p1, p3, p2, query); + const FT V2 = volume_3(p2, p3, p0, query); + const FT V3 = volume_3(p3, p1, p0, query); + + // Compute the inverted total volume of the tetrahedron. + CGAL_assertion(total_volume != FT(0)); + const FT inverted_total_volume = FT(1) / total_volume; + + // Compute coordinates. + const FT b0 = V1 * inverted_total_volume; + const FT b1 = V2 * inverted_total_volume; + const FT b2 = V3 * inverted_total_volume; + const FT b3 = FT(1) - b0 - b1 - b2; + + // Return coordinates. + *(coordinates++) = b0; + *(coordinates++) = b1; + *(coordinates++) = b2; + *(coordinates++) = b3; + + return coordinates; + } + +} +} +} + +#endif diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h new file mode 100644 index 000000000000..b1d8891e33ea --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h @@ -0,0 +1,94 @@ +#ifndef CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H +#define CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H + +#include + +namespace CGAL{ +namespace Barycentric_coordinates{ + + //return iterator (infer from traits) + template< + typename OutIterator, + typename GeomTraits> + OutIterator triangle_coordinates_3( + const typename GeomTraits::Point_3& p0, + const typename GeomTraits::Point_3& p1, + const typename GeomTraits::Point_3& p2, + const typename GeomTraits::Point_3& p3, + const typename GeomTraits::Point_2& query, + OutIterator c_begin, + const GeomTraits& traits) { + + return internal::planar_coordinates_3( + p0, p1, p2, p3, query, c_begin, traits); + } + + //return iterator(infer from point_3) + template< + typename Point_3, + typename OutIterator> + OutIterator triangle_coordinates_3( + const Point_3& p0, + const Point_3& p1, + const Point_3& p2, + const Point_3& p3, + const Point_3& query, + OutIterator c_begin) { + + using GeomTraits = typename Kernel_traits::Kernel; + const GeomTraits traits; + return triangle_coordinates_3( + p0, p1, p2, p3, query, c_begin, traits); + } + + //return tuple (infer from traits) + template + std::tuple< + typename GeomTraits::FT, + typename GeomTraits::FT, + typename GeomTraits::FT, + typename GeomTraits::FT> + triangle_coordinates_in_tuple_3( + const typename GeomTraits::Point_3& p0, + const typename GeomTraits::Point_3& p1, + const typename GeomTraits::Point_3& p2, + const typename GeomTraits::Point_3& p3, + const typename GeomTraits::Point_3& query, + const GeomTraits& traits) { + + using FT = typename GeomTraits::FT; + std::vector coordinates; + coordinates.reserve(4); + internal::planar_coordinates_3( + p0, p1, p2, p3, query, std::back_inserter(coordinates), traits); + CGAL_assertion(coordinates.size() == 4); + return std::make_tuple(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); + } + + //return tuple (infer from point_3) + template + std::tuple< + typename Kernel_traits::Kernel::FT, + typename Kernel_traits::Kernel::FT, + typename Kernel_traits::Kernel::FT, + typename Kernel_traits::Kernel::FT> + triangle_coordinates_in_tuple_3( + const Point_3& p0, + const Point_3& p1, + const Point_3& p2, + const Point_3& p3, + const Point_3& query) { + + using GeomTraits = typename Kernel_traits::Kernel; + const GeomTraits traits; + return triangle_coordinates_in_tuple_3( + p0, p1, p2, p3, query, traits); + } + + + +} +} + + +#endif \ No newline at end of file From 1f2daebe47d8e2f6afc5b4838108f13cc1d7051b Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Fri, 4 Jun 2021 01:01:48 -0300 Subject: [PATCH 03/68] Fixing cmakelist and updating package_info --- .../Barycentric_coordinates_3/CMakeLists.txt | 25 ++++++++----------- .../tetrahedron_coordinates.cpp | 3 +-- .../Barycentric_coordinates_3/copyright | 2 +- .../Barycentric_coordinates_3/description.txt | 2 +- .../long_description.txt | 2 +- .../Barycentric_coordinates_3/maintainer | 3 ++- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index 9a6e723b1fd6..e7d9b060c8fc 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -1,24 +1,19 @@ -# Created by the script cgal_create_cmake_script +# Created by the script cgal_create_cmake_script. # This is the CMake script for compiling a CGAL application. +project(Barycentric_coordinates_3_Examples) cmake_minimum_required(VERSION 3.1...3.15) -project( Barycentric_coordinates_3_Examples ) +set(CMAKE_CXX_STANDARD 14) +find_package(CGAL QUIET COMPONENTS Core) +if(CGAL_FOUND) -find_package(CGAL QUIET) - -if ( CGAL_FOUND ) - - # create a target per cppfile - file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - foreach(cppfile ${cppfiles}) - create_single_source_cgal_program( "${cppfile}" ) - endforeach() + include(${CGAL_USE_FILE}) + include(CGAL_CreateSingleSourceCGALProgram) + create_single_source_cgal_program("tetrahedron_coordinates.cpp") + else() - - message(STATUS "This program requires the CGAL library, and will not be compiled.") - + message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() - diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp index 43eb940adf14..e623e2d68ce3 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp @@ -16,7 +16,6 @@ int main(){ const Point_3 p3(0.0, 0.0, 1.0); // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. - const int number_of_query_points = 13; const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), Point_3(1.0/3, 1.0/3, 1.0/3), // boundary query points Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points @@ -50,4 +49,4 @@ int main(){ return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright index ad96e5727c9e..50ed011a7861 100644 --- a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/copyright @@ -1 +1 @@ -Università della Svizzera italiana (Switzerland) and INRIA Sophia-Antipolis (France) +GeometryFactory SARL diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt index ab0d977ebf04..85d1e502d63c 100644 --- a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/description.txt @@ -1 +1 @@ -2D Generalized Barycentric Coordinates for simple polygons +3D Generalized Barycentric Coordinates for convex simplicial polytopes. diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt index 4e4413d731dc..434bc650377a 100644 --- a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt @@ -1 +1 @@ -The package 2D Generalized Barycentric Coordinates offers an efficient and robust implementation of two-dimensional closed-form generalized barycentric coordinates defined for simple two-dimensional polygons. If coordinates with respect to multivariate scattered points instead of a polygon are required, please refer to natural neighbour coordinates from the package 2D and Surface Function Interpolation. +The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation of three-dimensional closed-form generalized barycentric coordinates defined for convex simplicial polytopes. If coordinates with respect to multivariate scattered points instead of a polygon are required, please refer to natural neighbour coordinates from the package 2D and Surface Function Interpolation. diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer index c5e411db8d5f..75ec31e4c681 100644 --- a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/maintainer @@ -1 +1,2 @@ -Dmitry Anisimov or +Dmitry Anisimov +Antonio Gomes From d96185b973e5c73f6c4e9edf47d5a5ce42bf4cc4 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Sun, 6 Jun 2021 23:33:17 -0300 Subject: [PATCH 04/68] fixing cmakelists of test and benchmark --- .../Barycentric_coordinates_3/CMakeLists.txt | 25 ++++++++----------- .../Barycentric_coordinates_3/CMakeLists.txt | 25 ++++++++----------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt index 88239efcb4c0..a6b3893b3432 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt @@ -1,24 +1,19 @@ -# Created by the script cgal_create_cmake_script +# Created by the script cgal_create_cmake_script. # This is the CMake script for compiling a CGAL application. +project(Barycentric_coordinates_3_Examples) cmake_minimum_required(VERSION 3.1...3.15) -project( Barycentric_coordinates_3_Benchmark ) +set(CMAKE_CXX_STANDARD 14) +find_package(CGAL QUIET COMPONENTS Core) +if(CGAL_FOUND) -find_package(CGAL QUIET) - -if ( CGAL_FOUND ) - - # create a target per cppfile - file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - foreach(cppfile ${cppfiles}) - create_single_source_cgal_program( "${cppfile}" ) - endforeach() + include(${CGAL_USE_FILE}) + include(CGAL_CreateSingleSourceCGALProgram) + create_single_source_cgal_program("benchmark_tetrahedon_coordinates.cpp") + else() - - message(STATUS "This program requires the CGAL library, and will not be compiled.") - + message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() - diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 0e8b670ca512..3ba401ea5145 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -1,24 +1,19 @@ -# Created by the script cgal_create_cmake_script +# Created by the script cgal_create_cmake_script. # This is the CMake script for compiling a CGAL application. +project(Barycentric_coordinates_3_Examples) cmake_minimum_required(VERSION 3.1...3.15) -project( Barycentric_coordinates_3_Tests ) +set(CMAKE_CXX_STANDARD 14) +find_package(CGAL QUIET COMPONENTS Core) +if(CGAL_FOUND) -find_package(CGAL QUIET) - -if ( CGAL_FOUND ) - - # create a target per cppfile - file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - foreach(cppfile ${cppfiles}) - create_single_source_cgal_program( "${cppfile}" ) - endforeach() + include(${CGAL_USE_FILE}) + include(CGAL_CreateSingleSourceCGALProgram) + create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") + else() - - message(STATUS "This program requires the CGAL library, and will not be compiled.") - + message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() - From bd7fd2dd66b91fddfc354d38e68480bfe9f9055a Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 7 Jun 2021 14:51:08 -0300 Subject: [PATCH 05/68] Created structure for wachspress coordinates --- .../include/CGAL/Barycentric_coordinates_3.h | 3 +- .../Wachspress_coordinates_3.h | 59 +++++++++++++++++++ .../internal/utils_3.h | 3 + .../test_tetrahedron_coordinates.cpp | 3 + 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index f6d5d572cdbc..6d64a21bae79 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -2,5 +2,6 @@ #define CGAL_BARYCENTRIC_COORDINATES_3_H #include +#include -#endif \ No newline at end of file +#endif diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h new file mode 100644 index 000000000000..ba5ec96d65b2 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -0,0 +1,59 @@ +#ifndef CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H +#define CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H + +// Internal includes. +#include + +namespace CGAL { +namespace Barycentric_coordinates { + +template +class Wachspress_coordinates_3{ + + public: + + // Dihedral angle calculation + using Dihedral_angle_3 = GeomTraits::Compute_approximate_dihedral_angle_3; + + // Number type + typedef typename GeomTraits::FT FT; + + // Point type. + typedef typename GeomTraits::Point_3 Point_3; + + // Mesh type + typedef CGAL::Surface_mesh Mesh; + + // Constructor just initializing data + Wachspress_coordinates_3( + const Mesh& mesh, + const GeomTraits traits = GeomTraits()) : + m_mesh(mesh), + m_traits(traits){} + + // Compute weights for query point q + template + OutputIterator compute( + const Point_3& query, OutIterator output, const bool normalize){ + + return output; + } + + + private: + + // Fields + const GeomTraits m_traits; + const Mesh m_mesh; + + const Dihedral_angle_3 m_dihedral_angle_3; + + +}; + + + +} +} + +#endif \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 5fe27dddcdd1..c82884eb5d68 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -4,6 +4,9 @@ // STL includes #include +// CGAL includes +#include + namespace CGAL{ namespace Barycentric_coordinates{ namespace internal{ diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp index e69de29bb2d1..bc2486d7fe04 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp @@ -0,0 +1,3 @@ +int main(){ + +} From ff89fdd53195f558b7d8584a1a5b2b922fb8fa52 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 8 Jun 2021 19:59:22 -0300 Subject: [PATCH 06/68] enhancing the code structure, ran some tests with circulators, property_maps and surface mesh --- .../Wachspress_coordinates_3.h | 45 ++++++++++++++----- .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_wp_tetrahedron.cpp | 29 ++++++++++++ 3 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index ba5ec96d65b2..a773f764b3b1 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -13,7 +13,7 @@ class Wachspress_coordinates_3{ public: // Dihedral angle calculation - using Dihedral_angle_3 = GeomTraits::Compute_approximate_dihedral_angle_3; + using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; // Number type typedef typename GeomTraits::FT FT; @@ -23,34 +23,55 @@ class Wachspress_coordinates_3{ // Mesh type typedef CGAL::Surface_mesh Mesh; + + // Custom types + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::property_map::type Point_property_map; + typedef typename CGAL::Face_around_target_circulator Face_circulator; // Constructor just initializing data Wachspress_coordinates_3( const Mesh& mesh, const GeomTraits traits = GeomTraits()) : m_mesh(mesh), - m_traits(traits){} - - // Compute weights for query point q - template - OutputIterator compute( - const Point_3& query, OutIterator output, const bool normalize){ - - return output; + m_traits(traits), + m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()){} + + //Test function to return dihedral angles of edges opposite to first vertex + std::vector dihedral_first(){ + + ppm = get(CGAL::vertex_point, m_mesh); + vertex_descriptor vd = (vertices(m_mesh).begin())[0]; + + Point_3 v0 = get(ppm, vd); + + m_face_circulator = Face_circulator(m_mesh.halfedge(vd), m_mesh); + Face_circulator done(m_face_circulator); + + do{ + + std::cout << *m_face_circulator++ << "\n"; + }while(m_face_circulator!=done); + + return std::vector(); } + private: // Fields const GeomTraits m_traits; - const Mesh m_mesh; - + const Mesh& m_mesh; const Dihedral_angle_3 m_dihedral_angle_3; + // Custom variables + Point_property_map ppm; + Face_circulator m_face_circulator; -}; +}; } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 3ba401ea5145..80e7ee68e345 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -13,6 +13,7 @@ if(CGAL_FOUND) include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") + create_single_source_cgal_program("test_wp_tetrahedron.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp new file mode 100644 index 000000000000..70d113b9d038 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +//Typedefs +using Kernel = CGAL::Simple_cartesian; + +using FT = Kernel::FT; +using Point_3 = Kernel::Point_3; +using Mesh = CGAL::Surface_mesh; + +int main(){ + + // Tetrahedron Mesh + Mesh ms; + + // Construct tetrahedron + const Point_3 p0(0.0, 0.0, 0.0); + const Point_3 p1(1.0, 0.0, 0.0); + const Point_3 p2(0.0, 1.0, 0.0); + const Point_3 p3(0.0, 0.0, 1.0); + + CGAL::make_tetrahedron(p0, p1, p2, p3, ms); + + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms, Kernel()); + ws.dihedral_first(); + + return EXIT_SUCCESS; +} \ No newline at end of file From 5ccfcdf27036812abbff727447cf5f43cc3f37be Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 9 Jun 2021 17:06:22 -0300 Subject: [PATCH 07/68] better api, barycentric_enums and tests with tetrahedrons in the new api --- .../Wachspress_coordinates_3.h | 195 +++++++++++------- .../barycentric_enum_3.h | 16 ++ .../internal/utils_3.h | 4 +- .../test_wp_tetrahedron.cpp | 10 +- 4 files changed, 150 insertions(+), 75 deletions(-) create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index a773f764b3b1..decc6a42aae5 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -1,80 +1,133 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + #ifndef CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H #define CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H +// #include + // Internal includes. #include namespace CGAL { namespace Barycentric_coordinates { -template -class Wachspress_coordinates_3{ - - public: - - // Dihedral angle calculation - using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; - - // Number type - typedef typename GeomTraits::FT FT; - - // Point type. - typedef typename GeomTraits::Point_3 Point_3; - - // Mesh type - typedef CGAL::Surface_mesh Mesh; - - // Custom types - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::property_map::type Point_property_map; - typedef typename CGAL::Face_around_target_circulator Face_circulator; - - // Constructor just initializing data - Wachspress_coordinates_3( - const Mesh& mesh, - const GeomTraits traits = GeomTraits()) : - m_mesh(mesh), - m_traits(traits), - m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()){} - - //Test function to return dihedral angles of edges opposite to first vertex - std::vector dihedral_first(){ - - ppm = get(CGAL::vertex_point, m_mesh); - vertex_descriptor vd = (vertices(m_mesh).begin())[0]; - - Point_3 v0 = get(ppm, vd); - - m_face_circulator = Face_circulator(m_mesh.halfedge(vd), m_mesh); - Face_circulator done(m_face_circulator); - - do{ - - std::cout << *m_face_circulator++ << "\n"; - }while(m_face_circulator!=done); - - return std::vector(); - } - - - - private: - - // Fields - const GeomTraits m_traits; - const Mesh& m_mesh; - const Dihedral_angle_3 m_dihedral_angle_3; - - // Custom variables - Point_property_map ppm; - Face_circulator m_face_circulator; - - -}; - - -} -} - -#endif \ No newline at end of file + template< + typename PolygonMesh, + typename GeomTraits, + typename VertexToPointMap = typename property_map_selector::const_type> + class Wachspress_coordinates_3 { + + public: + using Polygon_mesh = PolygonMesh; + using Geom_traits = GeomTraits; + using Vertex_to_point_map = VertexToPointMap; + + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Point_3 Point_3; + + public: + Wachspress_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + //const VertexToPointMap vertex_to_point_map = VertexToPointMap(), + const GeomTraits traits = GeomTraits()) : + m_polygon_mesh(polygon_mesh), + m_computation_policy(policy), + m_vertex_to_point_map(get_const_property_map(CGAL::vertex_point, polygon_mesh)), + m_traits(traits) { + + // preconditions, resize containers, etc. + + m_weights.resize(vertices(m_polygon_mesh).size()); + } + + template + OutIterator operator()(const Point_3& query, OutIterator c_begin) { + return compute(query, c_begin); + } + + private: + const PolygonMesh& m_polygon_mesh; + const Computation_policy_3 m_computation_policy; // skip it for the moment + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; + + std::vector m_weights; + + // put here any other necessary global containers + + template + OutputIterator compute( + const Point_3& query, OutputIterator coordinates) { + + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum > FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto vertex : vd) { + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi] / sum; + *(coordinates++) = coordinate; + ++vi; + } + } + + FT compute_weights(const Point_3& query) { + + // Sum of weights to normalize them later. + FT sum = FT(0); + // Vertex index. + std::size_t vi = 0; + // Vertex range, you can make it global. + const auto vd = vertices(m_polygon_mesh); + for (const auto vertex : vd) { + + std::cout << get(m_vertex_to_point_map, vertex) << "\n"; + const FT weight = FT(vi); // compute it here for query + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi + } + CGAL_assertion(sum > FT(0)); + return sum; + } + }; + + template< + typename Point_3, + typename OutIterator, + typename GeomTraits> + OutIterator wachspress_coordinates_3( + const CGAL::Surface_mesh& surface_mesh, + const Point_3& query, + OutIterator c_begin, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT) { + + using Geom_Traits = typename Kernel_traits::Kernel; + using SM = CGAL::Surface_mesh; + + Wachspress_coordinates_3 wachspress(surface_mesh, policy); + return wachspress(query, c_begin); + } + +} // namespace Barycentric_coordinates +} // namespace CGAL + +#endif // CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h new file mode 100644 index 000000000000..f19e5dd1cc76 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -0,0 +1,16 @@ +#ifndef CGAL_BARYCENTRIC_ENUM_3_H +#define CGAL_BARYCENTRIC_ENUM_3_H + +namespace CGAL { +namespace Barycentric_coordinates { + +enum class Computation_policy_3 { + + DEFAULT = 0 +}; + + +} // namespace Barycentric_coordinates +} // namespace CGAL + +#endif // CGAL_BARYCENTRIC_ENUM_3_H diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index c82884eb5d68..4d9d7553f93e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -4,8 +4,8 @@ // STL includes #include -// CGAL includes -#include +// Internal includes +#include namespace CGAL{ namespace Barycentric_coordinates{ diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 70d113b9d038..9b787b42e475 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -20,10 +20,16 @@ int main(){ const Point_3 p2(0.0, 1.0, 0.0); const Point_3 p3(0.0, 0.0, 1.0); + // Query Point + const Point_3 q(0.25, 0.25, 0.25, 0.25); + + // Store results + std::vector coordinates; + CGAL::make_tetrahedron(p0, p1, p2, p3, ms); - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms, Kernel()); - ws.dihedral_first(); + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); + ws(q, std::back_inserter(coordinates)); return EXIT_SUCCESS; } \ No newline at end of file From f4c74d4a66037338075e9777530f78c5298ce385 Mon Sep 17 00:00:00 2001 From: Dmitry Anisimov Date: Thu, 10 Jun 2021 16:11:37 +0200 Subject: [PATCH 08/68] making WP3 constructor work with default pmesh params --- .../Wachspress_coordinates_3.h | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index decc6a42aae5..5c430469aca6 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -39,24 +39,34 @@ namespace Barycentric_coordinates { public: Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, - const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, - //const VertexToPointMap vertex_to_point_map = VertexToPointMap(), + const Computation_policy_3 policy, + const VertexToPointMap vertex_to_point_map, const GeomTraits traits = GeomTraits()) : m_polygon_mesh(polygon_mesh), m_computation_policy(policy), - m_vertex_to_point_map(get_const_property_map(CGAL::vertex_point, polygon_mesh)), - m_traits(traits) { + m_vertex_to_point_map(vertex_to_point_map), + m_traits(traits) { // preconditions, resize containers, etc. m_weights.resize(vertices(m_polygon_mesh).size()); } + Wachspress_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + const GeomTraits traits = GeomTraits()) : + Wachspress_coordinates_3( + polygon_mesh, + policy, + get_const_property_map(CGAL::vertex_point, polygon_mesh), + traits) { } + template OutIterator operator()(const Point_3& query, OutIterator c_begin) { return compute(query, c_begin); - } + } private: const PolygonMesh& m_polygon_mesh; @@ -92,15 +102,15 @@ namespace Barycentric_coordinates { // Sum of weights to normalize them later. FT sum = FT(0); // Vertex index. - std::size_t vi = 0; + std::size_t vi = 0; // Vertex range, you can make it global. const auto vd = vertices(m_polygon_mesh); for (const auto vertex : vd) { - - std::cout << get(m_vertex_to_point_map, vertex) << "\n"; + + std::cout << get(m_vertex_to_point_map, vertex) << "\n"; const FT weight = FT(vi); // compute it here for query CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; + m_weights[vi] = weight; sum += weight; ++vi; // update vi } From ed812951397ab89a6d1b04445d468f2ac6aa1f0a Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Fri, 11 Jun 2021 05:21:59 -0300 Subject: [PATCH 09/68] first part of the calculation of wp coordinates --- .../Wachspress_coordinates_3.h | 16 ++++++- .../internal/utils_3.h | 46 +++++++++++++++++++ .../test_wp_tetrahedron.cpp | 6 +++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 5c430469aca6..fb734cdffe49 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -33,7 +33,7 @@ namespace Barycentric_coordinates { using Geom_traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; - typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; public: @@ -107,7 +107,19 @@ namespace Barycentric_coordinates { const auto vd = vertices(m_polygon_mesh); for (const auto vertex : vd) { - std::cout << get(m_vertex_to_point_map, vertex) << "\n"; + // Map vertex descriptor to point_3 + const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around some vertex(I think that edges will be better) + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + // Internal function to calculate wp coordinates + // To much arguments, i will change that once I got the function working + internal::calculate_wp_vertex_query(vertex_val, query, m_polygon_mesh, + m_vertex_to_point_map, face_circulator, m_traits); + + const FT weight = FT(vi); // compute it here for query CGAL_assertion(vi < m_weights.size()); m_weights[vi] = weight; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 4d9d7553f93e..9f3d14774880 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -6,6 +6,7 @@ // Internal includes #include +#include namespace CGAL{ namespace Barycentric_coordinates{ @@ -71,6 +72,51 @@ namespace internal{ return coordinates; } + // Compute wp coordinates for a given vertex v and a query q + template< + typename GeomTraits, + typename PolygonMesh, + typename VertexToPointMap, + typename FaceCirculator> + typename GeomTraits::FT calculate_wp_vertex_query( + const typename GeomTraits::Point_3& vertex, + const typename GeomTraits::Point_3& query, + const PolygonMesh& mesh, + const VertexToPointMap& vertex_to_point_map, + FaceCirculator& face_circulator, + const GeomTraits& traits){ + + using FT = typename GeomTraits::FT; + using Point_3 = typename GeomTraits::Point_3; + using Polygon_mesh = PolygonMesh; + + const auto det3 = traits.compute_determinant_3_object(); + const auto dot3 = traits.compute_scalar_product_3_object(); + + // Vertices of triangle + Point_3 p0; + Point_3 p1; + Point_3 p2; + + // Use face circulator + CGAL::Face_around_target_circulator done(face_circulator); + do{ + + //Figure out how to pick every vertex ofr one face; + auto vertices_of_face = mesh.vertex(mesh.edge(mesh.halfedge(*face_circulator)), 0); + + std::cout << p0 << std::endl; + + face_circulator++; + }while(face_circulator!=done); + + + // Vector between query and vertex + typename GeomTraits::Vector_3 query_vertex(query, vertex); + + return FT(0); + } + } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 9b787b42e475..46097d0115d0 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -31,5 +31,11 @@ int main(){ CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); ws(q, std::back_inserter(coordinates)); + std::cout << "Coordinates: " << std::endl; + std::cout << coordinates[0] << std::endl; + std::cout << coordinates[1] << std::endl; + std::cout << coordinates[2] << std::endl; + std::cout << coordinates[3] << std::endl; + return EXIT_SUCCESS; } \ No newline at end of file From a72d2d67a8745871e86c14e6e109c956a42e1468 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Fri, 11 Jun 2021 16:48:45 -0300 Subject: [PATCH 10/68] FIRST FUNCTIONAL VERSIONgit add .git add .git add . Just implemented the equations, now I will make my code more organized --- .../Wachspress_coordinates_3.h | 133 +++++++++++++----- .../internal/utils_3.h | 45 ------ .../test_wp_tetrahedron.cpp | 27 ++-- 3 files changed, 115 insertions(+), 90 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index fb734cdffe49..c3f1003dd310 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -33,8 +33,13 @@ namespace Barycentric_coordinates { using Geom_traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; + using Dot_3 = typename GeomTraits::Compute_scalar_product_3; + using Det_3 = typename GeomTraits::Compute_determinant_3; + using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; + typedef typename GeomTraits::Vector_3 Vector_3; public: Wachspress_coordinates_3( @@ -45,10 +50,12 @@ namespace Barycentric_coordinates { m_polygon_mesh(polygon_mesh), m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), - m_traits(traits) { + m_traits(traits), + m_dot3(m_traits.compute_scalar_product_3_object()), + m_det3(m_traits.compute_determinant_3_object()), + m_cross3(m_traits.construct_cross_product_vector_3_object()){ // preconditions, resize containers, etc. - m_weights.resize(vertices(m_polygon_mesh).size()); } @@ -74,53 +81,44 @@ namespace Barycentric_coordinates { const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; - std::vector m_weights; + const Dot_3 m_dot3; + const Det_3 m_det3; + const Cross_3 m_cross3; - // put here any other necessary global containers + std::vector m_weights; template OutputIterator compute( const Point_3& query, OutputIterator coordinates) { - // Compute weights. - const FT sum = compute_weights(query); - CGAL_assertion(sum > FT(0)); - - // The coordinates must be saved in the same order as vertices in the vertex range. - std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); - for (const auto vertex : vd) { - CGAL_assertion(vi < m_weights.size()); - const FT coordinate = m_weights[vi] / sum; - *(coordinates++) = coordinate; - ++vi; - } + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum > FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto vertex : vd) { + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi]/sum; + *(coordinates++) = coordinate; + ++vi; + } } - + FT compute_weights(const Point_3& query) { // Sum of weights to normalize them later. FT sum = FT(0); - // Vertex index. + // Vertex index. std::size_t vi = 0; // Vertex range, you can make it global. const auto vd = vertices(m_polygon_mesh); for (const auto vertex : vd) { - // Map vertex descriptor to point_3 - const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); - - // Circulator of faces around some vertex(I think that edges will be better) - CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); - - // Internal function to calculate wp coordinates - // To much arguments, i will change that once I got the function working - internal::calculate_wp_vertex_query(vertex_val, query, m_polygon_mesh, - m_vertex_to_point_map, face_circulator, m_traits); + // Call function to calculate wp coordinates + const FT weight = compute_wp_vertex_query(vertex, query); - - const FT weight = FT(vi); // compute it here for query CGAL_assertion(vi < m_weights.size()); m_weights[vi] = weight; sum += weight; @@ -129,6 +127,77 @@ namespace Barycentric_coordinates { CGAL_assertion(sum > FT(0)); return sum; } + + // Compute wp coordinates for a given vertex v and a query q + template + FT compute_wp_vertex_query(const Vertex& vertex, const Point_3& query){ + + // Map vertex descriptor to point_3 + const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around some vertex + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + CGAL::Face_around_target_circulator + done(face_circulator); + + // Vector connecting query point to vertex; + Vector_3 query_vertex(query, vertex_val); + + // First face. p_1 is negated because the order of the circulator is reversed + Vector_3 face_normal1 = get_face_normal(*face_circulator); + FT dist_perp1 = m_dot3(query_vertex, face_normal1); + Vector_3 p_1 = -face_normal1/dist_perp1; + face_circulator++; + + // Compute weight w_v + FT weight = 0; + + // Iterate using the circulator + do{ + + // Calculate normals of faces + Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; + Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; + + // Distance of query to face + FT perp_dist_i = m_dot3(query_vertex, face_normal_i); + FT perp_dist_i_1 = m_dot3(query_vertex, face_normal_i_1); + + // pf vector + Vector_3 p_i = face_normal_i/perp_dist_i; + Vector_3 p_i_1 = face_normal_i_1/perp_dist_i_1; + + // Sum partial result to weight + weight += m_det3(p_1, p_i, p_i_1); + + }while(face_circulator!=done); + + return weight; + } + + + // Compute normal vector of the face (not normalized). + template + Vector_3 get_face_normal(const Face& face) { + + const auto hedge = halfedge(face, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point3 = get(m_vertex_to_point_map, *vertex); + + const Vector_3 u = point2 - point1; + const Vector_3 v = point3 - point1; + const Vector_3 face_normal = m_cross3(u, v); + + return face_normal; + } + }; template< diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 9f3d14774880..f162d641c3b7 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -72,51 +72,6 @@ namespace internal{ return coordinates; } - // Compute wp coordinates for a given vertex v and a query q - template< - typename GeomTraits, - typename PolygonMesh, - typename VertexToPointMap, - typename FaceCirculator> - typename GeomTraits::FT calculate_wp_vertex_query( - const typename GeomTraits::Point_3& vertex, - const typename GeomTraits::Point_3& query, - const PolygonMesh& mesh, - const VertexToPointMap& vertex_to_point_map, - FaceCirculator& face_circulator, - const GeomTraits& traits){ - - using FT = typename GeomTraits::FT; - using Point_3 = typename GeomTraits::Point_3; - using Polygon_mesh = PolygonMesh; - - const auto det3 = traits.compute_determinant_3_object(); - const auto dot3 = traits.compute_scalar_product_3_object(); - - // Vertices of triangle - Point_3 p0; - Point_3 p1; - Point_3 p2; - - // Use face circulator - CGAL::Face_around_target_circulator done(face_circulator); - do{ - - //Figure out how to pick every vertex ofr one face; - auto vertices_of_face = mesh.vertex(mesh.edge(mesh.halfedge(*face_circulator)), 0); - - std::cout << p0 << std::endl; - - face_circulator++; - }while(face_circulator!=done); - - - // Vector between query and vertex - typename GeomTraits::Vector_3 query_vertex(query, vertex); - - return FT(0); - } - } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 46097d0115d0..f60989e5aac7 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -1,9 +1,9 @@ -#include +#include #include #include //Typedefs -using Kernel = CGAL::Simple_cartesian; +using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; using FT = Kernel::FT; using Point_3 = Kernel::Point_3; @@ -20,22 +20,23 @@ int main(){ const Point_3 p2(0.0, 1.0, 0.0); const Point_3 p3(0.0, 0.0, 1.0); - // Query Point - const Point_3 q(0.25, 0.25, 0.25, 0.25); - - // Store results - std::vector coordinates; + // Instantiate some interior query points for which we compute coordinates. + const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), + Point_3(0.1f, 0.1f, 0.1f)}; CGAL::make_tetrahedron(p0, p1, p2, p3, ms); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); - ws(q, std::back_inserter(coordinates)); - std::cout << "Coordinates: " << std::endl; - std::cout << coordinates[0] << std::endl; - std::cout << coordinates[1] << std::endl; - std::cout << coordinates[2] << std::endl; - std::cout << coordinates[3] << std::endl; + for(auto q:queries){ + + // Store results + std::vector coordinates; + ws(q, std::back_inserter(coordinates)); + + std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << + coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; + } return EXIT_SUCCESS; } \ No newline at end of file From c60b01cf9be42220deceab31529563f3a42e0416 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 14 Jun 2021 19:39:41 -0300 Subject: [PATCH 11/68] Testing better examples and make code look cleaner --- .../Wachspress_coordinates_3.h | 28 ++++++++++--------- .../test_wp_tetrahedron.cpp | 11 ++++---- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index c3f1003dd310..2925a404b5a7 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -14,8 +14,6 @@ #ifndef CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H #define CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H -// #include - // Internal includes. #include @@ -55,7 +53,8 @@ namespace Barycentric_coordinates { m_det3(m_traits.compute_determinant_3_object()), m_cross3(m_traits.construct_cross_product_vector_3_object()){ - // preconditions, resize containers, etc. + //put here conditions to ensure that the polyhedron is convex and combinatorial consistent + CGAL_precondition(vertices(m_polygon_mesh).size()>=4); m_weights.resize(vertices(m_polygon_mesh).size()); } @@ -77,7 +76,7 @@ namespace Barycentric_coordinates { private: const PolygonMesh& m_polygon_mesh; - const Computation_policy_3 m_computation_policy; // skip it for the moment + const Computation_policy_3 m_computation_policy; const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; @@ -93,12 +92,13 @@ namespace Barycentric_coordinates { // Compute weights. const FT sum = compute_weights(query); - CGAL_assertion(sum > FT(0)); + CGAL_assertion(sum != FT(0)); // The coordinates must be saved in the same order as vertices in the vertex range. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); for (const auto vertex : vd) { + CGAL_assertion(vi < m_weights.size()); const FT coordinate = m_weights[vi]/sum; *(coordinates++) = coordinate; @@ -110,9 +110,9 @@ namespace Barycentric_coordinates { // Sum of weights to normalize them later. FT sum = FT(0); + // Vertex index. std::size_t vi = 0; - // Vertex range, you can make it global. const auto vd = vertices(m_polygon_mesh); for (const auto vertex : vd) { @@ -124,7 +124,8 @@ namespace Barycentric_coordinates { sum += weight; ++vi; // update vi } - CGAL_assertion(sum > FT(0)); + + CGAL_assertion(sum != FT(0)); return sum; } @@ -135,7 +136,7 @@ namespace Barycentric_coordinates { // Map vertex descriptor to point_3 const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); - // Circulator of faces around some vertex + // Circulator of faces around the vertex CGAL::Face_around_target_circulator face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); @@ -146,9 +147,10 @@ namespace Barycentric_coordinates { Vector_3 query_vertex(query, vertex_val); // First face. p_1 is negated because the order of the circulator is reversed - Vector_3 face_normal1 = get_face_normal(*face_circulator); - FT dist_perp1 = m_dot3(query_vertex, face_normal1); - Vector_3 p_1 = -face_normal1/dist_perp1; + Vector_3 face_normal_1 = get_face_normal(*face_circulator); + FT dist_perp_1 = m_dot3(query_vertex, face_normal_1); + CGAL_assertion(dist_perp_1 != 0); + Vector_3 p_1 = -face_normal_1/dist_perp_1; face_circulator++; // Compute weight w_v @@ -156,14 +158,15 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ - // Calculate normals of faces Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; // Distance of query to face FT perp_dist_i = m_dot3(query_vertex, face_normal_i); + CGAL_assertion(perp_dist_i != 0); FT perp_dist_i_1 = m_dot3(query_vertex, face_normal_i_1); + CGAL_assertion(perp_dist_i_1 != 0); // pf vector Vector_3 p_i = face_normal_i/perp_dist_i; @@ -177,7 +180,6 @@ namespace Barycentric_coordinates { return weight; } - // Compute normal vector of the face (not normalized). template Vector_3 get_face_normal(const Face& face) { diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index f60989e5aac7..3d17a45b2406 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -10,7 +10,6 @@ using Point_3 = Kernel::Point_3; using Mesh = CGAL::Surface_mesh; int main(){ - // Tetrahedron Mesh Mesh ms; @@ -20,16 +19,16 @@ int main(){ const Point_3 p2(0.0, 1.0, 0.0); const Point_3 p3(0.0, 0.0, 1.0); - // Instantiate some interior query points for which we compute coordinates. - const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), - Point_3(0.1f, 0.1f, 0.1f)}; - CGAL::make_tetrahedron(p0, p1, p2, p3, ms); + // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. + const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points + Point_3(0.5f, 0.5f, 0.5f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points + }; + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); for(auto q:queries){ - // Store results std::vector coordinates; ws(q, std::back_inserter(coordinates)); From 14f6babe9590f988cbf728c29a3e64a7e8180b3f Mon Sep 17 00:00:00 2001 From: Dmitry Anisimov Date: Tue, 15 Jun 2021 15:37:36 +0200 Subject: [PATCH 12/68] review 1 for wachspress coordinates 3 --- .../include/CGAL/Barycentric_coordinates_3.h | 18 ++++++++++ .../Wachspress_coordinates_3.h | 36 ++++++++++++------- .../internal/utils_3.h | 7 ++-- .../long_description.txt | 4 ++- .../test_wp_tetrahedron.cpp | 19 ++++++++-- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index 6d64a21bae79..65614b72ec35 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -1,6 +1,24 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + +// REVIEW: Add the above license note to all .h files! + #ifndef CGAL_BARYCENTRIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_COORDINATES_3_H +// REVIEW: Add this commented license include in all .h files. +// #include + #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 2925a404b5a7..c7d708c27815 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -48,12 +48,14 @@ namespace Barycentric_coordinates { m_polygon_mesh(polygon_mesh), m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), - m_traits(traits), - m_dot3(m_traits.compute_scalar_product_3_object()), + m_traits(traits), + m_dot3(m_traits.compute_scalar_product_3_object()), m_det3(m_traits.compute_determinant_3_object()), m_cross3(m_traits.construct_cross_product_vector_3_object()){ //put here conditions to ensure that the polyhedron is convex and combinatorial consistent + // REVIEW: what about this: https://doc.cgal.org/latest/Convex_hull_3/group__PkgConvexityChecking.html#ga1e42ab960e30ee450cdebf0b5e58f9e3 ? + // Also: faces(m_polygon_mesh).size() >= 4 ? CGAL_precondition(vertices(m_polygon_mesh).size()>=4); m_weights.resize(vertices(m_polygon_mesh).size()); } @@ -76,10 +78,11 @@ namespace Barycentric_coordinates { private: const PolygonMesh& m_polygon_mesh; - const Computation_policy_3 m_computation_policy; + const Computation_policy_3 m_computation_policy; const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; + // REVIEW: change to m_dot_3 or m_dot_product_3, always use underscores, this is CGAL style. const Dot_3 m_dot3; const Det_3 m_det3; const Cross_3 m_cross3; @@ -97,24 +100,27 @@ namespace Barycentric_coordinates { // The coordinates must be saved in the same order as vertices in the vertex range. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); - for (const auto vertex : vd) { + // REVIEW: CGAL_assertion(m_weights.size() == vd.size()) ? + // Try to add as many as possible assertions. + for (const auto vertex : vd) { // REVIEW: I get warning: vertex is unused variable CGAL_assertion(vi < m_weights.size()); const FT coordinate = m_weights[vi]/sum; *(coordinates++) = coordinate; ++vi; } + // REVIEW: you forgot return! Do you compile with -Wall and -Wextra ? } - + FT compute_weights(const Point_3& query) { // Sum of weights to normalize them later. FT sum = FT(0); - + // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); - for (const auto vertex : vd) { + for (const auto vertex : vd) { // REVIEW: warning, here vertex should be passed by ref // Call function to calculate wp coordinates const FT weight = compute_wp_vertex_query(vertex, query); @@ -134,6 +140,9 @@ namespace Barycentric_coordinates { FT compute_wp_vertex_query(const Vertex& vertex, const Point_3& query){ // Map vertex descriptor to point_3 + // REVIEW: use Point_3& that is with the reference. + // Even if the property_map returns by value, the life time of this ref + // will be extended so it is ok. const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); // Circulator of faces around the vertex @@ -144,23 +153,26 @@ namespace Barycentric_coordinates { done(face_circulator); // Vector connecting query point to vertex; + // REVIEW: Use const Vector_3 query_vertex.... + // Or even better: https://doc.cgal.org/latest/Kernel_23/classKernel.html#abf20ed4375d501fdf4d0e139838ca0dc Vector_3 query_vertex(query, vertex_val); // First face. p_1 is negated because the order of the circulator is reversed + // REVIEW: const const const Vector_3 face_normal_1 = get_face_normal(*face_circulator); FT dist_perp_1 = m_dot3(query_vertex, face_normal_1); - CGAL_assertion(dist_perp_1 != 0); + CGAL_assertion(dist_perp_1 != 0); // REVIEW: != FT(0) because now you compare against integer but FT is double or whatsoever Vector_3 p_1 = -face_normal_1/dist_perp_1; face_circulator++; // Compute weight w_v - FT weight = 0; + FT weight = 0; // REVIEW: FT(0), better keep using FT(value), explicit conversion instead of implicit // Iterate using the circulator do{ // Calculate normals of faces Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; - Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; + Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; // Distance of query to face FT perp_dist_i = m_dot3(query_vertex, face_normal_i); @@ -174,12 +186,12 @@ namespace Barycentric_coordinates { // Sum partial result to weight weight += m_det3(p_1, p_i, p_i_1); - + }while(face_circulator!=done); return weight; } - + // Compute normal vector of the face (not normalized). template Vector_3 get_face_normal(const Face& face) { diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index f162d641c3b7..ea8d2d278031 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -1,11 +1,14 @@ #ifndef CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H #define CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H -// STL includes +// STL includes #include // Internal includes -#include +#include // REVIEW: I do not think you will need it here. +// REVIEW: Do not put Surface_mesh dependency here. Since you work with the face graph already, just put: +// #include - It should cover all basic functions like faces(), vertices(), is_border() etc. +// The Surface_mesh header should be included only in the files where the actual Surface_mesh<> is used. #include namespace CGAL{ diff --git a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt index 434bc650377a..a7b8fba2e727 100644 --- a/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt +++ b/Barycentric_coordinates_3/package_info/Barycentric_coordinates_3/long_description.txt @@ -1 +1,3 @@ -The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation of three-dimensional closed-form generalized barycentric coordinates defined for convex simplicial polytopes. If coordinates with respect to multivariate scattered points instead of a polygon are required, please refer to natural neighbour coordinates from the package 2D and Surface Function Interpolation. +The package 3D Generalized Barycentric Coordinates offers an efficient and robust +implementation of three-dimensional closed-form generalized barycentric coordinates +defined for convex simplicial polytopes. diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 3d17a45b2406..71f9a98623d6 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -9,6 +9,9 @@ using FT = Kernel::FT; using Point_3 = Kernel::Point_3; using Mesh = CGAL::Surface_mesh; +// REVIEW: Look here: https://github.com/danston/cgal/blob/Weights-new_package-danston/Weights/test/Weights/test_wachspress_weights.cpp +// and test all three kernels: SCKER, EPICK, and EPECK. Do not copy the test, just how the kernels are tested. + int main(){ // Tetrahedron Mesh Mesh ms; @@ -22,20 +25,32 @@ int main(){ CGAL::make_tetrahedron(p0, p1, p2, p3, ms); // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. + // REVIEW: keep all the lines within 80-100 characters (in Visual Studio Code e.g. you can set a ruler at 80 characters). + // Also configure your edtior to remove trailing white spaces and indent using 2 spaces. + // Do it in all files. const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points Point_3(0.5f, 0.5f, 0.5f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points }; CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); + // REVIEW: use "const query : queries". In general, try to use const as often as possible. for(auto q:queries){ // Store results + // REVIEW: put coordinates outside the loop and clear() at each run, it is much more efficient + // because the memory is allocated only once and reserve() in case you know the size and you know it, it is 4 here. std::vector coordinates; ws(q, std::back_inserter(coordinates)); - std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << + std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; } + // REVIEW: Actually, I do not see any tests here! The test must be something like here: + // https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp + // that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! + // This looks more like an example rather than a test. + return EXIT_SUCCESS; -} \ No newline at end of file +} +// REVIEW: always leave an empty line at the end of the file. Some machines do not like when it is not there! From 307cb858161bd9bf58c07aa855156df1ad2f0706 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 15 Jun 2021 16:39:11 -0300 Subject: [PATCH 13/68] fixing code issues after revision --- .../include/CGAL/Barycentric_coordinates_3.h | 3 - .../Wachspress_coordinates_3.h | 205 +++++++++--------- .../barycentric_enum_3.h | 15 ++ .../internal/utils_3.h | 22 +- .../tetrahedron_coordinates_3.h | 20 +- .../test_wp_tetrahedron.cpp | 80 +++---- 6 files changed, 189 insertions(+), 156 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index 65614b72ec35..a0a961315dc3 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -11,12 +11,9 @@ // Author(s) : Antonio Gomes, Dmitry Anisimov // -// REVIEW: Add the above license note to all .h files! - #ifndef CGAL_BARYCENTRIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_COORDINATES_3_H -// REVIEW: Add this commented license include in all .h files. // #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index c7d708c27815..564222692c0e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -14,8 +14,11 @@ #ifndef CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H #define CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H +// #include + // Internal includes. #include +#include namespace CGAL { namespace Barycentric_coordinates { @@ -23,7 +26,8 @@ namespace Barycentric_coordinates { template< typename PolygonMesh, typename GeomTraits, - typename VertexToPointMap = typename property_map_selector::const_type> + typename VertexToPointMap = typename property_map_selector::const_type> class Wachspress_coordinates_3 { public: @@ -34,6 +38,7 @@ namespace Barycentric_coordinates { using Dot_3 = typename GeomTraits::Compute_scalar_product_3; using Det_3 = typename GeomTraits::Compute_determinant_3; using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; + using Construct_vec_3 = typename GeomTraits::Construct_vector_3; typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; @@ -41,30 +46,29 @@ namespace Barycentric_coordinates { public: Wachspress_coordinates_3( - const PolygonMesh& polygon_mesh, - const Computation_policy_3 policy, - const VertexToPointMap vertex_to_point_map, - const GeomTraits traits = GeomTraits()) : + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy, + const VertexToPointMap vertex_to_point_map, + const GeomTraits traits = GeomTraits()) : m_polygon_mesh(polygon_mesh), m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), m_traits(traits), - m_dot3(m_traits.compute_scalar_product_3_object()), - m_det3(m_traits.compute_determinant_3_object()), - m_cross3(m_traits.construct_cross_product_vector_3_object()){ - - //put here conditions to ensure that the polyhedron is convex and combinatorial consistent - // REVIEW: what about this: https://doc.cgal.org/latest/Convex_hull_3/group__PkgConvexityChecking.html#ga1e42ab960e30ee450cdebf0b5e58f9e3 ? - // Also: faces(m_polygon_mesh).size() >= 4 ? - CGAL_precondition(vertices(m_polygon_mesh).size()>=4); - m_weights.resize(vertices(m_polygon_mesh).size()); + m_dot_3(m_traits.compute_scalar_product_3_object()), + m_det_3(m_traits.compute_determinant_3_object()), + m_cross_3(m_traits.construct_cross_product_vector_3_object()), + m_construct_vector_3(m_traits.construct_vector_3_object()){ + + // Check if polyhedron is strongly convex + CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); + m_weights.resize(vertices(m_polygon_mesh).size()); } Wachspress_coordinates_3( - const PolygonMesh& polygon_mesh, - const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, - const GeomTraits traits = GeomTraits()) : + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + const GeomTraits traits = GeomTraits()) : Wachspress_coordinates_3( polygon_mesh, policy, @@ -77,15 +81,15 @@ namespace Barycentric_coordinates { } private: - const PolygonMesh& m_polygon_mesh; + const PolygonMesh& m_polygon_mesh; const Computation_policy_3 m_computation_policy; const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; - // REVIEW: change to m_dot_3 or m_dot_product_3, always use underscores, this is CGAL style. - const Dot_3 m_dot3; - const Det_3 m_det3; - const Cross_3 m_cross3; + const Dot_3 m_dot_3; + const Det_3 m_det_3; + const Cross_3 m_cross_3; + const Construct_vec_3 m_construct_vector_3; std::vector m_weights; @@ -93,42 +97,41 @@ namespace Barycentric_coordinates { OutputIterator compute( const Point_3& query, OutputIterator coordinates) { - // Compute weights. - const FT sum = compute_weights(query); - CGAL_assertion(sum != FT(0)); - - // The coordinates must be saved in the same order as vertices in the vertex range. - std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); - // REVIEW: CGAL_assertion(m_weights.size() == vd.size()) ? - // Try to add as many as possible assertions. - for (const auto vertex : vd) { // REVIEW: I get warning: vertex is unused variable - - CGAL_assertion(vi < m_weights.size()); - const FT coordinate = m_weights[vi]/sum; - *(coordinates++) = coordinate; - ++vi; - } - // REVIEW: you forgot return! Do you compile with -Wall and -Wextra ? + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum != FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + const auto vd = vertices(m_polygon_mesh); + CGAL_assertion(m_weights.size() == vd.size()); + + for (std::size_t vi = 0; vi < vd.size(); vi++) { + + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi]/sum; + *(coordinates++) = coordinate; + } + + return coordinates; } FT compute_weights(const Point_3& query) { - // Sum of weights to normalize them later. - FT sum = FT(0); + // Sum of weights to normalize them later. + FT sum = FT(0); - // Vertex index. - std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); - for (const auto vertex : vd) { // REVIEW: warning, here vertex should be passed by ref + // Vertex index. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { - // Call function to calculate wp coordinates - const FT weight = compute_wp_vertex_query(vertex, query); + // Call function to calculate wp coordinates + const FT weight = compute_wp_vertex_query(vertex, query); - CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; - sum += weight; - ++vi; // update vi + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi } CGAL_assertion(sum != FT(0)); @@ -139,57 +142,51 @@ namespace Barycentric_coordinates { template FT compute_wp_vertex_query(const Vertex& vertex, const Point_3& query){ - // Map vertex descriptor to point_3 - // REVIEW: use Point_3& that is with the reference. - // Even if the property_map returns by value, the life time of this ref - // will be extended so it is ok. - const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); - - // Circulator of faces around the vertex - CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); - - CGAL::Face_around_target_circulator - done(face_circulator); - - // Vector connecting query point to vertex; - // REVIEW: Use const Vector_3 query_vertex.... - // Or even better: https://doc.cgal.org/latest/Kernel_23/classKernel.html#abf20ed4375d501fdf4d0e139838ca0dc - Vector_3 query_vertex(query, vertex_val); - - // First face. p_1 is negated because the order of the circulator is reversed - // REVIEW: const const const - Vector_3 face_normal_1 = get_face_normal(*face_circulator); - FT dist_perp_1 = m_dot3(query_vertex, face_normal_1); - CGAL_assertion(dist_perp_1 != 0); // REVIEW: != FT(0) because now you compare against integer but FT is double or whatsoever - Vector_3 p_1 = -face_normal_1/dist_perp_1; - face_circulator++; - - // Compute weight w_v - FT weight = 0; // REVIEW: FT(0), better keep using FT(value), explicit conversion instead of implicit - - // Iterate using the circulator - do{ - // Calculate normals of faces - Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; - Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; - - // Distance of query to face - FT perp_dist_i = m_dot3(query_vertex, face_normal_i); - CGAL_assertion(perp_dist_i != 0); - FT perp_dist_i_1 = m_dot3(query_vertex, face_normal_i_1); - CGAL_assertion(perp_dist_i_1 != 0); - - // pf vector - Vector_3 p_i = face_normal_i/perp_dist_i; - Vector_3 p_i_1 = face_normal_i_1/perp_dist_i_1; - - // Sum partial result to weight - weight += m_det3(p_1, p_i, p_i_1); - - }while(face_circulator!=done); - - return weight; + // Map vertex descriptor to point_3 + const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around the vertex + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + CGAL::Face_around_target_circulator + done(face_circulator); + + // Vector connecting query point to vertex; + const Vector_3 query_vertex = m_construct_vector_3(query, vertex_val); + + // First face. p_1 is negated because the order of the circulator is reversed + const Vector_3 face_normal_1 = get_face_normal(*face_circulator); + const FT dist_perp_1 = m_dot_3(query_vertex, face_normal_1); + CGAL_assertion(dist_perp_1 != FT(0)); + const Vector_3 p_1 = -face_normal_1/dist_perp_1; + face_circulator++; + + // Compute weight w_v + FT weight = FT(0); + + // Iterate using the circulator + do{ + // Calculate normals of faces + const Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; + const Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; + + // Distance of query to face + const FT perp_dist_i = m_dot_3(query_vertex, face_normal_i); + CGAL_assertion(perp_dist_i != 0); + const FT perp_dist_i_1 = m_dot_3(query_vertex, face_normal_i_1); + CGAL_assertion(perp_dist_i_1 != 0); + + // pf vector + const Vector_3 p_i = face_normal_i/perp_dist_i; + const Vector_3 p_i_1 = face_normal_i_1/perp_dist_i_1; + + // Sum partial result to weight + weight += m_det_3(p_1, p_i, p_i_1); + + }while(face_circulator!=done); + + return weight; } // Compute normal vector of the face (not normalized). @@ -207,7 +204,7 @@ namespace Barycentric_coordinates { const Vector_3 u = point2 - point1; const Vector_3 v = point3 - point1; - const Vector_3 face_normal = m_cross3(u, v); + const Vector_3 face_normal = m_cross_3(u, v); return face_normal; } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h index f19e5dd1cc76..cf8b5bbb29f2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -1,6 +1,21 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + #ifndef CGAL_BARYCENTRIC_ENUM_3_H #define CGAL_BARYCENTRIC_ENUM_3_H +// #include + namespace CGAL { namespace Barycentric_coordinates { diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index ea8d2d278031..7d124cb185c3 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -1,15 +1,27 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + #ifndef CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H #define CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H +// #include + // STL includes #include // Internal includes -#include // REVIEW: I do not think you will need it here. -// REVIEW: Do not put Surface_mesh dependency here. Since you work with the face graph already, just put: -// #include - It should cover all basic functions like faces(), vertices(), is_border() etc. -// The Surface_mesh header should be included only in the files where the actual Surface_mesh<> is used. -#include +#include +#include namespace CGAL{ namespace Barycentric_coordinates{ diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h index b1d8891e33ea..74b398c71727 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h @@ -1,6 +1,21 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + #ifndef CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H #define CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H +// #include + #include namespace CGAL{ @@ -85,10 +100,7 @@ namespace Barycentric_coordinates{ p0, p1, p2, p3, query, traits); } - - } } - -#endif \ No newline at end of file +#endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 71f9a98623d6..f47ef053ffa6 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -13,44 +13,44 @@ using Mesh = CGAL::Surface_mesh; // and test all three kernels: SCKER, EPICK, and EPECK. Do not copy the test, just how the kernels are tested. int main(){ - // Tetrahedron Mesh - Mesh ms; - - // Construct tetrahedron - const Point_3 p0(0.0, 0.0, 0.0); - const Point_3 p1(1.0, 0.0, 0.0); - const Point_3 p2(0.0, 1.0, 0.0); - const Point_3 p3(0.0, 0.0, 1.0); - - CGAL::make_tetrahedron(p0, p1, p2, p3, ms); - - // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. - // REVIEW: keep all the lines within 80-100 characters (in Visual Studio Code e.g. you can set a ruler at 80 characters). - // Also configure your edtior to remove trailing white spaces and indent using 2 spaces. - // Do it in all files. - const std::vector queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points - Point_3(0.5f, 0.5f, 0.5f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points - }; - - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); - - // REVIEW: use "const query : queries". In general, try to use const as often as possible. - for(auto q:queries){ - // Store results - // REVIEW: put coordinates outside the loop and clear() at each run, it is much more efficient - // because the memory is allocated only once and reserve() in case you know the size and you know it, it is 4 here. - std::vector coordinates; - ws(q, std::back_inserter(coordinates)); - - std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << - coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; - } - - // REVIEW: Actually, I do not see any tests here! The test must be something like here: - // https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp - // that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! - // This looks more like an example rather than a test. - - return EXIT_SUCCESS; + // Tetrahedron Mesh + Mesh ms; + + // Set cout precision + std::cout.precision(20); + + // Construct tetrahedron + const Point_3 p0(0.0, 0.0, 0.0); + const Point_3 p1(1.0, 0.0, 0.0); + const Point_3 p2(0.0, 1.0, 0.0); + const Point_3 p3(0.0, 0.0, 1.0); + + CGAL::make_tetrahedron(p0, p1, p2, p3, ms); + + // Instantiate some interior, boundary, and exterior query points for which we compute coordinates + const std::vector queries = {Point_3(0.25f, 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), + Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), + Point_3(0.5f, 0.5f, 0.5f), Point_3(-1.0f, -1.0f, 1.0f), + Point_3(0.5f, 0.5f, -2.0f) + }; + + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); + std::vector coordinates; + + for(const auto q:queries){ + // Store results + coordinates.clear(); + ws(q, std::back_inserter(coordinates)); + + std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << + coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; + } + + + // REVIEW: Actually, I do not see any tests here! The test must be something like here: + // https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp + // that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! + // This looks more like an example rather than a test. + + return EXIT_SUCCESS; } -// REVIEW: always leave an empty line at the end of the file. Some machines do not like when it is not there! From adf439253a5f120f8d71025f112b8a39a1ea92bd Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 15 Jun 2021 17:26:15 -0300 Subject: [PATCH 14/68] testing different kernels --- .../test_wp_tetrahedron.cpp | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index f47ef053ffa6..dce9ec6603e3 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -1,23 +1,27 @@ +#include +#include #include + #include #include //Typedefs -using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; -using FT = Kernel::FT; -using Point_3 = Kernel::Point_3; -using Mesh = CGAL::Surface_mesh; +template +void test_overolads(){ -// REVIEW: Look here: https://github.com/danston/cgal/blob/Weights-new_package-danston/Weights/test/Weights/test_wachspress_weights.cpp -// and test all three kernels: SCKER, EPICK, and EPECK. Do not copy the test, just how the kernels are tested. + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; -int main(){ // Tetrahedron Mesh Mesh ms; // Set cout precision - std::cout.precision(20); + std::cout.precision(8); // Construct tetrahedron const Point_3 p0(0.0, 0.0, 0.0); @@ -45,12 +49,26 @@ int main(){ std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; } +} - // REVIEW: Actually, I do not see any tests here! The test must be something like here: - // https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp - // that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! - // This looks more like an example rather than a test. +int main(){ + + std::cout << "SCKER" << std::endl; + test_overolads(); + std::cout << "EPICK" << std::endl; + test_overolads(); + std::cout << "EPECK" << std::endl; + test_overolads(); + + std::cout << "* test_wachspress_weights: SUCCESS" << std::endl; return EXIT_SUCCESS; } + + +// REVIEW: Actually, I do not see any tests here! The test must be something like here: +// https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp +// that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! +// This looks more like an example rather than a test. + From 907b48a36a2b0c2ea5cd5f5f525b3c8fdf932482 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 15 Jun 2021 17:48:42 -0300 Subject: [PATCH 15/68] fixed minor issue --- .../CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h | 2 +- .../test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 564222692c0e..78a12a0e257b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -40,7 +40,7 @@ namespace Barycentric_coordinates { using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; - typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; typedef typename GeomTraits::Vector_3 Vector_3; diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index dce9ec6603e3..21b3728e4fea 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -66,7 +66,7 @@ int main(){ return EXIT_SUCCESS; } - +//IMPORTANT!!!!! // REVIEW: Actually, I do not see any tests here! The test must be something like here: // https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp // that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! From 6fa3c270d490a1bb33045e1775bab56e42b7c748 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 16 Jun 2021 18:41:59 -0300 Subject: [PATCH 16/68] added tetrahedron vs wp test and the beggining of the general one --- .../tetrahedron_coordinates_3.h | 2 +- .../Barycentric_coordinates_3/CMakeLists.txt | 3 +- .../test_wp_tetrahedron.cpp | 103 ++++++++++-------- .../test_wp_weights.cpp | 48 ++++++++ 4 files changed, 110 insertions(+), 46 deletions(-) create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h index 74b398c71727..744cbb0b4bee 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h @@ -30,7 +30,7 @@ namespace Barycentric_coordinates{ const typename GeomTraits::Point_3& p1, const typename GeomTraits::Point_3& p2, const typename GeomTraits::Point_3& p3, - const typename GeomTraits::Point_2& query, + const typename GeomTraits::Point_3& query, OutIterator c_begin, const GeomTraits& traits) { diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 80e7ee68e345..96e16831e0c6 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -14,7 +14,8 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") create_single_source_cgal_program("test_wp_tetrahedron.cpp") - + create_single_source_cgal_program("test_wp_weights.cpp") + else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 21b3728e4fea..5379355bc311 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -1,74 +1,89 @@ #include -#include -#include #include #include +#include //Typedefs -using SCKER = CGAL::Simple_cartesian; -using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; -using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; +using Kernel = CGAL::Simple_cartesian; -template -void test_overolads(){ +int main(){ using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; using Mesh = typename CGAL::Surface_mesh; - // Tetrahedron Mesh - Mesh ms; - // Set cout precision - std::cout.precision(8); + std::cout.precision(20); - // Construct tetrahedron + // Regular tetrahedron + Mesh tetrahedron; + + // Vertices const Point_3 p0(0.0, 0.0, 0.0); const Point_3 p1(1.0, 0.0, 0.0); const Point_3 p2(0.0, 1.0, 0.0); const Point_3 p3(0.0, 0.0, 1.0); - CGAL::make_tetrahedron(p0, p1, p2, p3, ms); + // Construct tetrahedron + CGAL::make_tetrahedron(p0, p1, p2, p3, tetrahedron); - // Instantiate some interior, boundary, and exterior query points for which we compute coordinates - const std::vector queries = {Point_3(0.25f, 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), - Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), - Point_3(0.5f, 0.5f, 0.5f), Point_3(-1.0f, -1.0f, 1.0f), - Point_3(0.5f, 0.5f, -2.0f) - }; + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(tetrahedron); - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(ms); - std::vector coordinates; + std::vector tri_coordinates; + std::vector wp_coordinates; - for(const auto q:queries){ - // Store results - coordinates.clear(); - ws(q, std::back_inserter(coordinates)); + // Tolerance + const FT tol = FT(1.0e-13); - std::cout << "Coordinates: " << "(" << q << ") ---->>> " << coordinates[0] << " " << - coordinates[1] << " " << coordinates[2] << " " << coordinates[3] << std::endl; - } -} + // Sample points + const FT step = FT(1) / FT(500); + const FT scale = FT(100); + std::size_t count = 0; + const FT limit = scale * step; -int main(){ + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < FT(1) - x - y; z+= step){ // Excludes points inside faces - std::cout << "SCKER" << std::endl; - test_overolads(); - std::cout << "EPICK" << std::endl; - test_overolads(); - std::cout << "EPECK" << std::endl; - test_overolads(); + const Point_3 query = Point_3(x, y, z); - std::cout << "* test_wachspress_weights: SUCCESS" << std::endl; + ws(query, std::back_inserter(wp_coordinates)); - return EXIT_SUCCESS; -} + CGAL::Barycentric_coordinates::triangle_coordinates_3(p0, p1, + p2, p3, query, std::back_inserter(tri_coordinates)); + + assert(tri_coordinates[count + 0] > -tol && tri_coordinates[count + 0] <= FT(1)); + assert(tri_coordinates[count + 1] > -tol && tri_coordinates[count + 1] <= FT(1)); + assert(tri_coordinates[count + 2] > -tol && tri_coordinates[count + 2] <= FT(1)); + assert(tri_coordinates[count + 3] > -tol && tri_coordinates[count + 3] <= FT(1)); + + assert(wp_coordinates[count + 0] > -tol && wp_coordinates[count + 0] <= FT(1)); + assert(wp_coordinates[count + 1] > -tol && wp_coordinates[count + 1] <= FT(1)); + assert(wp_coordinates[count + 2] > -tol && wp_coordinates[count + 2] <= FT(1)); + assert(wp_coordinates[count + 3] > -tol && wp_coordinates[count + 3] <= FT(1)); + + // Problem with points near the faces. Example: (0.02, 0.49, 0.48) + /* + std::cout << query << std::endl; + std::cout << tri_coordinates[count + 0]<< " " << tri_coordinates[count + 1]<< " " << + tri_coordinates[count + 2]<< " " << tri_coordinates[count + 3]<< std::endl << + wp_coordinates[count + 0] << " " << wp_coordinates[count + 1] << " " << + wp_coordinates[count + 2] << " " << wp_coordinates[count + 3] << std::endl << std::endl; + */ -//IMPORTANT!!!!! -// REVIEW: Actually, I do not see any tests here! The test must be something like here: -// https://github.com/danston/cgal/blob/Barycentric_coordinates_2-danston/Barycentric_coordinates_2/test/Barycentric_coordinates_2/test_wp_triangle.cpp -// that is it must compute both Wachspress and tetrahedron coordinates and then use assert(wp_coord == tetra_coord)! -// This looks more like an example rather than a test. + assert( + CGAL::abs(tri_coordinates[count + 0] - wp_coordinates[count + 0]) < tol && + CGAL::abs(tri_coordinates[count + 1] - wp_coordinates[count + 1]) < tol && + CGAL::abs(tri_coordinates[count + 2] - wp_coordinates[count + 2]) < tol && + CGAL::abs(tri_coordinates[count + 3] - wp_coordinates[count + 3]) < tol); + count += 4; + } + } + } + + std::cout << "test_wp_tetrahedron: PASSED" << std::endl; + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp new file mode 100644 index 000000000000..5f28b03d92d2 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +#include + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_overloads() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // Meshes + Mesh regular_terahedron; + Mesh irregular_terahedron; + Mesh cube; + Mesh prism; + + // Vertices + const Point_3 p0(0.0, 0.0, 0.0); + const Point_3 p1(1.0, 0.0, 0.0); + const Point_3 p2(0.0, 1.0, 0.0); + const Point_3 p3(0.0, 0.0, 1.0); + const Point_3 p4(1.0, 1.0, 0.0); + const Point_3 p5(1.0, 0.0, 1.0); + const Point_3 p6(0.0, 1.0, 1.0); + const Point_3 p7(1.0, 1.0, 1.0); + + + //Construct polyhedron + CGAL::make_tetrahedron(p0, p1, p2, p3, irregular_terahedron); + +} + +int main(){ + + test_overloads(); + test_overloads(); + test_overloads(); + + return EXIT_SUCCESS; +} \ No newline at end of file From 8fd163f8e8423d32de51f8250b05afbc258957a5 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 17 Jun 2021 14:42:17 -0300 Subject: [PATCH 17/68] changing the name triangle_coordiantes_3 to tetrahedron_coordinates and removing -tol from the wp test --- .../tetrahedron_coordinates.cpp | 12 +++---- ...rdinates_3.h => tetrahedron_coordinates.h} | 12 +++---- .../test_wp_tetrahedron.cpp | 31 +++++++------------ 3 files changed, 23 insertions(+), 32 deletions(-) rename Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/{tetrahedron_coordinates_3.h => tetrahedron_coordinates.h} (92%) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp index e623e2d68ce3..124bf5146349 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp @@ -1,5 +1,5 @@ #include -#include +#include //Typedefs using Kernel = CGAL::Simple_cartesian; @@ -8,7 +8,7 @@ using FT = Kernel::FT; using Point_3 = Kernel::Point_3; int main(){ - + // Construct tetrahedron const Point_3 p0(0.0, 0.0, 0.0); const Point_3 p1(1.0, 0.0, 0.0); @@ -20,7 +20,7 @@ int main(){ Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), Point_3(1.0/3, 1.0/3, 1.0/3), // boundary query points Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points }; - + // Compute tetrahedra coordinates; std::vector coordinates; coordinates.reserve(queries.size()*4); @@ -29,9 +29,9 @@ int main(){ std::cout << std::endl << "tetrahedra coordinates (all queries): " << std::endl << std::endl; for (std::size_t i = 0; i < coordinates.size(); i += 3){ - std::cout << + std::cout << coordinates[i + 0] << ", " << - coordinates[i + 1] << ", " << + coordinates[i + 1] << ", " << coordinates[i + 2] << ", " << coordinates[i + 3] << std::endl; } @@ -40,7 +40,7 @@ int main(){ // Get a tuple of triangle coordinates for all query points for(std::size_t i = 0; i < queries.size(); i++){ const auto tuple = - CGAL::Barycentric_coordinates::triangle_coordinates_in_tuple_3(p0, p1, p2, p3, queries[i]); + CGAL::Barycentric_coordinates::tetrahedron_coordinates_in_tuple(p0, p1, p2, p3, queries[i]); std::cout << "tetrahedra coordinates (query " << i << "): " << std::get<0>(tuple) << " " << std::get<1>(tuple) << " " << diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h similarity index 92% rename from Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h rename to Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index 744cbb0b4bee..d3fd71281df8 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -25,7 +25,7 @@ namespace Barycentric_coordinates{ template< typename OutIterator, typename GeomTraits> - OutIterator triangle_coordinates_3( + OutIterator tetrahedron_coordinates( const typename GeomTraits::Point_3& p0, const typename GeomTraits::Point_3& p1, const typename GeomTraits::Point_3& p2, @@ -42,7 +42,7 @@ namespace Barycentric_coordinates{ template< typename Point_3, typename OutIterator> - OutIterator triangle_coordinates_3( + OutIterator tetrahedron_coordinates( const Point_3& p0, const Point_3& p1, const Point_3& p2, @@ -52,7 +52,7 @@ namespace Barycentric_coordinates{ using GeomTraits = typename Kernel_traits::Kernel; const GeomTraits traits; - return triangle_coordinates_3( + return tetrahedron_coordinates( p0, p1, p2, p3, query, c_begin, traits); } @@ -63,7 +63,7 @@ namespace Barycentric_coordinates{ typename GeomTraits::FT, typename GeomTraits::FT, typename GeomTraits::FT> - triangle_coordinates_in_tuple_3( + tetrahedron_coordinates_in_tuple( const typename GeomTraits::Point_3& p0, const typename GeomTraits::Point_3& p1, const typename GeomTraits::Point_3& p2, @@ -87,7 +87,7 @@ namespace Barycentric_coordinates{ typename Kernel_traits::Kernel::FT, typename Kernel_traits::Kernel::FT, typename Kernel_traits::Kernel::FT> - triangle_coordinates_in_tuple_3( + tetrahedron_coordinates_in_tuple( const Point_3& p0, const Point_3& p1, const Point_3& p2, @@ -96,7 +96,7 @@ namespace Barycentric_coordinates{ using GeomTraits = typename Kernel_traits::Kernel; const GeomTraits traits; - return triangle_coordinates_in_tuple_3( + return tetrahedron_coordinates_in_tuple( p0, p1, p2, p3, query, traits); } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 5379355bc311..18de807331f8 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include //Typedefs using Kernel = CGAL::Simple_cartesian; @@ -51,27 +51,18 @@ int main(){ ws(query, std::back_inserter(wp_coordinates)); - CGAL::Barycentric_coordinates::triangle_coordinates_3(p0, p1, + CGAL::Barycentric_coordinates::tetrahedron_coordinates(p0, p1, p2, p3, query, std::back_inserter(tri_coordinates)); - assert(tri_coordinates[count + 0] > -tol && tri_coordinates[count + 0] <= FT(1)); - assert(tri_coordinates[count + 1] > -tol && tri_coordinates[count + 1] <= FT(1)); - assert(tri_coordinates[count + 2] > -tol && tri_coordinates[count + 2] <= FT(1)); - assert(tri_coordinates[count + 3] > -tol && tri_coordinates[count + 3] <= FT(1)); - - assert(wp_coordinates[count + 0] > -tol && wp_coordinates[count + 0] <= FT(1)); - assert(wp_coordinates[count + 1] > -tol && wp_coordinates[count + 1] <= FT(1)); - assert(wp_coordinates[count + 2] > -tol && wp_coordinates[count + 2] <= FT(1)); - assert(wp_coordinates[count + 3] > -tol && wp_coordinates[count + 3] <= FT(1)); - - // Problem with points near the faces. Example: (0.02, 0.49, 0.48) - /* - std::cout << query << std::endl; - std::cout << tri_coordinates[count + 0]<< " " << tri_coordinates[count + 1]<< " " << - tri_coordinates[count + 2]<< " " << tri_coordinates[count + 3]<< std::endl << - wp_coordinates[count + 0] << " " << wp_coordinates[count + 1] << " " << - wp_coordinates[count + 2] << " " << wp_coordinates[count + 3] << std::endl << std::endl; - */ + assert(tri_coordinates[count + 0] >= FT(0) && tri_coordinates[count + 0] <= FT(1)); + assert(tri_coordinates[count + 1] >= FT(0) && tri_coordinates[count + 1] <= FT(1)); + assert(tri_coordinates[count + 2] >= FT(0) && tri_coordinates[count + 2] <= FT(1)); + assert(tri_coordinates[count + 3] >= FT(0) && tri_coordinates[count + 3] <= FT(1)); + + assert(wp_coordinates[count + 0] >= FT(0) && wp_coordinates[count + 0] <= FT(1)); + assert(wp_coordinates[count + 1] >= FT(0) && wp_coordinates[count + 1] <= FT(1)); + assert(wp_coordinates[count + 2] >= FT(0) && wp_coordinates[count + 2] <= FT(1)); + assert(wp_coordinates[count + 3] >= FT(0) && wp_coordinates[count + 3] <= FT(1)); assert( CGAL::abs(tri_coordinates[count + 0] - wp_coordinates[count + 0]) < tol && From 418dadfaa21e934aaccab56e98f8c2e9d51a0962 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 17 Jun 2021 17:33:35 -0300 Subject: [PATCH 18/68] first test with cube, testing unit partition, barycenter, weights between 0 and 1. Added utils.h to generate polyhedrons --- .../Barycentric_coordinates_3/include/utils.h | 57 ++++++++++++++ .../test_wp_tetrahedron.cpp | 2 +- .../test_wp_weights.cpp | 75 +++++++++++++++---- 3 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h new file mode 100644 index 000000000000..7620210af5c1 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -0,0 +1,57 @@ +#ifndef CGAL_BARYCENTRIC_TESTS_UTILS_H +#define CGAL_BARYCENTRIC_TESTS_UTILS_H + +// STL includes +#include +#include + +// CGAL includes +#include + +namespace tests{ + + template + FT get_tolerance() { + return FT(1) / FT(10000000000); + } + + template + Mesh get_irregular_tetrahedron(){ + + using Point_3 = typename Kernel::Point_3; + + Mesh tetrahedron0; + CGAL::make_tetrahedron(Point_3(0.0, 0.0, 0.0), Point_3(1.0, 0.0, 0.0), + Point_3(0.0, 1.0, 0.0), Point_3(0.0, 0.0, 1.0), tetrahedron0); + + return tetrahedron0; + } + + template + Mesh get_hexahedron(){ + + using Point_3 = typename Kernel::Point_3; + + Mesh hexahedron0; + CGAL::make_hexahedron(Point_3(1.0, 0.0, 0.0), Point_3(1.0, 1.0, 0.0), Point_3(0.0, 1.0, 0.0), + Point_3(0.0, 0.0, 0.0), Point_3(0.0, 0.0, 1.0), Point_3(1.0, 0.0, 1.0), + Point_3(1.0, 1.0, 1.0), Point_3(0.0, 1.0, 1.0), hexahedron0); + + return hexahedron0; + } + + template + Mesh get_regular_prism(){ + + using Point_3 = typename Kernel::Point_3; + using FT = typename Kernel::FT; + + Mesh prism0; + CGAL::make_regular_prism(3, prism0, Point_3(0.0, 0.0, 0.0), FT(1.0), FT(1.0), true); + + return prism0; + } + +} + +#endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index 18de807331f8..f256380d8743 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -34,7 +34,7 @@ int main(){ std::vector wp_coordinates; // Tolerance - const FT tol = FT(1.0e-13); + const FT tol = FT(1.0e-10); // Sample points const FT step = FT(1) / FT(500); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 5f28b03d92d2..16568216c0e5 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -3,6 +3,9 @@ #include #include +#include + +#include "include/utils.h" // Typedefs. using SCKER = CGAL::Simple_cartesian; @@ -17,32 +20,76 @@ void test_overloads() { using Mesh = typename CGAL::Surface_mesh; // Meshes - Mesh regular_terahedron; - Mesh irregular_terahedron; Mesh cube; Mesh prism; - // Vertices - const Point_3 p0(0.0, 0.0, 0.0); - const Point_3 p1(1.0, 0.0, 0.0); - const Point_3 p2(0.0, 1.0, 0.0); - const Point_3 p3(0.0, 0.0, 1.0); - const Point_3 p4(1.0, 1.0, 0.0); - const Point_3 p5(1.0, 0.0, 1.0); - const Point_3 p6(0.0, 1.0, 1.0); - const Point_3 p7(1.0, 1.0, 1.0); + cube = tests::get_hexahedron(); + prism = tests::get_regular_prism(); + + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_prism(prism); + + const FT step = FT(1) / FT(100); + const FT scale = FT(100); + const FT limit = step*scale; + const FT tol = tests::get_tolerance(); + + std::vector wp_coordinates_cube; + std::vector wp_coordinates_prism; + + std::size_t count = 0; + + // Test cube + //Check for barycenter + wp_cube(Point_3(0.5, 0.5, 0.5), std::back_inserter(wp_coordinates_cube)); + for(std::size_t i = 0; i < 8; i++) + assert(wp_coordinates_cube[i + count] == FT(0.125)); + count += 8; + + // Sample interior points + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < limit; z += step){ + + const Point_3 query(x, y, z); + wp_cube(query, std::back_inserter(wp_coordinates_cube)); - //Construct polyhedron - CGAL::make_tetrahedron(p0, p1, p2, p3, irregular_terahedron); + //Test unit partition and coordinates between 0 and 1 + FT sum = FT(0); + for(std::size_t i = 0; i < 8; i++){ + + sum += wp_coordinates_cube[i + count]; + assert(wp_coordinates_cube[i + count] >= FT(0) && + wp_coordinates_cube[i + count] <= FT(1)); + } + + //NOT EQUAL TO 1 WHEN (0.01, 0.01, 0.01) + assert(CGAL::abs(FT(1) - sum) < tol); + + count += 8; + } + } + } + + count = 0; } int main(){ + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; test_overloads(); + std::cout << "SCKER PASSED :" << std::endl; + + std::cout << "EPICK test :" << std::endl; test_overloads(); - test_overloads(); + std::cout << "EPICK PASSED :" << std::endl; + + //test_overloads(); return EXIT_SUCCESS; } \ No newline at end of file From 6abb524225e7aac2d5865e14a20249d27cd2a422 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 21 Jun 2021 02:36:56 -0300 Subject: [PATCH 19/68] finishing test for cube, add generator of random points inside tetraherdron --- .../.vscode/settings.json | 64 ++++++++++++++++ .../Wachspress_coordinates_3.h | 4 + .../Barycentric_coordinates_3/include/utils.h | 73 ++++++++++++++++--- .../test_wp_weights.cpp | 48 ++++++++---- 4 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 Barycentric_coordinates_3/.vscode/settings.json diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json new file mode 100644 index 000000000000..95c8207a2a15 --- /dev/null +++ b/Barycentric_coordinates_3/.vscode/settings.json @@ -0,0 +1,64 @@ +{ + "C_Cpp.errorSquiggles": "Disabled", + "files.associations": { + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__functional_base": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "string_view": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "vector": "cpp", + "*.tcc": "cpp", + "hashtable": "cpp" + } +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 78a12a0e257b..d893033db2d1 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -80,6 +80,10 @@ namespace Barycentric_coordinates { return compute(query, c_begin); } + VertexToPointMap get_vertex_to_point_map(){ + return m_vertex_to_point_map; + } + private: const PolygonMesh& m_polygon_mesh; const Computation_policy_3 m_computation_policy; diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index 7620210af5c1..0ba29b80fd35 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -7,6 +7,9 @@ // CGAL includes #include +#include +#include +#include namespace tests{ @@ -16,28 +19,35 @@ namespace tests{ } template - Mesh get_irregular_tetrahedron(){ + std::pair> get_irregular_tetrahedron(){ using Point_3 = typename Kernel::Point_3; Mesh tetrahedron0; - CGAL::make_tetrahedron(Point_3(0.0, 0.0, 0.0), Point_3(1.0, 0.0, 0.0), - Point_3(0.0, 1.0, 0.0), Point_3(0.0, 0.0, 1.0), tetrahedron0); + std::vector coords = {Point_3(1.0, 0.0, 0.0), Point_3(0.0, 1.0, 0.0), + Point_3(0.0, 0.0, 0.0), Point_3(0.0, 0.0, 1.0)}; - return tetrahedron0; + CGAL::make_tetrahedron(coords[0], coords[1], + coords[2], coords[3], tetrahedron0); + + return {tetrahedron0, coords}; } template - Mesh get_hexahedron(){ + std::pair> get_hexahedron(){ using Point_3 = typename Kernel::Point_3; Mesh hexahedron0; - CGAL::make_hexahedron(Point_3(1.0, 0.0, 0.0), Point_3(1.0, 1.0, 0.0), Point_3(0.0, 1.0, 0.0), - Point_3(0.0, 0.0, 0.0), Point_3(0.0, 0.0, 1.0), Point_3(1.0, 0.0, 1.0), - Point_3(1.0, 1.0, 1.0), Point_3(0.0, 1.0, 1.0), hexahedron0); + std::vector coords = {Point_3(1.0, 0.0, 0.0), Point_3(1.0, 1.0, 0.0), + Point_3(0.0, 1.0, 0.0), Point_3(0.0, 0.0, 0.0), + Point_3(0.0, 0.0, 1.0), Point_3(1.0, 0.0, 1.0), + Point_3(1.0, 1.0, 1.0), Point_3(0.0, 1.0, 1.0)}; + + CGAL::make_hexahedron(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], + coords[6], coords[7], hexahedron0); - return hexahedron0; + return {hexahedron0, coords}; } template @@ -52,6 +62,51 @@ namespace tests{ return prism0; } + template< + typename Point_3, + typename OutputIterator> + OutputIterator sample_random_inside_tetrahedron( + Point_3 p0, + Point_3 p1, + Point_3 p2, + Point_3 p3, + OutputIterator points, + int num_points){ + + using Kernel = typename CGAL::Kernel_traits::Kernel; + using Tetrahedron_3 = typename Kernel::Tetrahedron_3; + using Point_generator = CGAL::Random_points_in_tetrahedron_3 ; + + Tetrahedron_3 tr(p0, p1, p2, p3); + Point_generator point_gen(tr); + + std::copy_n(point_gen, num_points, points); + return points; + } + + /* + template + void sample_mesh_interior_points(Mesh& mesh, VertexToPointMap& vertex_to_point_map){ + + using Point_3 = typename Kernel::Point_3; + typedef CGAL::Delaunay_triangulation_3< Kernel > DT; + typedef typename DT::Segment_simplex_iterator Segment_simplex_iterator; + typedef CGAL::Triangulation_simplex_3 Simplex; + + std::vector points; + + const auto vd = vertices(mesh); + for(auto& vertex : vd){ + + Point_3 vertex_value = get(vertex_to_point_map, vertex); + points.push_back(vertex_value); + } + + DT dt(points.begin(), points.end()); + assert(dt.is_valid()); + } + */ + } #endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 16568216c0e5..12f165e28fb0 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -17,17 +17,15 @@ void test_overloads() { using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; + using Vector_3 = typename Kernel::Vector_3; using Mesh = typename CGAL::Surface_mesh; - // Meshes + // Cube Mesh cube; - Mesh prism; - - cube = tests::get_hexahedron(); - prism = tests::get_regular_prism(); + std::vector cube_coords; + std::tie(cube, cube_coords) = tests::get_hexahedron(); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_prism(prism); const FT step = FT(1) / FT(100); const FT scale = FT(100); @@ -35,8 +33,6 @@ void test_overloads() { const FT tol = tests::get_tolerance(); std::vector wp_coordinates_cube; - std::vector wp_coordinates_prism; - std::size_t count = 0; // Test cube @@ -55,17 +51,28 @@ void test_overloads() { wp_cube(query, std::back_inserter(wp_coordinates_cube)); - //Test unit partition and coordinates between 0 and 1 + //Test unit partition, coordinates between 0 and 1 and Linear combination of vertices FT sum = FT(0); + FT x_linear_comb = FT(0); + FT y_linear_comb = FT(0); + FT z_linear_comb = FT(0); + for(std::size_t i = 0; i < 8; i++){ sum += wp_coordinates_cube[i + count]; + x_linear_comb += cube_coords[i][0] * wp_coordinates_cube[i + count]; + y_linear_comb += cube_coords[i][1] * wp_coordinates_cube[i + count]; + z_linear_comb += cube_coords[i][2] * wp_coordinates_cube[i + count]; + assert(wp_coordinates_cube[i + count] >= FT(0) && wp_coordinates_cube[i + count] <= FT(1)); } - //NOT EQUAL TO 1 WHEN (0.01, 0.01, 0.01) assert(CGAL::abs(FT(1) - sum) < tol); + assert(CGAL::abs(x_linear_comb - query.x()) < tol && + CGAL::abs(y_linear_comb - query.y()) < tol && + CGAL::abs(z_linear_comb - query.z()) < tol); + count += 8; } @@ -74,6 +81,17 @@ void test_overloads() { count = 0; + /* + Mesh tetra; + std::vector coords; + std::vector points; + std::tie(tetra, coords) = tests::get_irregular_tetrahedron(); + + tests::sample_random_inside_tetrahedron(coords[0], coords[1], coords[2], coords[3], + std::back_inserter(points), 6); + */ + + } int main(){ @@ -83,13 +101,17 @@ int main(){ std::cout << "SCKER test :" << std::endl; test_overloads(); - std::cout << "SCKER PASSED :" << std::endl; + std::cout << "SCKER PASSED" << std::endl; std::cout << "EPICK test :" << std::endl; test_overloads(); - std::cout << "EPICK PASSED :" << std::endl; + std::cout << "EPICK PASSED" << std::endl; - //test_overloads(); + /* + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + */ return EXIT_SUCCESS; } \ No newline at end of file From d0a2b2f35c23da45f35ba5e17bcafc40969e3410 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 21 Jun 2021 02:37:45 -0300 Subject: [PATCH 20/68] removed vscode file --- .../.vscode/settings.json | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 Barycentric_coordinates_3/.vscode/settings.json diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json deleted file mode 100644 index 95c8207a2a15..000000000000 --- a/Barycentric_coordinates_3/.vscode/settings.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "C_Cpp.errorSquiggles": "Disabled", - "files.associations": { - "__bit_reference": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__functional_base": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__mutex_base": "cpp", - "__nullptr": "cpp", - "__split_buffer": "cpp", - "__string": "cpp", - "__threading_support": "cpp", - "__tuple": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "complex": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "exception": "cpp", - "string_view": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "locale": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "utility": "cpp", - "vector": "cpp", - "*.tcc": "cpp", - "hashtable": "cpp" - } -} \ No newline at end of file From 5e863a6bc2c23bdc19f2cf34a0cf70509c69b5b5 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 22 Jun 2021 02:05:16 -0300 Subject: [PATCH 21/68] fixing tests for wachspress coordinates after review --- .../internal/utils_3.h | 2 +- .../tetrahedron_coordinates.h | 4 +- .../Barycentric_coordinates_3/include/utils.h | 93 ++++++++++--------- .../test_wp_tetrahedron.cpp | 62 ++++++------- .../test_wp_weights.cpp | 66 +++---------- 5 files changed, 92 insertions(+), 135 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 7d124cb185c3..6d0da1fe54ee 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -41,7 +41,7 @@ namespace internal{ template< typename OutputIterator, typename GeomTraits> - OutputIterator planar_coordinates_3( + OutputIterator tetrahedron_coordinates_impl( const typename GeomTraits::Point_3& p0, const typename GeomTraits::Point_3& p1, const typename GeomTraits::Point_3& p2, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index d3fd71281df8..5966eebc58ab 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -34,7 +34,7 @@ namespace Barycentric_coordinates{ OutIterator c_begin, const GeomTraits& traits) { - return internal::planar_coordinates_3( + return internal::tetrahedron_coordinates_impl( p0, p1, p2, p3, query, c_begin, traits); } @@ -74,7 +74,7 @@ namespace Barycentric_coordinates{ using FT = typename GeomTraits::FT; std::vector coordinates; coordinates.reserve(4); - internal::planar_coordinates_3( + internal::tetrahedron_coordinates_impl( p0, p1, p2, p3, query, std::back_inserter(coordinates), traits); CGAL_assertion(coordinates.size() == 4); return std::make_tuple(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index 0ba29b80fd35..22693e33e885 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -7,9 +7,6 @@ // CGAL includes #include -#include -#include -#include namespace tests{ @@ -24,8 +21,8 @@ namespace tests{ using Point_3 = typename Kernel::Point_3; Mesh tetrahedron0; - std::vector coords = {Point_3(1.0, 0.0, 0.0), Point_3(0.0, 1.0, 0.0), - Point_3(0.0, 0.0, 0.0), Point_3(0.0, 0.0, 1.0)}; + std::vector coords = {Point_3(0.0, 0.0, 0.0), Point_3(1.0, 0.0, 0.0), + Point_3(0.0, 1.0, 0.0), Point_3(0.0, 0.0, 1.0)}; CGAL::make_tetrahedron(coords[0], coords[1], coords[2], coords[3], tetrahedron0); @@ -50,63 +47,67 @@ namespace tests{ return {hexahedron0, coords}; } - template - Mesh get_regular_prism(){ + template + void test_partition_of_unity(std::vector& coords){ - using Point_3 = typename Kernel::Point_3; using FT = typename Kernel::FT; - Mesh prism0; - CGAL::make_regular_prism(3, prism0, Point_3(0.0, 0.0, 0.0), FT(1.0), FT(1.0), true); + FT tol = get_tolerance(); - return prism0; + FT sum = FT(0); + for(auto& coord : coords) + sum += coord; + + assert(CGAL::abs(FT(1) - sum) < tol); } - template< - typename Point_3, - typename OutputIterator> - OutputIterator sample_random_inside_tetrahedron( - Point_3 p0, - Point_3 p1, - Point_3 p2, - Point_3 p3, - OutputIterator points, - int num_points){ - - using Kernel = typename CGAL::Kernel_traits::Kernel; - using Tetrahedron_3 = typename Kernel::Tetrahedron_3; - using Point_generator = CGAL::Random_points_in_tetrahedron_3 ; - - Tetrahedron_3 tr(p0, p1, p2, p3); - Point_generator point_gen(tr); - - std::copy_n(point_gen, num_points, points); - return points; + template + void test_barycenter(std::vector& coords){ + + using FT = typename Kernel::FT; + + std::size_t num_coords = coords.size(); + assert(num_coords != 0); + + for(auto& coord : coords) + assert(coord == FT(1)/FT(num_coords)); } - /* - template - void sample_mesh_interior_points(Mesh& mesh, VertexToPointMap& vertex_to_point_map){ + template + void test_linear_precision( + std::vector& coords, + const std::vector& vertices, + const typename Kernel::Point_3& query){ - using Point_3 = typename Kernel::Point_3; - typedef CGAL::Delaunay_triangulation_3< Kernel > DT; - typedef typename DT::Segment_simplex_iterator Segment_simplex_iterator; - typedef CGAL::Triangulation_simplex_3 Simplex; + using FT = typename Kernel::FT; - std::vector points; + FT tol = get_tolerance(); - const auto vd = vertices(mesh); - for(auto& vertex : vd){ + std::size_t num_coords = coords.size(); + FT x_linear_comb = FT(0); + FT y_linear_comb = FT(0); + FT z_linear_comb = FT(0); - Point_3 vertex_value = get(vertex_to_point_map, vertex); - points.push_back(vertex_value); + for(std::size_t i = 0; i < num_coords; i++){ + + x_linear_comb += vertices[i].x() * coords[i]; + y_linear_comb += vertices[i].y() * coords[i]; + z_linear_comb += vertices[i].z() * coords[i]; } - DT dt(points.begin(), points.end()); - assert(dt.is_valid()); + assert(CGAL::abs(x_linear_comb - query.x()) < tol && + CGAL::abs(y_linear_comb - query.y()) < tol && + CGAL::abs(z_linear_comb - query.z()) < tol); } - */ + template + void test_positivity(std::vector& coords){ + + using FT = typename Kernel::FT; + + for(auto& coord : coords) + assert(coord >= FT(0) && coord <= FT(1)); + } } #endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp index f256380d8743..f7b4ef066cfc 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp @@ -4,6 +4,8 @@ #include #include +#include "include/utils.h" + //Typedefs using Kernel = CGAL::Simple_cartesian; @@ -11,66 +13,58 @@ int main(){ using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; + using Mesh = typename CGAL::Surface_mesh; // Set cout precision std::cout.precision(20); // Regular tetrahedron Mesh tetrahedron; - - // Vertices - const Point_3 p0(0.0, 0.0, 0.0); - const Point_3 p1(1.0, 0.0, 0.0); - const Point_3 p2(0.0, 1.0, 0.0); - const Point_3 p3(0.0, 0.0, 1.0); - - // Construct tetrahedron - CGAL::make_tetrahedron(p0, p1, p2, p3, tetrahedron); + std::vector vertices; + std::tie(tetrahedron, vertices) = tests::get_irregular_tetrahedron(); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(tetrahedron); - std::vector tri_coordinates; + std::vector tetra_coordinates; std::vector wp_coordinates; - // Tolerance - const FT tol = FT(1.0e-10); - // Sample points - const FT step = FT(1) / FT(500); - const FT scale = FT(100); + const FT step = FT(1) / FT(20); + const FT scale = FT(10); + const FT tol = tests::get_tolerance(); std::size_t count = 0; const FT limit = scale * step; for(FT x = step; x < limit; x += step){ for(FT y = step; y < limit; y += step){ - for(FT z = step; z < FT(1) - x - y; z+= step){ // Excludes points inside faces + for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces const Point_3 query = Point_3(x, y, z); - ws(query, std::back_inserter(wp_coordinates)); + wp_coordinates.clear(); + tetra_coordinates.clear(); + tetra_coordinates.resize(4); + wp_coordinates.resize(4); - CGAL::Barycentric_coordinates::tetrahedron_coordinates(p0, p1, - p2, p3, query, std::back_inserter(tri_coordinates)); + ws(query, wp_coordinates.begin()); + CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], + vertices[2], vertices[3], query, tetra_coordinates.begin()); - assert(tri_coordinates[count + 0] >= FT(0) && tri_coordinates[count + 0] <= FT(1)); - assert(tri_coordinates[count + 1] >= FT(0) && tri_coordinates[count + 1] <= FT(1)); - assert(tri_coordinates[count + 2] >= FT(0) && tri_coordinates[count + 2] <= FT(1)); - assert(tri_coordinates[count + 3] >= FT(0) && tri_coordinates[count + 3] <= FT(1)); + tests::test_positivity(wp_coordinates); + tests::test_positivity(tetra_coordinates); - assert(wp_coordinates[count + 0] >= FT(0) && wp_coordinates[count + 0] <= FT(1)); - assert(wp_coordinates[count + 1] >= FT(0) && wp_coordinates[count + 1] <= FT(1)); - assert(wp_coordinates[count + 2] >= FT(0) && wp_coordinates[count + 2] <= FT(1)); - assert(wp_coordinates[count + 3] >= FT(0) && wp_coordinates[count + 3] <= FT(1)); + tests::test_partition_of_unity(wp_coordinates); + tests::test_partition_of_unity(tetra_coordinates); - assert( - CGAL::abs(tri_coordinates[count + 0] - wp_coordinates[count + 0]) < tol && - CGAL::abs(tri_coordinates[count + 1] - wp_coordinates[count + 1]) < tol && - CGAL::abs(tri_coordinates[count + 2] - wp_coordinates[count + 2]) < tol && - CGAL::abs(tri_coordinates[count + 3] - wp_coordinates[count + 3]) < tol); + tests::test_linear_precision(wp_coordinates, vertices, query); + tests::test_linear_precision(tetra_coordinates, vertices, query); - count += 4; + assert( + CGAL::abs(tetra_coordinates[count + 0] - wp_coordinates[count + 0]) < tol && + CGAL::abs(tetra_coordinates[count + 1] - wp_coordinates[count + 1]) < tol && + CGAL::abs(tetra_coordinates[count + 2] - wp_coordinates[count + 2]) < tol && + CGAL::abs(tetra_coordinates[count + 3] - wp_coordinates[count + 3]) < tol); } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 12f165e28fb0..d58d4826f06f 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -15,10 +15,9 @@ using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; template void test_overloads() { - using FT = typename Kernel::FT; + using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; - using Vector_3 = typename Kernel::Vector_3; - using Mesh = typename CGAL::Surface_mesh; + using Mesh = typename CGAL::Surface_mesh; // Cube Mesh cube; @@ -27,71 +26,36 @@ void test_overloads() { std::tie(cube, cube_coords) = tests::get_hexahedron(); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); - const FT step = FT(1) / FT(100); - const FT scale = FT(100); + const FT step = FT(1) / FT(10); + const FT scale = FT(10); const FT limit = step*scale; - const FT tol = tests::get_tolerance(); std::vector wp_coordinates_cube; - std::size_t count = 0; + wp_coordinates_cube.resize(8); // Test cube //Check for barycenter - wp_cube(Point_3(0.5, 0.5, 0.5), std::back_inserter(wp_coordinates_cube)); - for(std::size_t i = 0; i < 8; i++) - assert(wp_coordinates_cube[i + count] == FT(0.125)); - count += 8; + wp_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), wp_coordinates_cube.begin()); + tests::test_barycenter(wp_coordinates_cube); // Sample interior points for(FT x = step; x < limit; x += step){ for(FT y = step; y < limit; y += step){ for(FT z = step; z < limit; z += step){ - const Point_3 query(x, y, z); - - wp_cube(query, std::back_inserter(wp_coordinates_cube)); - - //Test unit partition, coordinates between 0 and 1 and Linear combination of vertices - FT sum = FT(0); - FT x_linear_comb = FT(0); - FT y_linear_comb = FT(0); - FT z_linear_comb = FT(0); - - for(std::size_t i = 0; i < 8; i++){ - - sum += wp_coordinates_cube[i + count]; - x_linear_comb += cube_coords[i][0] * wp_coordinates_cube[i + count]; - y_linear_comb += cube_coords[i][1] * wp_coordinates_cube[i + count]; - z_linear_comb += cube_coords[i][2] * wp_coordinates_cube[i + count]; - - assert(wp_coordinates_cube[i + count] >= FT(0) && - wp_coordinates_cube[i + count] <= FT(1)); - } - - assert(CGAL::abs(FT(1) - sum) < tol); - assert(CGAL::abs(x_linear_comb - query.x()) < tol && - CGAL::abs(y_linear_comb - query.y()) < tol && - CGAL::abs(z_linear_comb - query.z()) < tol); + wp_coordinates_cube.clear(); + wp_coordinates_cube.resize(8); + const Point_3 query(x, y, z); + wp_cube(query, wp_coordinates_cube.begin()); - count += 8; + tests::test_linear_precision(wp_coordinates_cube, cube_coords, query); + tests::test_partition_of_unity(wp_coordinates_cube); + tests::test_positivity(wp_coordinates_cube); } } } - count = 0; - - /* - Mesh tetra; - std::vector coords; - std::vector points; - std::tie(tetra, coords) = tests::get_irregular_tetrahedron(); - - tests::sample_random_inside_tetrahedron(coords[0], coords[1], coords[2], coords[3], - std::back_inserter(points), 6); - */ - - } int main(){ @@ -107,11 +71,9 @@ int main(){ test_overloads(); std::cout << "EPICK PASSED" << std::endl; - /* std::cout << "EPECK test :" << std::endl; test_overloads(); std::cout << "EPECK PASSED" << std::endl; - */ return EXIT_SUCCESS; } \ No newline at end of file From f1fd97edf105706f2159c5cffffdfe7ee22046d6 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 23 Jun 2021 02:16:40 -0300 Subject: [PATCH 22/68] First implementation of DH coordinates (not working yet) --- .../.vscode/settings.json | 70 ++++ .../Discrete_harmonic_coordinates_3.h | 299 ++++++++++++++++++ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_dh_weights.cpp | 35 ++ 4 files changed, 405 insertions(+) create mode 100644 Barycentric_coordinates_3/.vscode/settings.json create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json new file mode 100644 index 000000000000..1a76d2534e62 --- /dev/null +++ b/Barycentric_coordinates_3/.vscode/settings.json @@ -0,0 +1,70 @@ +{ + "C_Cpp.errorSquiggles": "Disabled", + "files.associations": { + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__functional_base": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "string_view": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "set": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "vector": "cpp", + "*.tcc": "cpp", + "cinttypes": "cpp", + "hashtable": "cpp" + } +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h new file mode 100644 index 000000000000..78ee04342837 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -0,0 +1,299 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + +#ifndef CGAL_BARYCENTRIC_DISCRETE_HARMONIC_COORDINATES_3_H +#define CGAL_BARYCENTRIC_DISCRETE_HARMONIC_COORDINATES_3_H + +// #include + +// Internal includes. +#include +#include + +namespace CGAL { +namespace Barycentric_coordinates { + + //Default sqrt + template + class Default_sqrt{ + typedef typename Traits::FT FT; + + public: + FT operator()(const FT &value) const{ + return FT(CGAL::sqrt(CGAL::to_double(value))); + } + }; + + BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_nested_type_Sqrt, Sqrt, false) + + // Case: do_not_use_default = false. + template::value> + class Get_sqrt + { + public: + typedef Default_sqrt Sqrt; + + static Sqrt sqrt_object(const Traits&) + { + return Sqrt(); + } + }; + + // Case: do_not_use_default = true. + template + class Get_sqrt + { + public: + typedef typename Traits::Sqrt Sqrt; + + static Sqrt sqrt_object(const Traits &traits) + { + return traits.sqrt_object(); + } + }; + + template< + typename PolygonMesh, + typename GeomTraits, + typename VertexToPointMap = typename property_map_selector::const_type> + class Discrete_harmonic_coordinates_3 { + + public: + using Polygon_mesh = PolygonMesh; + using Geom_traits = GeomTraits; + using Vertex_to_point_map = VertexToPointMap; + + using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; + using Construct_vec_3 = typename GeomTraits::Construct_vector_3; + using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; + using Dot_3 = typename GeomTraits::Compute_scalar_product_3; + using Sqrt = typename Get_sqrt::Sqrt; + + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Point_3 Point_3; + typedef typename GeomTraits::Vector_3 Vector_3; + + Discrete_harmonic_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy, + const VertexToPointMap vertex_to_point_map, + const GeomTraits traits = GeomTraits()) : + m_polygon_mesh(polygon_mesh), + m_computation_policy(policy), + m_vertex_to_point_map(vertex_to_point_map), + m_traits(traits), + m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()), + m_construct_vector_3(m_traits.construct_vector_3_object()), + m_cross_3(m_traits.construct_cross_product_vector_3_object()), + m_dot_3(m_traits.compute_scalar_product_3_object()), + sqrt(Get_sqrt::sqrt_object(m_traits)){ + + // Check if polyhedron is strongly convex + CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); + m_weights.resize(vertices(m_polygon_mesh).size()); + } + + Discrete_harmonic_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + const GeomTraits traits = GeomTraits()) : + Discrete_harmonic_coordinates_3( + polygon_mesh, + policy, + get_const_property_map(CGAL::vertex_point, polygon_mesh), + traits) { } + + template + OutIterator operator()(const Point_3& query, OutIterator c_begin) { + return compute(query, c_begin); + } + + VertexToPointMap get_vertex_to_point_map(){ + return m_vertex_to_point_map; + } + + private: + const PolygonMesh& m_polygon_mesh; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; + + const Dihedral_angle_3 m_dihedral_angle_3; + const Construct_vec_3 m_construct_vector_3; + const Cross_3 m_cross_3; + const Dot_3 m_dot_3; + const Sqrt sqrt; + + std::vector m_weights; + + template + OutputIterator compute( + const Point_3& query, OutputIterator coordinates) { + + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum != FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + const auto vd = vertices(m_polygon_mesh); + CGAL_assertion(m_weights.size() == vd.size()); + + for (std::size_t vi = 0; vi < vd.size(); vi++) { + + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi]/sum; + *(coordinates++) = coordinate; + } + + return coordinates; + } + + FT compute_weights(const Point_3& query) { + + // Sum of weights to normalize them later. + FT sum = FT(0); + + // Vertex index. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { + + // Call function to calculate wp coordinates + const FT weight = compute_dh_vertex_query(vertex, query); + + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi + } + + CGAL_assertion(sum != FT(0)); + return sum; + } + + // Compute wp coordinates for a given vertex v and a query q + template + FT compute_dh_vertex_query(const Vertex& vertex, const Point_3& query){ + + // Map vertex descriptor to point_3 + const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around the vertex + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + CGAL::Face_around_target_circulator + done(face_circulator); + + // Compute weight w_v + FT weight = FT(0); + + std::cout << query << " \n"; + + // Iterate using the circulator + do{ + + const auto hedge = halfedge(*face_circulator, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex_itr = vertices.begin(); + std::vector points; + + for(std::size_t i = 0; i < 3; i++){ + + if(*vertex_itr != vertex) + points.push_back(get(m_vertex_to_point_map, *vertex_itr)); + + ++vertex_itr; + } + + const Point_3& point1 = points[0]; + const Point_3& point2 = points[1]; + Vector_3 opposite_edge = m_construct_vector_3(point2, point1); + FT edge_length = sqrt(opposite_edge.squared_length ()); + + Vector_3 normal_query = m_cross_3(m_construct_vector_3(point2, query), + m_construct_vector_3(query, point1)); + + FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); + + weight += (cot_dihedral * edge_length) / 2; + face_circulator++; + std::cout << (cot_dihedral) << " a\n"; + + }while(face_circulator!=done); + + return weight; + } + + // Compute normal vector of the face (not normalized). + template + Vector_3 get_face_normal(const Face& face) { + + const auto hedge = halfedge(face, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point3 = get(m_vertex_to_point_map, *vertex); + + const Vector_3 u = point2 - point1; + const Vector_3 v = point3 - point1; + const Vector_3 face_normal = m_cross_3(u, v); + + return face_normal; + } + + //Compute cotangent of dihedral angle between two faces + FT cot_dihedral_angle(const Vector_3& normal_1, const Vector_3& normal_2){ + + FT approximate_cos = m_dot_3(normal_1, normal_2) / + sqrt(normal_1.squared_length()*normal_2.squared_length()); + + FT approximate_sin = sqrt(m_cross_3(normal_1, normal_2).squared_length()) / + sqrt(normal_1.squared_length()*normal_2.squared_length()); + + assert(approximate_sin != FT(0)); + + return approximate_cos/approximate_sin; + } + + }; + + template< + typename Point_3, + typename OutIterator, + typename GeomTraits> + OutIterator discrete_harmonic_coordinates_3( + const CGAL::Surface_mesh& surface_mesh, + const Point_3& query, + OutIterator c_begin, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT) { + + using Geom_Traits = typename Kernel_traits::Kernel; + using SM = CGAL::Surface_mesh; + + Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); + return discrete_harmonic(query, c_begin); + } + +} // namespace Barycentric_coordinates +} // namespace CGAL + +#endif // CGAL_BARYCENTRIC_DISCRETE_HARMONIC_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 96e16831e0c6..6557fdd8b9f7 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -15,6 +15,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") create_single_source_cgal_program("test_wp_tetrahedron.cpp") create_single_source_cgal_program("test_wp_weights.cpp") + create_single_source_cgal_program("test_dh_weights.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp new file mode 100644 index 000000000000..0cc6672f40b1 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using Kernel = CGAL::Simple_cartesian; + +using FT = typename Kernel::FT; +using Point_3 = typename Kernel::Point_3; +using Mesh = typename CGAL::Surface_mesh; + +int main(){ + + std::cout.precision(20); + + // Cube + Mesh cube; + std::vector cube_coords; + + std::tie(cube, cube_coords) = tests::get_hexahedron(); + + std::vector dh_coordinates_cube; + dh_coordinates_cube.resize(8); + + CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh_cube(cube); + dh_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), dh_coordinates_cube.begin()); + + for(auto c : dh_coordinates_cube) + std::cout << c << "\n"; + tests::test_barycenter(dh_coordinates_cube); +} + From db17cbee2064bdbd8702736f88b598e1fcb34600 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Fri, 25 Jun 2021 08:06:29 -0300 Subject: [PATCH 23/68] fixing some bugs of the previous version --- .../.vscode/settings.json | 70 ------------------- .../Discrete_harmonic_coordinates_3.h | 13 ++-- .../test_dh_weights.cpp | 18 ++--- 3 files changed, 16 insertions(+), 85 deletions(-) delete mode 100644 Barycentric_coordinates_3/.vscode/settings.json diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json deleted file mode 100644 index 1a76d2534e62..000000000000 --- a/Barycentric_coordinates_3/.vscode/settings.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "C_Cpp.errorSquiggles": "Disabled", - "files.associations": { - "__bit_reference": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__functional_base": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__mutex_base": "cpp", - "__nullptr": "cpp", - "__split_buffer": "cpp", - "__string": "cpp", - "__threading_support": "cpp", - "__tree": "cpp", - "__tuple": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "complex": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "exception": "cpp", - "string_view": "cpp", - "fstream": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "set": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "utility": "cpp", - "vector": "cpp", - "*.tcc": "cpp", - "cinttypes": "cpp", - "hashtable": "cpp" - } -} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 78ee04342837..5e137e069a53 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -200,14 +200,12 @@ namespace Barycentric_coordinates { // Compute weight w_v FT weight = FT(0); - std::cout << query << " \n"; - // Iterate using the circulator do{ const auto hedge = halfedge(*face_circulator, m_polygon_mesh); const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() >= 3); + CGAL_precondition(vertices.size() == 3); auto vertex_itr = vertices.begin(); std::vector points; @@ -222,17 +220,17 @@ namespace Barycentric_coordinates { const Point_3& point1 = points[0]; const Point_3& point2 = points[1]; + Vector_3 opposite_edge = m_construct_vector_3(point2, point1); - FT edge_length = sqrt(opposite_edge.squared_length ()); + FT edge_length = sqrt(opposite_edge.squared_length()); - Vector_3 normal_query = m_cross_3(m_construct_vector_3(point2, query), + Vector_3 normal_query = m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); weight += (cot_dihedral * edge_length) / 2; face_circulator++; - std::cout << (cot_dihedral) << " a\n"; }while(face_circulator!=done); @@ -262,6 +260,9 @@ namespace Barycentric_coordinates { //Compute cotangent of dihedral angle between two faces FT cot_dihedral_angle(const Vector_3& normal_1, const Vector_3& normal_2){ + assert(normal_1.squared_length() != FT(0)); + assert(normal_2.squared_length() != FT(0)); + FT approximate_cos = m_dot_3(normal_1, normal_2) / sqrt(normal_1.squared_length()*normal_2.squared_length()); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index 0cc6672f40b1..26d64c8f6539 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -17,19 +17,19 @@ int main(){ std::cout.precision(20); // Cube - Mesh cube; - std::vector cube_coords; + Mesh tetrahedron; + std::vector tetrahedron_coords; - std::tie(cube, cube_coords) = tests::get_hexahedron(); + std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); - std::vector dh_coordinates_cube; - dh_coordinates_cube.resize(8); + std::vector dh_coordinates_tetrahedron; + dh_coordinates_tetrahedron.resize(4); - CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh_cube(cube); - dh_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), dh_coordinates_cube.begin()); + CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh_tetrahedron(tetrahedron); + dh_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), dh_coordinates_tetrahedron.begin()); - for(auto c : dh_coordinates_cube) + for(auto c : dh_coordinates_tetrahedron) std::cout << c << "\n"; - tests::test_barycenter(dh_coordinates_cube); + tests::test_barycenter(dh_coordinates_tetrahedron); } From 700a1c94c866957450351750dfe162690bda05bd Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Sun, 27 Jun 2021 19:58:25 -0300 Subject: [PATCH 24/68] functional version and first test of dh coordinates --- .../Discrete_harmonic_coordinates_3.h | 58 +++------------ .../internal/utils_3.h | 39 +++++++++++ .../test_dh_weights.cpp | 70 +++++++++++++++---- 3 files changed, 107 insertions(+), 60 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 5e137e069a53..f613e36f385b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -23,45 +23,6 @@ namespace CGAL { namespace Barycentric_coordinates { - //Default sqrt - template - class Default_sqrt{ - typedef typename Traits::FT FT; - - public: - FT operator()(const FT &value) const{ - return FT(CGAL::sqrt(CGAL::to_double(value))); - } - }; - - BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_nested_type_Sqrt, Sqrt, false) - - // Case: do_not_use_default = false. - template::value> - class Get_sqrt - { - public: - typedef Default_sqrt Sqrt; - - static Sqrt sqrt_object(const Traits&) - { - return Sqrt(); - } - }; - - // Case: do_not_use_default = true. - template - class Get_sqrt - { - public: - typedef typename Traits::Sqrt Sqrt; - - static Sqrt sqrt_object(const Traits &traits) - { - return traits.sqrt_object(); - } - }; - template< typename PolygonMesh, typename GeomTraits, @@ -78,7 +39,7 @@ namespace Barycentric_coordinates { using Construct_vec_3 = typename GeomTraits::Construct_vector_3; using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; - using Sqrt = typename Get_sqrt::Sqrt; + using Sqrt = typename internal::Get_sqrt::Sqrt; typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; @@ -97,7 +58,7 @@ namespace Barycentric_coordinates { m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), - sqrt(Get_sqrt::sqrt_object(m_traits)){ + sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ // Check if polyhedron is strongly convex CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); @@ -209,11 +170,14 @@ namespace Barycentric_coordinates { auto vertex_itr = vertices.begin(); std::vector points; + int vertex_parity = 1; for(std::size_t i = 0; i < 3; i++){ if(*vertex_itr != vertex) points.push_back(get(m_vertex_to_point_map, *vertex_itr)); + else + vertex_parity *= (i & 1)? 1 : -1; ++vertex_itr; } @@ -224,7 +188,7 @@ namespace Barycentric_coordinates { Vector_3 opposite_edge = m_construct_vector_3(point2, point1); FT edge_length = sqrt(opposite_edge.squared_length()); - Vector_3 normal_query = m_cross_3(m_construct_vector_3(query, point2), + Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); @@ -263,15 +227,13 @@ namespace Barycentric_coordinates { assert(normal_1.squared_length() != FT(0)); assert(normal_2.squared_length() != FT(0)); - FT approximate_cos = m_dot_3(normal_1, normal_2) / - sqrt(normal_1.squared_length()*normal_2.squared_length()); + FT approximate_dot_3 = m_dot_3(normal_1, normal_2); - FT approximate_sin = sqrt(m_cross_3(normal_1, normal_2).squared_length()) / - sqrt(normal_1.squared_length()*normal_2.squared_length()); + FT approximate_cross_3_length = sqrt(m_cross_3(normal_1, normal_2).squared_length()); - assert(approximate_sin != FT(0)); + assert(approximate_cross_3_length != FT(0)); - return approximate_cos/approximate_sin; + return approximate_dot_3/approximate_cross_3_length; } }; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 6d0da1fe54ee..902db67ba194 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -27,6 +27,45 @@ namespace CGAL{ namespace Barycentric_coordinates{ namespace internal{ +//Default sqrt +template +class Default_sqrt{ + typedef typename Traits::FT FT; + +public: + FT operator()(const FT &value) const{ + return FT(CGAL::sqrt(CGAL::to_double(value))); + } +}; + +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_nested_type_Sqrt, Sqrt, false) + +// Case: do_not_use_default = false. +template::value> + class Get_sqrt +{ +public: + typedef Default_sqrt Sqrt; + + static Sqrt sqrt_object(const Traits&) + { + return Sqrt(); + } +}; + +// Case: do_not_use_default = true. +template + class Get_sqrt +{ +public: + typedef typename Traits::Sqrt Sqrt; + + static Sqrt sqrt_object(const Traits &traits) + { + return traits.sqrt_object(); + } +}; + // Get default values. template void get_default( diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index 26d64c8f6539..42590fd56a0c 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -6,30 +8,74 @@ #include "include/utils.h" // Typedefs. -using Kernel = CGAL::Simple_cartesian; +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; -using FT = typename Kernel::FT; -using Point_3 = typename Kernel::Point_3; -using Mesh = typename CGAL::Surface_mesh; +template +void test_overloads() { -int main(){ - - std::cout.precision(20); + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; - // Cube + // tetrahedron Mesh tetrahedron; std::vector tetrahedron_coords; std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); + CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh_tetrahedron(tetrahedron); + + const FT step = FT(1) / FT(10); + const FT scale = FT(10); + const FT limit = step*scale; std::vector dh_coordinates_tetrahedron; dh_coordinates_tetrahedron.resize(4); - CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh_tetrahedron(tetrahedron); + // Test cube + //Check for barycenter dh_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), dh_coordinates_tetrahedron.begin()); - - for(auto c : dh_coordinates_tetrahedron) - std::cout << c << "\n"; tests::test_barycenter(dh_coordinates_tetrahedron); + + // Sample interior points + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces + + dh_coordinates_tetrahedron.clear(); + dh_coordinates_tetrahedron.resize(4); + + //std::cout << x << " " << y << " " << z << "\n"; + + const Point_3 query(x, y, z); + dh_tetrahedron(query, dh_coordinates_tetrahedron.begin()); + + tests::test_linear_precision(dh_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(dh_coordinates_tetrahedron); + tests::test_positivity(dh_coordinates_tetrahedron); + } + } + } + } +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + + return EXIT_SUCCESS; +} From 6689419b8f2606373f33d63fe210f566b65760fb Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 28 Jun 2021 02:04:07 -0300 Subject: [PATCH 25/68] adding tests, fix for wp coordinates, dh now working for cube --- .../.vscode/settings.json | 70 +++++++++++++++++ .../Discrete_harmonic_coordinates_3.h | 32 +++----- .../Wachspress_coordinates_3.h | 4 +- .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../Barycentric_coordinates_3/include/utils.h | 15 ++++ .../test_dh_weights.cpp | 2 - .../Barycentric_coordinates_3/test_dh_wp.cpp | 77 +++++++++++++++++++ 7 files changed, 177 insertions(+), 24 deletions(-) create mode 100644 Barycentric_coordinates_3/.vscode/settings.json create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json new file mode 100644 index 000000000000..1a76d2534e62 --- /dev/null +++ b/Barycentric_coordinates_3/.vscode/settings.json @@ -0,0 +1,70 @@ +{ + "C_Cpp.errorSquiggles": "Disabled", + "files.associations": { + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__functional_base": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "string_view": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "set": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "vector": "cpp", + "*.tcc": "cpp", + "cinttypes": "cpp", + "hashtable": "cpp" + } +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index f613e36f385b..8d2b634a2ff1 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -156,7 +156,7 @@ namespace Barycentric_coordinates { face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); CGAL::Face_around_target_circulator - done(face_circulator); + face_done(face_circulator); // Compute weight w_v FT weight = FT(0); @@ -165,38 +165,28 @@ namespace Barycentric_coordinates { do{ const auto hedge = halfedge(*face_circulator, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() == 3); + CGAL::Vertex_around_face_circulator + vertex_circulator(hedge, m_polygon_mesh); - auto vertex_itr = vertices.begin(); - std::vector points; - int vertex_parity = 1; + while(*vertex_circulator!=vertex) + vertex_circulator++; - for(std::size_t i = 0; i < 3; i++){ - - if(*vertex_itr != vertex) - points.push_back(get(m_vertex_to_point_map, *vertex_itr)); - else - vertex_parity *= (i & 1)? 1 : -1; - - ++vertex_itr; - } - - const Point_3& point1 = points[0]; - const Point_3& point2 = points[1]; + vertex_circulator++; + const Point_3& point2 = get(m_vertex_to_point_map, *vertex_circulator); + vertex_circulator--; vertex_circulator--; + const Point_3& point1 = get(m_vertex_to_point_map, *vertex_circulator); Vector_3 opposite_edge = m_construct_vector_3(point2, point1); FT edge_length = sqrt(opposite_edge.squared_length()); - Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), + Vector_3 normal_query = m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); - weight += (cot_dihedral * edge_length) / 2; face_circulator++; - }while(face_circulator!=done); + }while(face_circulator!=face_done); return weight; } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index d893033db2d1..2c86704b3aae 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -127,6 +127,7 @@ namespace Barycentric_coordinates { // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { // Call function to calculate wp coordinates @@ -155,6 +156,7 @@ namespace Barycentric_coordinates { CGAL::Face_around_target_circulator done(face_circulator); + done--; done --; // Vector connecting query point to vertex; const Vector_3 query_vertex = m_construct_vector_3(query, vertex_val); @@ -173,7 +175,7 @@ namespace Barycentric_coordinates { do{ // Calculate normals of faces const Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; - const Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); face_circulator++; + const Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); // Distance of query to face const FT perp_dist_i = m_dot_3(query_vertex, face_normal_i); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 6557fdd8b9f7..233271ee92b5 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -16,6 +16,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_wp_tetrahedron.cpp") create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") + create_single_source_cgal_program("test_dh_wp.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index 22693e33e885..1a7534b9d74c 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -30,6 +30,21 @@ namespace tests{ return {tetrahedron0, coords}; } + template + std::pair> get_regular_tetrahedron(){ + + using Point_3 = typename Kernel::Point_3; + + Mesh tetrahedron0; + std::vector coords = {Point_3(1.0, 1.0, 1.0), Point_3(-1.0, 1.0, -1.0), + Point_3(1.0, -1.0, -1.0), Point_3(-1.0, -1.0, 1.0)}; + + CGAL::make_tetrahedron(coords[0], coords[1], + coords[2], coords[3], tetrahedron0); + + return {tetrahedron0, coords}; + } + template std::pair> get_hexahedron(){ diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index 42590fd56a0c..c9578ff3b74b 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -46,8 +46,6 @@ void test_overloads() { dh_coordinates_tetrahedron.clear(); dh_coordinates_tetrahedron.resize(4); - //std::cout << x << " " << y << " " << z << "\n"; - const Point_3 query(x, y, z); dh_tetrahedron(query, dh_coordinates_tetrahedron.begin()); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp new file mode 100644 index 000000000000..8c0c7232a546 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include +#include +#include + +#include "include/utils.h" + +//Typedefs +using Kernel = CGAL::Simple_cartesian; + +int main(){ + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // Set cout precision + std::cout.precision(20); + + // Regular tetrahedron + Mesh cube; + std::vector vertices; + std::tie(cube, vertices) = tests::get_hexahedron(); + + //CGAL::Polygon_mesh_processing::triangulate_faces(cube); + + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(cube); + CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh(cube); + + std::vector cube_coordinates; + std::vector wp_coordinates; + std::vector dh_coordinates; + + // Sample points + const FT step = FT(1) / FT(10); + const FT scale = FT(10); + const FT limit = step*scale; + const FT tol = tests::get_tolerance(); + + std::size_t count = 0; + + for(FT x = step; x < limit - step; x += step){ + for(FT y = step; y < limit - step; y += step){ + for(FT z = step; z < limit - step; z+= step){ + + const Point_3 query = Point_3(x, y, z); + + wp_coordinates.clear(); + dh_coordinates.clear(); + dh_coordinates.resize(8); + wp_coordinates.resize(8); + + ws(query, wp_coordinates.begin()); + dh(query, dh_coordinates.begin()); + + tests::test_positivity(wp_coordinates); + tests::test_positivity(dh_coordinates); + + tests::test_partition_of_unity(wp_coordinates); + tests::test_partition_of_unity(dh_coordinates); + + tests::test_linear_precision(wp_coordinates, vertices, query); + tests::test_linear_precision(dh_coordinates, vertices, query); + + for(std::size_t i = 0; i < 8; i++){ + assert(CGAL::abs(dh_coordinates[count + i] - wp_coordinates[count + i]) < tol); + } + + } + } + } + + std::cout << "test_dh_wp: PASSED" << std::endl; + return EXIT_SUCCESS; +} From 28cd2bc5d90f69232fd178bbf7d88bea398c1e52 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 28 Jun 2021 23:07:15 -0300 Subject: [PATCH 26/68] dh coordinates final adjustements --- .../Discrete_harmonic_coordinates_3.h | 25 +++++- .../Barycentric_coordinates_3/CMakeLists.txt | 3 +- .../test_dh_weights.cpp | 1 - .../Barycentric_coordinates_3/test_dh_wp.cpp | 77 ------------------- 4 files changed, 25 insertions(+), 81 deletions(-) delete mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 8d2b634a2ff1..db3959bd3df4 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -165,6 +165,11 @@ namespace Barycentric_coordinates { do{ const auto hedge = halfedge(*face_circulator, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + auto vertex_itr = vertices.begin(); + CGAL_precondition(vertices.size() == 3); + + /* CGAL::Vertex_around_face_circulator vertex_circulator(hedge, m_polygon_mesh); @@ -175,14 +180,32 @@ namespace Barycentric_coordinates { const Point_3& point2 = get(m_vertex_to_point_map, *vertex_circulator); vertex_circulator--; vertex_circulator--; const Point_3& point1 = get(m_vertex_to_point_map, *vertex_circulator); + */ + + + int vertex_parity = 1; + std::vector points; + for(std::size_t i = 0; i < 3; i++){ + + if(*vertex_itr!=vertex) + points.push_back(get(m_vertex_to_point_map, *vertex_itr)); + else + vertex_parity *= (i & 1)? -1 : 1; + + vertex_itr++; + } + + const Point_3& point2 = points[0]; + const Point_3& point1 = points[1]; Vector_3 opposite_edge = m_construct_vector_3(point2, point1); FT edge_length = sqrt(opposite_edge.squared_length()); - Vector_3 normal_query = m_cross_3(m_construct_vector_3(query, point2), + Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); + weight += (cot_dihedral * edge_length) / 2; face_circulator++; diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 233271ee92b5..690112af1af1 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -1,7 +1,7 @@ # Created by the script cgal_create_cmake_script. # This is the CMake script for compiling a CGAL application. -project(Barycentric_coordinates_3_Examples) +project(Barycentric_coordinates_3_Tests) cmake_minimum_required(VERSION 3.1...3.15) set(CMAKE_CXX_STANDARD 14) @@ -16,7 +16,6 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_wp_tetrahedron.cpp") create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") - create_single_source_cgal_program("test_dh_wp.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index c9578ff3b74b..eb0b1a3dfd1c 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -51,7 +51,6 @@ void test_overloads() { tests::test_linear_precision(dh_coordinates_tetrahedron, tetrahedron_coords, query); tests::test_partition_of_unity(dh_coordinates_tetrahedron); - tests::test_positivity(dh_coordinates_tetrahedron); } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp deleted file mode 100644 index 8c0c7232a546..000000000000 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_wp.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "include/utils.h" - -//Typedefs -using Kernel = CGAL::Simple_cartesian; - -int main(){ - - using FT = typename Kernel::FT; - using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; - - // Set cout precision - std::cout.precision(20); - - // Regular tetrahedron - Mesh cube; - std::vector vertices; - std::tie(cube, vertices) = tests::get_hexahedron(); - - //CGAL::Polygon_mesh_processing::triangulate_faces(cube); - - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(cube); - CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh(cube); - - std::vector cube_coordinates; - std::vector wp_coordinates; - std::vector dh_coordinates; - - // Sample points - const FT step = FT(1) / FT(10); - const FT scale = FT(10); - const FT limit = step*scale; - const FT tol = tests::get_tolerance(); - - std::size_t count = 0; - - for(FT x = step; x < limit - step; x += step){ - for(FT y = step; y < limit - step; y += step){ - for(FT z = step; z < limit - step; z+= step){ - - const Point_3 query = Point_3(x, y, z); - - wp_coordinates.clear(); - dh_coordinates.clear(); - dh_coordinates.resize(8); - wp_coordinates.resize(8); - - ws(query, wp_coordinates.begin()); - dh(query, dh_coordinates.begin()); - - tests::test_positivity(wp_coordinates); - tests::test_positivity(dh_coordinates); - - tests::test_partition_of_unity(wp_coordinates); - tests::test_partition_of_unity(dh_coordinates); - - tests::test_linear_precision(wp_coordinates, vertices, query); - tests::test_linear_precision(dh_coordinates, vertices, query); - - for(std::size_t i = 0; i < 8; i++){ - assert(CGAL::abs(dh_coordinates[count + i] - wp_coordinates[count + i]) < tol); - } - - } - } - } - - std::cout << "test_dh_wp: PASSED" << std::endl; - return EXIT_SUCCESS; -} From 8e483e82cc5f18f1fef0955a41335a891c259371 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 30 Jun 2021 23:41:06 -0300 Subject: [PATCH 27/68] draft version of mv coordinates --- .../Discrete_harmonic_coordinates_3.h | 16 +- .../Mean_value_coordinates_3.h | 266 ++++++++++++++++++ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_mv_weights.cpp | 78 +++++ 4 files changed, 346 insertions(+), 15 deletions(-) create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index db3959bd3df4..c7e284ec8b27 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -169,27 +169,13 @@ namespace Barycentric_coordinates { auto vertex_itr = vertices.begin(); CGAL_precondition(vertices.size() == 3); - /* - CGAL::Vertex_around_face_circulator - vertex_circulator(hedge, m_polygon_mesh); - - while(*vertex_circulator!=vertex) - vertex_circulator++; - - vertex_circulator++; - const Point_3& point2 = get(m_vertex_to_point_map, *vertex_circulator); - vertex_circulator--; vertex_circulator--; - const Point_3& point1 = get(m_vertex_to_point_map, *vertex_circulator); - */ - - int vertex_parity = 1; std::vector points; for(std::size_t i = 0; i < 3; i++){ if(*vertex_itr!=vertex) points.push_back(get(m_vertex_to_point_map, *vertex_itr)); - else + else vertex_parity *= (i & 1)? -1 : 1; vertex_itr++; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h new file mode 100644 index 000000000000..1d6ef901cc50 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -0,0 +1,266 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + +#ifndef CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H +#define CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H + +// #include + +// Internal includes. +#include +#include + +namespace CGAL { +namespace Barycentric_coordinates { + + template< + typename PolygonMesh, + typename GeomTraits, + typename VertexToPointMap = typename property_map_selector::const_type> + class Mean_value_coordinates_3 { + + public: + using Polygon_mesh = PolygonMesh; + using Geom_traits = GeomTraits; + using Vertex_to_point_map = VertexToPointMap; + + using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; + using Construct_vec_3 = typename GeomTraits::Construct_vector_3; + using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; + using Dot_3 = typename GeomTraits::Compute_scalar_product_3; + using Sqrt = typename internal::Get_sqrt::Sqrt; + + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Point_3 Point_3; + typedef typename GeomTraits::Vector_3 Vector_3; + + Mean_value_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy, + const VertexToPointMap vertex_to_point_map, + const GeomTraits traits = GeomTraits()) : + m_polygon_mesh(polygon_mesh), + m_computation_policy(policy), + m_vertex_to_point_map(vertex_to_point_map), + m_traits(traits), + m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()), + m_construct_vector_3(m_traits.construct_vector_3_object()), + m_cross_3(m_traits.construct_cross_product_vector_3_object()), + m_dot_3(m_traits.compute_scalar_product_3_object()), + sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ + + // Check if polyhedron is strongly convex + CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); + m_weights.resize(vertices(m_polygon_mesh).size()); + } + + Mean_value_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + const GeomTraits traits = GeomTraits()) : + Mean_value_coordinates_3( + polygon_mesh, + policy, + get_const_property_map(CGAL::vertex_point, polygon_mesh), + traits) { } + + template + OutIterator operator()(const Point_3& query, OutIterator c_begin) { + return compute(query, c_begin); + } + + VertexToPointMap get_vertex_to_point_map(){ + return m_vertex_to_point_map; + } + + private: + const PolygonMesh& m_polygon_mesh; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; + + const Dihedral_angle_3 m_dihedral_angle_3; + const Construct_vec_3 m_construct_vector_3; + const Cross_3 m_cross_3; + const Dot_3 m_dot_3; + const Sqrt sqrt; + + std::vector m_weights; + + template + OutputIterator compute( + const Point_3& query, OutputIterator coordinates) { + + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum != FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + const auto vd = vertices(m_polygon_mesh); + CGAL_assertion(m_weights.size() == vd.size()); + + for (std::size_t vi = 0; vi < vd.size(); vi++) { + + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi]/sum; + *(coordinates++) = coordinate; + } + + return coordinates; + } + + FT compute_weights(const Point_3& query) { + + // Sum of weights to normalize them later. + FT sum = FT(0); + + // Vertex index. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { + + // Call function to calculate wp coordinates + const FT weight = compute_mv_vertex_query(vertex, query); + + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi + } + + CGAL_assertion(sum != FT(0)); + return sum; + } + + // Compute wp coordinates for a given vertex v and a query q + template + FT compute_mv_vertex_query(const Vertex& vertex, const Point_3& query){ + + // Map vertex descriptor to point_3 + const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around the vertex + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + CGAL::Face_around_target_circulator + face_done(face_circulator); + + // Compute weight w_v + FT weight = FT(0); + + // Iterate using the circulator + do{ + + const auto hedge = halfedge(*face_circulator, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + auto vertex_itr = vertices.begin(); + CGAL_precondition(vertices.size() == 3); + + std::vector unit_vectors; + int vertex_idx = -1; + for(std::size_t i = 0; i < 3; i++){ + + if(*vertex_itr == vertex) + vertex_idx = i; + + Point_3 i_face_vertex = get(m_vertex_to_point_map, *vertex_itr); + Vector_3 i_query_vertex = m_construct_vector_3(query, i_face_vertex); + + assert(i_query_vertex.squared_length() != 0); + i_query_vertex = i_query_vertex/sqrt(i_query_vertex.squared_length()); + + unit_vectors.push_back(i_query_vertex); + + vertex_itr++; + } + + std::vector m_vectors; + for(std::size_t i = 0; i<3; i++){ + + Vector_3 normal = m_cross_3(unit_vectors[i], unit_vectors[(i+1)%3]); + normal = normal / sqrt(normal.squared_length()); + m_vectors.push_back(normal); + } + + //Missing area of circle + + + face_circulator++; + + }while(face_circulator!=face_done); + + return weight; + } + + // Compute normal vector of the face (not normalized). + template + Vector_3 get_face_normal(const Face& face) { + + const auto hedge = halfedge(face, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; + const Point_3& point3 = get(m_vertex_to_point_map, *vertex); + + const Vector_3 u = point2 - point1; + const Vector_3 v = point3 - point1; + const Vector_3 face_normal = m_cross_3(u, v); + + return face_normal; + } + + //Compute cotangent of dihedral angle between two faces + FT cot_dihedral_angle(const Vector_3& normal_1, const Vector_3& normal_2){ + + assert(normal_1.squared_length() != FT(0)); + assert(normal_2.squared_length() != FT(0)); + + FT approximate_dot_3 = m_dot_3(normal_1, normal_2); + + FT approximate_cross_3_length = sqrt(m_cross_3(normal_1, normal_2).squared_length()); + + assert(approximate_cross_3_length != FT(0)); + + return approximate_dot_3/approximate_cross_3_length; + } + + }; + + template< + typename Point_3, + typename OutIterator, + typename GeomTraits> + OutIterator mean_value_coordinates_3( + const CGAL::Surface_mesh& surface_mesh, + const Point_3& query, + OutIterator c_begin, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT) { + + using Geom_Traits = typename Kernel_traits::Kernel; + using SM = CGAL::Surface_mesh; + + Mean_value_coordinates_3 mean_value(surface_mesh, policy); + return mean_value(query, c_begin); + } + +} // namespace Barycentric_coordinates +} // namespace CGAL + +#endif // CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 690112af1af1..b91c31b357b6 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -16,6 +16,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_wp_tetrahedron.cpp") create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") + create_single_source_cgal_program("test_mv_weights.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp new file mode 100644 index 000000000000..f075819ff82e --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -0,0 +1,78 @@ +#include +#include +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_overloads() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // tetrahedron + Mesh tetrahedron; + std::vector tetrahedron_coords; + + std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); + CGAL::Barycentric_coordinates::Mean_value_coordinates_3 mv_tetrahedron(tetrahedron); + + const FT step = FT(1) / FT(10); + const FT scale = FT(10); + const FT limit = step*scale; + + std::vector mv_coordinates_tetrahedron; + mv_coordinates_tetrahedron.resize(4); + + // Test cube + //Check for barycenter + mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); + tests::test_barycenter(mv_coordinates_tetrahedron); + + // Sample interior points + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces + + mv_coordinates_tetrahedron.clear(); + mv_coordinates_tetrahedron.resize(4); + + const Point_3 query(x, y, z); + mv_tetrahedron(query, mv_coordinates_tetrahedron.begin()); + + tests::test_linear_precision(mv_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(mv_coordinates_tetrahedron); + } + } + } + +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + + return EXIT_SUCCESS; +} From 9d38440e7e0828d61d411670c9a652703a339089 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 1 Jul 2021 00:22:54 -0300 Subject: [PATCH 28/68] fixing some bugs --- .../Mean_value_coordinates_3.h | 14 ++++++++++++-- .../Barycentric_coordinates_3/test_mv_weights.cpp | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 1d6ef901cc50..11997b8956c3 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -40,6 +40,7 @@ namespace Barycentric_coordinates { using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; using Sqrt = typename internal::Get_sqrt::Sqrt; + using Approximate_angle_3 = typename GeomTraits::Compute_approximate_angle_3; typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; @@ -58,6 +59,7 @@ namespace Barycentric_coordinates { m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), + m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()), sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ // Check if polyhedron is strongly convex @@ -96,6 +98,7 @@ namespace Barycentric_coordinates { const Cross_3 m_cross_3; const Dot_3 m_dot_3; const Sqrt sqrt; + const Approximate_angle_3 m_approximate_angle_3; std::vector m_weights; @@ -188,21 +191,28 @@ namespace Barycentric_coordinates { } std::vector m_vectors; + std::vector angles; for(std::size_t i = 0; i<3; i++){ Vector_3 normal = m_cross_3(unit_vectors[i], unit_vectors[(i+1)%3]); + angles.push_back(m_approximate_angle_3(unit_vectors[i], unit_vectors[(i+1)%3])); normal = normal / sqrt(normal.squared_length()); m_vectors.push_back(normal); } - //Missing area of circle + FT dot_e_m = m_dot_3(unit_vectors[vertex_idx], m_vectors[(vertex_idx+1)%3]); + for(std::size_t i = 0; i<3; i++){ + + weight += m_dot_3(m_vectors[i], m_vectors[(vertex_idx+1)%3]) * angles[i]; + } + weight /= 2*dot_e_m; face_circulator++; }while(face_circulator!=face_done); - return weight; + return weight/sqrt(m_construct_vector_3(vertex_val, query).squared_length()); } // Compute normal vector of the face (not normalized). diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index f075819ff82e..692086819a46 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -36,6 +36,10 @@ void test_overloads() { // Test cube //Check for barycenter mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); + + for(auto u: mv_coordinates_tetrahedron) + std::cout << u << "\n"; + tests::test_barycenter(mv_coordinates_tetrahedron); // Sample interior points From 8e9540cc9d9c523a1c8e60c3e1618b227aa08a4c Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 1 Jul 2021 20:52:19 -0300 Subject: [PATCH 29/68] functional version of mv coordinates + tests --- .../.vscode/settings.json | 70 ------------- .../Discrete_harmonic_coordinates_3.h | 9 +- .../Mean_value_coordinates_3.h | 98 +++++++------------ .../test_mv_weights.cpp | 4 - 4 files changed, 40 insertions(+), 141 deletions(-) delete mode 100644 Barycentric_coordinates_3/.vscode/settings.json diff --git a/Barycentric_coordinates_3/.vscode/settings.json b/Barycentric_coordinates_3/.vscode/settings.json deleted file mode 100644 index 1a76d2534e62..000000000000 --- a/Barycentric_coordinates_3/.vscode/settings.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "C_Cpp.errorSquiggles": "Disabled", - "files.associations": { - "__bit_reference": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__functional_base": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__mutex_base": "cpp", - "__nullptr": "cpp", - "__split_buffer": "cpp", - "__string": "cpp", - "__threading_support": "cpp", - "__tree": "cpp", - "__tuple": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "complex": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "exception": "cpp", - "string_view": "cpp", - "fstream": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "iterator": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "set": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "utility": "cpp", - "vector": "cpp", - "*.tcc": "cpp", - "cinttypes": "cpp", - "hashtable": "cpp" - } -} \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index c7e284ec8b27..783c71cb242c 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -35,7 +35,6 @@ namespace Barycentric_coordinates { using Geom_traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; - using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; @@ -54,7 +53,6 @@ namespace Barycentric_coordinates { m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), m_traits(traits), - m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()), m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), @@ -91,7 +89,6 @@ namespace Barycentric_coordinates { const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; - const Dihedral_angle_3 m_dihedral_angle_3; const Construct_vec_3 m_construct_vector_3; const Cross_3 m_cross_3; const Dot_3 m_dot_3; @@ -131,7 +128,7 @@ namespace Barycentric_coordinates { const auto vd = vertices(m_polygon_mesh); for (const auto& vertex : vd) { - // Call function to calculate wp coordinates + // Call function to calculate coordinates const FT weight = compute_dh_vertex_query(vertex, query); CGAL_assertion(vi < m_weights.size()); @@ -148,9 +145,6 @@ namespace Barycentric_coordinates { template FT compute_dh_vertex_query(const Vertex& vertex, const Point_3& query){ - // Map vertex descriptor to point_3 - const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); - // Circulator of faces around the vertex CGAL::Face_around_target_circulator face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); @@ -164,6 +158,7 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ + //Vertices around face iterator const auto hedge = halfedge(*face_circulator, m_polygon_mesh); const auto vertices = vertices_around_face(hedge, m_polygon_mesh); auto vertex_itr = vertices.begin(); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 11997b8956c3..6da3cbaf5836 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -35,7 +35,6 @@ namespace Barycentric_coordinates { using Geom_traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; - using Dihedral_angle_3 = typename GeomTraits::Compute_approximate_dihedral_angle_3; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; @@ -55,7 +54,6 @@ namespace Barycentric_coordinates { m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), m_traits(traits), - m_dihedral_angle_3(m_traits.compute_approximate_dihedral_angle_3_object()), m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), @@ -93,7 +91,6 @@ namespace Barycentric_coordinates { const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; - const Dihedral_angle_3 m_dihedral_angle_3; const Construct_vec_3 m_construct_vector_3; const Cross_3 m_cross_3; const Dot_3 m_dot_3; @@ -134,7 +131,7 @@ namespace Barycentric_coordinates { const auto vd = vertices(m_polygon_mesh); for (const auto& vertex : vd) { - // Call function to calculate wp coordinates + // Call function to calculate coordinates const FT weight = compute_mv_vertex_query(vertex, query); CGAL_assertion(vi < m_weights.size()); @@ -167,89 +164,70 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ + // Vertices around face iterator const auto hedge = halfedge(*face_circulator, m_polygon_mesh); const auto vertices = vertices_around_face(hedge, m_polygon_mesh); auto vertex_itr = vertices.begin(); CGAL_precondition(vertices.size() == 3); - std::vector unit_vectors; + // Weight of vertex for this particular face + FT partial_weight = FT(0); int vertex_idx = -1; + + // Store useful information + std::vector query_vertex_vectors; + std::vector unit_vectors; + std::vector m_vectors; + std::vector angles; + query_vertex_vectors.resize(3); + unit_vectors.resize(3); + m_vectors.resize(3); + angles.resize(3); + for(std::size_t i = 0; i < 3; i++){ if(*vertex_itr == vertex) vertex_idx = i; - Point_3 i_face_vertex = get(m_vertex_to_point_map, *vertex_itr); - Vector_3 i_query_vertex = m_construct_vector_3(query, i_face_vertex); - - assert(i_query_vertex.squared_length() != 0); - i_query_vertex = i_query_vertex/sqrt(i_query_vertex.squared_length()); - - unit_vectors.push_back(i_query_vertex); - + const Vector_3 p = m_construct_vector_3(query, get(m_vertex_to_point_map, *vertex_itr)); + query_vertex_vectors[i] = p; vertex_itr++; } - std::vector m_vectors; - std::vector angles; - for(std::size_t i = 0; i<3; i++){ + // Current vertex should be present in face + assert(vertex_idx != -1); - Vector_3 normal = m_cross_3(unit_vectors[i], unit_vectors[(i+1)%3]); - angles.push_back(m_approximate_angle_3(unit_vectors[i], unit_vectors[(i+1)%3])); - normal = normal / sqrt(normal.squared_length()); - m_vectors.push_back(normal); - } + for(std::size_t i = 0; i < 3; i++){ - FT dot_e_m = m_dot_3(unit_vectors[vertex_idx], m_vectors[(vertex_idx+1)%3]); + assert(query_vertex_vectors[i].squared_length() > 0); + unit_vectors[i] = query_vertex_vectors[i]/sqrt(query_vertex_vectors[i].squared_length()); - for(std::size_t i = 0; i<3; i++){ + m_vectors[i] = m_cross_3(query_vertex_vectors[i], query_vertex_vectors[(i+1)%3]); + assert(m_vectors[i].squared_length() > 0); + m_vectors[i] /= sqrt(m_vectors[i].squared_length()); - weight += m_dot_3(m_vectors[i], m_vectors[(vertex_idx+1)%3]) * angles[i]; + angles[i] = m_approximate_angle_3(query_vertex_vectors[i], query_vertex_vectors[(i+1)%3]); } - weight /= 2*dot_e_m; - face_circulator++; + partial_weight += angles[0] * m_dot_3(m_vectors[0], m_vectors[(vertex_idx+1)%3]); + partial_weight += angles[1] * m_dot_3(m_vectors[1], m_vectors[(vertex_idx+1)%3]); + partial_weight += angles[2] * m_dot_3(m_vectors[2], m_vectors[(vertex_idx+1)%3]); - }while(face_circulator!=face_done); - - return weight/sqrt(m_construct_vector_3(vertex_val, query).squared_length()); - } - - // Compute normal vector of the face (not normalized). - template - Vector_3 get_face_normal(const Face& face) { + const FT dot_unit_m = m_dot_3(unit_vectors[vertex_idx], m_vectors[(vertex_idx+1)%3]); + assert(dot_unit_m != 0); + partial_weight /= dot_unit_m; - const auto hedge = halfedge(face, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() >= 3); + weight += partial_weight; - auto vertex = vertices.begin(); - const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point3 = get(m_vertex_to_point_map, *vertex); - - const Vector_3 u = point2 - point1; - const Vector_3 v = point3 - point1; - const Vector_3 face_normal = m_cross_3(u, v); - - return face_normal; - } - - //Compute cotangent of dihedral angle between two faces - FT cot_dihedral_angle(const Vector_3& normal_1, const Vector_3& normal_2){ - - assert(normal_1.squared_length() != FT(0)); - assert(normal_2.squared_length() != FT(0)); - - FT approximate_dot_3 = m_dot_3(normal_1, normal_2); + face_circulator++; - FT approximate_cross_3_length = sqrt(m_cross_3(normal_1, normal_2).squared_length()); + }while(face_circulator!=face_done); - assert(approximate_cross_3_length != FT(0)); + const FT vertex_query_squared_len = m_construct_vector_3(vertex_val, query).squared_length(); + assert(vertex_query_squared_len != 0); - return approximate_dot_3/approximate_cross_3_length; + return weight/sqrt(vertex_query_squared_len); } - }; template< diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index 692086819a46..f075819ff82e 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -36,10 +36,6 @@ void test_overloads() { // Test cube //Check for barycenter mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); - - for(auto u: mv_coordinates_tetrahedron) - std::cout << u << "\n"; - tests::test_barycenter(mv_coordinates_tetrahedron); // Sample interior points From 9f55c00e555cfdc116ae639a17585ad3d9a8dc8d Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 6 Jul 2021 04:16:00 -0300 Subject: [PATCH 30/68] First version voronoi coordinates (still debugging) --- .../Discrete_harmonic_coordinates_3.h | 10 +- .../Voronoi_coordinates_3.h | 269 ++++++++++++++++++ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_mv_weights.cpp | 1 - .../test_voronoi_weights.cpp | 85 ++++++ 5 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 783c71cb242c..32955155388e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -216,14 +216,14 @@ namespace Barycentric_coordinates { } //Compute cotangent of dihedral angle between two faces - FT cot_dihedral_angle(const Vector_3& normal_1, const Vector_3& normal_2){ + FT cot_dihedral_angle(const Vector_3& vec_1, const Vector_3& vec_2){ - assert(normal_1.squared_length() != FT(0)); - assert(normal_2.squared_length() != FT(0)); + assert(vec_1.squared_length() != FT(0)); + assert(vec_2.squared_length() != FT(0)); - FT approximate_dot_3 = m_dot_3(normal_1, normal_2); + FT approximate_dot_3 = m_dot_3(vec_1, vec_2); - FT approximate_cross_3_length = sqrt(m_cross_3(normal_1, normal_2).squared_length()); + FT approximate_cross_3_length = sqrt(m_cross_3(vec_1, vec_2).squared_length()); assert(approximate_cross_3_length != FT(0)); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h new file mode 100644 index 000000000000..e34d4c4a5a14 --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h @@ -0,0 +1,269 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + +#ifndef CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H +#define CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H + +// #include + +// Internal includes. +#include +#include + +namespace CGAL { +namespace Barycentric_coordinates { + + template< + typename PolygonMesh, + typename GeomTraits, + typename VertexToPointMap = typename property_map_selector::const_type> + class Voronoi_coordinates_3 { + + public: + using Polygon_mesh = PolygonMesh; + using Geom_traits = GeomTraits; + using Vertex_to_point_map = VertexToPointMap; + + using Construct_vec_3 = typename GeomTraits::Construct_vector_3; + using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; + using Dot_3 = typename GeomTraits::Compute_scalar_product_3; + using Sqrt = typename internal::Get_sqrt::Sqrt; + using Approximate_angle_3 = typename GeomTraits::Compute_approximate_angle_3; + + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Point_3 Point_3; + typedef typename GeomTraits::Vector_3 Vector_3; + + Voronoi_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy, + const VertexToPointMap vertex_to_point_map, + const GeomTraits traits = GeomTraits()) : + m_polygon_mesh(polygon_mesh), + m_computation_policy(policy), + m_vertex_to_point_map(vertex_to_point_map), + m_traits(traits), + m_construct_vector_3(m_traits.construct_vector_3_object()), + m_cross_3(m_traits.construct_cross_product_vector_3_object()), + m_dot_3(m_traits.compute_scalar_product_3_object()), + m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()), + sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ + + // Check if polyhedron is strongly convex + CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); + m_weights.resize(vertices(m_polygon_mesh).size()); + } + + Voronoi_coordinates_3( + const PolygonMesh& polygon_mesh, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT, + const GeomTraits traits = GeomTraits()) : + Voronoi_coordinates_3( + polygon_mesh, + policy, + get_const_property_map(CGAL::vertex_point, polygon_mesh), + traits) { } + + template + OutIterator operator()(const Point_3& query, OutIterator c_begin) { + return compute(query, c_begin); + } + + VertexToPointMap get_vertex_to_point_map(){ + return m_vertex_to_point_map; + } + + private: + const PolygonMesh& m_polygon_mesh; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; + + const Construct_vec_3 m_construct_vector_3; + const Cross_3 m_cross_3; + const Dot_3 m_dot_3; + const Sqrt sqrt; + const Approximate_angle_3 m_approximate_angle_3; + + std::vector m_weights; + + template + OutputIterator compute( + const Point_3& query, OutputIterator coordinates) { + + // Compute weights. + const FT sum = compute_weights(query); + CGAL_assertion(sum != FT(0)); + + // The coordinates must be saved in the same order as vertices in the vertex range. + const auto vd = vertices(m_polygon_mesh); + CGAL_assertion(m_weights.size() == vd.size()); + + for (std::size_t vi = 0; vi < vd.size(); vi++) { + + CGAL_assertion(vi < m_weights.size()); + const FT coordinate = m_weights[vi]/sum; + *(coordinates++) = coordinate; + } + + return coordinates; + } + + FT compute_weights(const Point_3& query) { + + // Sum of weights to normalize them later. + FT sum = FT(0); + + // Vertex index. + std::size_t vi = 0; + const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { + + // Call function to calculate coordinates + const FT weight = compute_mv_vertex_query(vertex, query); + + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi + } + + CGAL_assertion(sum != FT(0)); + return sum; + } + + // Compute wp coordinates for a given vertex v and a query q + template + FT compute_mv_vertex_query(const Vertex& vertex, const Point_3& query){ + + // Map vertex descriptor to point_3 + const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); + + // Circulator of faces around the vertex + CGAL::Face_around_target_circulator + face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + + CGAL::Face_around_target_circulator + face_done(face_circulator); + + // Compute weight w_v + FT weight = FT(0); + + // Iterate using the circulator + do{ + + // Vertices around face iterator + const auto hedge = halfedge(*face_circulator, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + auto vertex_itr = vertices.begin(); + CGAL_precondition(vertices.size() == 3); + + // Weight of vertex for this particular face + FT partial_weight = FT(0); + int vertex_idx = -1; + + // Store useful information + std::vector query_vertex_vectors; + std::vector m_vectors; + Vector_3 r_vector; + Vector_3 m_vector_vertex; + query_vertex_vectors.resize(3); + m_vectors.resize(3); + + for(std::size_t i = 0; i < 3; i++){ + + if(*vertex_itr == vertex) + vertex_idx = i; + + const Vector_3 p = m_construct_vector_3(query, get(m_vertex_to_point_map, *vertex_itr)); + query_vertex_vectors[i] = p; + vertex_itr++; + } + + // Current vertex should be present in face + assert(vertex_idx != -1); + + for(std::size_t i = 0; i < 3; i++){ + + m_vectors[i] = m_cross_3(query_vertex_vectors[i], query_vertex_vectors[(i+1)%3]); + assert(m_vectors[i].squared_length() > 0); + m_vectors[i] /= sqrt(m_vectors[i].squared_length()); + + Vector_3 edge_vector = query_vertex_vectors[i+1] - query_vertex_vectors[i]; + FT cot_1 = cot_dihedral_angle(-edge_vector, query_vertex_vectors[i]); + FT cot_2 = cot_dihedral_angle(edge_vector, query_vertex_vectors[i+1]); + + std::cout <<"Cotangents: "<< cot_1 << " " << cot_2 << "\n"; + + FT d_val = query_vertex_vectors[i].squared_length() * cot_2 + + query_vertex_vectors[i+1].squared_length() * cot_1; + + r_vector = r_vector + d_val * m_vectors[i]; + + if(i != vertex_idx && i+1 != vertex_idx) + m_vector_vertex = m_vectors[i]; + } + + weight += m_dot_3(r_vector, m_vector_vertex); + + std::cout << r_vector << "\n"; + + face_circulator++; + + }while(face_circulator!=face_done); + + std::cout << "Weight: "< + OutIterator voronoi_coordinates_3( + const CGAL::Surface_mesh& surface_mesh, + const Point_3& query, + OutIterator c_begin, + const Computation_policy_3 policy = + Computation_policy_3::DEFAULT) { + + using Geom_Traits = typename Kernel_traits::Kernel; + using SM = CGAL::Surface_mesh; + + Voronoi_coordinates_3 voronoi(surface_mesh, policy); + return voronoi(query, c_begin); + } + +} // namespace Barycentric_coordinates +} // namespace CGAL + +#endif // CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index b91c31b357b6..982934038439 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -17,6 +17,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") + create_single_source_cgal_program("test_voronoi_weights.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index f075819ff82e..83b028a16207 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -33,7 +33,6 @@ void test_overloads() { std::vector mv_coordinates_tetrahedron; mv_coordinates_tetrahedron.resize(4); - // Test cube //Check for barycenter mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); tests::test_barycenter(mv_coordinates_tetrahedron); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp new file mode 100644 index 000000000000..f582ad337118 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp @@ -0,0 +1,85 @@ +#include +#include +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_overloads() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // tetrahedron + Mesh tetrahedron; + std::vector tetrahedron_coords; + + std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); + CGAL::Barycentric_coordinates::Voronoi_coordinates_3 voronoi_tetrahedron(tetrahedron); + + const FT step = FT(1) / FT(10); + const FT scale = FT(10); + const FT limit = step*scale; + + std::vector voronoi_coordinates_tetrahedron; + voronoi_coordinates_tetrahedron.resize(4); + + //Check for barycenter + voronoi_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), + voronoi_coordinates_tetrahedron.begin()); + + for(std::size_t i = 0; i < 4; i++) + std::cout << voronoi_coordinates_tetrahedron[i] << std::endl; + + tests::test_barycenter(voronoi_coordinates_tetrahedron); + + // Sample interior points + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces + + voronoi_coordinates_tetrahedron.clear(); + voronoi_coordinates_tetrahedron.resize(4); + + const Point_3 query(x, y, z); + voronoi_tetrahedron(query, voronoi_coordinates_tetrahedron.begin()); + + for(std::size_t i = 0; i < 4; i++) + std::cout << voronoi_coordinates_tetrahedron[i] << std::endl; + + tests::test_linear_precision(voronoi_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(voronoi_coordinates_tetrahedron); + } + } + } + +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + + return EXIT_SUCCESS; +} From f8928b2118d75e49020785d38dc55e9fadff2247 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 6 Jul 2021 21:55:49 -0300 Subject: [PATCH 31/68] Fixing after first review, need to finish tests --- .../include/CGAL/Barycentric_coordinates_3.h | 2 + .../Discrete_harmonic_coordinates_3.h | 67 ++++++------------- .../Mean_value_coordinates_3.h | 32 +++++---- .../Wachspress_coordinates_3.h | 41 ++++-------- .../internal/utils_3.h | 57 ++++++++++++++++ .../Barycentric_coordinates_3/CMakeLists.txt | 2 +- .../test_dh_weights.cpp | 6 +- .../test_mv_weights.cpp | 6 +- .../test_wp_weights.cpp | 8 +-- 9 files changed, 117 insertions(+), 104 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index a0a961315dc3..16a6a0948847 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -18,5 +18,7 @@ #include #include +#include +#include #endif diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 32955155388e..cc76194056d5 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -32,7 +32,7 @@ namespace Barycentric_coordinates { public: using Polygon_mesh = PolygonMesh; - using Geom_traits = GeomTraits; + using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; @@ -166,10 +166,16 @@ namespace Barycentric_coordinates { int vertex_parity = 1; std::vector points; + points.resize(2); + int point_count = 0; + for(std::size_t i = 0; i < 3; i++){ - if(*vertex_itr!=vertex) - points.push_back(get(m_vertex_to_point_map, *vertex_itr)); + if(*vertex_itr!=vertex){ + + points[point_count] = get(m_vertex_to_point_map, *vertex_itr); + point_count++; + } else vertex_parity *= (i & 1)? -1 : 1; @@ -179,13 +185,16 @@ namespace Barycentric_coordinates { const Point_3& point2 = points[0]; const Point_3& point1 = points[1]; - Vector_3 opposite_edge = m_construct_vector_3(point2, point1); - FT edge_length = sqrt(opposite_edge.squared_length()); + const Vector_3 opposite_edge = m_construct_vector_3(point2, point1); + const FT edge_length = sqrt(opposite_edge.squared_length()); - Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), + const Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); - FT cot_dihedral = cot_dihedral_angle(get_face_normal(*face_circulator), normal_query); + const FT cot_dihedral = internal::cot_dihedral_angle( + internal::get_face_normal( + *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits), + normal_query, m_traits); weight += (cot_dihedral * edge_length) / 2; face_circulator++; @@ -195,58 +204,22 @@ namespace Barycentric_coordinates { return weight; } - // Compute normal vector of the face (not normalized). - template - Vector_3 get_face_normal(const Face& face) { - - const auto hedge = halfedge(face, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() >= 3); - - auto vertex = vertices.begin(); - const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point3 = get(m_vertex_to_point_map, *vertex); - - const Vector_3 u = point2 - point1; - const Vector_3 v = point3 - point1; - const Vector_3 face_normal = m_cross_3(u, v); - - return face_normal; - } - - //Compute cotangent of dihedral angle between two faces - FT cot_dihedral_angle(const Vector_3& vec_1, const Vector_3& vec_2){ - - assert(vec_1.squared_length() != FT(0)); - assert(vec_2.squared_length() != FT(0)); - - FT approximate_dot_3 = m_dot_3(vec_1, vec_2); - - FT approximate_cross_3_length = sqrt(m_cross_3(vec_1, vec_2).squared_length()); - - assert(approximate_cross_3_length != FT(0)); - - return approximate_dot_3/approximate_cross_3_length; - } - }; template< typename Point_3, - typename OutIterator, - typename GeomTraits> + typename Mesh, + typename OutIterator> OutIterator discrete_harmonic_coordinates_3( - const CGAL::Surface_mesh& surface_mesh, + const Mesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = Computation_policy_3::DEFAULT) { using Geom_Traits = typename Kernel_traits::Kernel; - using SM = CGAL::Surface_mesh; - Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); + Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); return discrete_harmonic(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 6da3cbaf5836..8a9f99f5dbef 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -32,7 +32,7 @@ namespace Barycentric_coordinates { public: using Polygon_mesh = PolygonMesh; - using Geom_traits = GeomTraits; + using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; @@ -63,6 +63,10 @@ namespace Barycentric_coordinates { // Check if polyhedron is strongly convex CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); m_weights.resize(vertices(m_polygon_mesh).size()); + query_vertex_vectors.resize(3); + unit_vectors.resize(3); + m_vectors.resize(3); + angles.resize(3); } Mean_value_coordinates_3( @@ -97,7 +101,12 @@ namespace Barycentric_coordinates { const Sqrt sqrt; const Approximate_angle_3 m_approximate_angle_3; - std::vector m_weights; + // Store useful information + std::vector m_weights; + std::vector query_vertex_vectors; + std::vector unit_vectors; + std::vector m_vectors; + std::vector angles; template OutputIterator compute( @@ -174,16 +183,6 @@ namespace Barycentric_coordinates { FT partial_weight = FT(0); int vertex_idx = -1; - // Store useful information - std::vector query_vertex_vectors; - std::vector unit_vectors; - std::vector m_vectors; - std::vector angles; - query_vertex_vectors.resize(3); - unit_vectors.resize(3); - m_vectors.resize(3); - angles.resize(3); - for(std::size_t i = 0; i < 3; i++){ if(*vertex_itr == vertex) @@ -232,19 +231,18 @@ namespace Barycentric_coordinates { template< typename Point_3, - typename OutIterator, - typename GeomTraits> + typename Mesh, + typename OutIterator> OutIterator mean_value_coordinates_3( - const CGAL::Surface_mesh& surface_mesh, + const Mesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = Computation_policy_3::DEFAULT) { using Geom_Traits = typename Kernel_traits::Kernel; - using SM = CGAL::Surface_mesh; - Mean_value_coordinates_3 mean_value(surface_mesh, policy); + Mean_value_coordinates_3 mean_value(surface_mesh, policy); return mean_value(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 2c86704b3aae..6c4d915af18a 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -32,7 +32,7 @@ namespace Barycentric_coordinates { public: using Polygon_mesh = PolygonMesh; - using Geom_traits = GeomTraits; + using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; @@ -162,7 +162,8 @@ namespace Barycentric_coordinates { const Vector_3 query_vertex = m_construct_vector_3(query, vertex_val); // First face. p_1 is negated because the order of the circulator is reversed - const Vector_3 face_normal_1 = get_face_normal(*face_circulator); + const Vector_3 face_normal_1 = internal::get_face_normal( + *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); const FT dist_perp_1 = m_dot_3(query_vertex, face_normal_1); CGAL_assertion(dist_perp_1 != FT(0)); const Vector_3 p_1 = -face_normal_1/dist_perp_1; @@ -174,8 +175,11 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ // Calculate normals of faces - const Vector_3 face_normal_i = get_face_normal(*face_circulator); face_circulator++; - const Vector_3 face_normal_i_1 = get_face_normal(*face_circulator); + const Vector_3 face_normal_i = internal::get_face_normal( + *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); + face_circulator++; + const Vector_3 face_normal_i_1 = internal::get_face_normal( + *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); // Distance of query to face const FT perp_dist_i = m_dot_3(query_vertex, face_normal_i); @@ -195,43 +199,22 @@ namespace Barycentric_coordinates { return weight; } - // Compute normal vector of the face (not normalized). - template - Vector_3 get_face_normal(const Face& face) { - - const auto hedge = halfedge(face, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() >= 3); - - auto vertex = vertices.begin(); - const Point_3& point1 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point2 = get(m_vertex_to_point_map, *vertex); ++vertex; - const Point_3& point3 = get(m_vertex_to_point_map, *vertex); - - const Vector_3 u = point2 - point1; - const Vector_3 v = point3 - point1; - const Vector_3 face_normal = m_cross_3(u, v); - - return face_normal; - } - }; template< typename Point_3, - typename OutIterator, - typename GeomTraits> + typename Mesh, + typename OutIterator> OutIterator wachspress_coordinates_3( - const CGAL::Surface_mesh& surface_mesh, + const Mesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = Computation_policy_3::DEFAULT) { using Geom_Traits = typename Kernel_traits::Kernel; - using SM = CGAL::Surface_mesh; - Wachspress_coordinates_3 wachspress(surface_mesh, policy); + Wachspress_coordinates_3 wachspress(surface_mesh, policy); return wachspress(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 902db67ba194..389f4e38f3ec 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -126,6 +126,63 @@ template return coordinates; } + // Compute normal vector of the face (not normalized). + template< + typename Face, + typename VertexToPointMap, + typename PolygonMesh, + typename GeomTraits> + typename GeomTraits::Vector_3 get_face_normal( + const Face& face, + const VertexToPointMap& vertex_to_point_map, + const PolygonMesh& polygon_mesh, + const GeomTraits& traits){ + + using Point_3 = typename GeomTraits::Point_3; + using Vector_3 = typename GeomTraits::Vector_3; + const auto& cross_3 = traits.construct_cross_product_vector_3_object(); + + const auto hedge = halfedge(face, polygon_mesh); + const auto vertices = vertices_around_face(hedge, polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const Point_3& point1 = get(vertex_to_point_map, *vertex); ++vertex; + const Point_3& point2 = get(vertex_to_point_map, *vertex); ++vertex; + const Point_3& point3 = get(vertex_to_point_map, *vertex); + + const Vector_3 u = point2 - point1; + const Vector_3 v = point3 - point1; + const Vector_3 face_normal = cross_3(u, v); + + return face_normal; + } + + //Compute cotangent of dihedral angle between two faces + template + typename GeomTraits::FT cot_dihedral_angle( + const typename GeomTraits::Vector_3& vec_1, + const typename GeomTraits::Vector_3& vec_2, + const GeomTraits& traits){ + + using FT = typename GeomTraits::FT; + const auto& dot_3 = traits.compute_scalar_product_3_object(); + const auto& cross_3 = traits.construct_cross_product_vector_3_object(); + const auto& sqrt(Get_sqrt::sqrt_object(traits)); + + assert(vec_1.squared_length() != FT(0)); + assert(vec_2.squared_length() != FT(0)); + + const FT approximate_dot_3 = dot_3(vec_1, vec_2); + + const FT approximate_cross_3_length = sqrt(cross_3(vec_1, vec_2).squared_length()); + + assert(approximate_cross_3_length != FT(0)); + + return approximate_dot_3/approximate_cross_3_length; + } + + } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 982934038439..507dd7f328c8 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -2,6 +2,7 @@ # This is the CMake script for compiling a CGAL application. project(Barycentric_coordinates_3_Tests) +set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wextra -Werror") cmake_minimum_required(VERSION 3.1...3.15) set(CMAKE_CXX_STANDARD 14) @@ -18,7 +19,6 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") create_single_source_cgal_program("test_voronoi_weights.cpp") - else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index eb0b1a3dfd1c..2f45825c658e 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -43,14 +43,14 @@ void test_overloads() { for(FT y = step; y < limit; y += step){ for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces - dh_coordinates_tetrahedron.clear(); - dh_coordinates_tetrahedron.resize(4); - const Point_3 query(x, y, z); dh_tetrahedron(query, dh_coordinates_tetrahedron.begin()); tests::test_linear_precision(dh_coordinates_tetrahedron, tetrahedron_coords, query); tests::test_partition_of_unity(dh_coordinates_tetrahedron); + + CGAL::Barycentric_coordinates::discrete_harmonic_coordinates_3( + tetrahedron, query, dh_coordinates_tetrahedron.begin()); } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index 83b028a16207..d88e2f06e8f3 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -42,14 +42,14 @@ void test_overloads() { for(FT y = step; y < limit; y += step){ for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces - mv_coordinates_tetrahedron.clear(); - mv_coordinates_tetrahedron.resize(4); - const Point_3 query(x, y, z); mv_tetrahedron(query, mv_coordinates_tetrahedron.begin()); tests::test_linear_precision(mv_coordinates_tetrahedron, tetrahedron_coords, query); tests::test_partition_of_unity(mv_coordinates_tetrahedron); + + CGAL::Barycentric_coordinates::mean_value_coordinates_3( + tetrahedron, query, mv_coordinates_tetrahedron.begin()); } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index d58d4826f06f..ef752725d967 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -17,7 +17,7 @@ void test_overloads() { using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; + using Mesh = CGAL::Surface_mesh; // Cube Mesh cube; @@ -43,15 +43,15 @@ void test_overloads() { for(FT y = step; y < limit; y += step){ for(FT z = step; z < limit; z += step){ - wp_coordinates_cube.clear(); - wp_coordinates_cube.resize(8); - const Point_3 query(x, y, z); wp_cube(query, wp_coordinates_cube.begin()); tests::test_linear_precision(wp_coordinates_cube, cube_coords, query); tests::test_partition_of_unity(wp_coordinates_cube); tests::test_positivity(wp_coordinates_cube); + + CGAL::Barycentric_coordinates::wachspress_coordinates_3( + cube, query, wp_coordinates_cube.begin()); } } } From fd6474f5bae77e71a80f90104d92f08f2f4de531 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 7 Jul 2021 22:36:56 -0300 Subject: [PATCH 32/68] more fixes and created first benchmark --- .../Barycentric_coordinates_3/CMakeLists.txt | 4 +- .../benchmark_polyhedron_8_vertices.cpp | 85 +++++++++++++++++ .../benchmark_tetrahedon_coordinates.cpp | 6 ++ .../Barycentric_coordinates_3/CMakeLists.txt | 2 +- .../Barycentric_coordinates_3/include/utils.h | 11 +++ .../test_mv_weights.cpp | 33 +++---- .../test_wp_dh_mv_tetrahedron.cpp | 94 +++++++++++++++++++ .../test_wp_tetrahedron.cpp | 74 --------------- .../test_wp_weights.cpp | 1 + 9 files changed, 218 insertions(+), 92 deletions(-) create mode 100644 Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp delete mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt index a6b3893b3432..867c102998e3 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/CMakeLists.txt @@ -2,6 +2,7 @@ # This is the CMake script for compiling a CGAL application. project(Barycentric_coordinates_3_Examples) +set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wextra -Werror") cmake_minimum_required(VERSION 3.1...3.15) set(CMAKE_CXX_STANDARD 14) @@ -13,7 +14,8 @@ if(CGAL_FOUND) include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("benchmark_tetrahedon_coordinates.cpp") - + create_single_source_cgal_program("benchmark_polyhedron_8_vertices.cpp") + else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp new file mode 100644 index 000000000000..3e953e1ac376 --- /dev/null +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = typename Kernel::FT; +using Point_3 = typename Kernel::Point_3; +using Timer = CGAL::Real_timer; +using Vertices = std::vector; +using SM = CGAL::Surface_mesh; + +using WPC3 = CGAL::Barycentric_coordinates::Wachspress_coordinates_3; +using MVC3 = CGAL::Barycentric_coordinates::Mean_value_coordinates_3; +using DHC3 = CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3; + +template +double benchmark_overload(){ + + // Cube + SM cube; + Vertices cube_coords; + + cube_coords = {Point_3(1.0, 0.0, 0.0), Point_3(1.0, 1.0, 0.0), + Point_3(0.0, 1.0, 0.0), Point_3(0.0, 0.0, 0.0), + Point_3(0.0, 0.0, 1.0), Point_3(1.0, 0.0, 1.0), + Point_3(1.0, 1.0, 1.0), Point_3(0.0, 1.0, 1.0)}; + + CGAL::make_hexahedron(cube_coords[0], cube_coords[1], cube_coords[2], cube_coords[3], + cube_coords[4], cube_coords[5], cube_coords[6], cube_coords[7], cube); + + CGAL::Polygon_mesh_processing::triangulate_faces(cube); + + COORD bar_cube(cube); + + const FT step = FT(1) / FT(100); + const FT scale = FT(100); + const FT limit = step*scale; + + std::vector bar_coordinates_cube; + bar_coordinates_cube.resize(8); + + Timer timer; + double time = 0.0; + + // Sample interior points + timer.start(); + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < limit; z += step){ + + const Point_3 query(x, y, z); + bar_cube(query, bar_coordinates_cube.begin()); + } + } + } + timer.stop(); + time += timer.time(); + timer.reset(); + + return time; +} + +int main(){ + + std::cout.precision(10); + + std::cout << "Wachspress : " << std::endl; + std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << + benchmark_overload() << " seconds" << std::endl; + + std::cout << "Discrete Harmonic : " << std::endl; + std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << + benchmark_overload() << " seconds" << std::endl; + + std::cout << "Mean Value : " << std::endl; + std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << + benchmark_overload() << " seconds" << std::endl; + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp index e69de29bb2d1..6fa311d054b9 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp @@ -0,0 +1,6 @@ + + +int main(){ + + return 0; +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 507dd7f328c8..8cc0286c1662 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -14,7 +14,7 @@ if(CGAL_FOUND) include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") - create_single_source_cgal_program("test_wp_tetrahedron.cpp") + create_single_source_cgal_program("test_wp_dh_mv_tetrahedron.cpp") create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index 1a7534b9d74c..c9631163a9e2 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -7,6 +7,7 @@ // CGAL includes #include +#include namespace tests{ @@ -123,6 +124,16 @@ namespace tests{ for(auto& coord : coords) assert(coord >= FT(0) && coord <= FT(1)); } + + template + OutIterator random_points_triangle_mesh(Mesh& mesh, OutIterator out, int n){ + + CGAL::Random_points_in_triangle_mesh_3 gen(mesh); + std::copy_n(gen, n, out); + + return out; + } + } #endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index d88e2f06e8f3..f06072112ff4 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -22,13 +22,11 @@ void test_overloads() { // tetrahedron Mesh tetrahedron; std::vector tetrahedron_coords; + std::vector sample_points; std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); CGAL::Barycentric_coordinates::Mean_value_coordinates_3 mv_tetrahedron(tetrahedron); - - const FT step = FT(1) / FT(10); - const FT scale = FT(10); - const FT limit = step*scale; + tests::random_points_triangle_mesh(tetrahedron, std::back_inserter(sample_points), 100000); std::vector mv_coordinates_tetrahedron; mv_coordinates_tetrahedron.resize(4); @@ -37,21 +35,24 @@ void test_overloads() { mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); tests::test_barycenter(mv_coordinates_tetrahedron); - // Sample interior points - for(FT x = step; x < limit; x += step){ - for(FT y = step; y < limit; y += step){ - for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces + for(auto& point : sample_points){ + + FT x = point.x(); + FT y = point.y(); + FT z = point.z(); + + if(x + y + z == FT(1) || x == FT(0) || y == FT(0) || z == FT(0)) //avoid points inside faces + continue; - const Point_3 query(x, y, z); - mv_tetrahedron(query, mv_coordinates_tetrahedron.begin()); + const Point_3 query(x, y, z); + mv_tetrahedron(query, mv_coordinates_tetrahedron.begin()); - tests::test_linear_precision(mv_coordinates_tetrahedron, tetrahedron_coords, query); - tests::test_partition_of_unity(mv_coordinates_tetrahedron); + tests::test_linear_precision(mv_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(mv_coordinates_tetrahedron); + tests::test_positivity(mv_coordinates_tetrahedron); - CGAL::Barycentric_coordinates::mean_value_coordinates_3( - tetrahedron, query, mv_coordinates_tetrahedron.begin()); - } - } + CGAL::Barycentric_coordinates::mean_value_coordinates_3( + tetrahedron, query, mv_coordinates_tetrahedron.begin()); } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp new file mode 100644 index 000000000000..0ce1ffea48c9 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include +#include +#include +#include + +#include "include/utils.h" + +//Typedefs +using Kernel = CGAL::Simple_cartesian; + +using FT = typename Kernel::FT; +using Point_3 = typename Kernel::Point_3; +using Mesh = typename CGAL::Surface_mesh; + +using WPC3 = CGAL::Barycentric_coordinates::Wachspress_coordinates_3; +using MVC3 = CGAL::Barycentric_coordinates::Mean_value_coordinates_3; +using DHC3 = CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3; + +template +void test_overload(){ + + // Regular tetrahedron + Mesh tetrahedron; + std::vector vertices; + std::tie(tetrahedron, vertices) = tests::get_irregular_tetrahedron(); + + COORD bar(tetrahedron); + + std::vector tetra_coordinates; + std::vector bar_coordinates; + tetra_coordinates.resize(4); + bar_coordinates.resize(4); + + // Sample points + const FT step = FT(1) / FT(20); + const FT scale = FT(10); + const FT tol = tests::get_tolerance(); + + std::size_t count = 0; + const FT limit = scale * step; + + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces + + const Point_3 query = Point_3(x, y, z); + + bar(query, bar_coordinates.begin()); + CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], + vertices[2], vertices[3], query, tetra_coordinates.begin()); + + tests::test_positivity(bar_coordinates); + tests::test_positivity(tetra_coordinates); + + tests::test_partition_of_unity(bar_coordinates); + tests::test_partition_of_unity(tetra_coordinates); + + tests::test_linear_precision(bar_coordinates, vertices, query); + tests::test_linear_precision(tetra_coordinates, vertices, query); + + assert( + CGAL::abs(tetra_coordinates[count + 0] - bar_coordinates[count + 0]) < tol && + CGAL::abs(tetra_coordinates[count + 1] - bar_coordinates[count + 1]) < tol && + CGAL::abs(tetra_coordinates[count + 2] - bar_coordinates[count + 2]) < tol && + CGAL::abs(tetra_coordinates[count + 3] - bar_coordinates[count + 3]) < tol); + } + } + } + +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "Wachspress: " << std::endl; + test_overload(); + std::cout << "Wachspress_tetrahedron PASSED" << std::endl; + + std::cout << "Discrete Harmonic: " << std::endl; + test_overload(); + std::cout << "Discrete_harmonic_tetrahedron PASSED" << std::endl; + + std::cout << "Mean Value: " << std::endl; + test_overload(); + std::cout << "Mean_value_tetrahedron PASSED" << std::endl; + + std::cout << "test_wp_dh_mv_tetrahedron: PASSED" << std::endl; + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp deleted file mode 100644 index f7b4ef066cfc..000000000000 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_tetrahedron.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include - -#include -#include -#include - -#include "include/utils.h" - -//Typedefs -using Kernel = CGAL::Simple_cartesian; - -int main(){ - - using FT = typename Kernel::FT; - using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; - - // Set cout precision - std::cout.precision(20); - - // Regular tetrahedron - Mesh tetrahedron; - std::vector vertices; - std::tie(tetrahedron, vertices) = tests::get_irregular_tetrahedron(); - - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 ws(tetrahedron); - - std::vector tetra_coordinates; - std::vector wp_coordinates; - - // Sample points - const FT step = FT(1) / FT(20); - const FT scale = FT(10); - const FT tol = tests::get_tolerance(); - - std::size_t count = 0; - const FT limit = scale * step; - - for(FT x = step; x < limit; x += step){ - for(FT y = step; y < limit; y += step){ - for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces - - const Point_3 query = Point_3(x, y, z); - - wp_coordinates.clear(); - tetra_coordinates.clear(); - tetra_coordinates.resize(4); - wp_coordinates.resize(4); - - ws(query, wp_coordinates.begin()); - CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], - vertices[2], vertices[3], query, tetra_coordinates.begin()); - - tests::test_positivity(wp_coordinates); - tests::test_positivity(tetra_coordinates); - - tests::test_partition_of_unity(wp_coordinates); - tests::test_partition_of_unity(tetra_coordinates); - - tests::test_linear_precision(wp_coordinates, vertices, query); - tests::test_linear_precision(tetra_coordinates, vertices, query); - - assert( - CGAL::abs(tetra_coordinates[count + 0] - wp_coordinates[count + 0]) < tol && - CGAL::abs(tetra_coordinates[count + 1] - wp_coordinates[count + 1]) < tol && - CGAL::abs(tetra_coordinates[count + 2] - wp_coordinates[count + 2]) < tol && - CGAL::abs(tetra_coordinates[count + 3] - wp_coordinates[count + 3]) < tol); - } - } - } - - std::cout << "test_wp_tetrahedron: PASSED" << std::endl; - return EXIT_SUCCESS; -} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index ef752725d967..4bc622e9632b 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -24,6 +24,7 @@ void test_overloads() { std::vector cube_coords; std::tie(cube, cube_coords) = tests::get_hexahedron(); + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); const FT step = FT(1) / FT(10); From d35eb958ec55c7dff96a590baf802988c8f57b95 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 8 Jul 2021 20:28:14 -0300 Subject: [PATCH 33/68] modifying api to handle edge cases --- Barycentric_coordinates_3/benchmark/bench.md | 6 ++ .../Wachspress_coordinates_3.h | 37 ++++++++ .../barycentric_enum_3.h | 4 +- .../internal/utils_3.h | 7 ++ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_wp_edge_cases.cpp | 84 +++++++++++++++++++ 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 Barycentric_coordinates_3/benchmark/bench.md create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp diff --git a/Barycentric_coordinates_3/benchmark/bench.md b/Barycentric_coordinates_3/benchmark/bench.md new file mode 100644 index 000000000000..fa001609b1f9 --- /dev/null +++ b/Barycentric_coordinates_3/benchmark/bench.md @@ -0,0 +1,6 @@ +Benchmark July 7: + +Here are the results for a triangulated cube, with 1000000 points sampled regularly: +1) Wachspress : benchmark_polyhedron_8_vertices (CPU time): 59.1783669 seconds +2) Discrete Harmonic : benchmark_polyhedron_8_vertices (CPU time): 167.832063 seconds +3) Mean Value : benchmark_polyhedron_8_vertices (CPU time): 199.651994 seconds \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 6c4d915af18a..b8c683d8a960 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -101,6 +101,36 @@ namespace Barycentric_coordinates { OutputIterator compute( const Point_3& query, OutputIterator coordinates) { + switch(m_computation_policy){ + + case Computation_policy_3::DEFAULT:{ + return compute_coords(query, coordinates); + } + + case Computation_policy_3::WITH_EDGE_CASES:{ + const auto edge_case = verify(query, coordinates); + if (edge_case == internal::Edge_case::BOUNDARY) { + return coordinates; + } + if (edge_case == internal::Edge_case::EXTERIOR) { + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + } + return compute_coords(query, coordinates); + } + + default:{ + internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + return coordinates; + } + } + return coordinates; + } + + template + OutputIterator compute_coords( + const Point_3& query, OutputIterator coordinates){ + // Compute weights. const FT sum = compute_weights(query); CGAL_assertion(sum != FT(0)); @@ -119,6 +149,13 @@ namespace Barycentric_coordinates { return coordinates; } + template + internal::Edge_case verify( + const Point_3& query, OutIterator output){ + + return internal::Edge_case::INTERIOR; + } + FT compute_weights(const Point_3& query) { // Sum of weights to normalize them later. diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h index cf8b5bbb29f2..7fca4e5fe132 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -21,7 +21,9 @@ namespace Barycentric_coordinates { enum class Computation_policy_3 { - DEFAULT = 0 + DEFAULT = 0, + WITH_EDGE_CASES = 1 + }; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 389f4e38f3ec..484b65082f60 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -27,6 +27,13 @@ namespace CGAL{ namespace Barycentric_coordinates{ namespace internal{ +enum class Edge_case { + + EXTERIOR = 0, // exterior part of the polygon + BOUNDARY = 1, // boundary part of the polygon + INTERIOR = 2 // interior part of the polygon +}; + //Default sqrt template class Default_sqrt{ diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 8cc0286c1662..c48af75c6ba9 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -19,6 +19,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") create_single_source_cgal_program("test_voronoi_weights.cpp") + create_single_source_cgal_program("test_wp_edge_cases.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp new file mode 100644 index 000000000000..84d70f2ce434 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp @@ -0,0 +1,84 @@ +#include +#include +#include + +#include +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; + +template +void test_overloads() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = CGAL::Surface_mesh; + + // Cube + Mesh cube; + std::vector cube_coords; + + std::tie(cube, cube_coords) = tests::get_hexahedron(); + + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube, CP3::WITH_EDGE_CASES); + + const FT step = FT(1) / FT(10); + const FT scale = FT(10); + const FT limit = step*scale; + + std::vector wp_coordinates_cube; + wp_coordinates_cube.resize(8); + + // Test cube + //Check for barycenter + wp_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), wp_coordinates_cube.begin()); + tests::test_barycenter(wp_coordinates_cube); + + // Sample interior points + for(FT x = step; x < limit; x += step){ + for(FT y = step; y < limit; y += step){ + for(FT z = step; z < limit; z += step){ + + const Point_3 query(x, y, z); + wp_cube(query, wp_coordinates_cube.begin()); + + tests::test_linear_precision(wp_coordinates_cube, cube_coords, query); + tests::test_partition_of_unity(wp_coordinates_cube); + tests::test_positivity(wp_coordinates_cube); + + CGAL::Barycentric_coordinates::wachspress_coordinates_3( + cube, query, wp_coordinates_cube.begin()); + } + } + } + +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + /* + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + */ + + return EXIT_SUCCESS; +} \ No newline at end of file From 25d9db8f6e052dd41189459325a88c0a0c12d886 Mon Sep 17 00:00:00 2001 From: Dmitry Anisimov Date: Fri, 9 Jul 2021 10:28:05 +0200 Subject: [PATCH 34/68] make it work with arbitrary face graphs + fixed warnings --- .../include/CGAL/Barycentric_coordinates_3.h | 2 +- .../Discrete_harmonic_coordinates_3.h | 2 +- .../Mean_value_coordinates_3.h | 6 +- .../Voronoi_coordinates_3.h | 10 +- .../Wachspress_coordinates_3.h | 4 +- .../Barycentric_coordinates_3/CMakeLists.txt | 2 + .../test_containers.cpp | 99 +++++++++++++++++++ 7 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index 16a6a0948847..f62fdbd0eb6c 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -16,7 +16,7 @@ // #include -#include +#include #include #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index cc76194056d5..f22d742840d6 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -147,7 +147,7 @@ namespace Barycentric_coordinates { // Circulator of faces around the vertex CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); CGAL::Face_around_target_circulator face_done(face_circulator); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 8a9f99f5dbef..dee6bc343a02 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -57,8 +57,8 @@ namespace Barycentric_coordinates { m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), - m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()), - sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ + sqrt(internal::Get_sqrt::sqrt_object(m_traits)), + m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()) { // Check if polyhedron is strongly convex CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); @@ -162,7 +162,7 @@ namespace Barycentric_coordinates { // Circulator of faces around the vertex CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); CGAL::Face_around_target_circulator face_done(face_circulator); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h index e34d4c4a5a14..de204172f56f 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h @@ -57,8 +57,8 @@ namespace Barycentric_coordinates { m_construct_vector_3(m_traits.construct_vector_3_object()), m_cross_3(m_traits.construct_cross_product_vector_3_object()), m_dot_3(m_traits.compute_scalar_product_3_object()), - m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()), - sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ + sqrt(internal::Get_sqrt::sqrt_object(m_traits)), + m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()) { // Check if polyhedron is strongly convex CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); @@ -149,7 +149,7 @@ namespace Barycentric_coordinates { FT compute_mv_vertex_query(const Vertex& vertex, const Point_3& query){ // Map vertex descriptor to point_3 - const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); + // const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); // Circulator of faces around the vertex CGAL::Face_around_target_circulator @@ -171,7 +171,7 @@ namespace Barycentric_coordinates { CGAL_precondition(vertices.size() == 3); // Weight of vertex for this particular face - FT partial_weight = FT(0); + // FT partial_weight = FT(0); int vertex_idx = -1; // Store useful information @@ -195,7 +195,7 @@ namespace Barycentric_coordinates { // Current vertex should be present in face assert(vertex_idx != -1); - for(std::size_t i = 0; i < 3; i++){ + for(int i = 0; i < 3; i++){ m_vectors[i] = m_cross_3(query_vertex_vectors[i], query_vertex_vectors[(i+1)%3]); assert(m_vectors[i].squared_length() > 0); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index b8c683d8a960..5b966f529444 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -151,7 +151,7 @@ namespace Barycentric_coordinates { template internal::Edge_case verify( - const Point_3& query, OutIterator output){ + const Point_3& /*query*/, OutIterator /*output*/){ return internal::Edge_case::INTERIOR; } @@ -189,7 +189,7 @@ namespace Barycentric_coordinates { // Circulator of faces around the vertex CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); + face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); CGAL::Face_around_target_circulator done(face_circulator); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index c48af75c6ba9..1c358bffb33d 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -20,6 +20,8 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_mv_weights.cpp") create_single_source_cgal_program("test_voronoi_weights.cpp") create_single_source_cgal_program("test_wp_edge_cases.cpp") + create_single_source_cgal_program("test_containers.cpp") + else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp new file mode 100644 index 000000000000..225ee5c373f1 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp @@ -0,0 +1,99 @@ +#include +#include +#include + +#include +#include +#include +#include + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_container(const Mesh& mesh, OuputContainer& coordinates) { + + namespace BC = CGAL::Barycentric_coordinates; + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + + const FT h = FT(1) / FT(2); + const Point_3 centroid(h, h, h); + + coordinates.clear(); + BC::Wachspress_coordinates_3 wp(mesh); + wp(centroid, std::back_inserter(coordinates)); + assert(coordinates.front() >= FT(0)); + assert(coordinates.back() >= FT(0)); + + coordinates.clear(); + BC::Discrete_harmonic_coordinates_3 dh(mesh); + dh(centroid, std::back_inserter(coordinates)); + assert(coordinates.front() >= FT(0)); + assert(coordinates.back() >= FT(0)); + + coordinates.clear(); + BC::Mean_value_coordinates_3 mv(mesh); + mv(centroid, std::back_inserter(coordinates)); + assert(coordinates.front() >= FT(0)); + assert(coordinates.back() >= FT(0)); +} + +template +void test_containers() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + namespace PMP = CGAL::Polygon_mesh_processing; + + using Polyhedron = CGAL::Polyhedron_3; + using Surface_mesh = CGAL::Surface_mesh; + + using LCoords = std::list; + using VCoords = std::vector; + + Polyhedron polyhedron; + Surface_mesh surface_mesh; + + const Point_3 p0(0, 0, 0); + const Point_3 p1(1, 0, 0); + const Point_3 p2(1, 1, 0); + const Point_3 p3(0, 1, 0); + + const Point_3 p4(0, 1, 1); + const Point_3 p5(0, 0, 1); + const Point_3 p6(1, 0, 1); + const Point_3 p7(1, 1, 1); + + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, polyhedron); + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, surface_mesh); + + PMP::triangulate_faces(faces(polyhedron), polyhedron); + PMP::triangulate_faces(faces(surface_mesh), surface_mesh); + + LCoords lcoords; + VCoords vcoords; + + test_container(polyhedron, lcoords); + test_container(polyhedron, vcoords); + + test_container(surface_mesh, lcoords); + test_container(surface_mesh, vcoords); +} + +int main() { + + std::cout << "SCKER test :" << std::endl; + test_containers(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_containers(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_containers(); + std::cout << "EPECK PASSED" << std::endl; +} From e1cd6921fc691afbabb50ef8da8cbe1cd02abbca Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 13 Jul 2021 04:29:01 -0300 Subject: [PATCH 35/68] adding test to verify boundary --- .../Wachspress_coordinates_3.h | 10 +++- .../internal/utils_3.h | 54 +++++++++++++++++++ .../test_wp_edge_cases.cpp | 27 ++-------- 3 files changed, 66 insertions(+), 25 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 5b966f529444..f6cd656021ef 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -110,17 +110,23 @@ namespace Barycentric_coordinates { case Computation_policy_3::WITH_EDGE_CASES:{ const auto edge_case = verify(query, coordinates); if (edge_case == internal::Edge_case::BOUNDARY) { + std::cout << "Boundary \n"; return coordinates; } if (edge_case == internal::Edge_case::EXTERIOR) { std::cerr << std::endl << "WARNING: query does not belong to the polygon!" << std::endl; + std::cout << "EXTERIOR \n"; } + else + std::cout << "Interior \n"; + return compute_coords(query, coordinates); } default:{ internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + std::cout << "Default \n"; return coordinates; } } @@ -151,9 +157,9 @@ namespace Barycentric_coordinates { template internal::Edge_case verify( - const Point_3& /*query*/, OutIterator /*output*/){ + const Point_3& query, OutIterator output){ - return internal::Edge_case::INTERIOR; + return internal::locate_query_edge(m_vertex_to_point_map, m_polygon_mesh, query, m_traits); } FT compute_weights(const Point_3& query) { diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 484b65082f60..c3dbc4029c52 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -83,6 +83,11 @@ template } } + template + FT get_tolerance() { + return FT(1) / FT(10000000000); + } + // Compute barycentric coordinates in the space. template< typename OutputIterator, @@ -189,6 +194,55 @@ template return approximate_dot_3/approximate_cross_3_length; } + // Determine if the query point is on the interior, exterior or boundary + template< + typename VertexToPointMap, + typename PolygonMesh, + typename GeomTraits> + Edge_case locate_query_edge( + const VertexToPointMap& vertex_to_point_map, + const PolygonMesh& polygon_mesh, + const typename GeomTraits::Point_3& query, + const GeomTraits& traits){ + + using Point_3 = typename GeomTraits::Point_3; + using Vector_3 = typename GeomTraits::Vector_3; + using FT = typename GeomTraits::FT; + const auto& dot_3 = traits.compute_scalar_product_3_object(); + const auto& construct_vector_3 = traits.construct_vector_3_object(); + + const FT tol = get_tolerance(); + auto face_range = faces(polygon_mesh); + + for(auto& face : face_range){ + + const auto hedge = halfedge(face, polygon_mesh); + const auto vertices = vertices_around_face(hedge, polygon_mesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const auto vertex_val = get(vertex_to_point_map, *vertex); + + // Vector connecting query point to vertex; + const Vector_3 query_vertex = construct_vector_3(query, vertex_val); + + // Calculate normals of faces + const Vector_3 face_normal_i = get_face_normal( + face, vertex_to_point_map, polygon_mesh, traits); + + // Distance of query to face + const FT perp_dist_i = dot_3(query_vertex, face_normal_i); + + // Verify location of query point; + if(CGAL::abs(perp_dist_i) < tol) + return Edge_case::BOUNDARY; + else if(perp_dist_i < 0) + return Edge_case::EXTERIOR; + } + + //Default case + return Edge_case::INTERIOR; + } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp index 84d70f2ce434..0b4f4a03d921 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp @@ -29,35 +29,16 @@ void test_overloads() { CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube, CP3::WITH_EDGE_CASES); - const FT step = FT(1) / FT(10); - const FT scale = FT(10); - const FT limit = step*scale; - std::vector wp_coordinates_cube; wp_coordinates_cube.resize(8); - // Test cube - //Check for barycenter - wp_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), wp_coordinates_cube.begin()); - tests::test_barycenter(wp_coordinates_cube); - - // Sample interior points - for(FT x = step; x < limit; x += step){ - for(FT y = step; y < limit; y += step){ - for(FT z = step; z < limit; z += step){ + wp_cube(Point_3(10.0, 10.0, 10.0), wp_coordinates_cube.begin()); - const Point_3 query(x, y, z); - wp_cube(query, wp_coordinates_cube.begin()); + for(auto u : wp_coordinates_cube){ + std::cout << u << " \n"; + } - tests::test_linear_precision(wp_coordinates_cube, cube_coords, query); - tests::test_partition_of_unity(wp_coordinates_cube); - tests::test_positivity(wp_coordinates_cube); - CGAL::Barycentric_coordinates::wachspress_coordinates_3( - cube, query, wp_coordinates_cube.begin()); - } - } - } } From 53cd8eb321c0b17d3b8a177b882494a1f28d0e62 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 15 Jul 2021 05:09:56 -0300 Subject: [PATCH 36/68] first functional version of edge cases(only wp now) --- .../Wachspress_coordinates_3.h | 7 +- .../internal/utils_3.h | 65 ++++++++++++++++++- .../test_wp_edge_cases.cpp | 18 +++-- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index f6cd656021ef..40d3fb3413ed 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -43,6 +43,8 @@ namespace Barycentric_coordinates { typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Point_3 Point_3; typedef typename GeomTraits::Vector_3 Vector_3; + typedef typename GeomTraits::Plane_3 Plane_3; + typedef typename GeomTraits::Point_2 Point_2; public: Wachspress_coordinates_3( @@ -108,7 +110,10 @@ namespace Barycentric_coordinates { } case Computation_policy_3::WITH_EDGE_CASES:{ - const auto edge_case = verify(query, coordinates); + // Calculate query position relative to the polyhedron + const auto edge_case = internal::locate_query_edge( + m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + if (edge_case == internal::Edge_case::BOUNDARY) { std::cout << "Boundary \n"; return coordinates; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index c3dbc4029c52..b7955d073170 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -22,6 +22,7 @@ // Internal includes #include #include +#include namespace CGAL{ namespace Barycentric_coordinates{ @@ -85,7 +86,7 @@ template template FT get_tolerance() { - return FT(1) / FT(10000000000); + return FT(1) / FT(100000); } // Compute barycentric coordinates in the space. @@ -194,15 +195,72 @@ template return approximate_dot_3/approximate_cross_3_length; } + template< + typename Face, + typename VertexToPointMap, + typename PolygonMesh, + typename OutIterator, + typename GeomTraits> + OutIterator boundary_coordinates_3( + const Face& face, + const VertexToPointMap& vertex_to_point_map, + const PolygonMesh& polygon_mesh, + const typename GeomTraits::Point_3& query, + OutIterator coordinates, + const GeomTraits& traits){ + + using Point_3 = typename GeomTraits::Point_3; + using Vector_3 = typename GeomTraits::Vector_3; + using FT = typename GeomTraits::FT; + using Plane_3 = typename GeomTraits::Plane_3; + using Point_2 = typename GeomTraits::Point_2; + + const auto hedge = halfedge(face, polygon_mesh); + const auto vertices_face = vertices_around_face(hedge, polygon_mesh); + CGAL_precondition(vertices_face.size() >= 3); + auto vertex = vertices_face.begin(); + + const auto v0 = *vertex; vertex++; + const auto v1 = *vertex; vertex++; + const auto v2 = *vertex; + + const Plane_3 plane_face(get(vertex_to_point_map, v0), get(vertex_to_point_map, v1), get(vertex_to_point_map, v2)); + const Point_2 query_2d = plane_face.to_2d(query); + const Point_2 v0_2d = plane_face.to_2d(get(vertex_to_point_map, v0)); + const Point_2 v1_2d = plane_face.to_2d(get(vertex_to_point_map, v1)); + const Point_2 v2_2d = plane_face.to_2d(get(vertex_to_point_map, v2)); + + std::array coordinates_2d = compute_triangle_coordinates_2( + v0_2d, v1_2d, v2_2d, query_2d, traits); + + const auto vd = vertices(polygon_mesh); + for(const auto& v : vd){ + + if(v == v0) + *coordinates = coordinates_2d[0]; + else if(v == v1) + *coordinates = coordinates_2d[1]; + else if(v == v2) + *coordinates = coordinates_2d[2]; + else + *coordinates = 0; + + coordinates++; + } + return coordinates; + } + // Determine if the query point is on the interior, exterior or boundary template< typename VertexToPointMap, typename PolygonMesh, + typename OutIterator, typename GeomTraits> Edge_case locate_query_edge( const VertexToPointMap& vertex_to_point_map, const PolygonMesh& polygon_mesh, const typename GeomTraits::Point_3& query, + OutIterator coordinates, const GeomTraits& traits){ using Point_3 = typename GeomTraits::Point_3; @@ -234,8 +292,11 @@ template const FT perp_dist_i = dot_3(query_vertex, face_normal_i); // Verify location of query point; - if(CGAL::abs(perp_dist_i) < tol) + if(CGAL::abs(perp_dist_i) < tol){ + + boundary_coordinates_3(face, vertex_to_point_map, polygon_mesh, query, coordinates, traits); return Edge_case::BOUNDARY; + } else if(perp_dist_i < 0) return Edge_case::EXTERIOR; } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp index 0b4f4a03d921..32e6758231f9 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp @@ -22,24 +22,22 @@ void test_overloads() { using Mesh = CGAL::Surface_mesh; // Cube - Mesh cube; - std::vector cube_coords; + Mesh tetrahedron; + std::vector tetrahedron_coords; - std::tie(cube, cube_coords) = tests::get_hexahedron(); + std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube, CP3::WITH_EDGE_CASES); + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_tetrahedron(tetrahedron, CP3::WITH_EDGE_CASES); - std::vector wp_coordinates_cube; - wp_coordinates_cube.resize(8); + std::vector wp_coordinates_tetrahedron; + wp_coordinates_tetrahedron.resize(4); - wp_cube(Point_3(10.0, 10.0, 10.0), wp_coordinates_cube.begin()); + wp_tetrahedron(Point_3(0.05, 0.1, 0.85), wp_coordinates_tetrahedron.begin()); - for(auto u : wp_coordinates_cube){ + for(auto u : wp_coordinates_tetrahedron){ std::cout << u << " \n"; } - - } int main(){ From b630fb6f0cad1120388f9d23d6f65256440061ae Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 19 Jul 2021 01:26:47 -0300 Subject: [PATCH 37/68] final version edge cases --- .../Wachspress_coordinates_3.h | 20 ++++------ .../internal/utils_3.h | 38 +++++++++---------- .../Barycentric_coordinates_3/include/utils.h | 36 ++++++++++++++---- .../test_dh_weights.cpp | 10 +++-- .../test_mv_weights.cpp | 12 +++--- .../test_wp_dh_mv_tetrahedron.cpp | 8 ++-- .../test_wp_edge_cases.cpp | 29 ++++++++------ .../test_wp_weights.cpp | 12 ++++-- 8 files changed, 97 insertions(+), 68 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 40d3fb3413ed..a0ab076ced6b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -111,27 +111,22 @@ namespace Barycentric_coordinates { case Computation_policy_3::WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron - const auto edge_case = internal::locate_query_edge( + const auto edge_case = internal::locate_wrt_polyhedron( m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); if (edge_case == internal::Edge_case::BOUNDARY) { - std::cout << "Boundary \n"; return coordinates; } if (edge_case == internal::Edge_case::EXTERIOR) { std::cerr << std::endl << "WARNING: query does not belong to the polygon!" << std::endl; - std::cout << "EXTERIOR \n"; } - else - std::cout << "Interior \n"; return compute_coords(query, coordinates); } default:{ internal::get_default(vertices(m_polygon_mesh).size(), coordinates); - std::cout << "Default \n"; return coordinates; } } @@ -160,13 +155,6 @@ namespace Barycentric_coordinates { return coordinates; } - template - internal::Edge_case verify( - const Point_3& query, OutIterator output){ - - return internal::locate_query_edge(m_vertex_to_point_map, m_polygon_mesh, query, m_traits); - } - FT compute_weights(const Point_3& query) { // Sum of weights to normalize them later. @@ -222,6 +210,12 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ + + // Check if it is a triangular mesh + const auto hedge = halfedge(*face_circulator, m_polygon_mesh); + const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + CGAL_precondition(vertices.size() == 3); + // Calculate normals of faces const Vector_3 face_normal_i = internal::get_face_normal( *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index b7955d073170..148131fb2716 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -86,7 +86,7 @@ template template FT get_tolerance() { - return FT(1) / FT(100000); + return FT(1) / FT(10000000000); } // Compute barycentric coordinates in the space. @@ -196,13 +196,13 @@ template } template< - typename Face, + typename VertexRange, typename VertexToPointMap, typename PolygonMesh, typename OutIterator, typename GeomTraits> OutIterator boundary_coordinates_3( - const Face& face, + VertexRange vertex, const VertexToPointMap& vertex_to_point_map, const PolygonMesh& polygon_mesh, const typename GeomTraits::Point_3& query, @@ -215,16 +215,14 @@ template using Plane_3 = typename GeomTraits::Plane_3; using Point_2 = typename GeomTraits::Point_2; - const auto hedge = halfedge(face, polygon_mesh); - const auto vertices_face = vertices_around_face(hedge, polygon_mesh); - CGAL_precondition(vertices_face.size() >= 3); - auto vertex = vertices_face.begin(); - const auto v0 = *vertex; vertex++; const auto v1 = *vertex; vertex++; const auto v2 = *vertex; - const Plane_3 plane_face(get(vertex_to_point_map, v0), get(vertex_to_point_map, v1), get(vertex_to_point_map, v2)); + const FT tol = get_tolerance(); + + const Plane_3 plane_face(get(vertex_to_point_map, v0), + get(vertex_to_point_map, v1), get(vertex_to_point_map, v2)); const Point_2 query_2d = plane_face.to_2d(query); const Point_2 v0_2d = plane_face.to_2d(get(vertex_to_point_map, v0)); const Point_2 v1_2d = plane_face.to_2d(get(vertex_to_point_map, v1)); @@ -237,13 +235,13 @@ template for(const auto& v : vd){ if(v == v0) - *coordinates = coordinates_2d[0]; + *coordinates = CGAL::abs(coordinates_2d[0]) < tol? 0: coordinates_2d[0]; else if(v == v1) - *coordinates = coordinates_2d[1]; + *coordinates = CGAL::abs(coordinates_2d[1]) < tol? 0: coordinates_2d[1]; else if(v == v2) - *coordinates = coordinates_2d[2]; + *coordinates = CGAL::abs(coordinates_2d[2]) < tol? 0: coordinates_2d[2]; else - *coordinates = 0; + *coordinates = FT(0); coordinates++; } @@ -256,7 +254,7 @@ template typename PolygonMesh, typename OutIterator, typename GeomTraits> - Edge_case locate_query_edge( + Edge_case locate_wrt_polyhedron( const VertexToPointMap& vertex_to_point_map, const PolygonMesh& polygon_mesh, const typename GeomTraits::Point_3& query, @@ -268,6 +266,7 @@ template using FT = typename GeomTraits::FT; const auto& dot_3 = traits.compute_scalar_product_3_object(); const auto& construct_vector_3 = traits.construct_vector_3_object(); + const auto& sqrt(Get_sqrt::sqrt_object(traits)); const FT tol = get_tolerance(); auto face_range = faces(polygon_mesh); @@ -275,18 +274,19 @@ template for(auto& face : face_range){ const auto hedge = halfedge(face, polygon_mesh); - const auto vertices = vertices_around_face(hedge, polygon_mesh); - CGAL_precondition(vertices.size() >= 3); + const auto vertices_face = vertices_around_face(hedge, polygon_mesh); + CGAL_precondition(vertices_face.size() >= 3); - auto vertex = vertices.begin(); + auto vertex = vertices_face.begin(); const auto vertex_val = get(vertex_to_point_map, *vertex); // Vector connecting query point to vertex; const Vector_3 query_vertex = construct_vector_3(query, vertex_val); // Calculate normals of faces - const Vector_3 face_normal_i = get_face_normal( + Vector_3 face_normal_i = get_face_normal( face, vertex_to_point_map, polygon_mesh, traits); + face_normal_i = face_normal_i / sqrt(face_normal_i.squared_length()); // Distance of query to face const FT perp_dist_i = dot_3(query_vertex, face_normal_i); @@ -294,7 +294,7 @@ template // Verify location of query point; if(CGAL::abs(perp_dist_i) < tol){ - boundary_coordinates_3(face, vertex_to_point_map, polygon_mesh, query, coordinates, traits); + boundary_coordinates_3(vertex, vertex_to_point_map, polygon_mesh, query, coordinates, traits); return Edge_case::BOUNDARY; } else if(perp_dist_i < 0) diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index c9631163a9e2..75965dbc582f 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -9,6 +9,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + namespace tests{ template @@ -32,13 +40,14 @@ namespace tests{ } template - std::pair> get_regular_tetrahedron(){ + std::pair> get_regular_tetrahedron( + typename Kernel::FT scale){ using Point_3 = typename Kernel::Point_3; Mesh tetrahedron0; - std::vector coords = {Point_3(1.0, 1.0, 1.0), Point_3(-1.0, 1.0, -1.0), - Point_3(1.0, -1.0, -1.0), Point_3(-1.0, -1.0, 1.0)}; + std::vector coords = {Point_3(scale, scale, scale), Point_3(-scale, scale, -scale), + Point_3(scale, -scale, -scale), Point_3(-scale, -scale, scale)}; CGAL::make_tetrahedron(coords[0], coords[1], coords[2], coords[3], tetrahedron0); @@ -125,11 +134,24 @@ namespace tests{ assert(coord >= FT(0) && coord <= FT(1)); } - template - OutIterator random_points_triangle_mesh(Mesh& mesh, OutIterator out, int n){ + template + OutIterator random_points_tetrahedron( + std::vector& coords, + OutIterator out, int n){ + + using Tetrahedron_3 = typename Kernel::Tetrahedron_3; + using Mesh = typename CGAL::Surface_mesh; + + CGAL_assertion(coords.size() == 4); + Tetrahedron_3 tetra_inside(coords[0], coords[1], coords[2], coords[3]); + Mesh tetra_surf; + CGAL::make_tetrahedron(coords[0], coords[1], + coords[2], coords[3], tetra_surf); - CGAL::Random_points_in_triangle_mesh_3 gen(mesh); - std::copy_n(gen, n, out); + CGAL::Random_points_in_tetrahedron_3 gen_in(tetra_inside); + CGAL::Random_points_in_triangle_mesh_3 gen_surf(tetra_surf); + std::copy_n(gen_in, n/2, out); + std::copy_n(gen_surf, n/2, out); return out; } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index 2f45825c658e..5d1aa3c8c958 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -31,11 +31,11 @@ void test_overloads() { const FT limit = step*scale; std::vector dh_coordinates_tetrahedron; - dh_coordinates_tetrahedron.resize(4); // Test cube //Check for barycenter - dh_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), dh_coordinates_tetrahedron.begin()); + dh_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), + std::back_inserter(dh_coordinates_tetrahedron)); tests::test_barycenter(dh_coordinates_tetrahedron); // Sample interior points @@ -44,13 +44,15 @@ void test_overloads() { for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces const Point_3 query(x, y, z); - dh_tetrahedron(query, dh_coordinates_tetrahedron.begin()); + dh_coordinates_tetrahedron.clear(); + dh_tetrahedron(query, std::back_inserter(dh_coordinates_tetrahedron)); tests::test_linear_precision(dh_coordinates_tetrahedron, tetrahedron_coords, query); tests::test_partition_of_unity(dh_coordinates_tetrahedron); + dh_coordinates_tetrahedron.clear(); CGAL::Barycentric_coordinates::discrete_harmonic_coordinates_3( - tetrahedron, query, dh_coordinates_tetrahedron.begin()); + tetrahedron, query, std::back_inserter(dh_coordinates_tetrahedron)); } } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp index f06072112ff4..eac0646a8cbe 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp @@ -26,13 +26,13 @@ void test_overloads() { std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); CGAL::Barycentric_coordinates::Mean_value_coordinates_3 mv_tetrahedron(tetrahedron); - tests::random_points_triangle_mesh(tetrahedron, std::back_inserter(sample_points), 100000); + tests::random_points_tetrahedron(tetrahedron_coords, std::back_inserter(sample_points), 1000); std::vector mv_coordinates_tetrahedron; - mv_coordinates_tetrahedron.resize(4); //Check for barycenter - mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), mv_coordinates_tetrahedron.begin()); + mv_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), + std::back_inserter(mv_coordinates_tetrahedron)); tests::test_barycenter(mv_coordinates_tetrahedron); for(auto& point : sample_points){ @@ -45,14 +45,16 @@ void test_overloads() { continue; const Point_3 query(x, y, z); - mv_tetrahedron(query, mv_coordinates_tetrahedron.begin()); + mv_coordinates_tetrahedron.clear(); + mv_tetrahedron(query, std::back_inserter(mv_coordinates_tetrahedron)); tests::test_linear_precision(mv_coordinates_tetrahedron, tetrahedron_coords, query); tests::test_partition_of_unity(mv_coordinates_tetrahedron); tests::test_positivity(mv_coordinates_tetrahedron); + mv_coordinates_tetrahedron.clear(); CGAL::Barycentric_coordinates::mean_value_coordinates_3( - tetrahedron, query, mv_coordinates_tetrahedron.begin()); + tetrahedron, query, std::back_inserter(mv_coordinates_tetrahedron)); } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp index 0ce1ffea48c9..6cac06ef5511 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_dh_mv_tetrahedron.cpp @@ -31,8 +31,6 @@ void test_overload(){ std::vector tetra_coordinates; std::vector bar_coordinates; - tetra_coordinates.resize(4); - bar_coordinates.resize(4); // Sample points const FT step = FT(1) / FT(20); @@ -48,9 +46,11 @@ void test_overload(){ const Point_3 query = Point_3(x, y, z); - bar(query, bar_coordinates.begin()); + bar_coordinates.clear(); + tetra_coordinates.clear(); + bar(query, std::back_inserter(bar_coordinates)); CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], - vertices[2], vertices[3], query, tetra_coordinates.begin()); + vertices[2], vertices[3], query, std::back_inserter(tetra_coordinates)); tests::test_positivity(bar_coordinates); tests::test_positivity(tetra_coordinates); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp index 32e6758231f9..673e913b72bf 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp @@ -19,25 +19,32 @@ void test_overloads() { using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; - using Mesh = CGAL::Surface_mesh; + using Mesh = typename CGAL::Surface_mesh; - // Cube + // tetrahedron Mesh tetrahedron; std::vector tetrahedron_coords; + std::vector sample_points; - std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); + std::tie(tetrahedron, tetrahedron_coords) = tests::get_regular_tetrahedron(FT(1.0)); - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_tetrahedron(tetrahedron, CP3::WITH_EDGE_CASES); + CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_tetrahedron( + tetrahedron, CP3::WITH_EDGE_CASES); + tests::random_points_tetrahedron(tetrahedron_coords, + std::back_inserter(sample_points), 10000); std::vector wp_coordinates_tetrahedron; - wp_coordinates_tetrahedron.resize(4); - wp_tetrahedron(Point_3(0.05, 0.1, 0.85), wp_coordinates_tetrahedron.begin()); + for(auto& point : sample_points){ - for(auto u : wp_coordinates_tetrahedron){ - std::cout << u << " \n"; - } + const Point_3 query(point.x(), point.y(), point.z()); + wp_coordinates_tetrahedron.clear(); + wp_tetrahedron(query, std::back_inserter(wp_coordinates_tetrahedron)); + tests::test_linear_precision(wp_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(wp_coordinates_tetrahedron); + tests::test_positivity(wp_coordinates_tetrahedron); + } } int main(){ @@ -49,7 +56,6 @@ int main(){ test_overloads(); std::cout << "SCKER PASSED" << std::endl; - /* std::cout << "EPICK test :" << std::endl; test_overloads(); std::cout << "EPICK PASSED" << std::endl; @@ -57,7 +63,6 @@ int main(){ std::cout << "EPECK test :" << std::endl; test_overloads(); std::cout << "EPECK PASSED" << std::endl; - */ return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 4bc622e9632b..6dea4c0c7643 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "include/utils.h" @@ -18,12 +19,14 @@ void test_overloads() { using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; using Mesh = CGAL::Surface_mesh; + namespace PMP = CGAL::Polygon_mesh_processing; // Cube Mesh cube; std::vector cube_coords; std::tie(cube, cube_coords) = tests::get_hexahedron(); + PMP::triangulate_faces(faces(cube), cube); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); @@ -32,11 +35,10 @@ void test_overloads() { const FT limit = step*scale; std::vector wp_coordinates_cube; - wp_coordinates_cube.resize(8); // Test cube //Check for barycenter - wp_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), wp_coordinates_cube.begin()); + wp_cube(Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)/FT(2)), std::back_inserter(wp_coordinates_cube)); tests::test_barycenter(wp_coordinates_cube); // Sample interior points @@ -45,14 +47,16 @@ void test_overloads() { for(FT z = step; z < limit; z += step){ const Point_3 query(x, y, z); - wp_cube(query, wp_coordinates_cube.begin()); + wp_coordinates_cube.clear(); + wp_cube(query, std::back_inserter(wp_coordinates_cube)); tests::test_linear_precision(wp_coordinates_cube, cube_coords, query); tests::test_partition_of_unity(wp_coordinates_cube); tests::test_positivity(wp_coordinates_cube); + wp_coordinates_cube.clear(); CGAL::Barycentric_coordinates::wachspress_coordinates_3( - cube, query, wp_coordinates_cube.begin()); + cube, query, std::back_inserter(wp_coordinates_cube)); } } } From 436f6f791355468e6ca221e2282766bf9fded531 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 19 Jul 2021 08:52:29 -0300 Subject: [PATCH 38/68] minor fix --- .../include/CGAL/Barycentric_coordinates_3/internal/utils_3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 148131fb2716..f20430fb064b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -228,7 +228,7 @@ template const Point_2 v1_2d = plane_face.to_2d(get(vertex_to_point_map, v1)); const Point_2 v2_2d = plane_face.to_2d(get(vertex_to_point_map, v2)); - std::array coordinates_2d = compute_triangle_coordinates_2( + const std::array coordinates_2d = compute_triangle_coordinates_2( v0_2d, v1_2d, v2_2d, query_2d, traits); const auto vd = vertices(polygon_mesh); From 83efe5a04ccad2fa2c3bafbacbbae842c627bd4e Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 20 Jul 2021 03:19:56 -0300 Subject: [PATCH 39/68] fixes after review + adding doxygen files --- .../Barycentric_coordinates_3.txt | 842 +++ .../Concepts/BarycentricTraits_2.h | 186 + .../Concepts/DiscretizedDomain_2.h | 70 + .../deprecated/BarycentricCoordinates_2.h | 88 + .../doc/Barycentric_coordinates_3/Doxyfile.in | 8 +- .../PackageDescription.txt | 82 + .../Barycentric_coordinates_3/dependencies | 16 + .../Barycentric_coordinates_3/examples.txt | 14 +- .../fig/aff_coord_example.svg | 81 + .../fig/analytic_timings.png | Bin 0 -> 77272 bytes .../fig/barcoord_thumb.png | Bin 0 -> 14797 bytes .../fig/dh_coord_example.svg | 126 + .../fig/dh_notations.svg | 302 ++ .../fig/hm_4_bench.svg | 371 ++ .../fig/hm_n_bench.svg | 201 + .../fig/mv_coord_example.svg | 135 + .../fig/mv_notations.svg | 153 + .../fig/mv_weight_signs.svg | 173 + .../fig/overview.svg | 260 + .../fig/seg_coord.svg | 69 + .../fig/seg_coord_example.svg | 76 + .../fig/seg_coord_projection.svg | 113 + .../fig/shape_deformation.svg | 1100 ++++ .../Barycentric_coordinates_3/fig/terrain.svg | 195 + .../fig/terrain_interpolation.png | Bin 0 -> 46508 bytes .../fig/terrain_triangulation.svg | 4633 +++++++++++++++++ .../fig/tri_coord.svg | 56 + .../fig/tri_coord_example.svg | 112 + .../fig/tri_notations.svg | 75 + .../fig/wp_coord_example.svg | 174 + .../fig/wp_notations.svg | 226 + .../fig/wp_zero_set.svg | 106 + .../Wachspress_coordinates_3.h | 9 +- .../internal/utils_3.h | 40 +- .../test_wp_edge_cases.cpp | 21 +- 35 files changed, 10090 insertions(+), 23 deletions(-) mode change 100644 => 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h mode change 100644 => 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in mode change 100644 => 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt mode change 100644 => 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies mode change 100644 => 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg create mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt old mode 100644 new mode 100755 index e69de29bb2d1..c1a21a0fcb44 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -0,0 +1,842 @@ +namespace CGAL { +namespace Barycentric_coordinates { + +/*! +\mainpage User Manual +\anchor Chapter_2D_Generalized_Barycentric_Coordinates +\cgalAutoToc + +\authors Dmitry Anisimov, David Bommes, Kai Hormann, and Pierre Alliez + + +\section gbc_introduction Introduction + +Barycentric coordinates are widely used in computer graphics and computational mechanics +to determine a position of a point in the plane with respect to a triangle. These coordinates +have been later generalized to support simple polygons in 2D and polyhedra in 3D. + +This package offers an efficient and robust implementation of 2D generalized barycentric +coordinates defined for simple polygons in the plane. If coordinates with respect to multivariate +scattered points instead of a polygon are required, please refer to natural neighbor coordinates +from the package \ref chapinterpolation "2D and Surface Function Interpolation". + +In particular, this package includes an implementation of \ref wp_example "Wachspress", +\ref dh_example "discrete harmonic", \ref mv_example "mean value", and \ref hm_example "harmonic" +coordinates, and provides some extra functions to compute barycentric coordinates with respect +to \ref seg_example "segments" and \ref tri_example "triangles". + +\cgalFigureBegin{overview, overview.svg} +Wachspress (WP), discrete harmonic (DH), mean value (MV), and harmonic (HM) coordinate functions +for a convex polygon plotted with respect to the marked vertex. +\cgalFigureEnd + + +\section gbc_interface Software Design + +Mean value and harmonic coordinates are the most generic coordinates in this package, +because they allow an arbitrary simple polygon as input. Wachspress and discrete harmonic coordinates +are, by definition, limited to strictly convex polygons. Segment coordinates take as input +any non-degenerate segment, and triangle coordinates allow an arbitrary non-degenerate triangle. + +Wachspress, discrete harmonic, mean value, and harmonic coordinates are all generalized +barycentric coordinates. However, while Wachspress, discrete harmonic, and mean value +coordinates can be computed analytically, harmonic coordinates cannot. They first need +to be approximated over a triangulation of the interior part of the polygon. Once approximated, +they can be evaluated analytically at any point inside the polygon. + +For all analytic coordinates, we provide two algorithms. One has a linear time complexity, +but may suffer imprecisions near the polygon boundary, while the second one is precise +but has a quadratic time complexity. The user can choose the preferred algorithm by +specifying a computation policy `Barycentric_coordinates::Computation_policy_2`. + +All analytic barycentric coordinates for polygons can be computed either by instantiating a class +or through one of the free functions. Harmonic coordinates can be computed only by +instantiating a class that must be parameterized by a model of the concept `DiscretizedDomain_2`. +Segment and triangle coordinates can be computed only through the free functions. +For more information see the \ref PkgBarycentricCoordinates2Ref "Reference Manual". + +Any point in the plane may be taken as a query point. However, we do not recommend using +Wachspress and discrete harmonic coordinates with query points outside the closure +of a polygon, because they are not well-defined for some of these points. The same holds +for harmonic coordinates, which are not defined everywhere outside the polygon. For more +information see Section \ref gbc_degeneracies. + +The output of the computation is a set of coordinate values at the given query point +with respect to the polygon vertices. That means that the number of returned coordinates +per query point equates the number of polygon vertices. The ordering of the coordinates +is the same as the ordering of polygon vertices. + +All class and function templates are parameterized by a traits class, which is a model +of the concept `BarycentricTraits_2`. It provides all necessary geometric primitives, +predicates, and constructions, which are required for the computation. All models of `Kernel` +can be used. A polygon is provided as a range of vertices with a +\ref PkgPropertyMapRef "property map" that maps a vertex from the polygon to `CGAL::Point_2`. + +If you do not know which coordinate function best fits your application, you can address the +table below for some advise. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CoordinatesPropertiesValid domainClosed formQueriesSpeed
SegmentAll2D non-degenerate segmentsYesEverywhere on the supporting line+++
TriangleAll2D non-degenerate trianglesYesEverywhere in 2D+++
Discrete harmonicMay be negativeStrongly convex polygonsYesEverywhere inside the polygon++
WachspressAllStrongly convex polygonsYesEverywhere inside the polygon++
Mean valueMay be negativeSimple polygonsYesEverywhere in 2D++
HarmonicAllSimple polygonsNoEverywhere inside the polygon+
+ +\note This is the second version of the package with the modified and improved API. +The package still supports the old API. See more details \ref depr_example "here". + + +\section gbc_examples Examples + +In order to facilitate the process of learning this package, we provide various examples +with a basic usage of different barycentric components. + + +\subsection seg_example Segment Coordinates + +This example illustrates the use of the global function `segment_coordinates_2()`. +We compute coordinates at three green points along the segment \f$[v_0, v_1]\f$ and at two blue points outside this segment +but along its supporting line. The symmetry of the query points helps recognizing errors that +may have occurred during construction of the example. The used `Kernel` is exact. + +\anchor seg_coord_example +\cgalFigureBegin{seg_example, seg_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\cgalExample{Barycentric_coordinates_2/segment_coordinates.cpp} + + +\subsection tri_example Triangle Coordinates + +In this example, we show how to use the global function `triangle_coordinates_2()`. +We compute coordinates for three sets of points: interior (green), boundary (red), and exterior (blue). +Note that some of the coordinate values for the exterior points are negative. +The used `Kernel` is inexact. + +\anchor tri_coord_example +\cgalFigureBegin{tri_example, tri_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\cgalExample{Barycentric_coordinates_2/triangle_coordinates.cpp} + + +\subsection wp_example Wachspress Coordinates + +In the following example, we generate 100 random points (green/red/black), then we take the +convex hull (red/black) of this set of points as our polygon (black), and compute Wachspress +coordinates at all the generated points. The used `Kernel` is inexact. + +\anchor wp_coord_example +\cgalFigureBegin{wp_example, wp_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\cgalExample{Barycentric_coordinates_2/wachspress_coordinates.cpp} + + +\subsection dh_example Discrete Harmonic Coordinates + +In this example, we compute discrete harmonic coordinates for a set of green (interior), +red (boundary), and blue (exterior) points with respect to a unit square. We also demonstrate +the use of various containers, both random access and serial access, different property maps, +and the ability to choose a computation policy. For points on the polygon boundary, +we use the free function `boundary_coordinates_2()`. +The used `Kernel` is exact. + +\anchor dh_coord_example +\cgalFigureBegin{dh_example, dh_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\cgalExample{Barycentric_coordinates_2/discrete_harmonic_coordinates.cpp} + + +\subsection mv_example Mean Value Coordinates + +This is an example that illustrates how to compute mean value coordinates for a set of green points +in a star-shaped polygon. We note that this type of coordinates is well-defined for such a concave polygon +while Wachspress and discrete harmonic coordinates are not. However, it may yield negative +coordinate values for points outside the polygon's kernel +(shown in red). We speed up the computation using the linear time complexity algorithm by specifying +a computation policy `Barycentric_coordinates::Computation_policy_2`. The used `Kernel` is inexact. + +\anchor mv_coord_example +\cgalFigureBegin{mv_example, mv_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\cgalExample{Barycentric_coordinates_2/mean_value_coordinates.cpp} + + +\subsection hm_example Harmonic Coordinates +This example illustrates how to \ref terrain_triangulation_fig "discretize" the interior part +of the \ref terrain_example_fig "polygon" and compute harmonic coordinates at the vertices +of the discretized domain, which is represented by a 2D Delaunay triangulation. Once computed, +harmonic coordinate functions can be evaluated at any point in the closure of the polygon. +To illustrate such an evaluation, we compute the barycenter of each triangle and evaluate harmonic coordinates +at this barycenter. Since harmonic coordinates can only be approximated, the used `Kernel` is inexact. + +\anchor hm_coord_example +\cgalExample{Barycentric_coordinates_2/harmonic_coordinates.cpp} + + +\subsection height_inter_example Terrain Modeling + +This is an advanced example that illustrates how to use generalized barycentric coordinates +for height interpolation with applications to terrain modeling. It also shows how to use +a non-default traits class with our package instead of a `Kernel` traits class. +Suppose we know the boundary of three-dimensional piece of terrain that can be represented +as a polygon with several three-dimensional vertices, where the third dimension indicates +the corresponding height. The task is to propagate the height from the known sample points +on the boundary to the polygon's interior. This gives an approximate estimation of +the terrain's surface in this region. + +\anchor terrain_example_fig +\cgalFigureBegin{terrain_example, terrain.svg} +A 2D polygon with 50 vertices representing a piece of terrain with convex and concave parts. The height is not shown. +\cgalFigureEnd + +In this example, we project a 3D polygon orthogonally onto the 2D plane using the class +`CGAL::Projection_traits_xy_3`, triangulate its interior using the class `Delaunay_domain_2`, +and compute mean value coordinates at the vertices of this triangulation with respect to the polygon vertices. +Finally, we interpolate the height data from the polygon boundary to its interior +using the computed coordinates and the global interpolation function from the +package \ref chapinterpolation "2D and Surface Function Interpolation". + +\anchor terrain_example +\cgalExample{Barycentric_coordinates_2/terrain_height_modeling.cpp} + +As a result, we get a smooth function inside the polygon that approximates the underlying terrain surface. + +\cgalFigureBegin{terrain_interpolation_example, terrain_interpolation.png} +The interpolated data. The color bar represents the corresponding height. +\cgalFigureEnd + + +\subsection shape_deform_example Shape Deformation +This is another advanced example that shows how to use generalized barycentric coordinates +in order to deform a given 2D shape into another shape as shown in the figure below. +Harmonic coordinates satisfy all the properties of barycentric coordinates for complicated +concave polygons and hence this is our choice to perform a shape deformation. Note that +even though harmonic coordinates are guaranteed to be positive inside a polygon, they +do not guarantee a bijective mapping between the source and target shapes that is +the target mesh can fold over the target polygon after the mapping (see the little fold over +in the left shoulder of the target shape). + +\cgalFigureBegin{shape_deformation_example, shape_deformation.svg} +The shape on the left is deformed into the shape on the right. The zoom shows a fold over +in the left shoulder of the target shape where the red triangle goes over the polygon boundary. +\cgalFigureEnd + +\anchor deformation_example +\cgalExample{Barycentric_coordinates_2/shape_deformation.cpp} + + +\subsection aff_example Affine Coordinates +This is an example, where we show how a +lambda expression +can be used to define a model of generalized barycentric coordinates. To make this example +useful, we implement affine generalized coordinates for a set of scattered points. +The used `Kernel` is inexact. + +\anchor aff_coord_example_fig +\cgalFigureBegin{aff_example, aff_coord_example.svg} +Example's point pattern. +\cgalFigureEnd + +\anchor aff_coord_example +\cgalExample{Barycentric_coordinates_2/affine_coordinates.cpp} + + +\subsection depr_example Deprecated Coordinates +This example illustrates how to quickly update the code in order to use the deprecated +version of mean value coordinates that is the version below 5.2. Basically, one +needs to add the suffix `_depr` to the namespace and include the corresponding +deprecated headers. The used `Kernel` is inexact and the used coordinates are +mean value coordinates. The result is identical to the one from +\ref mv_coord_example "this example". + +\anchor depr_coord_example +\cgalExample{Barycentric_coordinates_2/deprecated_coordinates.cpp} + + +\section gbc_degeneracies Edge Cases + +Not all presented coordinates are general enough to handle any query point in the plane, that is +why we highly recommend reading this section in order to learn what can be expected from +each coordinate function. If you want to get more mathematical details about each coordinate +function as well as the complete history and theory behind barycentric coordinates, you should +read \cgalCite{cgal:bc:hs-gbcicg-17}. You can also read an overview +here +(chapters 1 and 2). + + +\anchor compute_seg_coord +\subsection gbc_deg_segment_coordinates Segment Coordinates + +The segment coordinate function with respect to a given segment vertex is a linear +function along the supporting line of this segment that grows from zero at the opposite +vertex to one at the chosen vertex (see the figure below). + +\cgalFigureBegin{seg_coord, seg_coord.svg} +The segment coordinate function with respect to the vertex \f$v_0\f$. +\cgalFigureEnd + +Segment coordinates can be computed exactly if an exact number type is chosen. +The segment itself, with respect to which we compute coordinates, must be non-degenerate. +If both conditions are satisfied, then the computation never fails. However, to compute coordinates, +the user must ensure that the query point lies exactly on the line \f$L\f$ supporting the segment. +Since in many applications this is not the case, and a query point may lie very close but not exactly on this line, +we provide a solution to remedy this situation. + +\cgalFigureBegin{seg_coord_projection, seg_coord_projection.svg} +The orthogonal projection \f$p'\f$ of the vector \f$p\f$ (green) onto the vector \f$q\f$ (red). +\cgalFigureEnd + +Suppose that some query point \f$v\f$ does not lie exactly on the line \f$L\f$, +but is some distance \f$d\f$ away as shown in the figure above. If we want to compute +the segment coordinate \f$b_1(v)\f$ with respect to the vertex \f$v_1\f$, we first find the +orthogonal projection \f$p'\f$ of the vector \f$p\f$ onto the vector \f$q\f$ and +then normalize it by the length of \f$q\f$. This yields the segment coordinate \f$b_1(v') = b_1(v)\f$ +if \f$v\f$ lies exactly on the line. The other segment coordinate \f$b_0(v')\f$ that is equal +to \f$b_0(v)\f$ when \f$v\f$ is on the line \f$L\f$ is computed the same way but with the +projection of the vector \f$\vec{vv_1}\f$. + +\b Warning: do not abuse the feature described above, because it does not yield correct +segment coordinates for the point \f$v\f$ but rather those for \f$v'\f$. Moreover, segment +coordinates for a point \f$v\f$, which does not lie exactly on the line \f$L\f$, do not exist. +But if the non-zero distance \f$d\f$ is due to some numerical instability when computing the +location of the point \f$v\f$ or any other problem, which causes the point to be not exactly on +the line, the final segment coordinates will be, at least approximately, correct. + +With inexact number types, the resulting coordinate values are correct up to the precision of the chosen type. + + +\anchor compute_tri_coord +\subsection gbc_deg_triangular_coordinates Triangle Coordinates + +The triangle coordinate function with respect to a given triangle vertex is a linear function +that grows from zero along the opposite edge to one at the chosen vertex (see the figure below). + +\cgalFigureBegin{tri_coord, tri_coord.svg} +The triangle coordinate function with respect to the vertex \f$v_0\f$. +\cgalFigureEnd + +To compute the triangle coordinates of the query point \f$v\f$, we adopt the standard simple formula + +
+\f$b_i = \frac{A_i}{A}\f$ with \f$i = 0\dots 2\f$ +
+ +where \f$A_i\f$ is the signed area of the sub-triangle opposite to the +vertex \f$i\f$ and \f$A\f$ is the total area of the triangle that is \f$A = A_0 + A_1 + A_2\f$ +(see the figure below). + +\anchor tri_notations +\cgalFigureBegin{tri_notations, tri_notations.svg} +Notation for triangle coordinates. +\cgalFigureEnd + +These coordinates can be computed exactly if an exact number type is chosen, for any query point in the plane +and with respect to any non-degenerate triangle. No special cases are handled. The computation always +yields the correct result. The notion of correctness depends on the precision of the used number type. +Note that for exterior points some coordinate values will be negative. + + +\anchor compute_wp_coord +\subsection gbc_deg_wachspress_coordinates Wachspress Coordinates + +Wachspress coordinates are well-defined in the closure of any strictly convex polygon. +Therefore, when using an exact number type, for any query point from the polygon's closure, +these coordinates are computed exactly and no false result is expected. For exterior query points, +the coordinates can also be computed but not everywhere (see below for more details). For inexact number types, +the resulting precision of the computation is due to the involved algorithm and a chosen number type. +In the following paragraph, we discuss two available algorithms for computing Wachspress coordinates +when an inexact number type is used. The chosen algorithm is specified by a computation policy +`Barycentric_coordinates::Computation_policy_2`. + +\anchor wp_polygon +\cgalFigureBegin{wp_notations, wp_notations.svg} +Notation for Wachspress coordinates. +\cgalFigureEnd + +To compute Wachspress weights, we follow \cgalCite{cgal:bc:fhk-gcbcocp-06} and use the formula + +
+\f$w_i = \frac{C_i}{A_{i-1}A_i}\f$ +
+ +with \f$i = 1\dots n\f$ where \f$n\f$ is the number of polygon vertices. +In order to compute the coordinates, we normalize these weights, + +
+\f$b_i = \frac{w_i}{W^{wp}}\qquad\f$ with \f$\qquad W^{wp} = \sum_{j=1}^n w_j.\f$ +
+ +This formula becomes unstable when approaching the boundary of the polygon (\f$\approx 1.0e-10\f$ and closer). +To fix the problem, we modify the weights \f$w_i\f$ as + +
+\f$\bar{w}_i = C_i\prod_{j\not=i-1,i} A_j\f$. +
+ +After the above normalization, this gives us the precise algorithm to compute Wachspress coordinates +but with \f$O(n^2)\f$ performance only. The max speed \f$O(n)\f$ algorithm uses the standard +weights \f$w_i\f$. Note that mathematically this modification does not change the coordinates. One should +be cautious when using the unnormalized Wachspress weights. In that case, you must choose the +\f$O(n)\f$ type. + +It is known that for strictly convex polygons the denominator's zero set of the +Wachspress coordinates (\f$W^{wp} = 0~\f$) is a curve, which (in many cases) lies quite +far away from the polygon. More specifically, it interpolates the intersection points of +the supporting lines of the polygon edges. Therefore, the computation of Wachspress coordinates +outside the polygon is possible only at points that do not belong to this curve. + +\cgalFigureBegin{wp_zero_set, wp_zero_set.svg} +Zero set (red) of the Wachspress coordinates' denominator \f$W^{wp}\f$ for a non-regular hexagon. +\cgalFigureEnd + +\b Warning: we do not recommend using Wachspress coordinates for exterior points! + + +\anchor compute_dh_coord +\subsection gbc_deg_discrete_harmonic_coordinates Discrete Harmonic Coordinates + +Discrete harmonic coordinates have the same requirements as Wachspress coordinates. +They are well-defined in the closure of any strictly convex polygon and, +if an exact number type is chosen, they are computed exactly. However, and unlike Wachspress basis functions, +these coordinates are not necessarily positive. In particular, the weight \f$w_i\f$ is positive +if and only if \f$\alpha+\beta < \pi\f$ (see the figure below for notation). For inexact number types, +the precision of the computation is due to the involved algorithm and a chosen number type. Again, +we describe two algorithms to compute the coordinates when an inexact number type is used: +one is of max precision and one is of max speed. + +\anchor dh_polygon +\cgalFigureBegin{dh_notations, dh_notations.svg} +Notation for discrete harmonic coordinates. +\cgalFigureEnd + +To compute discrete harmonic weights, we follow \cgalCite{cgal:bc:fhk-gcbcocp-06} and use the formula + +
+\f$w_i = \frac{r_{i+1}^2A_{i-1}-r_i^2B_i+r_{i-1}^2A_i}{A_{i-1}A_i}\f$ +
+ +with \f$i = 1\dots n\f$ where \f$n\f$ is the number of polygon vertices. +In order to compute the coordinates, we normalize these weights, + +
+\f$b_i = \frac{w_i}{W^{dh}}\qquad\f$ with \f$\qquad W^{dh} = \sum_{j=1}^n w_j.\f$ +
+ +This formula becomes unstable when approaching the boundary of the polygon (\f$\approx 1.0e-10\f$ and closer). +To fix the problem, similarly to the previous subsection, we modify the weights \f$w_i\f$ as + +
+\f$\bar{w}_i = (r_{i+1}^2A_{i-1}-r_i^2B_i+r_{i-1}^2A_i)\prod_{j\not=i-1,i} A_j\f$. +
+ +After the above normalization, this yields the precise algorithm to compute discrete harmonic coordinates +but with \f$O(n^2)\f$ performance only. The max speed \f$O(n)\f$ algorithm uses the standard +weights \f$w_i\f$. Again, mathematically this modification does not change the coordinates, +one should be cautious when using the unnormalized discrete harmonic weights. In that case, +you must choose the \f$O(n)\f$ type. + +\b Warning: as for Wachspress coordinates, we do not recommend using discrete harmonic coordinates +for exterior points, because the curve \f$W^{dh} = 0\f$ may have several components, +and one of them interpolates the polygon vertices. However, if you are sure that +the query point does not belong to this curve, you can compute the coordinates +as shown in \ref dh_example " this example". + + +\anchor compute_mv_coord +\subsection gbc_deg_mean_value_coordinates Mean Value Coordinates + +Unlike the previous coordinates, mean value coordinates cannot be computed exactly due to +an inevitable square root operation. Although, if an exact number type is used, the default precision +of the computation depends only on two \cgal functions: `CGAL::to_double()` and `CGAL::sqrt()`. +It is worth saying that providing a number type that supports exact or nearly exact computation +of the square root is possible, however since such types are usually impractical due to the +large overhead, the conversion to a floating-point format above is always effective. On the other hand, +mean value coordinates are well-defined everywhere in the plane for any simple polygon. In addition, +if your traits class provides a more precise version of the square root function, the final precision +of the computation with exact number types will depend only on the precision of that function. + +\anchor mv_polygon +\cgalFigureBegin{mv_notations, mv_notations.svg} +Notation for mean value coordinates. +\cgalFigureEnd + +For these coordinates, we provide two algorithms: one is of max precision and one is of max speed. +The first one works everywhere in the plane, and the precision of the computation depends only +on the chosen number type, including the remarks above. This algorithm is based on the following weight +formula from \cgalCite{cgal:bc:f-wmvc-14} + +
+\f$w_i = \sigma_i\bar{w}_i\qquad\f$ with \f$\qquad\bar{w}_i = (r_{i-1}r_{i+1}-d_{i-1}d_{i+1})^{1/2}\prod_{j\not= i-1,i}(r_jr_{j+1} + d_jd_{j+1})^{1/2}\qquad\f$ +where \f$\qquad r_i = \|d_i\|.\f$ +
+ +Since \f$\bar{w}_i\f$ is always positive, we must append to it the proper sign \f$\sigma_i\f$ +of the signed mean value weight, which can be found efficiently (see the figures below). +This weight is always positive to the left of the red piecewise linear curve, +and it is negative to the right of this curve, moving in the counterclockwise direction. + +\cgalFigureBegin{mv_weight_signs, mv_weight_signs.svg} +Signs of the mean value weight \f$w_i\f$ depending on the region with respect to a +convex polygon \f$P\f$ and a concave polygon \f$P'\f$. +\cgalFigureEnd + +After the normalization of these weights as before + +
+\f$b_i = \frac{w_i}{W^{mv}}\qquad\f$ with \f$\qquad W^{mv} = \sum_{j=1}^n w_j\f$ +
+ +we obtain the max precision \f$O(n^2)\f$ algorithm. The max speed O(n) algorithm computes the +weights \f$w_i\f$ using the pseudocode from here. +These weights + +
+\f$w_i = \frac{t_{i-1} + t_i}{r_i}\qquad\f$ +with \f$\qquad t_i = \frac{\text{det}(d_i, d_{i+1})}{r_ir_{i+1} + d_id_{i+1}}\f$ +
+ +are also normalized. Note that they are unstable if a query point is closer than \f$\approx 1.0e-10\f$ +to the polygon boundary, similarly to Wachspress and discrete harmonic coordinates and +one should be cautious when using the unnormalized mean value weights. In that case, you must choose the +\f$O(n)\f$ type. + + +\anchor compute_hm_coord +\subsection gbc_deg_harmonic_coordinates Harmonic Coordinates + +The harmonic coordinates are computed by solving the Laplace equation + +
+\f$\Delta \boldsymbol{b} = \boldsymbol{0}\f$ +
+ +subject to suitable Dirichlet boundary conditions. Harmonic coordinates are the only coordinates +in this package, which are guaranteed to be non-negative in the closure of any simple polygon and +satisfy all properties of barycentric coordinates, however such desirable properties come with the fact +that these coordinates are well-defined only inside a polygon. If an exterior query point is provided, +its coordinates are set to zero. + +Another disadvantage of these coordinates is that they cannot be computed +exactly, because harmonic coordinates do not have a simple closed-form expression and +must be approximated. The common way to approximate these coordinates is +by discretizing over the space of piecewise linear functions with respect to +a triangulation of the polygon. The denser triangulation of the interior part of +the polygon, the better approximation of the coordinates. To get a high quality +approximation of the coordinates, the user should provide a rather dense partition +of the polygon's interior domain that in turn leads to larger running times when +computing the coordinates. + +\anchor terrain_triangulation_fig +\cgalFigureBegin{terrain_triangulation, terrain_triangulation.svg} +Sparse triangulation of the polygon's interior domain (left): smaller running times, +lower coordinates quality; dense triangulation (right): larger running times, +higher coordinates quality. +\cgalFigureEnd + +From all this follows, that any exact `Kernel` will be rejected and it is not possible +to compute analytic harmonic weights. However, once the coordinates are computed at the +vertices of the triangulation, they can be evaluated analytically at any interior +query point. For evaluation, we first locate a triangle that contains the query point +and then linearly interpolate harmonic coordinates defined at the vertices of this +triangle to the query point with the help of \ref gbc_deg_triangular_coordinates "triangle coordinates" as + +
+\f$b_i = b_0^{tr} b_i^0 + b_1^{tr} b_i^1 + b_2^{tr} b_i^2\f$ +
+ +with \f$i = 1\dots n\f$, where \f$n\f$ is the number of polygon vertices, \f$b_{0}^{tr}\f$, +\f$b_{1}^{tr}\f$, and \f$b_{2}^{tr}\f$ are the triangle coordinates of the query point with +respect the three vertices of the located triangle, and \f$b_i^{0}\f$, \f$b_i^{1}\f$, and \f$b_i^{2}\f$ +are the harmonic coordinates pre-computed at the triangle vertices. + + +\section gbc_performance Performance + +We strive for robustness and efficiency at the same time. Efficiency is especially important. +These coordinates are used in many applications where they must be computed for millions of points +and, thus, the real time computation of coordinates has been made possible. In this section, +we present next the computation runtimes of the implemented algorithms. + +The structure of the speed test that we use to evaluate the running times consists +of computing coordinate values (or weights) at >= 1 million strictly interior points +with respect to a polygon (or triangle, or segment). At each iteration of the loop, +we create a query point and compute its coordinates. The time presented in the log-log scale plot +at the end of the section is the arithmetic mean of all trials in the loop of 10 iterations. +The time presented in the plot is for analytic coordinates only since harmonic coordinates +of a reasonable (application-dependent) quality are substantially slower to compute and +cannot be fairly compared to the analytic coordinate functions. + +The time to compute coordinates depends on many factors such as memory allocation, input kernel, output container, +number of points, etc. In our tests, we used the most standard C++ and \cgal features with minimum memory allocation. +Therefore, the final time presented is the average time that can be expected without deep optimization +but still with efficient memory allocation. It also means that it may vary depending on the usage of the package. + +To benchmark analytic coordinates, we used a MacBook Pro 2011 with 2 GHz Intel Core i7 processor (2 cores) +and 8 GB 1333 MHz DDR3 memory. The installed operating system was OS X 10.9 Maverick. +The resulting timings for all closed-form coordinates can be found in the figure below. + +\cgalFigureBegin{analytic_timings, analytic_timings.png} +Time in seconds to compute \f$n\f$ coordinate values for a polygon with \f$n\f$ vertices +at 1 million query points with the max speed \f$O(n)\f$ algorithms (dashed) and +the max precision \f$0(n^2)\f$ algorithms (solid) for Wachspress (blue), discrete +harmonic (red), and mean value (green) coordinates. +\cgalFigureEnd + +From the figure above we observe that the \f$O(n^2)\f$ algorithm is as fast +as the \f$O(n)\f$ algorithm if we have a polygon with a small number of vertices. +But as the number of vertices is increased, the linear algorithm outperforms the squared one, +as expected. One of the reasons for this behavior is that for a small number of vertices +the multiplications of \f$n-2\f$ elements inside the \f$O(n^2)\f$ algorithm take almost the +same time as the corresponding divisions in the \f$O(n)\f$ algorithm. For a polygon with +many vertices, these multiplications are substantially slower. + +To benchmark harmonic coordinates, we used a MacBook Pro 2018 with 2.2 GHz Intel Core i7 processor (6 cores) +and 32 GB 2400 MHz DDR4 memory. The installed operating system was OS X 10.15 Catalina. +The average time to compute harmonic coordinates in the loop of 10 iterations can be found in the tables below. + +The first table shows how the time to compute the coordinates on a unit square depends on the number of +triangulation vertices. We show separately the time to setup the matrix, factorize it, and solve it +with respect to the four vertices of the unit square. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Number of queries (approx.)Setup (in seconds)Factorize (in seconds)Solve (in seconds)Total (in seconds)
1000.0000560.0000990.0000150.000170
5000.0002660.0005740.0000640.000904
1,0000.0005090.0011940.0001470.001850
25,0000.0147490.0711520.0081910.094092
50,0000.0342550.1842370.0181660.236658
100,0000.0651170.5431770.0440880.652382
500,0000.5765307.6971430.3107658.584438
1,000,0001.29516326.769450.73737228.80199
+ +The same results can be seen in the figure. + +\cgalFigureBegin{hm_4_bench, hm_4_bench.svg} +Time in seconds to setup (red), factorize (green), and solve (blue) for harmonic +coordinate values with respect to a unit square. +\cgalFigureEnd + +The second table shows how the time to compute the coordinates for 100k queries depends on the number of +the polygon vertices. We show separately the time to setup the matrix, factorize it, and solve it +with respect to the \f$n\f$ vertices of the polygon. It can be seen that, unlike in the +first table, the time to factorize the matrix here stays constant. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Number of vertices (approx.)Setup (in seconds)Factorize (in seconds)Solve (in seconds)Total (in seconds)
50.0834440.6318230.0598270.775094
100.0602940.4505340.0945830.605411
250.0627600.4786830.2549530.796396
500.0973590.4922330.5396541.129246
1000.1294870.4507711.1525441.732802
5000.4306940.4603216.6200617.511076
10000.8123620.48005216.1423917.43480
+ +The same results can be seen in the figure. + +\cgalFigureBegin{hm_n_bench, hm_n_bench.svg} +Time in seconds to setup (red), factorize (green), and solve (blue) for harmonic +coordinate values with respect to a polygon with \f$n\f$ vertices at 100k query points. +\cgalFigureEnd + +While, in the first table, the most significant step is to factorize the matrix, in the +second table, the slowest step is to solve for coordinates, as expected. + + +\section gbc_history History +The package was first released in 2015 and included segment, triangle, Wachspress, +discrete harmonic, and mean value coordinates. The API of that version is now deprecated +but can still be used. An example of the old API can be found \ref depr_example "here". +The docs of that API are also preserved and maintained \ref PkgBarycentricCoordinates2RefDeprecated "here". + +In 2018, this package was modified and improved by Keyu Chen and Dmitry Anisimov +during the Google Summer of Code. The API was changed to the current version. In 2020, the new version +was cleaned up and documented that includes: +- the classes `Segment_coordinates_2` and `Triangle_coordinates_2` have been removed, only the free +functions are preserved; +- the entry class `Generalized_barycentric_coordinates_2` was removed since it is not +flexible enough to accommodate all types of 2D barycentric coordinates; +- the classes `Wachspress_2`, `Discrete harmonic_2`, and `Mean_value_2` have been renamed +and modified so that they can be used now on their own without the class `Generalized_barycentric_coordinates_2`; +- harmonic coordinates have been added; +- the free functions for segment and triangle coordinates have been modified and improved; +- the free functions for Wachspress, discrete harmonic, and mean value weights and coordinates have been added; +- the free functions to compute barycentric coordinates for points on the polygon boundary have been added; +- all functions and classes are now using ranges and property maps; +- examples, tests, and benchmarks are modified/extended/improved; +- the docs are refactored and simplified. + + +\section gbc_acknowledgments Acknowledgments +The authors wish to thank Teseo Schneider +and Randolf Schaerfig for helpful comments and discussions. We also appreciate the +great effort invested in this package by our reviewers Andreas Fabri, Sébastien Loriot, +and Efi Fogel. +*/ + +} /* namespace Barycentric_coordinates */ +} /* namespace CGAL */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h new file mode 100755 index 000000000000..1f5e7aed7e17 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h @@ -0,0 +1,186 @@ +namespace CGAL { +namespace Barycentric_coordinates { + +/*! +\ingroup PkgBarycentricCoordinates2RefConcepts +\cgalConcept + +A concept that describes the set of requirements of the template parameter +`GeomTraits` used to parameterize all classes and functions with 2D barycentric +coordinates from the namespace `CGAL::Barycentric_coordinates`. + +\cgalHasModel +- All models of `Kernel` +*/ +class BarycentricTraits_2 { + +public: + +/// \name Types +/// @{ + +/*! + A model of `FieldNumberType`. +*/ +typedef unspecified_type FT; + +/*! + `CGAL::Comparison_result` or `Uncertain`. +*/ +typedef unspecified_type Comparison_result; + +/*! + `CGAL::Orientation` or `Uncertain`. +*/ +typedef unspecified_type Orientation; + +/// @} + +/// \name 2D Geometric Objects +/// @{ + +/*! + A model of `Kernel::Point_2`. +*/ +typedef unspecified_type Point_2; + +/*! + A model of `Kernel::Vector_2`. +*/ +typedef unspecified_type Vector_2; + +/// @} + +/// \name 2D Generalized Constructions +/// @{ + +/*! + A construction object that must provide the function operator: + + `FT operator(const Point_2& p, const Point_2& q, const Point_2& r)` + + that returns the signed area of the triangle defined by the points `p`, `q`, and `r`. +*/ +typedef unspecified_type Compute_area_2; + +/*! + A construction object that must provide the function operator: + + `FT operator(const Point_2& p, const Point_2& q)` + + that returns the squared Euclidean distance between the points `p` and `q`. +*/ +typedef unspecified_type Compute_squared_distance_2; + +/*! + A construction object that must provide the function operator: + + `FT operator(const Vector_2& v)` + + that returns the squared length of the vector `v`. +*/ +typedef unspecified_type Compute_squared_length_2; + +/*! + A construction object that must provide the function operator: + + `FT operator(const Vector_2& v, const Vector_2& w)` + + that returns the scalar product of the vectors `v` and `w`. +*/ +typedef unspecified_type Compute_scalar_product_2; + +/*! + A construction object that must provide the function operator: + + `FT operator(const Vector_2& v, const Vector_2& w)` + + that returns the determinant of the vectors `v` and `w`. +*/ +typedef unspecified_type Compute_determinant_2; + +/*! + A construction object that must provide the function operator: + + `%Vector_2 operator()(const Point_2& p, const Point_2& q)` + + that returns the vector through the points `p` and `q`. +*/ +typedef unspecified_type Construct_vector_2; + +/// @} + +/// \name 2D Generalized Predicates +/// @{ + +/*! + A predicate object that must provide the function operator: + + `bool operator(const Point_2& p, const Point_2& q)` + + that returns `true` if `p = q` and `false` otherwise. +*/ +typedef unspecified_type Equal_2; + +/*! + A predicate object that must provide the function operator: + + `bool operator(const Point_2& p, const Point_2& q, const Point_2& r)` + + that returns `true` if the points `p`, `q`, and `r` are collinear and `false` otherwise. +*/ +typedef unspecified_type Collinear_2; + +/*! + A predicate object that must provide the function operator: + + `bool operator(const Point_2& p, const Point_2& q, const Point_2& r)` + + that returns `true` if the point `q` lies between the points `p` and `r` and all three points are collinear. +*/ +typedef unspecified_type Collinear_are_ordered_along_line_2; + +/*! + A predicate object that must provide the function operator: + + `bool operator(const Point_2& p, const Point_2& q)` + + that returns `true` iff the x-coordinate of `p` is smaller than the x-coordinate of `q` or + if they are the same and the y-coordinate of `p` is smaller than the y-coordinate of `q`. +*/ +typedef unspecified_type Less_xy_2; + +/*! + A predicate object that must provide the function operator: + + `Comparison_result operator(const Point_2& p, const Point_2& q)` + + that compares the Cartesian x-coordinates of the points `p` and `q`. +*/ +typedef unspecified_type Compare_x_2; + +/*! + A predicate object that must provide the function operator: + + `Comparison_result operator(const Point_2& p, const Point_2& q)` + + that compares the Cartesian y-coordinates of the points `p` and `q`. +*/ +typedef unspecified_type Compare_y_2; + +/*! + A predicate object that must provide the function operator: + + `Orientation operator(const Point_2& p, const Point_2& q, const Point_2& r)` + + that returns `CGAL::LEFT_TURN` if `r` lies to the left of the oriented line `l` defined by `p` and `q`, + returns `CGAL::RIGHT_TURN` if `r` lies to the right of `l`, and returns `CGAL::COLLINEAR` if `r` lies on `l`. +*/ +typedef unspecified_type Orientation_2; + +/// @} + +}; + +} // namespace Barycentric_coordinates +} // namespace CGAL diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h new file mode 100755 index 000000000000..5804d2505163 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h @@ -0,0 +1,70 @@ +namespace CGAL { +namespace Barycentric_coordinates { + +/*! +\ingroup PkgBarycentricCoordinates2RefConcepts +\cgalConcept + +A concept that describes the set of methods that should be defined for all +discretized domains obtained by meshing the interior part of a simple polygon. + +After meshing, the interior part of the polygon is split into multiple finite +elements, which share common edges and vertices. These finite elements are then +used to approximate certain types of generalized barycentric coordinate functions. +The domain is bounded by the polygon. + +\cgalHasModel +- `Delaunay_domain_2` +*/ +class DiscretizedDomain_2 { + +public: + + /*! + returns the number of vertices after meshing the domain. + */ + std::size_t number_of_vertices() const { + + } + + /*! + returns a const reference to the vertex with the index `query_index`, the + `Vertex_2` type being a model of `Kernel::Point_2`. + */ + const Vertex_2& vertex( + const std::size_t query_index) const { + + } + + /*! + verifies if the vertex with the index `query_index` is on the + boundary of the domain. + */ + bool is_on_boundary( + const std::size_t query_index) const { + + } + + /*! + fills `neighbors` with the indices of the vertices, which form the one-ring + neighborhood of the vertex with the index `query_index`, the neighbors have to + be in the counterclockwise order and form a simple polygon. + */ + void operator()( + const std::size_t query_index, std::vector& neighbors) { + + } + + /*! + fills `indices` with the indices of the vertices, which form a finite element + of the domain, that contains a `query` point; if no indices are found, the + `query` point does not belong to the domain; the type `Query_2` is a model of `Kernel::Point_2`. + */ + void locate( + const Query_2& query, std::vector& indices) { + + } +}; + +} // namespace Barycentric_coordinates +} // namespace CGAL diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h new file mode 100755 index 000000000000..1e1df7a5727a --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h @@ -0,0 +1,88 @@ +namespace CGAL { +namespace Barycentric_coordinates_depr { + +/*! +\ingroup PkgBarycentricCoordinates2RefConcepts +\cgalConcept + +A concept that describes the set of methods that should be defined for all coordinate +models used to parameterize the class `Generalized_barycentric_coordinates_2`. + +\cgalHasModel +- `Wachspress_2` +- `Mean_value_2` +- `Discrete_harmonic_2` + +\deprecated This part of the package is deprecated since the version 5.4 of \cgal. +*/ +class BarycentricCoordinates_2 { + +public: + + /// \name Creation + /// @{ + + /*! + Creates a class that implements generalized barycentric coordinates for any query + point that does not belong to the polygon's boundary. The polygon is given by a + range of vertices of the type `Traits::Point_2` stored in a container of the + type `std::vector`. + */ + BarycentricCoordinates_2( + const std::vector& vertices, const Traits& barycentric_traits) { + + } + + /// @} + + /// \name Functions + /// @{ + + /*! + A function that computes generalized barycentric coordinates without normalization + that are called generalized baycentric weights (as fast as possible algorithm is used). + Weights are computed with respect to a query point of the type `Traits::Point_2` and + stored in the output iterator `output`. The function returns a pointer to the last stored element. + */ + boost::optional + weights( + const Traits::Point_2& query_point, OutputIterator& output) { + + } + + /*! + A function that computes generalized barycentric coordinates on the bounded side + of a polygon with one of two possible algorithms: one is precise and one is fast. + The algorithm type is specified by the parameter `type_of_algorithm`. Coordinates + are computed with respect to a query point of the type `Traits::Point_2` and stored + in the output iterator `output`. The function returns a pointer to the last stored element. + */ + boost::optional + coordinates_on_bounded_side( + const Traits::Point_2& query_point, + OutputIterator& output, + const Type_of_algorithm type_of_algorithm) { + + } + + /*! + A function that computes generalized barycentric coordinates on the unbounded side + of a polygon with one of two possible algorithms: one is precise and one is fast. + The algorithm type is specified by the parameter `type_of_algorithm`. Coordinates + are computed with respect to a query point of the type `Traits::Point_2` and stored + in the output iterator `output`. The function returns a pointer to the last stored element. + */ + boost::optional + coordinates_on_unbounded_side( + const Traits::Point_2& query_point, + OutputIterator& output, + const Type_of_algorithm type_of_algorithm) { + + } + +/// @} + +}; + +} // namespace Barycentric_coordinates_depr +} // namespace CGAL diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in old mode 100644 new mode 100755 index 1ae7018bb52e..0e974af9d661 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in @@ -1,6 +1,6 @@ @INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} - PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Generalized Barycentric Coordinates" -EXTRACT_ALL = false -HIDE_UNDOC_MEMBERS = true -HIDE_UNDOC_CLASSES = true +EXTRACT_ALL = false + +HIDE_UNDOC_MEMBERS = true +HIDE_UNDOC_CLASSES = true diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt old mode 100644 new mode 100755 index e69de29bb2d1..2a0096073017 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -0,0 +1,82 @@ +namespace CGAL { +namespace Barycentric_coordinates { + +/*! +\defgroup PkgBarycentricCoordinates2Ref 2D Generalized Barycentric Coordinates Reference + +\defgroup PkgBarycentricCoordinates2RefConcepts Concepts +\ingroup PkgBarycentricCoordinates2Ref + +Generalized barycentric concepts. + +\defgroup PkgBarycentricCoordinates2RefAnalytic Analytic Coordinates +\ingroup PkgBarycentricCoordinates2Ref + +Analytic coordinates and related classes. + +\defgroup PkgBarycentricCoordinates2RefHarmonic Harmonic Coordinates +\ingroup PkgBarycentricCoordinates2Ref + +Harmonic coordinates and related classes. + +\defgroup PkgBarycentricCoordinates2RefFunctions Free Functions +\ingroup PkgBarycentricCoordinates2Ref + +Free functions to compute barycentric weights and coordinates. + +\defgroup PkgBarycentricCoordinates2RefDeprecated Deprecated +\ingroup PkgBarycentricCoordinates2Ref + +Deprecated classes and functions. + +\addtogroup PkgBarycentricCoordinates2Ref + +\cgalPkgDescriptionBegin{2D Generalized Barycentric Coordinates, PkgBarycentricCoordinates2} +\cgalPkgPicture{barcoord_thumb.png} + +\cgalPkgSummaryBegin +\cgalPkgAuthors{Dmitry Anisimov, David Bommes, Kai Hormann, and Pierre Alliez} +\cgalPkgDesc{This package offers an efficient and robust implementation of 2D generalized barycentric +coordinates defined for simple polygons in the plane. If coordinates with respect to multivariate +scattered points instead of a polygon are required, please refer to natural neighbor +coordinates from the Package \ref PkgInterpolation2.} +\cgalPkgManuals{Chapter_2D_Generalized_Barycentric_Coordinates, PkgBarycentricCoordinates2Ref} +\cgalPkgSummaryEnd + +\cgalPkgShortInfoBegin +\cgalPkgSince{4.6} +\cgalPkgBib{cgal:abha-gbc} +\cgalPkgLicense{\ref licensesGPL "GPL"} +\cgalPkgShortInfoEnd +\cgalPkgDescriptionEnd + +\cgalClassifedRefPages + +## Concepts ## +- `BarycentricTraits_2` +- `DiscretizedDomain_2` + +## Analytic Coordinates ## +- `Wachspress_coordinates_2` +- `Mean_value_coordinates_2` +- `Discrete_harmonic_coordinates_2` + +## Harmonic Coordinates ## +- `Delaunay_domain_2` +- `Harmonic_coordinates_2` + +## Free Functions ## +- `segment_coordinates_2()` +- `triangle_coordinates_2()` +- `wachspress_weights_2()` +- `wachspress_coordinates_2()` +- `mean_value_weights_2()` +- `mean_value_coordinates_2()` +- `discrete_harmonic_weights_2()` +- `discrete_harmonic_coordinates_2()` +- `harmonic_coordinates_2()` +- `boundary_coordinates_2()` +*/ + +} /* namespace Barycentric_coordinates */ +} /* namespace CGAL */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies old mode 100644 new mode 100755 index e69de29bb2d1..b01ca3d8bcac --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies @@ -0,0 +1,16 @@ +Manual +Mesh_2 +Polygon +Kernel_d +Kernel_23 +Generator +Convex_hull_2 +Number_types +Interpolation +STL_Extension +Triangulation_2 +Algebraic_foundations +Circulator +Stream_support +Property_map +Surface_mesh_parameterization diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt old mode 100644 new mode 100755 index 21f7878a7e3e..e4e0873bcd03 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt @@ -1,6 +1,12 @@ /*! - -\example Barycentric_coordinates_3/tetrahedon_coordinates.cpp - - +\example Barycentric_coordinates_2/segment_coordinates.cpp +\example Barycentric_coordinates_2/triangle_coordinates.cpp +\example Barycentric_coordinates_2/wachspress_coordinates.cpp +\example Barycentric_coordinates_2/discrete_harmonic_coordinates.cpp +\example Barycentric_coordinates_2/mean_value_coordinates.cpp +\example Barycentric_coordinates_2/harmonic_coordinates.cpp +\example Barycentric_coordinates_2/terrain_height_modeling.cpp +\example Barycentric_coordinates_2/shape_deformation.cpp +\example Barycentric_coordinates_2/affine_coordinates.cpp +\example Barycentric_coordinates_2/deprecated_coordinates.cpp */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg new file mode 100755 index 000000000000..357e9e4b0a33 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png new file mode 100755 index 0000000000000000000000000000000000000000..513a8feee30ca15ec1e025eff6e12f51729ec9e8 GIT binary patch literal 77272 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� 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg new file mode 100755 index 000000000000..c6dc54c50e0b --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg new file mode 100755 index 000000000000..283cea5b95dc --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg new file mode 100755 index 000000000000..70702d86c99f --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg new file mode 100755 index 000000000000..b79edb78ab18 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg new file mode 100755 index 000000000000..ba6a3da53786 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg new file mode 100755 index 000000000000..f708a9e2dd1a --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg new file mode 100755 index 000000000000..012de1cf3e9c --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg new file mode 100755 index 000000000000..d02ec9b376ff --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg new file mode 100755 index 000000000000..0a9010081d3e --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg new file mode 100755 index 000000000000..86c0fce6e7b6 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg new file mode 100755 index 000000000000..bc771f38332c --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg @@ -0,0 +1,1100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg new file mode 100755 index 000000000000..80aa95077676 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png new file mode 100755 index 0000000000000000000000000000000000000000..d879858822e91b03a2d2ced8d606b065fba395f1 GIT binary patch literal 46508 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 U#zUMQ&;S4c07*qoM6N<$f@4=^A^-pY literal 0 HcmV?d00001 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg new file mode 100755 index 000000000000..41cb58d59188 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg @@ -0,0 +1,4633 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg new file mode 100755 index 000000000000..7bb1b0acba4e --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg new file mode 100755 index 000000000000..16b75982fb0b --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg new file mode 100755 index 000000000000..4a64b8a8c3f7 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg new file mode 100755 index 000000000000..58b673435a07 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg new file mode 100755 index 000000000000..fc4637fdbfb0 --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg new file mode 100755 index 000000000000..b497a3f490ae --- /dev/null +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index a0ab076ced6b..64b27eb1d44e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -114,10 +114,15 @@ namespace Barycentric_coordinates { const auto edge_case = internal::locate_wrt_polyhedron( m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); - if (edge_case == internal::Edge_case::BOUNDARY) { + if(edge_case == internal::Edge_case::BOUNDARY) { return coordinates; } - if (edge_case == internal::Edge_case::EXTERIOR) { + if(edge_case == internal::Edge_case::EXTERIOR_BOUNDARY){ + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + return coordinates; + } + if(edge_case == internal::Edge_case::EXTERIOR) { std::cerr << std::endl << "WARNING: query does not belong to the polygon!" << std::endl; } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index f20430fb064b..6c1eb3ec1677 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -30,9 +30,10 @@ namespace internal{ enum class Edge_case { - EXTERIOR = 0, // exterior part of the polygon - BOUNDARY = 1, // boundary part of the polygon - INTERIOR = 2 // interior part of the polygon + EXTERIOR = 0, // exterior part of the polyhedron + INTERIOR = 1, // interior part of the polyhedron + BOUNDARY = 2, // boundary part of the polyhedron + EXTERIOR_BOUNDARY = 3, // extension of the boundary }; //Default sqrt @@ -219,6 +220,11 @@ template const auto v1 = *vertex; vertex++; const auto v2 = *vertex; + // Check if the three vertices are not identical + CGAL_assertion(v0 != v1); + CGAL_assertion(v0 != v2); + CGAL_assertion(v1 != v2); + const FT tol = get_tolerance(); const Plane_3 plane_face(get(vertex_to_point_map, v0), @@ -235,11 +241,11 @@ template for(const auto& v : vd){ if(v == v0) - *coordinates = CGAL::abs(coordinates_2d[0]) < tol? 0: coordinates_2d[0]; + *coordinates = CGAL::abs(coordinates_2d[0]) < tol? FT(0): coordinates_2d[0]; else if(v == v1) - *coordinates = CGAL::abs(coordinates_2d[1]) < tol? 0: coordinates_2d[1]; + *coordinates = CGAL::abs(coordinates_2d[1]) < tol? FT(0): coordinates_2d[1]; else if(v == v2) - *coordinates = CGAL::abs(coordinates_2d[2]) < tol? 0: coordinates_2d[2]; + *coordinates = CGAL::abs(coordinates_2d[2]) < tol? FT(0): coordinates_2d[2]; else *coordinates = FT(0); @@ -268,6 +274,10 @@ template const auto& construct_vector_3 = traits.construct_vector_3_object(); const auto& sqrt(Get_sqrt::sqrt_object(traits)); + // Flags that indicates position of the query point + bool exterior_flag = false; + bool boundary_flag = false; + const FT tol = get_tolerance(); auto face_range = faces(polygon_mesh); @@ -294,15 +304,23 @@ template // Verify location of query point; if(CGAL::abs(perp_dist_i) < tol){ - boundary_coordinates_3(vertex, vertex_to_point_map, polygon_mesh, query, coordinates, traits); - return Edge_case::BOUNDARY; + if(!boundary_flag) + boundary_coordinates_3(vertex, vertex_to_point_map, polygon_mesh, query, coordinates, traits); + boundary_flag = true; } else if(perp_dist_i < 0) - return Edge_case::EXTERIOR; + exterior_flag = true; } - //Default case - return Edge_case::INTERIOR; + // Choose location + if(boundary_flag && exterior_flag) + return Edge_case::EXTERIOR_BOUNDARY; + else if(boundary_flag) + return Edge_case::BOUNDARY; + else if(exterior_flag) + return Edge_case::EXTERIOR; + else + return Edge_case::INTERIOR; } } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp index 673e913b72bf..63c10c2e4f6a 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp @@ -26,12 +26,29 @@ void test_overloads() { std::vector tetrahedron_coords; std::vector sample_points; + // Offsets + const FT tol = tests::get_tolerance()/FT(10); + + // Sample interior and boundary std::tie(tetrahedron, tetrahedron_coords) = tests::get_regular_tetrahedron(FT(1.0)); + tests::random_points_tetrahedron(tetrahedron_coords, + std::back_inserter(sample_points), 100); + + // Face offsets + std::vector tetrahedron_tol_diff; + std::tie(std::ignore, tetrahedron_tol_diff) = tests::get_regular_tetrahedron(FT(1.0) - tol); + tests::random_points_tetrahedron(tetrahedron_tol_diff, + std::back_inserter(sample_points), 100); + + // Vertice offsets + for(auto& v : tetrahedron_coords){ + sample_points.push_back(Point_3(v.x(), v.y(), v.z())); + } + + // WP coordinates CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_tetrahedron( tetrahedron, CP3::WITH_EDGE_CASES); - tests::random_points_tetrahedron(tetrahedron_coords, - std::back_inserter(sample_points), 10000); std::vector wp_coordinates_tetrahedron; From 5e74195d22f69cecba5fb88080e7fd4eb1a46df6 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Sun, 25 Jul 2021 04:27:01 -0300 Subject: [PATCH 40/68] final version edge_cases --- .../Discrete_harmonic_coordinates_3.h | 59 ++++++++- .../Mean_value_coordinates_3.h | 42 +++++- .../Barycentric_coordinates_3/CMakeLists.txt | 2 +- .../test_dh_weights.cpp | 2 - .../test_edge_cases.cpp | 123 ++++++++++++++++++ .../test_wp_edge_cases.cpp | 85 ------------ 6 files changed, 219 insertions(+), 94 deletions(-) create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp delete mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index f22d742840d6..e366506718c6 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -100,6 +100,45 @@ namespace Barycentric_coordinates { OutputIterator compute( const Point_3& query, OutputIterator coordinates) { + switch(m_computation_policy){ + + case Computation_policy_3::DEFAULT:{ + return compute_coords(query, coordinates); + } + + case Computation_policy_3::WITH_EDGE_CASES:{ + // Calculate query position relative to the polyhedron + const auto edge_case = internal::locate_wrt_polyhedron( + m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + + if(edge_case == internal::Edge_case::BOUNDARY) { + return coordinates; + } + if(edge_case == internal::Edge_case::EXTERIOR_BOUNDARY){ + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + return coordinates; + } + if(edge_case == internal::Edge_case::EXTERIOR) { + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + } + + return compute_coords(query, coordinates); + } + + default:{ + internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + return coordinates; + } + } + return coordinates; + } + + template + OutputIterator compute_coords( + const Point_3& query, OutputIterator coordinates){ + // Compute weights. const FT sum = compute_weights(query); CGAL_assertion(sum != FT(0)); @@ -126,9 +165,10 @@ namespace Barycentric_coordinates { // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { - // Call function to calculate coordinates + // Call function to calculate wp coordinates const FT weight = compute_dh_vertex_query(vertex, query); CGAL_assertion(vi < m_weights.size()); @@ -145,6 +185,8 @@ namespace Barycentric_coordinates { template FT compute_dh_vertex_query(const Vertex& vertex, const Point_3& query){ + const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); + // Circulator of faces around the vertex CGAL::Face_around_target_circulator face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); @@ -191,10 +233,17 @@ namespace Barycentric_coordinates { const Vector_3 normal_query = vertex_parity * m_cross_3(m_construct_vector_3(query, point2), m_construct_vector_3(query, point1)); - const FT cot_dihedral = internal::cot_dihedral_angle( - internal::get_face_normal( - *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits), - normal_query, m_traits); + const Vector_3 face_normal = internal::get_face_normal( + *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); + + FT cot_dihedral = internal::cot_dihedral_angle( + face_normal, normal_query, m_traits); + + const Vector_3 vertex_query = m_construct_vector_3(vertex_val, query); + + // Treat case when the point is outside + if(m_dot_3(face_normal, vertex_query) > 0) + cot_dihedral *= -1; weight += (cot_dihedral * edge_length) / 2; face_circulator++; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index dee6bc343a02..f3169c33e7b0 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -112,6 +112,45 @@ namespace Barycentric_coordinates { OutputIterator compute( const Point_3& query, OutputIterator coordinates) { + switch(m_computation_policy){ + + case Computation_policy_3::DEFAULT:{ + return compute_coords(query, coordinates); + } + + case Computation_policy_3::WITH_EDGE_CASES:{ + // Calculate query position relative to the polyhedron + const auto edge_case = internal::locate_wrt_polyhedron( + m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + + if(edge_case == internal::Edge_case::BOUNDARY) { + return coordinates; + } + if(edge_case == internal::Edge_case::EXTERIOR_BOUNDARY){ + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + return coordinates; + } + if(edge_case == internal::Edge_case::EXTERIOR) { + std::cerr << std::endl << + "WARNING: query does not belong to the polygon!" << std::endl; + } + + return compute_coords(query, coordinates); + } + + default:{ + internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + return coordinates; + } + } + return coordinates; + } + + template + OutputIterator compute_coords( + const Point_3& query, OutputIterator coordinates){ + // Compute weights. const FT sum = compute_weights(query); CGAL_assertion(sum != FT(0)); @@ -138,9 +177,10 @@ namespace Barycentric_coordinates { // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); + for (const auto& vertex : vd) { - // Call function to calculate coordinates + // Call function to calculate wp coordinates const FT weight = compute_mv_vertex_query(vertex, query); CGAL_assertion(vi < m_weights.size()); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 1c358bffb33d..b29b547f4d6a 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -19,7 +19,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") create_single_source_cgal_program("test_voronoi_weights.cpp") - create_single_source_cgal_program("test_wp_edge_cases.cpp") + create_single_source_cgal_program("test_edge_cases.cpp") create_single_source_cgal_program("test_containers.cpp") else() diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp index 5d1aa3c8c958..d179aec776df 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp @@ -32,7 +32,6 @@ void test_overloads() { std::vector dh_coordinates_tetrahedron; - // Test cube //Check for barycenter dh_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), std::back_inserter(dh_coordinates_tetrahedron)); @@ -56,7 +55,6 @@ void test_overloads() { } } } - } int main(){ diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp new file mode 100644 index 000000000000..45d9a52705a3 --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; + +template +void test_coordinate() { + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // tetrahedron + Mesh tetrahedron; + std::vector tetrahedron_coords; + std::tie(tetrahedron, tetrahedron_coords) = tests::get_regular_tetrahedron(FT(1.0)); + + std::vector sample_points; + std::vector sampled_tetrahedron_coords; + const FT tol = tests::get_tolerance(); + int num_interior_samples = 0; + + for(auto& offset : {FT(0), tol, -tol}){ + + // Sample interior and boundary + std::tie(std::ignore, sampled_tetrahedron_coords) = + tests::get_regular_tetrahedron(FT(1.0) + offset); + + tests::random_points_tetrahedron(sampled_tetrahedron_coords, + std::back_inserter(sample_points), 100); + num_interior_samples += 100; + + // Add points close to vertices + if(offset != tol){ + + for(auto& v : sampled_tetrahedron_coords){ + + sample_points.push_back(v); + num_interior_samples++; + } + } + } + + // Exterior points + std::tie(std::ignore, sampled_tetrahedron_coords) = + tests::get_regular_tetrahedron(FT(2.0)); + + tests::random_points_tetrahedron(sampled_tetrahedron_coords, + std::back_inserter(sample_points), 100); + + // BC coordinates + BC bc_tetrahedron(tetrahedron, CP3::WITH_EDGE_CASES); + + std::vector bc_coordinates_tetrahedron; + int num_samples = 0; + + for(auto& point : sample_points){ + + const Point_3 query(point.x(), point.y(), point.z()); + bc_coordinates_tetrahedron.clear(); + bc_tetrahedron(query, std::back_inserter(bc_coordinates_tetrahedron)); + + tests::test_linear_precision(bc_coordinates_tetrahedron, tetrahedron_coords, query); + tests::test_partition_of_unity(bc_coordinates_tetrahedron); + + if(num_samples < num_interior_samples) + tests::test_positivity(bc_coordinates_tetrahedron); + + num_samples++; + } +} + +template +void test_overloads(){ + + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + using WP = CGAL::Barycentric_coordinates::Wachspress_coordinates_3; + using DH = CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3; + using MV = CGAL::Barycentric_coordinates::Mean_value_coordinates_3; + + std::cout << "WP test: " << std::endl; + test_coordinate(); + std::cout << "WP passed" << std::endl; + + std::cout << "DH test: " << std::endl; + test_coordinate(); + std::cout << "DH passed" << std::endl; + + std::cout << "MV test: " << std::endl; + test_coordinate(); + std::cout << "MV passed" << std::endl; +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp deleted file mode 100644 index 63c10c2e4f6a..000000000000 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_edge_cases.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -#include "include/utils.h" - -// Typedefs. -using SCKER = CGAL::Simple_cartesian; -using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; -using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; -using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; - -template -void test_overloads() { - - using FT = typename Kernel::FT; - using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; - - // tetrahedron - Mesh tetrahedron; - std::vector tetrahedron_coords; - std::vector sample_points; - - // Offsets - const FT tol = tests::get_tolerance()/FT(10); - - // Sample interior and boundary - std::tie(tetrahedron, tetrahedron_coords) = tests::get_regular_tetrahedron(FT(1.0)); - tests::random_points_tetrahedron(tetrahedron_coords, - std::back_inserter(sample_points), 100); - - // Face offsets - std::vector tetrahedron_tol_diff; - std::tie(std::ignore, tetrahedron_tol_diff) = tests::get_regular_tetrahedron(FT(1.0) - tol); - tests::random_points_tetrahedron(tetrahedron_tol_diff, - std::back_inserter(sample_points), 100); - - // Vertice offsets - for(auto& v : tetrahedron_coords){ - - sample_points.push_back(Point_3(v.x(), v.y(), v.z())); - } - - // WP coordinates - CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_tetrahedron( - tetrahedron, CP3::WITH_EDGE_CASES); - - std::vector wp_coordinates_tetrahedron; - - for(auto& point : sample_points){ - - const Point_3 query(point.x(), point.y(), point.z()); - wp_coordinates_tetrahedron.clear(); - wp_tetrahedron(query, std::back_inserter(wp_coordinates_tetrahedron)); - - tests::test_linear_precision(wp_coordinates_tetrahedron, tetrahedron_coords, query); - tests::test_partition_of_unity(wp_coordinates_tetrahedron); - tests::test_positivity(wp_coordinates_tetrahedron); - } -} - -int main(){ - - // Set cout precision - std::cout.precision(20); - - std::cout << "SCKER test :" << std::endl; - test_overloads(); - std::cout << "SCKER PASSED" << std::endl; - - std::cout << "EPICK test :" << std::endl; - test_overloads(); - std::cout << "EPICK PASSED" << std::endl; - - std::cout << "EPECK test :" << std::endl; - test_overloads(); - std::cout << "EPECK PASSED" << std::endl; - - return EXIT_SUCCESS; -} From 43aa9518675941d38da1ef3440d2636751e27bba Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 27 Jul 2021 00:51:19 -0300 Subject: [PATCH 41/68] added shape_deformation example --- .../Barycentric_coordinates_3/CMakeLists.txt | 4 +- .../shape_deformation.cpp | 92 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index e7d9b060c8fc..57702a3ecda7 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -2,6 +2,7 @@ # This is the CMake script for compiling a CGAL application. project(Barycentric_coordinates_3_Examples) +set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wextra -Werror") cmake_minimum_required(VERSION 3.1...3.15) set(CMAKE_CXX_STANDARD 14) @@ -13,7 +14,8 @@ if(CGAL_FOUND) include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("tetrahedron_coordinates.cpp") - + create_single_source_cgal_program("shape_deformation.cpp") + else() message(WARNING "This program requires the CGAL library, and will not be compiled.") endif() diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp new file mode 100644 index 000000000000..edba17cbe85c --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp @@ -0,0 +1,92 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; + +// default triangulation for Surface_mesher +using Tr = CGAL::Surface_mesh_default_triangulation_3; + +using C2t3 = CGAL::Complex_2_in_triangulation_3; +using Sphere_3 = Kernel::Sphere_3; +using Point_3 = Kernel::Point_3; +using FT = Kernel::FT; + +typedef FT (*Function)(Point_3); +using Surface_3 = CGAL::Implicit_surface_3; +using Surface_mesh = CGAL::Surface_mesh; +namespace PMP = CGAL::Polygon_mesh_processing; + +FT sphere_function (Point_3 p){ + const FT x2=p.x()*p.x(), y2=p.y()*p.y(), z2=p.z()*p.z(); + return x2+y2+z2-1; +} + +int main() { + + Tr tr; + C2t3 c2t3(tr); + + Surface_3 surface(sphere_function, Sphere_3(CGAL::ORIGIN, 2.)); + CGAL::Surface_mesh_default_criteria_3 criteria(30., 0.1, 0.1); + CGAL::make_surface_mesh(c2t3, surface, criteria, CGAL::Non_manifold_tag()); + + Surface_mesh sm; + Surface_mesh deformed; + CGAL::facets_in_complex_2_to_triangle_mesh(c2t3, sm); + deformed = sm; + + Surface_mesh quad_cage; + + const Point_3 p0(2, -2, -2); const Point_3 p0_new(5, -5, -5); + const Point_3 p1(2, 2, -2); const Point_3 p1_new(3, 3, -3); + const Point_3 p2(-2, 2, -2); const Point_3 p2_new(-2, 2, -2); + const Point_3 p3(-2, -2, -2); const Point_3 p3_new(-3, -3, -3); + + const Point_3 p4(-2, -2, 2); const Point_3 p4_new(-3, -3, 3); + const Point_3 p5(2, -2, 2); const Point_3 p5_new(4, -4, 4); + const Point_3 p6(2, 2, 2); const Point_3 p6_new(2, 2, 3); + const Point_3 p7(-2, 2, 2); const Point_3 p7_new(-3, 3, 3); + + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, quad_cage); + PMP::triangulate_faces(faces(quad_cage), quad_cage); + + CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh(quad_cage); + auto vertex_to_point_map = get_property_map(CGAL::vertex_point, deformed); + + std::vector coords; + std::vector target_cube{p0_new, p1_new, p2_new, p3_new, + p4_new, p5_new, p6_new, p7_new}; + + for(auto& v : vertices(deformed)){ + + const Point_3 vertex_val = get(vertex_to_point_map, v); + coords.clear(); + dh(vertex_val, std::back_inserter(coords)); + + FT x = FT(0), y = FT(0), z = FT(0); + for(std::size_t i = 0; i < 8; i++){ + + x += target_cube[i].x() * coords[i]; + y += target_cube[i].y() * coords[i]; + z += target_cube[i].z() * coords[i]; + } + + put(vertex_to_point_map, v, Point_3(x, y, z)); + } + + std::ofstream out_original("sphere.off"); + out_original << sm << std::endl; + + std::ofstream out_deformed("deformed_sphere.off"); + out_deformed << deformed << std::endl; +} \ No newline at end of file From da7395851e398f43ffca2c5f2de58f460605ccf8 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 27 Jul 2021 05:24:59 -0300 Subject: [PATCH 42/68] one more example and update docs --- .../Barycentric_coordinates_3.txt | 4 +- .../PackageDescription.txt | 66 +++++++------------ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../wachspress_coordinates.cpp | 46 +++++++++++++ .../include/CGAL/Barycentric_coordinates_3.h | 4 ++ .../Discrete_harmonic_coordinates_3.h | 8 +-- .../Mean_value_coordinates_3.h | 8 +-- .../Voronoi_coordinates_3.h | 4 +- .../Wachspress_coordinates_3.h | 8 +-- .../barycentric_enum_3.h | 11 +++- .../test_edge_cases.cpp | 2 +- 11 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index c1a21a0fcb44..d9a50117a902 100755 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -3,10 +3,10 @@ namespace Barycentric_coordinates { /*! \mainpage User Manual -\anchor Chapter_2D_Generalized_Barycentric_Coordinates +\anchor Chapter_3D_Generalized_Barycentric_Coordinates \cgalAutoToc -\authors Dmitry Anisimov, David Bommes, Kai Hormann, and Pierre Alliez +\authors Antonio Gomes, Dmitry Anisimov \section gbc_introduction Introduction diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 2a0096073017..d5c88e00801d 100755 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -2,45 +2,36 @@ namespace CGAL { namespace Barycentric_coordinates { /*! -\defgroup PkgBarycentricCoordinates2Ref 2D Generalized Barycentric Coordinates Reference +\defgroup PkgBarycentricCoordinates3Ref 3D Generalized Barycentric Coordinates Reference -\defgroup PkgBarycentricCoordinates2RefConcepts Concepts -\ingroup PkgBarycentricCoordinates2Ref +\defgroup PkgBarycentricCoordinates3RefConcepts Concepts +\ingroup PkgBarycentricCoordinates3Ref Generalized barycentric concepts. -\defgroup PkgBarycentricCoordinates2RefAnalytic Analytic Coordinates -\ingroup PkgBarycentricCoordinates2Ref +\defgroup PkgBarycentricCoordinates3RefAnalytic Analytic Coordinates +\ingroup PkgBarycentricCoordinates3Ref Analytic coordinates and related classes. -\defgroup PkgBarycentricCoordinates2RefHarmonic Harmonic Coordinates -\ingroup PkgBarycentricCoordinates2Ref - -Harmonic coordinates and related classes. - -\defgroup PkgBarycentricCoordinates2RefFunctions Free Functions -\ingroup PkgBarycentricCoordinates2Ref +\defgroup PkgBarycentricCoordinates3RefFunctions Free Functions +\ingroup PkgBarycentricCoordinates3Ref Free functions to compute barycentric weights and coordinates. -\defgroup PkgBarycentricCoordinates2RefDeprecated Deprecated -\ingroup PkgBarycentricCoordinates2Ref +\defgroup PkgBarycentricCoordinates3RefDeprecated Deprecated +\ingroup PkgBarycentricCoordinates3Ref -Deprecated classes and functions. -\addtogroup PkgBarycentricCoordinates2Ref - -\cgalPkgDescriptionBegin{2D Generalized Barycentric Coordinates, PkgBarycentricCoordinates2} +\cgalPkgDescriptionBegin{3D Generalized Barycentric Coordinates, PkgBarycentricCoordinates3} \cgalPkgPicture{barcoord_thumb.png} \cgalPkgSummaryBegin -\cgalPkgAuthors{Dmitry Anisimov, David Bommes, Kai Hormann, and Pierre Alliez} -\cgalPkgDesc{This package offers an efficient and robust implementation of 2D generalized barycentric -coordinates defined for simple polygons in the plane. If coordinates with respect to multivariate -scattered points instead of a polygon are required, please refer to natural neighbor -coordinates from the Package \ref PkgInterpolation2.} -\cgalPkgManuals{Chapter_2D_Generalized_Barycentric_Coordinates, PkgBarycentricCoordinates2Ref} +\cgalPkgAuthors{Antonio Gomes, Dmitry Anisimov} +\cgalPkgDesc{The package 3D Generalized Barycentric Coordinates offers an efficient and robust +implementation of three-dimensional closed-form generalized barycentric coordinates +defined for convex simplicial polytopes.} +\cgalPkgManuals{Chapter_3D_Generalized_Barycentric_Coordinates, PkgBarycentricCoordinates3Ref} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin @@ -53,29 +44,18 @@ coordinates from the Package \ref PkgInterpolation2.} \cgalClassifedRefPages ## Concepts ## -- `BarycentricTraits_2` -- `DiscretizedDomain_2` +- `BarycentricTraits_3` ## Analytic Coordinates ## -- `Wachspress_coordinates_2` -- `Mean_value_coordinates_2` -- `Discrete_harmonic_coordinates_2` - -## Harmonic Coordinates ## -- `Delaunay_domain_2` -- `Harmonic_coordinates_2` +- `Wachspress_coordinates_2` +- `Mean_value_coordinates_2` +- `Discrete_harmonic_coordinates_3` ## Free Functions ## -- `segment_coordinates_2()` -- `triangle_coordinates_2()` -- `wachspress_weights_2()` -- `wachspress_coordinates_2()` -- `mean_value_weights_2()` -- `mean_value_coordinates_2()` -- `discrete_harmonic_weights_2()` -- `discrete_harmonic_coordinates_2()` -- `harmonic_coordinates_2()` -- `boundary_coordinates_2()` +- `tetrahedron_coordinates()` +- `wachspress_coordinates_3()` +- `mean_value_coordinates_3()` +- `discrete_harmonic_coordinates_3()` */ } /* namespace Barycentric_coordinates */ diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index 57702a3ecda7..cd131e4daac2 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -15,6 +15,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("tetrahedron_coordinates.cpp") create_single_source_cgal_program("shape_deformation.cpp") + create_single_source_cgal_program("wachspress_coordinates.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp new file mode 100644 index 000000000000..b8edfd758254 --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = Kernel::FT; +using Point_3 = Kernel::Point_3; +using Surface_mesh = CGAL::Surface_mesh; +namespace PMP = CGAL::Polygon_mesh_processing; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; +using WP = CGAL::Barycentric_coordinates::Wachspress_coordinates_3; + +int main() +{ + + CGAL::Random_points_in_sphere_3 gen(4.0); + std::vector points; + + const std::size_t number_of_points = 250; + std::copy_n(gen, number_of_points, std::back_inserter(points)); + + Surface_mesh sm; + CGAL::convex_hull_3(points.begin(), points.end(), sm); + PMP::triangulate_faces(faces(sm), sm); + const std::size_t number_of_vertices = CGAL::vertices(sm).size(); + + WP wp(sm, CP3::FAST_WITH_EDGE_CASES); + + std::cout << "Computed Wachspress coordinates: " << std::endl << std::endl; + for(std::size_t i = 0; i < number_of_points; i++){ + + std::vector coordinates; + coordinates.reserve(number_of_vertices); + wp(points[i], std::back_inserter(coordinates)); + + std::cout << "Point " << i + 1 << ": " << std::endl; + for(std::size_t j = 0; j < number_of_vertices; j++) + std::cout << "Coordinate " << j + 1 << " = " << coordinates[j] << "; " << std::endl; + std::cout << std::endl; + } + + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index f62fdbd0eb6c..763dee800eaa 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -11,6 +11,10 @@ // Author(s) : Antonio Gomes, Dmitry Anisimov // +/*! + \file Barycentric_coordinates_3.h +*/ + #ifndef CGAL_BARYCENTRIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index e366506718c6..58e00430a50b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -66,7 +66,7 @@ namespace Barycentric_coordinates { Discrete_harmonic_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, + Computation_policy_3::FAST, const GeomTraits traits = GeomTraits()) : Discrete_harmonic_coordinates_3( polygon_mesh, @@ -102,11 +102,11 @@ namespace Barycentric_coordinates { switch(m_computation_policy){ - case Computation_policy_3::DEFAULT:{ + case Computation_policy_3::FAST:{ return compute_coords(query, coordinates); } - case Computation_policy_3::WITH_EDGE_CASES:{ + case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); @@ -264,7 +264,7 @@ namespace Barycentric_coordinates { const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT) { + Computation_policy_3::FAST) { using Geom_Traits = typename Kernel_traits::Kernel; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index f3169c33e7b0..1650d819a48b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -72,7 +72,7 @@ namespace Barycentric_coordinates { Mean_value_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, + Computation_policy_3::FAST, const GeomTraits traits = GeomTraits()) : Mean_value_coordinates_3( polygon_mesh, @@ -114,11 +114,11 @@ namespace Barycentric_coordinates { switch(m_computation_policy){ - case Computation_policy_3::DEFAULT:{ + case Computation_policy_3::FAST:{ return compute_coords(query, coordinates); } - case Computation_policy_3::WITH_EDGE_CASES:{ + case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); @@ -278,7 +278,7 @@ namespace Barycentric_coordinates { const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT) { + Computation_policy_3::FAST) { using Geom_Traits = typename Kernel_traits::Kernel; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h index de204172f56f..17da878872ea 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h @@ -68,7 +68,7 @@ namespace Barycentric_coordinates { Voronoi_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, + Computation_policy_3::FAST, const GeomTraits traits = GeomTraits()) : Voronoi_coordinates_3( polygon_mesh, @@ -254,7 +254,7 @@ namespace Barycentric_coordinates { const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT) { + Computation_policy_3::FAST) { using Geom_Traits = typename Kernel_traits::Kernel; using SM = CGAL::Surface_mesh; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 64b27eb1d44e..28cc0516534e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -69,7 +69,7 @@ namespace Barycentric_coordinates { Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT, + Computation_policy_3::FAST, const GeomTraits traits = GeomTraits()) : Wachspress_coordinates_3( polygon_mesh, @@ -105,11 +105,11 @@ namespace Barycentric_coordinates { switch(m_computation_policy){ - case Computation_policy_3::DEFAULT:{ + case Computation_policy_3::FAST:{ return compute_coords(query, coordinates); } - case Computation_policy_3::WITH_EDGE_CASES:{ + case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); @@ -257,7 +257,7 @@ namespace Barycentric_coordinates { const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = - Computation_policy_3::DEFAULT) { + Computation_policy_3::FAST) { using Geom_Traits = typename Kernel_traits::Kernel; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h index 7fca4e5fe132..bbb24c0203f4 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -17,12 +17,19 @@ // #include namespace CGAL { + +/*! + \ingroup PkgBarycentricCoordinates3Ref + + The namespace `Barycentric_coordinates` contains implementations of all + generalized barycentric coordinates: 2D, 3D, related enumerations, etc. +*/ namespace Barycentric_coordinates { enum class Computation_policy_3 { - DEFAULT = 0, - WITH_EDGE_CASES = 1 + FAST = 0, + FAST_WITH_EDGE_CASES = 1 }; diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp index 45d9a52705a3..58077fbdd53a 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_edge_cases.cpp @@ -59,7 +59,7 @@ void test_coordinate() { std::back_inserter(sample_points), 100); // BC coordinates - BC bc_tetrahedron(tetrahedron, CP3::WITH_EDGE_CASES); + BC bc_tetrahedron(tetrahedron, CP3::FAST_WITH_EDGE_CASES); std::vector bc_coordinates_tetrahedron; int num_samples = 0; From 6e0214e42835ea2030ce3ba7cac6611ef12ba556 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 27 Jul 2021 21:41:32 -0300 Subject: [PATCH 43/68] adding final example --- .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../mean_value_coordinates.cpp | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index cd131e4daac2..067adcc8f90e 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -16,6 +16,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("tetrahedron_coordinates.cpp") create_single_source_cgal_program("shape_deformation.cpp") create_single_source_cgal_program("wachspress_coordinates.cpp") + create_single_source_cgal_program("mean_value_coordinates.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp new file mode 100644 index 000000000000..ec51d080bd9c --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp @@ -0,0 +1,46 @@ +#define PHI 1.6180339887498948482 + +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = Kernel::FT; +using Point_3 = Kernel::Point_3; +using Surface_mesh = CGAL::Surface_mesh; +namespace PMP = CGAL::Polygon_mesh_processing; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; +using MV = CGAL::Barycentric_coordinates::Mean_value_coordinates_3; + +int main(){ + + Surface_mesh icosahedron; + CGAL::make_icosahedron(icosahedron, Point_3(0.0, 0.0, 0.0), 2.0); + PMP::triangulate_faces(faces(icosahedron), icosahedron); + + std::vector coords; + std::vector queries{ + Point_3(-1, 1 + PHI, PHI), Point_3(0.5, (1+3*PHI)/2, PHI/2), Point_3(1, 1+PHI, -PHI), //Boundary + Point_3(-1, 1, 1), Point_3(0, 0, 1), Point_3(0, 2, 1), //Interior + Point_3(0, 2*PHI, 4), Point_3(0, 3, 2*PHI), Point_3(4, 0, 0)}; //EXterior + + std::cout << std::endl << "Mean value coordinates : " << std::endl << std::endl; + + for (const auto& query : queries){ + + coords.clear(); + CGAL::Barycentric_coordinates::mean_value_coordinates_3( + icosahedron, query, std::back_inserter(coords), CP3::FAST_WITH_EDGE_CASES); + + // Output mean value coordinates. + for (std::size_t i = 0; i < coords.size() -1; ++i) { + std::cout << coords[i] << ", "; + } + std::cout << coords[coords.size() -1] << std::endl; + } + std::cout << std::endl; + + + return EXIT_SUCCESS; +} \ No newline at end of file From 9acc3e2a615dce6e0b72b05ff57f80f7b4e0e64d Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 28 Jul 2021 07:32:53 -0300 Subject: [PATCH 44/68] fixed some examples + updated the docs --- .../Barycentric_coordinates_3.txt | 36 +++-- ...entricTraits_2.h => BarycentricTraits_3.h} | 28 ++-- .../Concepts/DiscretizedDomain_2.h | 70 -------- .../deprecated/BarycentricCoordinates_2.h | 88 ---------- .../PackageDescription.txt | 9 +- .../Barycentric_coordinates_3/dependencies | 0 .../Barycentric_coordinates_3/examples.txt | 14 +- .../fig/aff_coord_example.svg | 0 .../fig/analytic_timings.png | Bin .../fig/barcoord_thumb.png | Bin .../fig/dh_coord_example.svg | 0 .../fig/dh_notations.svg | 0 .../fig/hm_4_bench.svg | 0 .../fig/hm_n_bench.svg | 0 .../fig/image_warping.png | Bin 0 -> 60489 bytes .../fig/mv_coord_example.svg | 0 .../fig/mv_notations.svg | 0 .../fig/mv_weight_signs.svg | 0 .../fig/overview.svg | 0 .../fig/seg_coord.svg | 0 .../fig/seg_coord_example.svg | 0 .../fig/seg_coord_projection.svg | 0 .../fig/shape_deformation.svg | 0 .../Barycentric_coordinates_3/fig/terrain.svg | 0 .../fig/terrain_interpolation.png | Bin .../fig/terrain_triangulation.svg | 0 .../fig/tri_coord.svg | 0 .../fig/tri_coord_example.svg | 0 .../fig/tri_notations.svg | 0 .../fig/wp_coord_example.svg | 0 .../fig/wp_notations.svg | 0 .../fig/wp_zero_set.svg | 0 .../shape_deformation.cpp | 18 +-- .../tetrahedron_coordinates.cpp | 11 +- .../include/CGAL/Barycentric_coordinates_3.h | 11 +- .../Discrete_harmonic_coordinates_3.h | 150 ++++++++++++++++- .../Mean_value_coordinates_3.h | 147 ++++++++++++++++- .../Wachspress_coordinates_3.h | 152 +++++++++++++++++- .../barycentric_enum_3.h | 21 ++- .../tetrahedron_coordinates.h | 89 +++++++++- 40 files changed, 611 insertions(+), 233 deletions(-) mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt rename Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/{BarycentricTraits_2.h => BarycentricTraits_3.h} (88%) mode change 100755 => 100644 delete mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h delete mode 100755 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt old mode 100755 new mode 100644 index d9a50117a902..c68a9743cb61 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -268,25 +268,39 @@ The interpolated data. The color bar represents the corresponding height. \subsection shape_deform_example Shape Deformation + This is another advanced example that shows how to use generalized barycentric coordinates in order to deform a given 2D shape into another shape as shown in the figure below. Harmonic coordinates satisfy all the properties of barycentric coordinates for complicated concave polygons and hence this is our choice to perform a shape deformation. Note that even though harmonic coordinates are guaranteed to be positive inside a polygon, they do not guarantee a bijective mapping between the source and target shapes that is -the target mesh can fold over the target polygon after the mapping (see the little fold over +the target mesh can fold-over the target polygon after the mapping (see the little fold-over in the left shoulder of the target shape). \cgalFigureBegin{shape_deformation_example, shape_deformation.svg} -The shape on the left is deformed into the shape on the right. The zoom shows a fold over +The shape on the left is deformed into the shape on the right. The zoom shows a fold-over in the left shoulder of the target shape where the red triangle goes over the polygon boundary. \cgalFigureEnd \anchor deformation_example \cgalExample{Barycentric_coordinates_2/shape_deformation.cpp} +But despite the possible fold-overs, a similar technique can be used for image warping +in 2D and character articulation in 3D. For example in 2D, we first enclose an image, +which we want to deform, into a simple polygon so-called *cage*, we then +bound each image pixel to this cage using barycentric coordinates, and finally deform +this cage into a new one, which also deforms the underlying image, as shown +in the figure below for harmonic coordinates. + +\cgalFigureBegin{image_warping_example, image_warping.png} +An image on the left is deformed into a new image on the right using a 2D concave polygon (grey) +and harmonic coordinates computed at each image pixel with respect to the vertices of this polygon. +\cgalFigureEnd + \subsection aff_example Affine Coordinates + This is an example, where we show how a lambda expression can be used to define a model of generalized barycentric coordinates. To make this example @@ -303,16 +317,18 @@ Example's point pattern. \subsection depr_example Deprecated Coordinates -This example illustrates how to quickly update the code in order to use the deprecated -version of mean value coordinates that is the version below 5.2. Basically, one -needs to add the suffix `_depr` to the namespace and include the corresponding -deprecated headers. The used `Kernel` is inexact and the used coordinates are -mean value coordinates. The result is identical to the one from -\ref mv_coord_example "this example". + +This example illustrates how to use the deprecated API of this package. +The used `Kernel` is inexact and the used coordinates are mean value coordinates. +The result is identical to the one from \ref mv_coord_example "this example". \anchor depr_coord_example \cgalExample{Barycentric_coordinates_2/deprecated_coordinates.cpp} +\note The headers `Segment_coordinates_2.h` and `Triangle_coordinates_2.h` are +not capitalized in the new version that is they are named `segment_coordinates_2.h` +and `triangle_coordinates_2.h`. + \section gbc_degeneracies Edge Cases @@ -548,7 +564,7 @@ After the normalization of these weights as before we obtain the max precision \f$O(n^2)\f$ algorithm. The max speed O(n) algorithm computes the -weights \f$w_i\f$ using the pseudocode from here. +weights \f$w_i\f$ using the pseudocode from here. These weights

@@ -808,6 +824,7 @@ second table, the slowest step is to solve for coordinates, as expected. \section gbc_history History + The package was first released in 2015 and included segment, triangle, Wachspress, discrete harmonic, and mean value coordinates. The API of that version is now deprecated but can still be used. An example of the old API can be found \ref depr_example "here". @@ -832,6 +849,7 @@ and modified so that they can be used now on their own without the class `Genera \section gbc_acknowledgments Acknowledgments + The authors wish to thank Teseo Schneider and Randolf Schaerfig for helpful comments and discussions. We also appreciate the great effort invested in this package by our reviewers Andreas Fabri, Sébastien Loriot, diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h old mode 100755 new mode 100644 similarity index 88% rename from Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h rename to Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h index 1f5e7aed7e17..b1b2921af46a --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_2.h +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h @@ -2,17 +2,17 @@ namespace CGAL { namespace Barycentric_coordinates { /*! -\ingroup PkgBarycentricCoordinates2RefConcepts +\ingroup PkgBarycentricCoordinates3RefConcepts \cgalConcept A concept that describes the set of requirements of the template parameter -`GeomTraits` used to parameterize all classes and functions with 2D barycentric +`GeomTraits` used to parameterize all classes and functions with 3D barycentric coordinates from the namespace `CGAL::Barycentric_coordinates`. \cgalHasModel - All models of `Kernel` */ -class BarycentricTraits_2 { +class BarycentricTraits_3 { public: @@ -24,34 +24,24 @@ class BarycentricTraits_2 { */ typedef unspecified_type FT; -/*! - `CGAL::Comparison_result` or `Uncertain`. -*/ -typedef unspecified_type Comparison_result; - -/*! - `CGAL::Orientation` or `Uncertain`. -*/ -typedef unspecified_type Orientation; - /// @} -/// \name 2D Geometric Objects +/// \name 3D Geometric Objects /// @{ /*! - A model of `Kernel::Point_2`. + A model of `Kernel::Point_3`. */ -typedef unspecified_type Point_2; +typedef unspecified_type Point_3; /*! - A model of `Kernel::Vector_2`. + A model of `Kernel::Vector_3`. */ -typedef unspecified_type Vector_2; +typedef unspecified_type Vector_3; /// @} -/// \name 2D Generalized Constructions +/// \name 3D Generalized Constructions /// @{ /*! diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h deleted file mode 100755 index 5804d2505163..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/DiscretizedDomain_2.h +++ /dev/null @@ -1,70 +0,0 @@ -namespace CGAL { -namespace Barycentric_coordinates { - -/*! -\ingroup PkgBarycentricCoordinates2RefConcepts -\cgalConcept - -A concept that describes the set of methods that should be defined for all -discretized domains obtained by meshing the interior part of a simple polygon. - -After meshing, the interior part of the polygon is split into multiple finite -elements, which share common edges and vertices. These finite elements are then -used to approximate certain types of generalized barycentric coordinate functions. -The domain is bounded by the polygon. - -\cgalHasModel -- `Delaunay_domain_2` -*/ -class DiscretizedDomain_2 { - -public: - - /*! - returns the number of vertices after meshing the domain. - */ - std::size_t number_of_vertices() const { - - } - - /*! - returns a const reference to the vertex with the index `query_index`, the - `Vertex_2` type being a model of `Kernel::Point_2`. - */ - const Vertex_2& vertex( - const std::size_t query_index) const { - - } - - /*! - verifies if the vertex with the index `query_index` is on the - boundary of the domain. - */ - bool is_on_boundary( - const std::size_t query_index) const { - - } - - /*! - fills `neighbors` with the indices of the vertices, which form the one-ring - neighborhood of the vertex with the index `query_index`, the neighbors have to - be in the counterclockwise order and form a simple polygon. - */ - void operator()( - const std::size_t query_index, std::vector& neighbors) { - - } - - /*! - fills `indices` with the indices of the vertices, which form a finite element - of the domain, that contains a `query` point; if no indices are found, the - `query` point does not belong to the domain; the type `Query_2` is a model of `Kernel::Point_2`. - */ - void locate( - const Query_2& query, std::vector& indices) { - - } -}; - -} // namespace Barycentric_coordinates -} // namespace CGAL diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h deleted file mode 100755 index 1e1df7a5727a..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/deprecated/BarycentricCoordinates_2.h +++ /dev/null @@ -1,88 +0,0 @@ -namespace CGAL { -namespace Barycentric_coordinates_depr { - -/*! -\ingroup PkgBarycentricCoordinates2RefConcepts -\cgalConcept - -A concept that describes the set of methods that should be defined for all coordinate -models used to parameterize the class `Generalized_barycentric_coordinates_2`. - -\cgalHasModel -- `Wachspress_2` -- `Mean_value_2` -- `Discrete_harmonic_2` - -\deprecated This part of the package is deprecated since the version 5.4 of \cgal. -*/ -class BarycentricCoordinates_2 { - -public: - - /// \name Creation - /// @{ - - /*! - Creates a class that implements generalized barycentric coordinates for any query - point that does not belong to the polygon's boundary. The polygon is given by a - range of vertices of the type `Traits::Point_2` stored in a container of the - type `std::vector`. - */ - BarycentricCoordinates_2( - const std::vector& vertices, const Traits& barycentric_traits) { - - } - - /// @} - - /// \name Functions - /// @{ - - /*! - A function that computes generalized barycentric coordinates without normalization - that are called generalized baycentric weights (as fast as possible algorithm is used). - Weights are computed with respect to a query point of the type `Traits::Point_2` and - stored in the output iterator `output`. The function returns a pointer to the last stored element. - */ - boost::optional - weights( - const Traits::Point_2& query_point, OutputIterator& output) { - - } - - /*! - A function that computes generalized barycentric coordinates on the bounded side - of a polygon with one of two possible algorithms: one is precise and one is fast. - The algorithm type is specified by the parameter `type_of_algorithm`. Coordinates - are computed with respect to a query point of the type `Traits::Point_2` and stored - in the output iterator `output`. The function returns a pointer to the last stored element. - */ - boost::optional - coordinates_on_bounded_side( - const Traits::Point_2& query_point, - OutputIterator& output, - const Type_of_algorithm type_of_algorithm) { - - } - - /*! - A function that computes generalized barycentric coordinates on the unbounded side - of a polygon with one of two possible algorithms: one is precise and one is fast. - The algorithm type is specified by the parameter `type_of_algorithm`. Coordinates - are computed with respect to a query point of the type `Traits::Point_2` and stored - in the output iterator `output`. The function returns a pointer to the last stored element. - */ - boost::optional - coordinates_on_unbounded_side( - const Traits::Point_2& query_point, - OutputIterator& output, - const Type_of_algorithm type_of_algorithm) { - - } - -/// @} - -}; - -} // namespace Barycentric_coordinates_depr -} // namespace CGAL diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt old mode 100755 new mode 100644 index d5c88e00801d..0b6627ae911b --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -19,9 +19,7 @@ Analytic coordinates and related classes. Free functions to compute barycentric weights and coordinates. -\defgroup PkgBarycentricCoordinates3RefDeprecated Deprecated -\ingroup PkgBarycentricCoordinates3Ref - +\addtogroup PkgBarycentricCoordinates3Ref \cgalPkgDescriptionBegin{3D Generalized Barycentric Coordinates, PkgBarycentricCoordinates3} \cgalPkgPicture{barcoord_thumb.png} @@ -45,10 +43,11 @@ defined for convex simplicial polytopes.} ## Concepts ## - `BarycentricTraits_3` +- `BarycentricCoordinates_3` ## Analytic Coordinates ## -- `Wachspress_coordinates_2` -- `Mean_value_coordinates_2` +- `Wachspress_coordinates_3` +- `Mean_value_coordinates_3` - `Discrete_harmonic_coordinates_3` ## Free Functions ## diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt old mode 100755 new mode 100644 index e4e0873bcd03..d3b5fb955e34 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt @@ -1,12 +1,6 @@ /*! -\example Barycentric_coordinates_2/segment_coordinates.cpp -\example Barycentric_coordinates_2/triangle_coordinates.cpp -\example Barycentric_coordinates_2/wachspress_coordinates.cpp -\example Barycentric_coordinates_2/discrete_harmonic_coordinates.cpp -\example Barycentric_coordinates_2/mean_value_coordinates.cpp -\example Barycentric_coordinates_2/harmonic_coordinates.cpp -\example Barycentric_coordinates_2/terrain_height_modeling.cpp -\example Barycentric_coordinates_2/shape_deformation.cpp -\example Barycentric_coordinates_2/affine_coordinates.cpp -\example Barycentric_coordinates_2/deprecated_coordinates.cpp +\example Barycentric_coordinates_3/tetrahedron_coordinates.cpp +\example Barycentric_coordinates_3/wachspress_coordinates.cpp +\example Barycentric_coordinates_3/mean_value_coordinates.cpp +\example Barycentric_coordinates_3/shape_deformation.cpp */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_coord_example.svg old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg old mode 100755 new mode 100644 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png new file mode 100644 index 0000000000000000000000000000000000000000..0afe949b3b99037be9a9f71c4fe82dcf6c59dc3d GIT binary patch literal 60489 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 queries = { Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points - Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), Point_3(1.0/3, 1.0/3, 1.0/3), // boundary query points - Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f) // exterior query points - }; + const std::vector queries = { + Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), // interior query points + Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points + Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), // boundary query points + Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), // boundary query points + Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), // exterior query points + Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f)}; // exterior query point // Compute tetrahedra coordinates; std::vector coordinates; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index 763dee800eaa..baf822287125 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -11,15 +11,18 @@ // Author(s) : Antonio Gomes, Dmitry Anisimov // -/*! - \file Barycentric_coordinates_3.h -*/ - #ifndef CGAL_BARYCENTRIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_COORDINATES_3_H // #include +/** +* \ingroup PkgBarycentricCoordinates3Ref +* \file CGAL/Barycentric_coordinates_3.h +* A convenience header that includes all free functions and classes for +* 3D barycentric coordinates in closed form. +*/ + #include #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 58e00430a50b..34bfa23b439e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -23,6 +23,29 @@ namespace CGAL { namespace Barycentric_coordinates { + /*! + \ingroup PkgBarycentricCoordinates3RefAnalytic + + \brief 3D discrete harmonic coordinates. + + This class implements 2D discrete harmonic coordinates ( \cite cgal:bc:fhk-gcbcocp-06, + \cite cgal:pp-cdmsc-93, \cite cgal:bc:eddhls-maam-95 ), which can be computed + at any point inside a strictly convex polygon. + + Discrete harmonic coordinates are well-defined in the closure of a strictly + convex polygon but they are not necessarily positive. The coordinates are + computed analytically. See more details in the user manual \ref compute_dh_coord "here". + + \tparam VertexRange + a model of `ConstRange` whose iterator type is `RandomAccessIterator` + + \tparam GeomTraits + a model of `BarycentricTraits_3` + + \tparam PointMap + a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and + value type is `Point_2`. The default is `CGAL::Identity_property_map`. + */ template< typename PolygonMesh, typename GeomTraits, @@ -31,6 +54,11 @@ namespace Barycentric_coordinates { class Discrete_harmonic_coordinates_3 { public: + + /// \name Types + /// @{ + + /// \cond SKIP_IN_MANUAL using Polygon_mesh = PolygonMesh; using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; @@ -39,11 +67,47 @@ namespace Barycentric_coordinates { using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Dot_3 = typename GeomTraits::Compute_scalar_product_3; using Sqrt = typename internal::Get_sqrt::Sqrt; + /// \endcond + /// Number type. typedef typename GeomTraits::FT FT; + + /// Point type. typedef typename GeomTraits::Point_3 Point_3; + + /// Vector type. typedef typename GeomTraits::Vector_3 Vector_3; + /// @} + + /// \name Initialization + /// @{ + + /*! + \brief initializes all internal data structures. + + This class implements the behavior of discrete harmonic coordinates + for 2D query points. + + \param polygon + an instance of `VertexRange` with the vertices of a strictly convex polygon + + \param policy + one of the `Computation_policy_2`; + the default is `Computation_policy_2::PRECISE_WITH_EDGE_CASES` + + \param traits + a traits class with geometric objects, predicates, and constructions; + the default initialization is provided + + \param point_map + an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`; + the default initialization is provided + + \pre polygon.size() >= 3 + \pre polygon is simple + \pre polygon is strictly convex + */ Discrete_harmonic_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy, @@ -63,6 +127,8 @@ namespace Barycentric_coordinates { m_weights.resize(vertices(m_polygon_mesh).size()); } + /// @} + Discrete_harmonic_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = @@ -74,14 +140,40 @@ namespace Barycentric_coordinates { get_const_property_map(CGAL::vertex_point, polygon_mesh), traits) { } + /// \name Access + /// @{ + + + /*! + \brief computes 2D discrete harmonic coordinates. + + This function fills `c_begin` with 2D discrete harmonic coordinates computed + at the `query` point with respect to the vertices of the input polygon. + + The number of returned coordinates equals to the number of polygon vertices. + + After the coordinates \f$b_i\f$ with \f$i = 1\dots n\f$ are computed, where + \f$n\f$ is the number of polygon vertices, the query point \f$q\f$ can be obtained + as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polygon vertices. + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `FT` + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + */ template OutIterator operator()(const Point_3& query, OutIterator c_begin) { return compute(query, c_begin); } - VertexToPointMap get_vertex_to_point_map(){ - return m_vertex_to_point_map; - } + /// @} private: const PolygonMesh& m_polygon_mesh; @@ -255,6 +347,58 @@ namespace Barycentric_coordinates { }; + + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes 2D discrete harmonic weights. + + This function computes 2D discrete harmonic weights at a given `query` point + with respect to the vertices of a strictly convex `polygon`, that is one + weight per vertex. The weights are stored in a destination range + beginning at `w_begin`. + + Internally, the class `Discrete_harmonic_coordinates_2` is used. If one wants to process + multiple query points, it is better to use that class. When using the free function, + internal memory is allocated for each query point, while when using the class, + it is allocated only once, which is much more efficient. However, for a few query + points, it is easier to use this function. It can also be used when the processing + time is not a concern. + + \tparam PointRange + a model of `ConstRange` whose iterator type is `RandomAccessIterator` + and value type is `GeomTraits::Point_2` + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \tparam GeomTraits + a model of `BarycentricTraits_2` + + \param polygon + an instance of `PointRange` with 2D points, which form a strictly convex polygon + + \param query + a query point + + \param w_begin + the beginning of the destination range with the computed weights + + \param traits + a traits class with geometric objects, predicates, and constructions; + this parameter can be omitted if the traits class can be deduced from the point type + + \param policy + one of the `Computation_policy_2`; + the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + + \return an output iterator to the element in the destination range, + one past the last weight stored + + \pre polygon.size() >= 3 + \pre polygon is simple + \pre polygon is strictly convex + */ template< typename Point_3, typename Mesh, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 1650d819a48b..d5ea7e1a2196 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -23,6 +23,29 @@ namespace CGAL { namespace Barycentric_coordinates { + /*! + \ingroup PkgBarycentricCoordinates3RefAnalytic + + \brief 3D mean value coordinates. + + This class implements 3D mean value coordinates, which can be computed + at any point in the plane. + + Mean value coordinates are well-defined everywhere in the plane and are + non-negative in the kernel of a star-shaped polygon. The coordinates are + computed analytically. See more details in the user manual \ref compute_mv_coord "here". + + \tparam PolygonMesh + must be a model of the concept `FaceListGraph`. + + \tparam GeomTraits + a model of `BarycentricTraits_3` + + \tparam PointMap + a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector::const_type`. + */ template< typename PolygonMesh, typename GeomTraits, @@ -31,6 +54,11 @@ namespace Barycentric_coordinates { class Mean_value_coordinates_3 { public: + + /// \name Types + /// @{ + + /// \cond SKIP_IN_MANUAL using Polygon_mesh = PolygonMesh; using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; @@ -40,11 +68,47 @@ namespace Barycentric_coordinates { using Dot_3 = typename GeomTraits::Compute_scalar_product_3; using Sqrt = typename internal::Get_sqrt::Sqrt; using Approximate_angle_3 = typename GeomTraits::Compute_approximate_angle_3; + /// \endcond + /// Number type. typedef typename GeomTraits::FT FT; + + /// Point type. typedef typename GeomTraits::Point_3 Point_3; + + /// Vector type. typedef typename GeomTraits::Vector_3 Vector_3; + /// @} + + /// \name Initialization + /// @{ + + /*! + \brief initializes all internal data structures. + + This class implements the behavior of mean value coordinates + for 3D query points. + + \param polygon_mesh + an instance of `PolygonMesh` with the vertices of a simple polyhedron + + \param policy + one of the `Computation_policy_3`; + the default is `Computation_policy_3::FAST` + + \param traits + a traits class with geometric objects, predicates, and constructions; + the default initialization is provided + + \param point_map + an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + the default initialization is provided + + \pre vertices(polygon_mesh).size() >= 4 + \pre polygon_mesh is strongly convex + \pre polygon_mesh should only have triangular faces. + */ Mean_value_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy, @@ -69,6 +133,8 @@ namespace Barycentric_coordinates { angles.resize(3); } + /// @} + Mean_value_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = @@ -80,14 +146,39 @@ namespace Barycentric_coordinates { get_const_property_map(CGAL::vertex_point, polygon_mesh), traits) { } + /// \name Access + /// @{ + + /*! + \brief computes 3D mean value coordinates. + + This function fills `c_begin` with 3D mean value coordinates computed + at the `query` point with respect to the vertices of the input surface mesh. + + The number of returned coordinates equals to the number of vertices. + + After the coordinates \f$b_i\f$ with \f$i = 1\dots n\f$ are computed, where + \f$n\f$ is the number of vertices, the query point \f$q\f$ can be obtained + as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polyhedron vertices. + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `FT` + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + */ template OutIterator operator()(const Point_3& query, OutIterator c_begin) { return compute(query, c_begin); } - VertexToPointMap get_vertex_to_point_map(){ - return m_vertex_to_point_map; - } + /// @} private: const PolygonMesh& m_polygon_mesh; @@ -269,6 +360,56 @@ namespace Barycentric_coordinates { } }; + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes 3D mean value weights. + + This function computes 3D mean value weights at a given `query` point + with respect to the vertices of a simple `polyhedron`, that is one + weight per vertex. The weights are stored in a destination range + beginning at `c_begin`. + + Internally, the class `Mean_value_coordinates_3` is used. If one wants to process + multiple query points, it is better to use that class. When using the free function, + internal memory is allocated for each query point, while when using the class, + it is allocated only once, which is much more efficient. However, for a few query + points, it is easier to use this function. It can also be used when the processing + time is not a concern. + + \tparam Point_3 + A model of `Kernel::Point_3`. + + \tparam Mesh + must be a model of the concept `FaceListGraph`. + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \param surface_mesh + an instance of `PolygonMesh` with the vertices of a simple polyhedron + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed weights + + \param traits + a traits class with geometric objects, predicates, and constructions; + this parameter can be omitted if the traits class can be deduced from the point type + + \param policy + one of the `Computation_policy_3`; + the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + + \return an output iterator to the element in the destination range, + one past the last weight stored + + \pre vertices(polygon_mesh).size() >= 4 + \pre polygon_mesh is strongly convex + \pre polygon_mesh should only have triangular faces. + */ template< typename Point_3, typename Mesh, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 28cc0516534e..33db0e4a93b9 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -23,6 +23,30 @@ namespace CGAL { namespace Barycentric_coordinates { + + /*! + \ingroup PkgBarycentricCoordinates3RefAnalytic + + \brief 2D Wachspress coordinates. + + This class implements 2D Wachspress coordinates ( \cite cgal:bc:fhk-gcbcocp-06, + \cite cgal:bc:mlbd-gbcip-02, \cite cgal:bc:w-rfeb-75 ), which can be computed + at any point inside a strictly convex polygon. + + Wachspress coordinates are well-defined and non-negative in the closure + of a strictly convex polygon. The coordinates are computed analytically. + See more details in the user manual \ref compute_wp_coord "here". + + \tparam VertexRange + a model of `ConstRange` whose iterator type is `RandomAccessIterator` + + \tparam GeomTraits + a model of `BarycentricTraits_2` + + \tparam PointMap + a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and + value type is `Point_2`. The default is `CGAL::Identity_property_map`. + */ template< typename PolygonMesh, typename GeomTraits, @@ -31,6 +55,11 @@ namespace Barycentric_coordinates { class Wachspress_coordinates_3 { public: + + /// \name Types + /// @{ + + /// \cond SKIP_IN_MANUAL using Polygon_mesh = PolygonMesh; using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; @@ -39,14 +68,47 @@ namespace Barycentric_coordinates { using Det_3 = typename GeomTraits::Compute_determinant_3; using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; using Construct_vec_3 = typename GeomTraits::Construct_vector_3; + /// \endcond + /// Number type. typedef typename GeomTraits::FT FT; + + /// Point type. typedef typename GeomTraits::Point_3 Point_3; + + /// Vector type. typedef typename GeomTraits::Vector_3 Vector_3; - typedef typename GeomTraits::Plane_3 Plane_3; - typedef typename GeomTraits::Point_2 Point_2; - public: + /// @} + + /// \name Initialization + /// @{ + + /*! + \brief initializes all internal data structures. + + This class implements the behavior of Wachspress coordinates + for 2D query points. + + \param polygon + an instance of `VertexRange` with the vertices of a strictly convex polygon + + \param policy + one of the `Computation_policy_2`; + the default is `Computation_policy_2::PRECISE_WITH_EDGE_CASES` + + \param traits + a traits class with geometric objects, predicates, and constructions; + the default initialization is provided + + \param point_map + an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`; + the default initialization is provided + + \pre polygon.size() >= 3 + \pre polygon is simple + \pre polygon is strictly convex + */ Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy, @@ -66,6 +128,8 @@ namespace Barycentric_coordinates { m_weights.resize(vertices(m_polygon_mesh).size()); } + /// @} + Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = @@ -77,14 +141,39 @@ namespace Barycentric_coordinates { get_const_property_map(CGAL::vertex_point, polygon_mesh), traits) { } + /// \name Access + /// @{ + + /*! + \brief computes 2D Wachspress coordinates. + + This function fills `c_begin` with 2D Wachspress coordinates computed + at the `query` point with respect to the vertices of the input polygon. + + The number of returned coordinates equals to the number of polygon vertices. + + After the coordinates \f$b_i\f$ with \f$i = 1\dots n\f$ are computed, where + \f$n\f$ is the number of polygon vertices, the query point \f$q\f$ can be obtained + as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polygon vertices. + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `FT` + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + */ template OutIterator operator()(const Point_3& query, OutIterator c_begin) { return compute(query, c_begin); } - VertexToPointMap get_vertex_to_point_map(){ - return m_vertex_to_point_map; - } + /// @} private: const PolygonMesh& m_polygon_mesh; @@ -248,6 +337,57 @@ namespace Barycentric_coordinates { }; + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes 2D Wachspress weights. + + This function computes 2D Wachspress weights at a given `query` point + with respect to the vertices of a strictly convex `polygon`, that is one + weight per vertex. The weights are stored in a destination range + beginning at `w_begin`. + + Internally, the class `Wachspress_coordinates_2` is used. If one wants to process + multiple query points, it is better to use that class. When using the free function, + internal memory is allocated for each query point, while when using the class, + it is allocated only once, which is much more efficient. However, for a few query + points, it is easier to use this function. It can also be used when the processing + time is not a concern. + + \tparam PointRange + a model of `ConstRange` whose iterator type is `RandomAccessIterator` + and value type is `GeomTraits::Point_2` + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \tparam GeomTraits + a model of `BarycentricTraits_2` + + \param polygon + an instance of `PointRange` with 2D points, which form a strictly convex polygon + + \param query + a query point + + \param w_begin + the beginning of the destination range with the computed weights + + \param traits + a traits class with geometric objects, predicates, and constructions; + this parameter can be omitted if the traits class can be deduced from the point type + + \param policy + one of the `Computation_policy_2`; + the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + + \return an output iterator to the element in the destination range, + one past the last weight stored + + \pre polygon.size() >= 3 + \pre polygon is simple + \pre polygon is strictly convex + */ template< typename Point_3, typename Mesh, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h index bbb24c0203f4..0b6166790c8c 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -26,13 +26,32 @@ namespace CGAL { */ namespace Barycentric_coordinates { +/// \name Computation Policies +/// @{ + +/*! + `Computation_policy_3` provides a way to choose an asymptotic time complexity + of the algorithm and its precision for computing 3D barycentric weights and coordinates. +*/ enum class Computation_policy_3 { + /*! + Computation has a linear time complexity with respect to the number of + polygon vertices, but may suffer imprecisions near the polygon boundary. + No extra checks are carried out. + */ FAST = 0, - FAST_WITH_EDGE_CASES = 1 + /*! + Computation has a linear time complexity with respect to the number of + polygon vertices, but may suffer imprecisions near the polygon boundary. In + addition, we check a position of the query point with respect to the polygon + and use different computation strategies for different positions. + */ + FAST_WITH_EDGE_CASES = 1 }; +/// @} } // namespace Barycentric_coordinates } // namespace CGAL diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index 5966eebc58ab..e77d5c96e515 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -21,7 +21,53 @@ namespace CGAL{ namespace Barycentric_coordinates{ - //return iterator (infer from traits) + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes tetrahedron coordinates. + + This function computes barycentric coordinates at a given `query` point + with respect to the points `p0`, `p1`, `p2`, and `p3, which form a tetrahedron, that is one + coordinate per point. The coordinates are stored in a destination range + beginning at `c_begin`. + + After the coordinates \f$b_0\f$, \f$b_1\f$, \f$b_2\f$, and \f$b_2\f$ are computed, the query + point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. See more details + in the user manual \ref compute_tetra_coord "here". + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \tparam GeomTraits + a model of `BarycentricTraits_3` + + \param p0 + the first vertex of a tetrahedron + + \param p1 + the second vertex of a tetrahedron + + \param p2 + the third vertex of a tetrahedron + + \param p3 + the fourth vertex of a tetrahedron + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \param traits + a traits class with geometric objects, predicates, and constructions; + this parameter can be omitted if the traits class can be deduced from the point type + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + + \pre volume_3(p0, p1, p2, p3) != 0 + */ template< typename OutIterator, typename GeomTraits> @@ -56,7 +102,46 @@ namespace Barycentric_coordinates{ p0, p1, p2, p3, query, c_begin, traits); } - //return tuple (infer from traits) + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes tetrahedron coordinates. + + This function computes barycentric coordinates at a given `query` point + with respect to the points `p0`, `p1`, `p2`, and `p3`, which form a triangle, that is one + coordinate per point. The coordinates are returned in a tuple. + + After the coordinates \f$b_0\f$, \f$b_1\f$, \f$b_2\f$, and \f$b_3\f$ are computed, the query + point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. See more details + in the user manual \ref compute_tetra_coord "here". + + \tparam GeomTraits + a model of `BarycentricTraits_3` + + \param p0 + the first vertex of a tetrahedron + + \param p1 + the second vertex of a tetrahedron + + \param p2 + the third vertex of a tetrahedron + + \param p3 + the fourth vertex of a tetrahedron + + \param query + a query point + + \param traits + a traits class with geometric objects, predicates, and constructions; + this parameter can be omitted if the traits class can be deduced from the point type + + \return a tuple `std::tuple` + with the computed coordinates + + \pre volume_3(p0, p1, p2, p3) != 0 + */ template std::tuple< typename GeomTraits::FT, From 2ea40bce12b7b3e44b2f2518283a8c2968b93364 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 29 Jul 2021 09:09:01 -0300 Subject: [PATCH 45/68] first version docs --- .../Barycentric_coordinates_3.txt | 853 +-- .../Concepts/BarycentricTraits_3.h | 118 +- .../PackageDescription.txt | 7 +- .../fig/aff_coord_example.svg | 81 - .../fig/analytic_timings.png | Bin 77272 -> 0 bytes .../fig/dh_coord_example.svg | 126 - .../fig/dh_notations.svg | 302 -- .../fig/hm_4_bench.svg | 371 -- .../fig/hm_n_bench.svg | 201 - .../fig/image_warping.png | Bin 60489 -> 0 bytes .../fig/mv_coord_example.svg | 135 - .../fig/mv_notations.svg | 153 - .../fig/mv_weight_signs.svg | 173 - .../fig/overview.svg | 260 - .../fig/seg_coord.svg | 69 - .../fig/seg_coord_example.svg | 76 - .../fig/seg_coord_projection.svg | 113 - .../fig/shape_deformation.svg | 1100 ---- .../Barycentric_coordinates_3/fig/terrain.svg | 195 - .../fig/terrain_interpolation.png | Bin 46508 -> 0 bytes .../fig/terrain_triangulation.svg | 4633 ----------------- .../fig/tri_coord.svg | 56 - .../fig/tri_coord_example.svg | 112 - .../fig/tri_notations.svg | 75 - .../fig/wp_coord_example.svg | 174 - .../fig/wp_notations.svg | 226 - .../fig/wp_zero_set.svg | 106 - .../wachspress_coordinates.cpp | 4 +- .../Discrete_harmonic_coordinates_3.h | 101 +- .../Mean_value_coordinates_3.h | 49 +- .../Voronoi_coordinates_3.h | 269 - .../Wachspress_coordinates_3.h | 103 +- .../tetrahedron_coordinates.h | 10 +- .../Barycentric_coordinates_3/CMakeLists.txt | 1 - .../test_voronoi_weights.cpp | 85 - 35 files changed, 217 insertions(+), 10120 deletions(-) delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg delete mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h delete mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index c68a9743cb61..6093139a1049 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -11,849 +11,96 @@ namespace Barycentric_coordinates { \section gbc_introduction Introduction -Barycentric coordinates are widely used in computer graphics and computational mechanics -to determine a position of a point in the plane with respect to a triangle. These coordinates -have been later generalized to support simple polygons in 2D and polyhedra in 3D. +Barycentric coordinates are an important tool in computer graphics and geometric modeling. +Originally, these coordinates were used to represent a given point with respect to a simplex but have been later + generalized to more complex shapes. -This package offers an efficient and robust implementation of 2D generalized barycentric -coordinates defined for simple polygons in the plane. If coordinates with respect to multivariate -scattered points instead of a polygon are required, please refer to natural neighbor coordinates -from the package \ref chapinterpolation "2D and Surface Function Interpolation". +The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation +of three-dimensional closed-form generalized barycentric coordinates defined for convex simplicial polytopes. -In particular, this package includes an implementation of \ref wp_example "Wachspress", -\ref dh_example "discrete harmonic", \ref mv_example "mean value", and \ref hm_example "harmonic" -coordinates, and provides some extra functions to compute barycentric coordinates with respect -to \ref seg_example "segments" and \ref tri_example "triangles". - -\cgalFigureBegin{overview, overview.svg} -Wachspress (WP), discrete harmonic (DH), mean value (MV), and harmonic (HM) coordinate functions -for a convex polygon plotted with respect to the marked vertex. -\cgalFigureEnd +In particular, this package includes an implementation of Wachspress, discrete harmonic, mean value, and + one extra function to calculate barycentric coordinates with respect to tetrahedrons. \section gbc_interface Software Design -Mean value and harmonic coordinates are the most generic coordinates in this package, -because they allow an arbitrary simple polygon as input. Wachspress and discrete harmonic coordinates -are, by definition, limited to strictly convex polygons. Segment coordinates take as input -any non-degenerate segment, and triangle coordinates allow an arbitrary non-degenerate triangle. - -Wachspress, discrete harmonic, mean value, and harmonic coordinates are all generalized -barycentric coordinates. However, while Wachspress, discrete harmonic, and mean value -coordinates can be computed analytically, harmonic coordinates cannot. They first need -to be approximated over a triangulation of the interior part of the polygon. Once approximated, -they can be evaluated analytically at any point inside the polygon. - -For all analytic coordinates, we provide two algorithms. One has a linear time complexity, -but may suffer imprecisions near the polygon boundary, while the second one is precise -but has a quadratic time complexity. The user can choose the preferred algorithm by -specifying a computation policy `Barycentric_coordinates::Computation_policy_2`. - -All analytic barycentric coordinates for polygons can be computed either by instantiating a class -or through one of the free functions. Harmonic coordinates can be computed only by -instantiating a class that must be parameterized by a model of the concept `DiscretizedDomain_2`. -Segment and triangle coordinates can be computed only through the free functions. -For more information see the \ref PkgBarycentricCoordinates2Ref "Reference Manual". - -Any point in the plane may be taken as a query point. However, we do not recommend using -Wachspress and discrete harmonic coordinates with query points outside the closure -of a polygon, because they are not well-defined for some of these points. The same holds -for harmonic coordinates, which are not defined everywhere outside the polygon. For more -information see Section \ref gbc_degeneracies. - -The output of the computation is a set of coordinate values at the given query point -with respect to the polygon vertices. That means that the number of returned coordinates -per query point equates the number of polygon vertices. The ordering of the coordinates -is the same as the ordering of polygon vertices. - -All class and function templates are parameterized by a traits class, which is a model -of the concept `BarycentricTraits_2`. It provides all necessary geometric primitives, -predicates, and constructions, which are required for the computation. All models of `Kernel` -can be used. A polygon is provided as a range of vertices with a -\ref PkgPropertyMapRef "property map" that maps a vertex from the polygon to `CGAL::Point_2`. - -If you do not know which coordinate function best fits your application, you can address the -table below for some advise. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CoordinatesPropertiesValid domainClosed formQueriesSpeed
SegmentAll2D non-degenerate segmentsYesEverywhere on the supporting line+++
TriangleAll2D non-degenerate trianglesYesEverywhere in 2D+++
Discrete harmonicMay be negativeStrongly convex polygonsYesEverywhere inside the polygon++
WachspressAllStrongly convex polygonsYesEverywhere inside the polygon++
Mean valueMay be negativeSimple polygonsYesEverywhere in 2D++
HarmonicAllSimple polygonsNoEverywhere inside the polygon+
- -\note This is the second version of the package with the modified and improved API. -The package still supports the old API. See more details \ref depr_example "here". - +Wachspress, discrete harmonic, and mean value coordinates are all generalized barycentric coordinates +that can be computed analytically. In this implementation, we restrict our polyhedra to convex ones +with triangular faces, although some of the coordinates may accept more general shapes. -\section gbc_examples Examples +All of the three barycentric coordinates can be computed either by instantiating a class or through a +free function. Tetrahedron coordinates can be computed only through the free function. -In order to facilitate the process of learning this package, we provide various examples -with a basic usage of different barycentric components. +We can specify a computation policy Barycentric_coordinates::Computation_policy_3 that can be FAST or +FAST_WITH_EDGE_CASES for each of the three coordinates. The difference between them is that FAST_WITH_EDGE_CASES + treats points near the boundaries by projecting them into the face of the polyhedron and then calculating triangle coordinates. +The output of a query point is the coordinate value with respect to each vertex, the number of coordinate + values is the same as the number of vertices, and the ordering is also the same. -\subsection seg_example Segment Coordinates +All class and function templates are parameterized by a traits class, which is a model of the concept +BarycentricTraits_3. It provides all necessary geometric primitives, predicates, and constructions, +which are required for the computation. All models of Kernel can be used. A polyhedron is provided as +a model of the concept FaceListGraph with a property map that maps a vertex from the polyhedron to CGAL::Point_3. -This example illustrates the use of the global function `segment_coordinates_2()`. -We compute coordinates at three green points along the segment \f$[v_0, v_1]\f$ and at two blue points outside this segment -but along its supporting line. The symmetry of the query points helps recognizing errors that -may have occurred during construction of the example. The used `Kernel` is exact. -\anchor seg_coord_example -\cgalFigureBegin{seg_example, seg_coord_example.svg} -Example's point pattern. -\cgalFigureEnd - -\cgalExample{Barycentric_coordinates_2/segment_coordinates.cpp} - - -\subsection tri_example Triangle Coordinates +\section gbc_examples Examples -In this example, we show how to use the global function `triangle_coordinates_2()`. -We compute coordinates for three sets of points: interior (green), boundary (red), and exterior (blue). -Note that some of the coordinate values for the exterior points are negative. -The used `Kernel` is inexact. +In order to facilitate the process of learning this package, we provide various examples +with a basic usage of different barycentric components. -\anchor tri_coord_example -\cgalFigureBegin{tri_example, tri_coord_example.svg} -Example's point pattern. -\cgalFigureEnd -\cgalExample{Barycentric_coordinates_2/triangle_coordinates.cpp} +\subsection tetra_example Tetrahedron Coordinates +In this example, we use the global function tetrahedron_coordinates_in_tuple(). +We compute coordinates for the tetrahedron whose vertices are the points in the +set {(0,0,0), (1,0,0), (0,1,0), (0,0,1)}. Were used points inside, outside and +at the boundary of the tetrahedron. +\anchor tetra_coord_example +\cgalExample{Barycentric_coordinates_3/tetrahedron_coordinates.cpp} \subsection wp_example Wachspress Coordinates -In the following example, we generate 100 random points (green/red/black), then we take the -convex hull (red/black) of this set of points as our polygon (black), and compute Wachspress -coordinates at all the generated points. The used `Kernel` is inexact. - +In this example, we generate 250 random points inside a unitary sphere, +centered at the origin, then we take the convex hull of this set of points +and use this as our polyhedron. Finally, we calculate Wachspress coordinates +for all of these 250 points. \anchor wp_coord_example -\cgalFigureBegin{wp_example, wp_coord_example.svg} -Example's point pattern. -\cgalFigureEnd - -\cgalExample{Barycentric_coordinates_2/wachspress_coordinates.cpp} - +\cgalExample{Barycentric_coordinates_3/wachspress_coordinates.cpp} \subsection dh_example Discrete Harmonic Coordinates -In this example, we compute discrete harmonic coordinates for a set of green (interior), -red (boundary), and blue (exterior) points with respect to a unit square. We also demonstrate -the use of various containers, both random access and serial access, different property maps, -and the ability to choose a computation policy. For points on the polygon boundary, -we use the free function `boundary_coordinates_2()`. -The used `Kernel` is exact. - +This is an example that shows how to deform a simple smooth sphere into +another shape, topologically equivalent. To achieve this, we enclose the +sphere inside a cube cage, then we calculate the barycentric +coordinate of each vertex with respect to the cube cage. After deforming +the cage, we calculate the linear combination of the points to get the +deformed sphere vertices. \anchor dh_coord_example -\cgalFigureBegin{dh_example, dh_coord_example.svg} -Example's point pattern. -\cgalFigureEnd - -\cgalExample{Barycentric_coordinates_2/discrete_harmonic_coordinates.cpp} - +\cgalExample{Barycentric_coordinates_3/shape_deformation.cpp} \subsection mv_example Mean Value Coordinates -This is an example that illustrates how to compute mean value coordinates for a set of green points -in a star-shaped polygon. We note that this type of coordinates is well-defined for such a concave polygon -while Wachspress and discrete harmonic coordinates are not. However, it may yield negative -coordinate values for points outside the polygon's kernel -(shown in red). We speed up the computation using the linear time complexity algorithm by specifying -a computation policy `Barycentric_coordinates::Computation_policy_2`. The used `Kernel` is inexact. - +This example is very similar to the one used with tetrahedron coordinates. +We started with a regular icosahedron and for points inside, outside and +at the boundary, we calculate mean value coordinates. In this example, we +use the fast with edge cases algorithm because it treats points very close +to the boundaries. \anchor mv_coord_example -\cgalFigureBegin{mv_example, mv_coord_example.svg} -Example's point pattern. -\cgalFigureEnd - -\cgalExample{Barycentric_coordinates_2/mean_value_coordinates.cpp} - - -\subsection hm_example Harmonic Coordinates -This example illustrates how to \ref terrain_triangulation_fig "discretize" the interior part -of the \ref terrain_example_fig "polygon" and compute harmonic coordinates at the vertices -of the discretized domain, which is represented by a 2D Delaunay triangulation. Once computed, -harmonic coordinate functions can be evaluated at any point in the closure of the polygon. -To illustrate such an evaluation, we compute the barycenter of each triangle and evaluate harmonic coordinates -at this barycenter. Since harmonic coordinates can only be approximated, the used `Kernel` is inexact. - -\anchor hm_coord_example -\cgalExample{Barycentric_coordinates_2/harmonic_coordinates.cpp} - - -\subsection height_inter_example Terrain Modeling - -This is an advanced example that illustrates how to use generalized barycentric coordinates -for height interpolation with applications to terrain modeling. It also shows how to use -a non-default traits class with our package instead of a `Kernel` traits class. -Suppose we know the boundary of three-dimensional piece of terrain that can be represented -as a polygon with several three-dimensional vertices, where the third dimension indicates -the corresponding height. The task is to propagate the height from the known sample points -on the boundary to the polygon's interior. This gives an approximate estimation of -the terrain's surface in this region. - -\anchor terrain_example_fig -\cgalFigureBegin{terrain_example, terrain.svg} -A 2D polygon with 50 vertices representing a piece of terrain with convex and concave parts. The height is not shown. -\cgalFigureEnd - -In this example, we project a 3D polygon orthogonally onto the 2D plane using the class -`CGAL::Projection_traits_xy_3`, triangulate its interior using the class `Delaunay_domain_2`, -and compute mean value coordinates at the vertices of this triangulation with respect to the polygon vertices. -Finally, we interpolate the height data from the polygon boundary to its interior -using the computed coordinates and the global interpolation function from the -package \ref chapinterpolation "2D and Surface Function Interpolation". - -\anchor terrain_example -\cgalExample{Barycentric_coordinates_2/terrain_height_modeling.cpp} - -As a result, we get a smooth function inside the polygon that approximates the underlying terrain surface. - -\cgalFigureBegin{terrain_interpolation_example, terrain_interpolation.png} -The interpolated data. The color bar represents the corresponding height. -\cgalFigureEnd - - -\subsection shape_deform_example Shape Deformation - -This is another advanced example that shows how to use generalized barycentric coordinates -in order to deform a given 2D shape into another shape as shown in the figure below. -Harmonic coordinates satisfy all the properties of barycentric coordinates for complicated -concave polygons and hence this is our choice to perform a shape deformation. Note that -even though harmonic coordinates are guaranteed to be positive inside a polygon, they -do not guarantee a bijective mapping between the source and target shapes that is -the target mesh can fold-over the target polygon after the mapping (see the little fold-over -in the left shoulder of the target shape). - -\cgalFigureBegin{shape_deformation_example, shape_deformation.svg} -The shape on the left is deformed into the shape on the right. The zoom shows a fold-over -in the left shoulder of the target shape where the red triangle goes over the polygon boundary. -\cgalFigureEnd - -\anchor deformation_example -\cgalExample{Barycentric_coordinates_2/shape_deformation.cpp} - -But despite the possible fold-overs, a similar technique can be used for image warping -in 2D and character articulation in 3D. For example in 2D, we first enclose an image, -which we want to deform, into a simple polygon so-called *cage*, we then -bound each image pixel to this cage using barycentric coordinates, and finally deform -this cage into a new one, which also deforms the underlying image, as shown -in the figure below for harmonic coordinates. - -\cgalFigureBegin{image_warping_example, image_warping.png} -An image on the left is deformed into a new image on the right using a 2D concave polygon (grey) -and harmonic coordinates computed at each image pixel with respect to the vertices of this polygon. -\cgalFigureEnd - - -\subsection aff_example Affine Coordinates - -This is an example, where we show how a -lambda expression -can be used to define a model of generalized barycentric coordinates. To make this example -useful, we implement affine generalized coordinates for a set of scattered points. -The used `Kernel` is inexact. - -\anchor aff_coord_example_fig -\cgalFigureBegin{aff_example, aff_coord_example.svg} -Example's point pattern. -\cgalFigureEnd - -\anchor aff_coord_example -\cgalExample{Barycentric_coordinates_2/affine_coordinates.cpp} - - -\subsection depr_example Deprecated Coordinates - -This example illustrates how to use the deprecated API of this package. -The used `Kernel` is inexact and the used coordinates are mean value coordinates. -The result is identical to the one from \ref mv_coord_example "this example". - -\anchor depr_coord_example -\cgalExample{Barycentric_coordinates_2/deprecated_coordinates.cpp} - -\note The headers `Segment_coordinates_2.h` and `Triangle_coordinates_2.h` are -not capitalized in the new version that is they are named `segment_coordinates_2.h` -and `triangle_coordinates_2.h`. - +\cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} \section gbc_degeneracies Edge Cases -Not all presented coordinates are general enough to handle any query point in the plane, that is -why we highly recommend reading this section in order to learn what can be expected from -each coordinate function. If you want to get more mathematical details about each coordinate -function as well as the complete history and theory behind barycentric coordinates, you should -read \cgalCite{cgal:bc:hs-gbcicg-17}. You can also read an overview -here -(chapters 1 and 2). - - -\anchor compute_seg_coord -\subsection gbc_deg_segment_coordinates Segment Coordinates - -The segment coordinate function with respect to a given segment vertex is a linear -function along the supporting line of this segment that grows from zero at the opposite -vertex to one at the chosen vertex (see the figure below). - -\cgalFigureBegin{seg_coord, seg_coord.svg} -The segment coordinate function with respect to the vertex \f$v_0\f$. -\cgalFigureEnd - -Segment coordinates can be computed exactly if an exact number type is chosen. -The segment itself, with respect to which we compute coordinates, must be non-degenerate. -If both conditions are satisfied, then the computation never fails. However, to compute coordinates, -the user must ensure that the query point lies exactly on the line \f$L\f$ supporting the segment. -Since in many applications this is not the case, and a query point may lie very close but not exactly on this line, -we provide a solution to remedy this situation. - -\cgalFigureBegin{seg_coord_projection, seg_coord_projection.svg} -The orthogonal projection \f$p'\f$ of the vector \f$p\f$ (green) onto the vector \f$q\f$ (red). -\cgalFigureEnd - -Suppose that some query point \f$v\f$ does not lie exactly on the line \f$L\f$, -but is some distance \f$d\f$ away as shown in the figure above. If we want to compute -the segment coordinate \f$b_1(v)\f$ with respect to the vertex \f$v_1\f$, we first find the -orthogonal projection \f$p'\f$ of the vector \f$p\f$ onto the vector \f$q\f$ and -then normalize it by the length of \f$q\f$. This yields the segment coordinate \f$b_1(v') = b_1(v)\f$ -if \f$v\f$ lies exactly on the line. The other segment coordinate \f$b_0(v')\f$ that is equal -to \f$b_0(v)\f$ when \f$v\f$ is on the line \f$L\f$ is computed the same way but with the -projection of the vector \f$\vec{vv_1}\f$. - -\b Warning: do not abuse the feature described above, because it does not yield correct -segment coordinates for the point \f$v\f$ but rather those for \f$v'\f$. Moreover, segment -coordinates for a point \f$v\f$, which does not lie exactly on the line \f$L\f$, do not exist. -But if the non-zero distance \f$d\f$ is due to some numerical instability when computing the -location of the point \f$v\f$ or any other problem, which causes the point to be not exactly on -the line, the final segment coordinates will be, at least approximately, correct. - -With inexact number types, the resulting coordinate values are correct up to the precision of the chosen type. - - -\anchor compute_tri_coord -\subsection gbc_deg_triangular_coordinates Triangle Coordinates - -The triangle coordinate function with respect to a given triangle vertex is a linear function -that grows from zero along the opposite edge to one at the chosen vertex (see the figure below). - -\cgalFigureBegin{tri_coord, tri_coord.svg} -The triangle coordinate function with respect to the vertex \f$v_0\f$. -\cgalFigureEnd - -To compute the triangle coordinates of the query point \f$v\f$, we adopt the standard simple formula - -
-\f$b_i = \frac{A_i}{A}\f$ with \f$i = 0\dots 2\f$ -
- -where \f$A_i\f$ is the signed area of the sub-triangle opposite to the -vertex \f$i\f$ and \f$A\f$ is the total area of the triangle that is \f$A = A_0 + A_1 + A_2\f$ -(see the figure below). - -\anchor tri_notations -\cgalFigureBegin{tri_notations, tri_notations.svg} -Notation for triangle coordinates. -\cgalFigureEnd - -These coordinates can be computed exactly if an exact number type is chosen, for any query point in the plane -and with respect to any non-degenerate triangle. No special cases are handled. The computation always -yields the correct result. The notion of correctness depends on the precision of the used number type. -Note that for exterior points some coordinate values will be negative. - - -\anchor compute_wp_coord -\subsection gbc_deg_wachspress_coordinates Wachspress Coordinates - -Wachspress coordinates are well-defined in the closure of any strictly convex polygon. -Therefore, when using an exact number type, for any query point from the polygon's closure, -these coordinates are computed exactly and no false result is expected. For exterior query points, -the coordinates can also be computed but not everywhere (see below for more details). For inexact number types, -the resulting precision of the computation is due to the involved algorithm and a chosen number type. -In the following paragraph, we discuss two available algorithms for computing Wachspress coordinates -when an inexact number type is used. The chosen algorithm is specified by a computation policy -`Barycentric_coordinates::Computation_policy_2`. - -\anchor wp_polygon -\cgalFigureBegin{wp_notations, wp_notations.svg} -Notation for Wachspress coordinates. -\cgalFigureEnd - -To compute Wachspress weights, we follow \cgalCite{cgal:bc:fhk-gcbcocp-06} and use the formula - -
-\f$w_i = \frac{C_i}{A_{i-1}A_i}\f$ -
- -with \f$i = 1\dots n\f$ where \f$n\f$ is the number of polygon vertices. -In order to compute the coordinates, we normalize these weights, - -
-\f$b_i = \frac{w_i}{W^{wp}}\qquad\f$ with \f$\qquad W^{wp} = \sum_{j=1}^n w_j.\f$ -
- -This formula becomes unstable when approaching the boundary of the polygon (\f$\approx 1.0e-10\f$ and closer). -To fix the problem, we modify the weights \f$w_i\f$ as - -
-\f$\bar{w}_i = C_i\prod_{j\not=i-1,i} A_j\f$. -
- -After the above normalization, this gives us the precise algorithm to compute Wachspress coordinates -but with \f$O(n^2)\f$ performance only. The max speed \f$O(n)\f$ algorithm uses the standard -weights \f$w_i\f$. Note that mathematically this modification does not change the coordinates. One should -be cautious when using the unnormalized Wachspress weights. In that case, you must choose the -\f$O(n)\f$ type. - -It is known that for strictly convex polygons the denominator's zero set of the -Wachspress coordinates (\f$W^{wp} = 0~\f$) is a curve, which (in many cases) lies quite -far away from the polygon. More specifically, it interpolates the intersection points of -the supporting lines of the polygon edges. Therefore, the computation of Wachspress coordinates -outside the polygon is possible only at points that do not belong to this curve. - -\cgalFigureBegin{wp_zero_set, wp_zero_set.svg} -Zero set (red) of the Wachspress coordinates' denominator \f$W^{wp}\f$ for a non-regular hexagon. -\cgalFigureEnd - -\b Warning: we do not recommend using Wachspress coordinates for exterior points! - - -\anchor compute_dh_coord -\subsection gbc_deg_discrete_harmonic_coordinates Discrete Harmonic Coordinates - -Discrete harmonic coordinates have the same requirements as Wachspress coordinates. -They are well-defined in the closure of any strictly convex polygon and, -if an exact number type is chosen, they are computed exactly. However, and unlike Wachspress basis functions, -these coordinates are not necessarily positive. In particular, the weight \f$w_i\f$ is positive -if and only if \f$\alpha+\beta < \pi\f$ (see the figure below for notation). For inexact number types, -the precision of the computation is due to the involved algorithm and a chosen number type. Again, -we describe two algorithms to compute the coordinates when an inexact number type is used: -one is of max precision and one is of max speed. - -\anchor dh_polygon -\cgalFigureBegin{dh_notations, dh_notations.svg} -Notation for discrete harmonic coordinates. -\cgalFigureEnd - -To compute discrete harmonic weights, we follow \cgalCite{cgal:bc:fhk-gcbcocp-06} and use the formula - -
-\f$w_i = \frac{r_{i+1}^2A_{i-1}-r_i^2B_i+r_{i-1}^2A_i}{A_{i-1}A_i}\f$ -
- -with \f$i = 1\dots n\f$ where \f$n\f$ is the number of polygon vertices. -In order to compute the coordinates, we normalize these weights, - -
-\f$b_i = \frac{w_i}{W^{dh}}\qquad\f$ with \f$\qquad W^{dh} = \sum_{j=1}^n w_j.\f$ -
- -This formula becomes unstable when approaching the boundary of the polygon (\f$\approx 1.0e-10\f$ and closer). -To fix the problem, similarly to the previous subsection, we modify the weights \f$w_i\f$ as - -
-\f$\bar{w}_i = (r_{i+1}^2A_{i-1}-r_i^2B_i+r_{i-1}^2A_i)\prod_{j\not=i-1,i} A_j\f$. -
- -After the above normalization, this yields the precise algorithm to compute discrete harmonic coordinates -but with \f$O(n^2)\f$ performance only. The max speed \f$O(n)\f$ algorithm uses the standard -weights \f$w_i\f$. Again, mathematically this modification does not change the coordinates, -one should be cautious when using the unnormalized discrete harmonic weights. In that case, -you must choose the \f$O(n)\f$ type. - -\b Warning: as for Wachspress coordinates, we do not recommend using discrete harmonic coordinates -for exterior points, because the curve \f$W^{dh} = 0\f$ may have several components, -and one of them interpolates the polygon vertices. However, if you are sure that -the query point does not belong to this curve, you can compute the coordinates -as shown in \ref dh_example " this example". - - -\anchor compute_mv_coord -\subsection gbc_deg_mean_value_coordinates Mean Value Coordinates - -Unlike the previous coordinates, mean value coordinates cannot be computed exactly due to -an inevitable square root operation. Although, if an exact number type is used, the default precision -of the computation depends only on two \cgal functions: `CGAL::to_double()` and `CGAL::sqrt()`. -It is worth saying that providing a number type that supports exact or nearly exact computation -of the square root is possible, however since such types are usually impractical due to the -large overhead, the conversion to a floating-point format above is always effective. On the other hand, -mean value coordinates are well-defined everywhere in the plane for any simple polygon. In addition, -if your traits class provides a more precise version of the square root function, the final precision -of the computation with exact number types will depend only on the precision of that function. - -\anchor mv_polygon -\cgalFigureBegin{mv_notations, mv_notations.svg} -Notation for mean value coordinates. -\cgalFigureEnd - -For these coordinates, we provide two algorithms: one is of max precision and one is of max speed. -The first one works everywhere in the plane, and the precision of the computation depends only -on the chosen number type, including the remarks above. This algorithm is based on the following weight -formula from \cgalCite{cgal:bc:f-wmvc-14} - -
-\f$w_i = \sigma_i\bar{w}_i\qquad\f$ with \f$\qquad\bar{w}_i = (r_{i-1}r_{i+1}-d_{i-1}d_{i+1})^{1/2}\prod_{j\not= i-1,i}(r_jr_{j+1} + d_jd_{j+1})^{1/2}\qquad\f$ -where \f$\qquad r_i = \|d_i\|.\f$ -
- -Since \f$\bar{w}_i\f$ is always positive, we must append to it the proper sign \f$\sigma_i\f$ -of the signed mean value weight, which can be found efficiently (see the figures below). -This weight is always positive to the left of the red piecewise linear curve, -and it is negative to the right of this curve, moving in the counterclockwise direction. - -\cgalFigureBegin{mv_weight_signs, mv_weight_signs.svg} -Signs of the mean value weight \f$w_i\f$ depending on the region with respect to a -convex polygon \f$P\f$ and a concave polygon \f$P'\f$. -\cgalFigureEnd - -After the normalization of these weights as before - -
-\f$b_i = \frac{w_i}{W^{mv}}\qquad\f$ with \f$\qquad W^{mv} = \sum_{j=1}^n w_j\f$ -
- -we obtain the max precision \f$O(n^2)\f$ algorithm. The max speed O(n) algorithm computes the -weights \f$w_i\f$ using the pseudocode from here. -These weights - -
-\f$w_i = \frac{t_{i-1} + t_i}{r_i}\qquad\f$ -with \f$\qquad t_i = \frac{\text{det}(d_i, d_{i+1})}{r_ir_{i+1} + d_id_{i+1}}\f$ -
- -are also normalized. Note that they are unstable if a query point is closer than \f$\approx 1.0e-10\f$ -to the polygon boundary, similarly to Wachspress and discrete harmonic coordinates and -one should be cautious when using the unnormalized mean value weights. In that case, you must choose the -\f$O(n)\f$ type. - - -\anchor compute_hm_coord -\subsection gbc_deg_harmonic_coordinates Harmonic Coordinates - -The harmonic coordinates are computed by solving the Laplace equation - -
-\f$\Delta \boldsymbol{b} = \boldsymbol{0}\f$ -
- -subject to suitable Dirichlet boundary conditions. Harmonic coordinates are the only coordinates -in this package, which are guaranteed to be non-negative in the closure of any simple polygon and -satisfy all properties of barycentric coordinates, however such desirable properties come with the fact -that these coordinates are well-defined only inside a polygon. If an exterior query point is provided, -its coordinates are set to zero. - -Another disadvantage of these coordinates is that they cannot be computed -exactly, because harmonic coordinates do not have a simple closed-form expression and -must be approximated. The common way to approximate these coordinates is -by discretizing over the space of piecewise linear functions with respect to -a triangulation of the polygon. The denser triangulation of the interior part of -the polygon, the better approximation of the coordinates. To get a high quality -approximation of the coordinates, the user should provide a rather dense partition -of the polygon's interior domain that in turn leads to larger running times when -computing the coordinates. - -\anchor terrain_triangulation_fig -\cgalFigureBegin{terrain_triangulation, terrain_triangulation.svg} -Sparse triangulation of the polygon's interior domain (left): smaller running times, -lower coordinates quality; dense triangulation (right): larger running times, -higher coordinates quality. -\cgalFigureEnd - -From all this follows, that any exact `Kernel` will be rejected and it is not possible -to compute analytic harmonic weights. However, once the coordinates are computed at the -vertices of the triangulation, they can be evaluated analytically at any interior -query point. For evaluation, we first locate a triangle that contains the query point -and then linearly interpolate harmonic coordinates defined at the vertices of this -triangle to the query point with the help of \ref gbc_deg_triangular_coordinates "triangle coordinates" as - -
-\f$b_i = b_0^{tr} b_i^0 + b_1^{tr} b_i^1 + b_2^{tr} b_i^2\f$ -
- -with \f$i = 1\dots n\f$, where \f$n\f$ is the number of polygon vertices, \f$b_{0}^{tr}\f$, -\f$b_{1}^{tr}\f$, and \f$b_{2}^{tr}\f$ are the triangle coordinates of the query point with -respect the three vertices of the located triangle, and \f$b_i^{0}\f$, \f$b_i^{1}\f$, and \f$b_i^{2}\f$ -are the harmonic coordinates pre-computed at the triangle vertices. - \section gbc_performance Performance -We strive for robustness and efficiency at the same time. Efficiency is especially important. -These coordinates are used in many applications where they must be computed for millions of points -and, thus, the real time computation of coordinates has been made possible. In this section, -we present next the computation runtimes of the implemented algorithms. - -The structure of the speed test that we use to evaluate the running times consists -of computing coordinate values (or weights) at >= 1 million strictly interior points -with respect to a polygon (or triangle, or segment). At each iteration of the loop, -we create a query point and compute its coordinates. The time presented in the log-log scale plot -at the end of the section is the arithmetic mean of all trials in the loop of 10 iterations. -The time presented in the plot is for analytic coordinates only since harmonic coordinates -of a reasonable (application-dependent) quality are substantially slower to compute and -cannot be fairly compared to the analytic coordinate functions. - -The time to compute coordinates depends on many factors such as memory allocation, input kernel, output container, -number of points, etc. In our tests, we used the most standard C++ and \cgal features with minimum memory allocation. -Therefore, the final time presented is the average time that can be expected without deep optimization -but still with efficient memory allocation. It also means that it may vary depending on the usage of the package. - -To benchmark analytic coordinates, we used a MacBook Pro 2011 with 2 GHz Intel Core i7 processor (2 cores) -and 8 GB 1333 MHz DDR3 memory. The installed operating system was OS X 10.9 Maverick. -The resulting timings for all closed-form coordinates can be found in the figure below. - -\cgalFigureBegin{analytic_timings, analytic_timings.png} -Time in seconds to compute \f$n\f$ coordinate values for a polygon with \f$n\f$ vertices -at 1 million query points with the max speed \f$O(n)\f$ algorithms (dashed) and -the max precision \f$0(n^2)\f$ algorithms (solid) for Wachspress (blue), discrete -harmonic (red), and mean value (green) coordinates. -\cgalFigureEnd - -From the figure above we observe that the \f$O(n^2)\f$ algorithm is as fast -as the \f$O(n)\f$ algorithm if we have a polygon with a small number of vertices. -But as the number of vertices is increased, the linear algorithm outperforms the squared one, -as expected. One of the reasons for this behavior is that for a small number of vertices -the multiplications of \f$n-2\f$ elements inside the \f$O(n^2)\f$ algorithm take almost the -same time as the corresponding divisions in the \f$O(n)\f$ algorithm. For a polygon with -many vertices, these multiplications are substantially slower. - -To benchmark harmonic coordinates, we used a MacBook Pro 2018 with 2.2 GHz Intel Core i7 processor (6 cores) -and 32 GB 2400 MHz DDR4 memory. The installed operating system was OS X 10.15 Catalina. -The average time to compute harmonic coordinates in the loop of 10 iterations can be found in the tables below. - -The first table shows how the time to compute the coordinates on a unit square depends on the number of -triangulation vertices. We show separately the time to setup the matrix, factorize it, and solve it -with respect to the four vertices of the unit square. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Number of queries (approx.)Setup (in seconds)Factorize (in seconds)Solve (in seconds)Total (in seconds)
1000.0000560.0000990.0000150.000170
5000.0002660.0005740.0000640.000904
1,0000.0005090.0011940.0001470.001850
25,0000.0147490.0711520.0081910.094092
50,0000.0342550.1842370.0181660.236658
100,0000.0651170.5431770.0440880.652382
500,0000.5765307.6971430.3107658.584438
1,000,0001.29516326.769450.73737228.80199
- -The same results can be seen in the figure. - -\cgalFigureBegin{hm_4_bench, hm_4_bench.svg} -Time in seconds to setup (red), factorize (green), and solve (blue) for harmonic -coordinate values with respect to a unit square. -\cgalFigureEnd - -The second table shows how the time to compute the coordinates for 100k queries depends on the number of -the polygon vertices. We show separately the time to setup the matrix, factorize it, and solve it -with respect to the \f$n\f$ vertices of the polygon. It can be seen that, unlike in the -first table, the time to factorize the matrix here stays constant. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Number of vertices (approx.)Setup (in seconds)Factorize (in seconds)Solve (in seconds)Total (in seconds)
50.0834440.6318230.0598270.775094
100.0602940.4505340.0945830.605411
250.0627600.4786830.2549530.796396
500.0973590.4922330.5396541.129246
1000.1294870.4507711.1525441.732802
5000.4306940.4603216.6200617.511076
10000.8123620.48005216.1423917.43480
- -The same results can be seen in the figure. - -\cgalFigureBegin{hm_n_bench, hm_n_bench.svg} -Time in seconds to setup (red), factorize (green), and solve (blue) for harmonic -coordinate values with respect to a polygon with \f$n\f$ vertices at 100k query points. -\cgalFigureEnd - -While, in the first table, the most significant step is to factorize the matrix, in the -second table, the slowest step is to solve for coordinates, as expected. - \section gbc_history History -The package was first released in 2015 and included segment, triangle, Wachspress, -discrete harmonic, and mean value coordinates. The API of that version is now deprecated -but can still be used. An example of the old API can be found \ref depr_example "here". -The docs of that API are also preserved and maintained \ref PkgBarycentricCoordinates2RefDeprecated "here". - -In 2018, this package was modified and improved by Keyu Chen and Dmitry Anisimov -during the Google Summer of Code. The API was changed to the current version. In 2020, the new version -was cleaned up and documented that includes: -- the classes `Segment_coordinates_2` and `Triangle_coordinates_2` have been removed, only the free -functions are preserved; -- the entry class `Generalized_barycentric_coordinates_2` was removed since it is not -flexible enough to accommodate all types of 2D barycentric coordinates; -- the classes `Wachspress_2`, `Discrete harmonic_2`, and `Mean_value_2` have been renamed -and modified so that they can be used now on their own without the class `Generalized_barycentric_coordinates_2`; -- harmonic coordinates have been added; -- the free functions for segment and triangle coordinates have been modified and improved; -- the free functions for Wachspress, discrete harmonic, and mean value weights and coordinates have been added; -- the free functions to compute barycentric coordinates for points on the polygon boundary have been added; -- all functions and classes are now using ranges and property maps; -- examples, tests, and benchmarks are modified/extended/improved; -- the docs are refactored and simplified. - \section gbc_acknowledgments Acknowledgments -The authors wish to thank Teseo Schneider -and Randolf Schaerfig for helpful comments and discussions. We also appreciate the -great effort invested in this package by our reviewers Andreas Fabri, Sébastien Loriot, -and Efi Fogel. + */ } /* namespace Barycentric_coordinates */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h index b1b2921af46a..ae40f236deb0 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h @@ -29,6 +29,11 @@ typedef unspecified_type FT; /// \name 3D Geometric Objects /// @{ +/*! + A model of `Kernel::Point_2`. +*/ +typedef unspecified_type Point_2; + /*! A model of `Kernel::Point_3`. */ @@ -56,117 +61,72 @@ typedef unspecified_type Compute_area_2; /*! A construction object that must provide the function operator: - `FT operator(const Point_2& p, const Point_2& q)` - - that returns the squared Euclidean distance between the points `p` and `q`. + `FT operator(const Point_3& p0, const Point_3& p1, const Point_3& p2, const Point_3& p3)` + + that returns the signed volume of the tetrahedron defined by the four points `p0`, `p1`, `p2`, and `p3`. */ -typedef unspecified_type Compute_squared_distance_2; +typedef unspecified_type Compute_volume_3; /*! A construction object that must provide the function operator: - `FT operator(const Vector_2& v)` - - that returns the squared length of the vector `v`. + `FT operator(const Vector_3& u, const Vector_3& v)` + + that returns an approximation of the angle between `u` and `v`. + The angle is given in degrees. */ -typedef unspecified_type Compute_squared_length_2; +typedef unspecified_type Compute_approximate_angle_3; /*! A construction object that must provide the function operator: - `FT operator(const Vector_2& v, const Vector_2& w)` - - that returns the scalar product of the vectors `v` and `w`. + `FT operator(const Vector_3& v, const Vector_3& w)` + + that returns the scalar (inner) product of the two vectors `v` and `w`. */ -typedef unspecified_type Compute_scalar_product_2; +typedef unspecified_type Compute_scalar_product_3; /*! A construction object that must provide the function operator: - `FT operator(const Vector_2& v, const Vector_2& w)` - - that returns the determinant of the vectors `v` and `w`. + `FT operator(const Vector_3& u, const Vector_3& v, const Vector_3& w)` + + that returns the determinant of the three vectors `u`, `v` and `w`. */ -typedef unspecified_type Compute_determinant_2; +typedef unspecified_type Compute_determinant_3; /*! A construction object that must provide the function operator: - `%Vector_2 operator()(const Point_2& p, const Point_2& q)` - - that returns the vector through the points `p` and `q`. -*/ -typedef unspecified_type Construct_vector_2; - -/// @} - -/// \name 2D Generalized Predicates -/// @{ - -/*! - A predicate object that must provide the function operator: - - `bool operator(const Point_2& p, const Point_2& q)` - - that returns `true` if `p = q` and `false` otherwise. -*/ -typedef unspecified_type Equal_2; - -/*! - A predicate object that must provide the function operator: - - `bool operator(const Point_2& p, const Point_2& q, const Point_2& r)` - - that returns `true` if the points `p`, `q`, and `r` are collinear and `false` otherwise. + `FT operator(const FT& value)` + + that returns the square root of value. */ -typedef unspecified_type Collinear_2; +typedef unspecified_type Sqrt; /*! - A predicate object that must provide the function operator: - - `bool operator(const Point_2& p, const Point_2& q, const Point_2& r)` - - that returns `true` if the point `q` lies between the points `p` and `r` and all three points are collinear. -*/ -typedef unspecified_type Collinear_are_ordered_along_line_2; - -/*! - A predicate object that must provide the function operator: - - `bool operator(const Point_2& p, const Point_2& q)` - - that returns `true` iff the x-coordinate of `p` is smaller than the x-coordinate of `q` or - if they are the same and the y-coordinate of `p` is smaller than the y-coordinate of `q`. -*/ -typedef unspecified_type Less_xy_2; - -/*! - A predicate object that must provide the function operator: - - `Comparison_result operator(const Point_2& p, const Point_2& q)` + A construction object that must provide the function operator: - that compares the Cartesian x-coordinates of the points `p` and `q`. + `Vector_3 operator(const Point_3& p, const Point_3& q)` + + that returns the vector `q` - `p`. */ -typedef unspecified_type Compare_x_2; +typedef unspecified_type Construct_vector_3; /*! - A predicate object that must provide the function operator: - - `Comparison_result operator(const Point_2& p, const Point_2& q)` + A construction object that must provide the function operator: - that compares the Cartesian y-coordinates of the points `p` and `q`. + `Vector_3 operator(const Vector_3& u, const Vector_3& v)` + + that returns the cross product between `u` and `v`. */ -typedef unspecified_type Compare_y_2; +typedef unspecified_type Construct_cross_product_vector_3; -/*! - A predicate object that must provide the function operator: +/// @} - `Orientation operator(const Point_2& p, const Point_2& q, const Point_2& r)` +/// \name 2D Generalized Predicates +/// @{ - that returns `CGAL::LEFT_TURN` if `r` lies to the left of the oriented line `l` defined by `p` and `q`, - returns `CGAL::RIGHT_TURN` if `r` lies to the right of `l`, and returns `CGAL::COLLINEAR` if `r` lies on `l`. -*/ -typedef unspecified_type Orientation_2; /// @} diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 0b6627ae911b..8b1e73a9d834 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -17,7 +17,7 @@ Analytic coordinates and related classes. \defgroup PkgBarycentricCoordinates3RefFunctions Free Functions \ingroup PkgBarycentricCoordinates3Ref -Free functions to compute barycentric weights and coordinates. +Free functions to compute barycentric coordinates. \addtogroup PkgBarycentricCoordinates3Ref @@ -33,8 +33,8 @@ defined for convex simplicial polytopes.} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin -\cgalPkgSince{4.6} -\cgalPkgBib{cgal:abha-gbc} +\cgalPkgSince{} +\cgalPkgBib{} \cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd @@ -43,7 +43,6 @@ defined for convex simplicial polytopes.} ## Concepts ## - `BarycentricTraits_3` -- `BarycentricCoordinates_3` ## Analytic Coordinates ## - `Wachspress_coordinates_3` diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg deleted file mode 100644 index 357e9e4b0a33..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/aff_coord_example.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/analytic_timings.png deleted file mode 100644 index 513a8feee30ca15ec1e025eff6e12f51729ec9e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77272 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� 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg deleted file mode 100644 index c6dc54c50e0b..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/dh_notations.svg +++ /dev/null @@ -1,302 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg deleted file mode 100644 index 283cea5b95dc..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_4_bench.svg +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg deleted file mode 100644 index 70702d86c99f..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/hm_n_bench.svg +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/image_warping.png deleted file mode 100644 index 0afe949b3b99037be9a9f71c4fe82dcf6c59dc3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60489 zcmXt9V{~L))9u*l*!IMkcw(CqPHatVt7F^d#I`Z9Cbn%m`SQGLeP^xi+yDBWTc>vI zs=aTRqPzqWJRUp%06>zG6jcTQz`?$LH^V}IeMg)`k$hd?>?AcE0RU{&|2|-&RJQ^E z00}@!R7k}w^DGO-4P$;~=yTPIqw^7jfrj?)2($qSsWK28po=N=e)i~be!kMa7@!U% z3I=uD2|@$+hg61*jRimM{9V^M@tBFu;fyV;6gYY5{y=&*IXQVYnR%Pt{HBN8&#Z4? zV9+XG0|7tge3;UBYEQr%2DQ433BII*ugdaTQ8IYfTbGypUomXUQf*bq6_NI;hd_4OrMcQa$9`S`bNm=PW(6!OA z6%taEougypGk>yfjW_@SVaz#*P*H<%Ea>0&HYr7YK-P4#^=4QCwY(q&mVxX|Du4Aw z*mjN4qONfcFPw=L-IEuQEETJ__4SSfGxPV%cNRt@E6D#gqW<4T;$vlf@9Q;9}f$RTJ4{^sk1*YdTyo! zUb!;faWyqGDm0J);4A2T35n_1+>B{^KNHgSu#8HM+Sb9XhHyI<|DlhC1uk`DWn-D|3ljobVgn#TlP7z> zvO6dg=2ev;TB;)8L_iT_11^a8uaIyG!2m~*0@19-zTI*HhhK2_Hk$4G4 z3;G(Tf^IypGp;@dsnJootL+eCa$YiU*YS+iu_u8vcW@wI3rbhK8kh8M_1XlB@&>s8 z%<(OVZ=|^GNlB{H zmW`zqmY2t#{XD;zHZ6D32kH)b6G|Js+w*z17Ef>Tqp(-?DR5{(^^6 zfFnBmXJ%Fh*ta(Hy6qfuOQVY=Aic5e=%SOh({4c@z``X8g*Y_ zk2(Kt-48w7>W|?U6A^)+$AYG=cc_x_iVX@X@)cgYr$C%eNE%BvdiLEE4Gk$(7@9Z) z?~VRZq1lR98z@lQIFY2fmk4;D#+%q=8ZR1xM^~?4?kV+@OK*x{RA5+)A?1DIp>Fc|}E^27NfbeL! zI<>UAURBV0Tm~FT&j)$6SI1qO%;bmYv*Fq6e}etq=)l!#y3l8;WUR>sBwGdJ0Q9`? zk=OxP8?FjN7l{ng{pC!ZxsyM9B#jk0r3wv+hRh8?c;xD=G(LsFx82`~GztUatt0V((uvErnc9gn1hY+umwfwzZ^@$dEs4{xo*0Ag<5jHC-5 z!cYJE5!3UJXZrZa-zI0N#t)V!;QyUkcH;ourZ)*o0PpoveDwNTgQ3`%@1|$=A@$D) z3h>osJ~AR0h|K#NUxC+Bq3T8a_AYT=@Iqz3MAXj5-l3&BYwA^IP|!Y8Pir5Uo4dO+ zPh2MtN8jfDN!Gti{&B;uuC6a=>7P{)7&#!g_xnoHDxB}nzR&l~Pe;2`Bv=uD!1R-z zWM$-3ncnY^q+;n5{-wOPT_qvgcD#h=9o|wTJT|K9 z27_qJ64?)OTy&DxuG*?wRZ&sV-y4qm9ps0kLGHI7P6FLyrspNr?iLsM5r+QPb@u7w zDodR~Xfm1K?2#+9WvSb$U}ACDhn$R@=fjugzGmY5KUfwpJ}a#*7g_R~EEN16&Bm^x zGg~{hkDTI_1)>i_9@*+;Ia4)+Sb)`UwS)u*l#2AD~#CifvW?Tqk zNU{b>EbK?^8$+t|^C_56Z3L4x*(qxQAd>uof@PT@h(d zy@?M=Wjh_`Z?7UXHE%)RI_zOr0GXr0^uNucFO02)q@MnFJiToCFG(%4Dm|^Rd%Sm-KFB=T` zU8LvGf9f!BXm|E-&V!+2eSn76Hn2Ve7-`MU)o#M~UD?%d4Bg;tPU~t4Tkm-3b0k3% zB|_{sdY(+rS2Hqn# z>d^+H8)Kd)oaZM<@2^Pe-)sIiZ0+{xD6O5Dp9MuZ43^i%tU`aV2|YDUJ7T;ZiwoEZ zyg(K-`TGkaDP3_RSJc>v%XE{Ydi6sJmW=l0@fJMx@~9+7y`ZfOeq`i-rHONma`eA@ zf>PDW2=YX#<%?45S8t-Ze||P&h>L@0jh2sIr$sD`FL(L=U*RL|uF%<>dHjs#IhYaG z@4V2Af-W+_?948a>!ami{dm0G1SX7#e(H7`pj%&B?I$hjib#)gya*&LgDY^F$>I~i zXZ3=}TlXcSPsh1;Zj)kV+-d#|?i>td59Uo>NEjgi^**eS)h@<^l1~w_Q{)4K2sT>- zz&+l&u~(Ze&&)whW>Z811EQODCZYeG03BF3r2S$%oF#Oaldz5PT)82^f@VJ&4>k4$ z*pjH|9oT1WdCgAw*V**@QKgZQhd}QyHqni*2H{s@v;L!O)}Hd|`PF}HUnvV1d>naa z;Ys5c|3=oi0r(>cSulbqz@!cvb%il-;z?>sx1F~sQdlDF z+X{c(9%IWyRe<&M-5d(yx!H3OXsnLQP$-XQvSiT)J`^(5x{%#8TFjchaQ>k=2`dSq zMr^~f<0fb*YiKC4i%p?Rlt$*&-XqjHz4OaJ2YN6RgZuaHWWv3MtQ*E=l_3{r7SQ)ZifHe-x2ZmL2_n zh#Sn9zHWANqc~d}k{1p{HWCu~iC-KU8Q2}d3eKB_g1Qxc)#IYRZnbGW#QJ!3^&-uG z;qgdV;qJ~K;GK27v^Fx#P?qrVF!sGszQn3?_o5y1th&GNW=N2#1qq)szqk#$KNwg7 z&cjOtoGq5`DxPP6qg;Zg*0{b$5T0qJ$BDwqY*!3Bc~R?l^<}jyE5$0%Q zDU03nCMtJrB(>iHlFuisy)#J{E$P42!i^kI*DfCX!Y^n%Ny3~}nymB$zj zm0(}F<5lC3;^XLiL~?>LNS40uQ}?dFSOm)L>`%$n)z08jqY=!%lT(({jbkURe6-TV z<7z3&*fXQeu6G(5b6ZteG(rC_pm5kQ!WT z$xW>52}WZuoG3@?zq9znV2uyVk2_#}DXqpr8{sY}!riB_(m=v#kbSB=DfMwQ3 zByJQ`fFy1{fyLY!VwaG^v;!?l{#n9#HGKBq$z+iWOiyG{93I%ZZ9ff>8U&Xm+*o!M zq~9yZt3}Pu&Tct>SZ%k?NOnntN7s=vfq0hx7egp=9He}*lf&o9F+q29Dq5w;wRKGy zVaK}*xlpCEc-aa51yqLU-&sse4*!W_|65QmnVre7JetnCy1VOXbMBsb(p^O0q2NmV z#>9gFmJ?xWLU#v^&Z~`SJ{woDDL>WKdj!bp^?MCT+0ssfv*0!aK|bL8=aeW$Tu z&Fj_gg@xhAgkhyp5WTkj6niUN<|5|CT8%N*e;Iy&+Zdc_x_M&Fu?bo3)88oU#lby= zQNZR#E-c)NZofEbv=$Nq%ZIt|WQ(PEA^hQl!_CdjhKQCD>vzn#Zp?YOT@Tz4?|;@t z>^`;`Z)tVShA=pa6-5ju3MEdiK3miU`$ElQzcpBH?hu)^Pn=(|v*Kd_%B)^Hl)6MAS6n83?p?GPB$3)P65#|Q4B>|++BC?B%R>4MrQ5{h z@jLi1mpIjpfk*=5>rXV`;mn=U@Z@Mdp^{YP=he0d)3FdqUJOKrZ;!W>u-D5Ai!#7J zt^PMYxN)EF?~#0;EV?hA;p;dYzO{5Mo0yoW*o)vm9vkMn-3Qe$6T(pLd|9<#R#B1g zwsTt~o~=!~*!YbBzIIyUSNxRwQ?fmHX5j_~`e}PS z&Fl9@9p;ZLELOA0p2QJ!&n(KY%g-hoLKosS_~It;JS7p9k6PE-CS{oV+MiPqIDpgg zJ3Dkcl#^-hC>#bvADTsj7INZ`biC9ozSsrJlLy>^b~_D=f#F!xEmI;P194g+uTz<` z&kwy8vjA^${m~j`3bE&{COD3Ph@of&5RQ!8f7GIm^F= zgbm3KCOQWX_GT9rf`fpQFq~~jbsAth5rorJ*m=T6;&GFR{3VV-def$V-8*q7Oc|-1 zmn9k}1E><9R`HQyZFHiV3Xiu~n+64An$nn!J{*TNABaInQo*|abm8=wQ%O*+N{4dk z1^^^nlB=DlqUwT%BBJy>)G%=n1s;3~T`>%+E?Z~EvnuJS6faTu8NX*Wo=zf|VqGYr zf*D-T)`E!D-e6HvgmSB4Y`R~4o@i5DEQ|mN5L#F3FjLBMAnxXM)5o&o>eesdyyEOx z5S83{9Vw7_bhtge_t>qrS~x;3D!%!JL{(p};KysfBO86}1$E)R9f+)#J327ko2OwS zp4|>a{K0_LQIDv{>r1@R#)Rrcgssd%90p-M_Wn^vNBFUE@nkpXGM>$hQO(aqgvt+p zXmosBX3!rpR=ux&d^)g+c|%CwaY@wD)}knq`{&Pda3a9OpjLR`cPNfwS@`G~hFii1{dO%hJ@nIG(Fv zB3#@A=CR!^3=GT{FCFP*tok~gl~%qwV@%cM ztnJWdjuyO|-7;dTw0>6XOiG((sl205?C zsH@2tt32zjbr4dvx@80g8X%bvuP&?9G}fn~s|xgnK1VdJ=G8Zy-1s-VZUxww^yiL@ zBMDf>P4hQjoKLU1FF!&!lHi|%f)vZ=IAUt8H1hMKgc4!xFdgmGBMHQi#4+XpWTe>s zHsGS}O*t&b2~K()Q0@{bJG~;S%ua8z<9c=idp8&Ili97{R*R##YUO`Q9>RuF-bgpT zct;Bu>4;UKQ_R4_@1$!=N~)FL#`e5?I_*C1&?@Y7_wxK;;YyGEy;)}#t2zocmI4;- zlzLiPfzbAVMDf*ipNM-?y8+#;t0$Tf{_sDK0zuZ*8zNDDgc9Mov0==XHJ>KkN5NP~ zPhPljZznGM35ECtf7ajK7|=fhJP+xde`H++Ve1ZTJ-Ok@((`7VUv1fC8{eNOpc0|x z5hi8I=1@bFMnoVaGiXJ-ZoFOr+etMws;G_crEdTp2Q3}f2V|1 z!J)I>USYTd5s%$3s0#dTuxtI#IQYbnuGDZ%g_wDhB>n#`eU6rrDFl8Y0Rr0Fd31e_ zH(dlChxX}8hfxm0+p_Hy=Zu9DwO(5!r3uM(emG{;(K;{1{u%Tx(B8T<&bx@+fkyA^ z!>=)YE?H{!CQtro!D6ghN5ZqJ>M3Kliu1Xn@s4)0ZMNAI8+zKr|B6X33ACSckjKpc z+)H)mcm8R&9Och>-)?5pEN&}%B={6L_bu*hm2CKscmW}rtj7P|wg07db^=gB^X4ew# zn%q?KsnhmL*4DHguN(NK4m?OPIIGZteseG)yt`X%JqQ-%&HeE=k0XsWxLI)E< z%0$tPjWKY&WJKV`UZ7#2MnK&>RU1i5Cbr+Kh8UvlOLLnloNLhe$(BO5c6dO&SzJOw z)Gajm0iOf)4*E{JwaA5P9e^x>+Td^GF<~`kHcv=Nwnuz4CAh!e2_8m{fI@k|*F=ys zL7J3e>h_sAW1`g7T+rE>ojuo%Wsk)nmlF+KRFZq%SiOy6BOwIGmHT@{<1otA_AX^` zK|AaT6(12XWx$GJp!z2ZSXCZ5AdN@Yb2sYZG6sLm01^ecr1;;B3EJOhKJ+9_@-66s zy|z-S}>is4h`_jSCXMRI2xYo`M z&$dQyA}agt&Jbmia3bWN)s-IP+W_TO2$cz>*!CA@y|+uxI7y29kLpD|pFgI9585$M zvqql2Bz^}Rgz%>mqrNe?yL0#M-@y_Jsp^;gR?^eQYAU;hgDTqax%8Vf_!hm7g(m92IGf{`xEh4Mz>nojQDg1J$0OJ*`> z5v5egg+V>8Te+@=`a>M=Dx;8hmX@Yc0ia=^TrzJH6!`rU14Djy)qg2AtDnza{0z8} zVPQfx7vD7KP1iWqU>tW>ZVRF@;0)MUQLcV7D<>u@9HGO*8RP~ zFzBVIsJs=SWJtR0?-O!ni=Q>_K2nbFyLWY3Rw-J1hM(~<|MP%Rigp;2k6YYBNRmLj zuu*XTcIe37CVTdf27QNZ3Ijr{Bn5QW2ndYEc|E;|W%H~$mpr+AE&x)jbqL|ptLgdt zQQ&O2j4ZgPJKHlI53F!690!W?SNn7~yfJ%7L^s@65qE~3;1FE-%*(xN4(T2vDnVcn z$L7%PRcxVTPX^Nmh-KHvzHFPGmeNx8Cl@)NBQ|rSpN|mE&dX?x%y?17OahZxcgc!Z9S9*ps>cp$r3FN~mgDo@Cq;sC*G4qb8vko6 zyHBjT>n{FfNLApL(AZ%t_lU;EQ6_?AU%xDJRo6oQFl0_M*t zu!JI>d3N?WF&oNy4y23S-}nF!oGy9IdprGjCpj*!FNBLaVeqUSi<*xQa$o?TJt-%I zP_4*OlW|Mx1!X>_2p?%M*iRg8C?3bw}iv>=waXLcRO{ zUVyYW)49b)1i1Toz)_E3Z$69s6}kO8kx-PPE7+VgpY~vG<&45YYA!nb^^&!`M$=Cu zm`-GJuU2e{+y}dW5k$6f+&QZj8msSQm5M>_;;$tCvgn(_W9xTCWHoCEWV*Hdw_>++ zLqz^&e@mdAhMD;xx$KLQhMTt3>`JWaO~6X^98R0-(T4~2PEdEF*?FLzer{{Evs~+3 zevrW=5FD|zg~H{+A8(T$K{$VomenYZpnQUudNR)+mSe%bomr(dOlSg);4*i@GNy&d z+P~FO7Sg6Zr3}Uc%eY5I)1i}F(AbKKifX{Yd3Sdc|Mpsx(#G#R!s{RW;;|be;8mh* z#r9S>AbLBlSq~Z%8*HoYe$8p9l%p&YB7~Qb9xf)Mrj~$&f)-{>ApKn9W@BexUay`s z4*myt8Uai53+%`ASc{_wjF<}ugD?(YwU8maTzv|35$qfI%lUEJBus$@R}tJ3BT!i! z+!OVW_D&q6(uP7ySDrg>Y0+BD4ihXG`5!4{Yh-VntCU;nuqVgiY}8?3H1j7wWi(i_ z92Ym1CGla{5X?z&hnwE%3koR!jBR)zS641?#>q;kz3X%Wbd)j}vqVIQj#+vW2>k2>u-D@D+-3krNSZV%<>OpB zw$SQieS`^oitEOIf99l+_Gm??)pkTv4qt!Qm-~fena-|aAw7k zr6(Y;>OVxMe>WU=g8nreG#dWrvx^odZ0Cu!;o`q`B?SjJXYIlvkn2xU)u$1 z|9cY&xBejskA*@dSmSc?mg)D_j$4H7?35KJPlRZG^i)Hz>C1}yx$BlQp!yw`*bDau zBkxWxSvLAkZ(TC)wjmX#|9~yrep1r3CEN6=b3=-^`mn7e5m_y45@wR(AC)`RuMX_( zuyXNrKJ1b6<1(zo!rDtw5pbc+3xkR<=;CdR&MUs10*u~D_7vX@5CNlYa3-uyyGI=L zDIFq_fN6N9FuDnx(z}rbn(mVpG$8&>txZ|85uW_fakC^)^{lnpYzEZz*vB;9-&@kY z|I%`32nb2A!Yo{W8X{`n;q>XV37602H8o+8L${JH{RB|MVQV8pM6~L_!m_FRL0~FL z&e<8%G25j$lI%iA7l8@MYr75OoKv=&)n?}PmUBlqfkI5rY4Gd zQs4DzJ^wg`J1WsA2S~nkm!!|R%A_>Cxg22&4HKl1EzDvfncXin)T40@y6vp`Zou>x z$bi{j%C0VNko~TlnU(#hg5@Rv4ZFo&b8Bo-i+dEHT3AzA;y%tNnf@;Gw_4Ia(|fPH ze$Zr<>+5U>}3pIsEc+oPvL$eZ? z`x)?~5sEZAYoGsTAk^@mnY`TK2u&97&?m&cHF~=QwYD|yTdwjFYA(vu~mX^&ZjFU5QTt{a64B~kPM5Qw_@GVX)N=B2wn8FtHd(_XOPGBTu98_ zUE+Wv!nz_G&D9>otU|b0jp55!{J=NJ?JmCCrIAE(HsLyhWq95+z{#wdA*mh&AcV|)xI%x(L9?zAfUaB*NdE;wV4og|vxclx zi^1>*n#RE*(e}t8b4lZH{WDQK4EX(<2F6IlIz(Lz9GV<_c*zT|{b7G6ic?q2OBN;5 zm|%dKvUg%);^1}I`@4M~*9*yZ!0r%hTz2-x2F2(Qj7WU^Z?$Mm13s{K6sxm(v$dbq zi&|I$H~JC(sz+Iz>O`9FR?BwrhQHtAU>yYKILPS}>A{Or_#@;*gn&~Ja?;wPoc)>K zEo_@T7x+agSm*huu?>|3r&V{8`y&Bawl1g9YVhtf#O?;cps(DKR^a3AXZKiF9&kLJ zTcq9W)8FcWN`sX1b!(_()o(4e|s?DD<0Xl=?Px38I|;<{e}`^cuy=(sL8$M^#>bVRMx*RDvaq%jNk#r;&qFi@b|IbS8p#2_>(Mup7`$rWi6)J9S%qE;4ymi3cEWB;rJrkew7T_o; z!xJ75;C1BW(Uu#Diy?Ol6Y4J?8X5T%c@KF<9;y{GkP!weufTb`%xZ=>)$s!QVHZ72 zIk%a&Cv#SWJwh2aS*2@$NCP7V6aI?D3N-(YA&+#eXO+SJ?Vb@`+W zdXHzzfwe~B^^_SLEsAOjaTrU5m+KZ&;V}acRq+p()zYf727uUTe~IoYzhVEFJfO?T zqbFiK$?cHk+0&6bq<3h#66aVmk}uYLcP~*$VE!|~l1sg}ki$6&6BYX> zag|pLt8rKf>!LVbR)S!31~`>)(KI>D62hj>6P-MGL9uk*A{f^Cv^apE6})8HMKG%Uu&H z8V(!l+36`W<^w?pSsri%$SJyK3DgStBYxEK*@c9i#$-fPf8CAb*@(&fqqIZEM6twQ zZ(?_4lkU3OT$y;q?|o$l!Oqy;x;@?|*1(cqb&ipA_x?@bHw9j0x%J z;HB5AMVsQg-P?S(x)9TmL;4$S9W!DT`4DmMRdkNtF=HN3Ka46|1*nuJJW;ShVI4z{ z7Fy>66~6fnzi+_eTlFjxUqbjE_0!<<pa>)4a<+Skj9LCVr^Kep!17Se+y6?T0m^g}G3A4-i} z@Jr{d6Yt51WPealYqq(#=%fXxRcXxB(ORe3Eum*~m|-$rfPB{b-msgq%PY3B>Tl!e0QI&R{DB1@8izo< zDznX+t}yO1x)|459+W!^|8YBf$w7d1GdGv0`94Q=Ku)2b$H9^aY0K zr^qY#rNUF*w@7gdCScLn*5}T{4iiu^j|^Nn@A%K^2ehfem}Li5oAd8I#5~`ocfa^h z{vp^x73o=AEYZ=aX-ikd?toR&jeza$+M!ZA7*C6;?*0g_vJ;QNdv5po8wIOLm{82U z=RslaWZ7<_FHa`%Vk_f_Pauknv(EwvLw|flys^5vbnZ)Ci(IL&1$1~vaxf_ zgz!PXv~Mkk>0AzNQoo$<>kGLgAy2KJgNQk3SjhhsbF7#w$Cmd8*00BUbnv074V#(s zgW(`QVB@h&N5sUb0RMn9tLEB0lFGiGS+6-f_Pgy^yoRk&0`;Fy^|H!0pBCzz|GiDLTIUC>xQuUa!O#!J^{$v0(W5&;q%SxR3gWh z6g5mtoIi(JAK>pS7&~GNd-VttGuFQx0G>MGJwtmoAT+EGsz$x;?@-iU2`*EECb~dF zM8?b)gZj-hKkKt^`tPoA5V0~$2D9f0oCs2wXaEh&t8)H6$`|VJ#`au}0T+pyB18CuQOQ9# zq8Etir<$Q;pPgHKyWsZOZ&%NNwj#JZe=w&(1&o(upLcft&l?$xltd?-&#&Q--OHWD z)AKVa3#osX&38N{HBJA6Vyrg65gcwzXnbC|aDbo!4B{P@ffMs3P{vaR`CvO?ej*gn zzGu=4vsm=XqESeXR29m>49+qgY%!Y@3YnQd6X+N-i|lB21>ymUEstH@@$s-6M@z7H zFA!wp;NYRjM)^J8UXofzasHRKq0T7eMW%%lms?Az(Sa7F)tp|97mo6M{SQch_<8(8 zyokduhYzsZ3o?W@CSQ0}({LY?$zMH_`1?acuSQ;AJ_j&|j~P?RsMu&lWGe69u!Khh zoCv{l1E{9_kx#(_1Vv!S#BmYlz#STYvqih79H#h>zEIXo=+6xJSIX|8)y`CvK8VrM z(IO(<-v>k{P=mCI)W6KaBL%A*1J)91ck+|+I4r+mzx*=Js_|d}6}0Dl%&8cc5wG!} zty`S*+S7eNBsk2k0-_T5yV9SUJ(ySKse&Yj{ex1F1!)?~=N_=eBy4yST=A=r^)r#J zfs~<0_1YCz=ica^bo4C@g_GCYo~GqwyW+Q-KA+y%^C%=M$O9-Xr<3l!T1t6UxF_A% z6nDaF8QqneAkV|i`@?KIfxM26{0vDH9R8C?EH_vW{JX{#aS#{O?o5f9(M&*$9oIbu z|NDzlM{qd48As*_Sq*3#*8dm$sC zj1l4T*x{$%L=18oE}ZM@>#ds>8XB6y6^MrpUt?t^CZ-Z#gH;p>3nLLfKfhvQV_sDr z(ZmEp>7Vkz0JEORUir|Qfx+>&=*~9-foR#`tqU&xCahzY?=z?&4y(+J4VT#y9lxLz zCRDfnOqE}abFHGP>Bi*T=(7agn4%}KX7jY5y>hw8Hn!fI=#=#CfKYM9b>o zE9yI*i;`TvLwGY4fbEw9ZhK|nW>1Jm+GFYaiM&z@IY7}1f16&2j6(*Gv_ltMA{Q4p z*>Uj<9}Pn_a>Z?OUA$=e?6_?@mV9BsZJmXHfEa*}pD$Ng;cHGlz#`!Pw;>xicP|bK zBITk9O@@xk$QpQ-)Kn-9NnN->zS$w2t8DJW79!JUPUK+O)22Ucb)vdfLno%nhTI63 zhY zLsnyWj<8fqN?85oh->Ts_g5iuu#`oFWyzW(JgJDrl0hi23knPWQDcvuntuHbPCPR$ zlp`7?qx`OvA{ZtILLCg7NSIL;aoVsUx9Jb2Ue`5rlmdEHMFO!oFpiJ?Jc1CpwS?QR zKqd5B;5v(UZe-i!D@35SA9*XDTT7^YjYbFysdky92RD~6Q4;~XzNFtB+Db`QM^_^h zI5JE^Um=NV)IE}^23t|X@-I>2N5AS`eNk0a_Wb<(AK6Axem;VBl9HT4_>IygG%EYI zJ6YOiM47Q;5~)97yT&(yLWpvrbEYcKAKI1ydsUSoO@n?%xEF+Q{9q#v8smZ@*~qFT zoV|DFKCoU1N}T(|U*71e#7K#HFEh7@ZJZ+nYir!U9!tRc_J|f(6;;8VQ`rD@Li=E| z91rQ|Tc++%{EUpoJtE2-{>cbocSx(97dDtl z)wM)qoWIOR(Ln+hmHgg*u#)kuJhpc)fyQ^#+GwvGl1YZ;K#HzJ+LQzYt+_*~{_=g8 zId2hKs;cvDmjO^!YR>{3gz#!pr8*7$-&@~l*h}DnL>E~3L6gF<#4lX zk9<8JY+s5-_IVus-{wQqZSd>*zZ-Lvh06RP*U|j%vBk(k}>*DDyIRy;Uc+tcx~QP$WbA9X;WUsX`c3g5&r?(^r^=n_I{CSww7{>#!=#w zJFOVk{|dBUPPMIst(UNp0qG{olDKC#DKX+mRk4a(%jPY+raD;7rIE#;CxaO1m*Abw)1qhE?YK9&W0YMyx4#@@Hg9I z&eF>AqKnNuk>H(!<$6>U9JhVEOPv;zNqVoh>k=-vBW4Etw{~ILC`q{%_HVJwHfN9( zFI${Xyk}wG3s8|wiM&9(SM6bM=C!rIIp2zOVI2{L{Mw26KP=(GZ|G_rSK%xNXfeLa zW+wYVp}OX)Xa<9uu#C>BGJnr<=c&Ebj;lWbe4h=;=l?CrG}{q{aV)vcVmxy@taXoh zXF1RQu1v;{jWG1A!v=UEZa^DD;L0ut7?27eg!&N)cknOPpCSB&o8%)7^0RBKfR}9n zlP;#giKYf2h@gerp@y2Ne6guyvf(ho>0Y`{Tu{JotwwYzeXXbPK$9`$E42%*l%wn@ z-X7q1Nm&T2e1CwrZ;N-0y%`(lkb1G)2HftR2Qd7>BQ+=lWu!yG zdR!(^sK-j6IHLgPqgP~%K)3XdjmPUdQ0GFAIVTv3%@UI^bMtk}Xw7}7<8%YLK|ojq z9Ols+@pkT~!j`@zUg7s|nC_bOHhdrN|Hj;Q=hd(FSf`fQAwxM!3rj;yyFw(83tvz= z7+XszAVnZELi5-uzHQrPP`F)alfE}w5sk(g^lw$#hqR6&W&?qhSgdy*(4=JiZPF&O zs?KS}>HLuFULZPQ6k?1hu}{u~t__Xqfs)HlXkTB~wa;OvFLIElrV~=R?`aNB>d&-K zaxDb)FNF!pwg)=t&cj%_VqcI%46M zgsq$e$2KF1Ec>gwnw*@hG?QCZ6Yl>GnSa}9w;h0tVEuBQkO9rdSdS01!{XYysA+AO zdym5Q$?xNg;q_tF=EG3LXD?M-%>AVkL2HeKcUP%jrN2`#G2d&a9sIcQ25Ia20!4=1 z8D_*FK9`0A3SZgvTzKdgM+j`2#eLfLw^$tPGz+3=>0qM{9ewz~C1BY1yQB}Sl?sXz zdI08%jh#~o8dEl>2hvIz?*1e8aiT;|!7RtYNUO2QW;y>wRqj)XBZ;`ex%mdfVr&7z z!JW^8xwp3Bu=;>E{9q6k7MWg~i##|m#oHiVw7)r0(zu)M|IZJ7{NU{D{Pf}b+3ybl zV^>uOq8L-Qja%ylW1O5!W%J?9_B#24-1{{$O`zz#3bo^X>gf}><$0Rbcpv+bF8F$v z5aY@N>gyAxf7vn!wLR4~xasky&VixkpR#%QVZkJe7RyI*VTaz@CvpOAjTG3&x{m_; z92$uXib#QIN;wxYN%4UwqeXXcz3N3sgT8ob?^#OQ#jVF(>+jBEaYhJ7F6ogX4XjSV zPozZ}rL`p8azxoW20iy@p&Z*ADJTs@xK%_7b^43E0ztG~IA`nb@^}z1rwH_+S7?Tz zo;egNKbSSSG%~FHSYvugI2k)SpPs%?fI5dJU09PppR0Y1Mic+Afn4Gi-K#qnSfcNS z!1iq+!XgkPnNF@Az~o3(a9l6s_PIhL+?sAoovRAMa{ z2Zlf2*voQh5ucuBR!gy%wcxM5|Ku{v-^_Q#1zj@FPicLeHzQ#ZwD*sWjE%*(K-POf z=W<+@?1yIZk$|Qv3X_F0ykl$u8|!oqqh!TAMY}L8QDsB$37d_2Phhl(vGVdyTifb# zZqfW#_btn*OIJ`dxrqtp@c8%u1d#sdsnBH8H-_)q>rq*Z8<^=jFZv;Tg!!tvAyh1= z=^meBOVkwF<#H`dEP_~UWu}1UTc;qOI9B;yMX#XID0O|^_4?x`IU^Oc!S=R_l?ZJD zsfix^1pjCIH#jtM^VJ8!(CF|J9EpmSwl*@ON0vD_ydFoJ9E{=SWz*W+Wn=$D83%ul z%LF%=FD*iM6Qt}+IoW`b7w~PUCe?gEOkzjFk_Z~~EW{X_pW9ft>n#k!_=fJ+)PZOo z@v8@p*zh)$+jtuV18F2xYdhna)yM5#mzUX!<{~2-^g@N1O~^E`=Wp0@|I_s|Nf1%T zmzrcLF0erC7n`OVm%DC^(1urRbzVbSAeaHNBFE|Wp`D2laT>W{hx>4)K`BaX4jquX ztVQGMYL{>l$D!NUG<%@K-I4TE+l#>XivS2~x* zcj2&_!|tpZq8#JEW9P9BH&k(0=wB$=$|StE_0 z=Y-Y{{?dpVo9#njO{?1c^fpDOPR7I8EOOIW+=QhB0h3=>7vE*$42K@n^0P!?7H)W= zKdUL2s;Gd^;u7ChXk~_+L^V$|or|O=2XAhfo7=a14e(F32v%P4}PNs2!hY!); z55P!_{2jKR(RTjCc<&5?cCBat5jg&H?A5wV33z8RNE!}|6TwVBMK^cIZ?Ml;>m*~gUu>> z;~W}s16|AkXxzU;$YKGWh~Ostu{VX&Y3zj8d;x(mfN(*E(Z%1vq9+jb8CmJJagaMR z_1m28jJ$*cTPN&_y0RZ+5MAtN#_!#dJ^eYc54_ynXbaT=146N5$4;0n48p_zWNY#dBK7bQoIBTEBCOMD zZF)jTS?(Qk@4|(n#mGs^gM4uJ<+YJv&}y-u(QJm+!a&01!j7UiEFk$ob#M|oVA$YT zTqvwTkdH4-7VL>jFc&=ad9>y<0`@!td8tsG-Gxy}(QtKYKQ4C{8J4a;g3)%m?){N4 z77op$(CYz>mh>!Opw?KR){v?nK&xY5FfcHg$l`{ckzISoil-ci^5DW?;$e5b5@jt6 zj-PD{ib%)$T7pN{A5 zort990k}}vfH{e-m^aiJQJ!5URt_K$GJUFx{i7hR{_G0|Bn(pEbcvpp`&Q>j%Ze(7H>jSD;O;!IsTiuEZ@a;N^>RE$3+2VNb|nqWNqE^j@REvC-yS zzxq9di{JQ_x56F0##!V!@%ei$4ThODE7C_Yz-aQ|TVE8J1k zTG~x1%so;BC&xO}{k8!@$YAll^eRitm@%MoKL&&ZV|a`o!UJ3}I(`6-<($J%{~II8 zC&ZPD%yI@w69<6~3Thjco_`Us7qWUmF2y*RI3NnKQbrMh~Q<(22ThCU|RW8xrFas3)wYrN!nB z+dT#YP+3)F$580pZqw#17#I32Atmq*J=PoB)el*yC& zbi`g>9%yP&(grdf9v;XzejK&+4T$Seonb|<(IZQpjph^0P-eElAxs92r1#R*>K!1) zfFqCB%Mc)Ai?753932;q&T`ag8quh4w)vSdXEREFI}Kq{_j6*(^5LKMB|JTSFeIi^ z#^achkDl-li8L;iF z7D87A?wl%zua^Kv&gk&s-E!oYTQE3;hX4-_28Z$xA0dE1ECl#`%o`_wk1Gdp1Nc~# zX2d{Ork849SIRBHn7`=hj%SW%oS;VH1NSfJxoQ720!U`@jby!F2`961;OMC6wbCPv zgos8q5z--UyPgvOpU>w`nLJHAF?J$z@Qaj&n)3|;aj2M2dI1x=n-~PdL(oK|ZyAOv zY4Pn??*x(qK-z}Z>l;zrR*Eyq0)&P7V`$7Eq@|p~;HXGQctUD8bVcOIgLXII(?cE3+MvcZgmL2?{AtwV(S zmAfj|pSEB|vNHe@PJG}_AOsrIFUtNz&+nbDkyd=+Y0ey5o7^UC^Ooi zIirET$N-Lx1D*pH9AW40M8*{2eUx)~y+Q;!lMcRoUr3}P=*>D@P*>PIMxl(06F;7T zBM;y<*MiqlM<2}Q&*03caO<>@aB=KB&dn*=Q1CU_cSechrAD;q`LHmg8mGJW^I9P* zbh2V^ZDr_+pG??&Noz7%fZfGlTnGzFnOH9bf*JIQmA-r5VXZ*Sr4j7#o z3bE~jB$Z8xGaf>8Y-i7o{reBna3cvp_Mrb60n2~<8Jjk4LRffMpIu=$3S^=u>T!9e z&mb`|zMCPCvj1R*Hhw`tJ`6@PTwGjw-MmgtPW;<%yF(u17OgwKzEE3!qLRy(@pxS4 z_KSoVty+cw$scI=fA+ouJdW!6{>;qws&2_Dw%jDky<+2ryTLZ5_a0IRfe`W!$uEHb zp@f8zKtit$4!sy-umJZ^g(T!ZqDzlSh< zPSo@Ow|^tjOV=W;XAXw+J>^@l^GGeSv~?XikUJB-lYqrrJjl*6!{?Hi;4fY*JE&mf z&;+d5(16Pa@ldOPf^-d*@AKj6VT!hJ<#(!&zbteP8Zw-Wg;bGI0b0yfttte^3+U6Q zMV(@|B>_~ce&k3IE?F?Y$sMGB`1cDhwAjs)N5;@0Lvid_G0R;>gU9VgSw#iLj~&ZQ zrdor&u@N79{4pkuAJ1G1D=RA*!6xBVv=DhmfEIM4K_4}ZL!vzrfl^QRZ@&2!OTYL6 zpDtd)h*D{38F)cpkq&CKl$w@$vQ74YygVkHtf;KS`gQA3ShyPN)~&~}lH>4rJkaa) zF-ZXt9;%qwwR;b~`|dkrW#=@B%8-rqWtUuzh5z^u^t1Ia6wu88ZBGr{2Ru0OaT)5@ z*&(*(XQn32CA3JI#7}67P(FJB)I^Bfv z!}RE%qr&$){rGVC2^icmvQ+`3Y80dyI2hGHhLHog1aGgAp!NCCi}Sh30gfA78bF=ot|sPjm-@(EbB{2R6d zLWt2*`$)Q2sV8}IQcx{My~&e8zo(=mciS=`ie^wFkFv6I7RD?qEp1|rOQm6hskEtI zU*zZYXZ7MlfT;W_BP151$&nfiT`*-*%qBF2D_1#xcK6Q(!(fwMpQ?l90y8*6^wpLA zYT|h$t9Y1Ni6xCl9E3boK3onDteIN8bL2B71d0Y{nTXwmc@VDqbBlYGm+nEox{dhN ztydyBqMOG9FMSKGw-nR*^DVsiyDJ<%4r{B_u-OB+s(^e9+CJ{Br5sG@ChR=u#N`7? z6@2PBRM%TrBw^YB9zK!7XZuxwyz}NeAA0oBlga}MZ-4vUDpo;2*3vgze{JOT-fevX zK3}>FhYlacZ-3KtBBGsbuiRYNY%NVp&KoW$=|3aeI#hHjdb8cs3{VSS^AF142q1AbUGd8&z}dM&(Fk;q-8i3 zL0D)$OG!>fYHBKr@PuEZIcV;nYKZ1zo`^4 zA9|QoI9TI_8!Ai~nv6@v3+UZb%}%AHDA=@5!iTFGQB&!_#NGyEn`7^MoB;*Jf(b^+ zk6iVsXxBH#e7OFS42&MYW93c>E5577@N_l4J?e#4r@}R(bTDXu8YhoU&i;7qoll#* zE>+w7>tD~~fd~E+ZM}TB+mL`2->$@0Uwy?|$BLrF2F$=v^8;E$iTNVrLPm>Yb3_j2 z&L9mmn4%3>`g_Uo;9?(b;8QoRjEoF+JOMF)mEW&q9RulaWUCr}EIp2zengT2iX0TK zUc){!Edmtgi{?GQZ|`2;?;pF*>nw0v{WSqdUJ1OKhjE}0>VDCwGL;6VsRSfNDGuhq z|RCN=Uxn)GWtVSBL(6`@B4IDV-$gy4bhx0FD(OLrHNlbEG7pU+>&L7|_3e z6H>7Bi{<$G>#vzx+)X!JdvY$L)XFO_y@VCJzC}$!9ZX{l;4D!#2ofOKB)B#?G4|pV zsI*NNvx~E5AjiVGbm9Bbwb&b_nc4cMkE8KG4SJ>MFf>z*p&8ssGEIv5b#*!T<<*n0 z^H2qrt=xo3nQoYMEx+GJFNYoF3jE;U5Gfr2q?iSmv^;!Xi5U>dqQcS!q^3_cDllmk z7I`FLUr^vjI;z;`mhPglY4-ySUIh-n43$QJpyHr6sG-uTV76+}H-ke#F98;Vf`g^N z?qf21NW`wg^{`i0BS{0~B@0N>aV>KAOf#S;tm2Eb11B5`rc6u0_T6q|NKMzcBD(?~ zIH;-OCGpg`cLac(MTPo=@Z>+Q6q{j>YilHN&JJ%<#iq(TL4a%VGNa=ux9E zecF^3q~zXve_g%7wKXwefkjPoqi3@l&SE!<)N2#9Fb^?+%WSG_GjJSwYt=16^O^co zT-s+I_UATNIqetPLCVb-<- zl~}#V*36-P@E%)3LE%TMng_y zYBdL~mVU(k)Titob9VlG44n;*R z^Z#UMGi$~Sy!-z9Z5|>$jtqp>ZP?K2_!KR}wjOuN~)c*133ink3OLsb0MZ?rlJ#gbpHH$WEJFH;mQ9ll! zaG|E&3(@C;B5}~Ec<3|&)B+!JO61@tEtMq0?U&#Qh%lQCNKMuwr-vT>vo)A9gomYB zgq5DT`>2eMR=eSpk}-Meg}DBP8qR?_9NhQLklkkr>W5^%J&cnYT2wBa|`bI{hy-V|HxxcV8DO@ zxcbV=qTPM=NC1VrKK^7evrr{=diiCSbb4t2Oh`i{Xje|XtZ8>o3x2Gy-svJ1DIk&& zNj##Z3N~)o;C}A4f6B)b%eCgICN)B9zHC$A-S2_7))y3Jg>v|6vjA(726>}R;F|>v z7UaxC4^28WS{3%$4&uAYbx}Xj*AErp;KpNYBI%czhjFs@hda{9K2!$ouEU~u2y z4(wm=t;0~`5ty1a(AZnW<8YnKYVxVuU=OnZ*)O7BvViM|IK?~8z;!>lOW+<;39;_RW|<4|PdVATP+ zNiv-`|DzdH|zdZ6dTX;fv_PJ-DxhJkS=pr0HQK~%k)HAsC)>~M!q~>_z z_1Cd*(IV{GyBF>#20$eKtE#SMg1J~d?8BfGg2iISy7e2FOBzKH1FeV#5$a7(1U)^i z2a*yK*sjTHvA|?BAvrk(X+6@|hJY4MO4sp1+vOl6_FYxDnk}^R=Uo`ri6(=jo!hpd z_{br+?bYzR8o*0#c%2T&q6cml)r-rl^()6*5~&NH3N%P{dJ~KmD|99+1ick{YbsJR zvN8I+aqPJH5TmF0j&v5}u~1S{60`auGE7XGj5VuPVg2THIMH|lHBv2VrFsZi0yG0P za4)qZ|9mSnCIJn{{BRe`Ft~KkxHYiojIbK@)CCpmSFM4_1z?AOQCa~>J_Xj1CXDFW zdCLqD{3Ep-O6p}QlLD_2ShzgFszGK7kE~>%N3z0F0d8L~3KtMLmZnn)fKd;S7+HHC#3J$5X+E^W27 z*syLL4(-{7`V$9Hd*U#X1m~%4fmHhE=HN;jg@>MgF{*~Hs;U}~J^n-!OZsN$dJ+(s z=cI+LJ2z7yqH^HCLDs9B@_cjW&Bg2pGoSXLwx-5?VE=yM@S#IAOBsuci>m{EKQRZ1 zzVP&n46DIlaQ5!gM>~AP2wtnzo?Mb6NkKG{5>6G+r2D}~pW>=3F2%alYwWN7_Z3x% zsa%sdD?!c0XiBA2OYj!?kgN#kGoxu3DNEl2m-M+P_&5dlVKt=_>SV)=wbd2b?Y$Zjan9VNWy=Aut+wn2bC)KKhF(3Wxt}6QKwK=J%&?Jp_T(t_> zIlXrN_BVGAkL$&|2zUSbUNY=|liqFk@Zq@a)|*}>4X-7>{QJEOlo z`Y2v{5-n&B+SSgQJ{|9U@BuP2Gg%n^=#e9B68W}3z8#54iOBEQ9|QXJ!%a8e zjER$5y709J$BrJuzyA3TY~5IhM2#DxveX!ms>CgBBM}>^*PDYDzZ$oULv=FhWD`f44ThKv>8q@hH=Lozi4$1rijZQ9{+zhI^{SNyNuFBmGEw>#)1oH zATKxbWb}{Vw%f7d<0bI9-EhbX67_ma7c?-@j=eyK$$ewKO{by4CShxdh~kC-Y8!nJ zv;r&^1CkSTNHD6P*MjvuA=2e=lZU^8>N*b^8oda3{75pW(JN7d{z)8G75i}Kga5+C zmt4}~aNqy%6KvhGg{5|z>32E=w8{GSv;V*cZ@-32od-_8g0Zu&#)FUjE#}LU;C%7o z&+y!{&%}C2*OCCraew{I3a0d(G-*&k`EOfmTPKiG*#7AVts>M z^85Y9%IfMyNs>q($%j4w&A9ILv{bzy2x4k-vdU;Q`jZk9mF%o+b?@H2k(ZYzQ|VAj zYHHlQ8i`R;3LplPYJz=x_d3@U7OIXMI>0&V%OroJjg$S2JQr}OlTAEt*78Wu1smk> zyaG8OqrOps(gYa>2A|e2?^WZ8+>lq#$y_ zBZoH9Q_w|7z3eGEv2*)&_uKC*RQA;F_q$R&R^u2W&o?ugaDMB=kTDh*do-PmsJ%en z(tI4-SH*sJOCB8am&2Gs^{WExWnR>+FUL5yWIz1UHRn?x(LSgK+ytkFQb;|<4U z_&5PZvlfGL)$sZ`94Ym~=M5m>6`}Y7NYQb~Fa*o_+6yZ4iRPJ3n<;~VhsnfKnQqI^ zBy)Iomkftb#1(_ICqMtYJ7tU>VuiijiP7!oeQ?|BCvfNF4D<*Elyo{C-`moh?<_4e zg&z}ckP{j^-A)*I_pk4zYK1A`f5)15{{8H;`1rHWux0Buh+-6D8M7JlF1!$h>(;ia z4+w+O2m|x;;B>gKXWw3?u-B+HY+|P!U44W7Wc2KGLGvVOiu&c|Vo<>#j2t-yM5||JW~+}SQD{IE@r$4RB-TT^h6K-uvWqcay7UxzjN#T{1S(4r9iP=g5Idb$UFn459i?Ya@cZ6!ro&6I2}H? zU4DqZ0MZNs(u_RP46#iJ>wUn7JMFk?xD^SS=#QcA(4gmGvG8af8K%kQ?QH=@1g;rw zU<0VgZ$Ndm8w>Kj#t7lKvP0i!=G664<3rZcdm2zke6fKxO^#Z1_pE&u{f=IjUdw%r`$hQ5sc(8_v z(mEQ%ooeeOrgaIU+O1&2b_XVlq)u=7o)rRch!-JQ63}zd5cD5dz{p8P$wfOKppHKYHn{RoSez}y}uxT@Aw>KI(YSE7u6lO7- zjGm!`2MI$54+%`4J{@!B&ef{b>ZpObHHC%F#UFnt+fMA~d3U*XP&%)sJnoqy>{b;m ztlj7bw(fLcq??8}hbonVajrkX(V*lPoOl@a5I%y)l-|N67d0J50$czjD@9Z`?Km2ABiv%=_0-!sV2;Tkc zdlN>E8dXoGK%}t0_uhMAUWc!}@(L3GQj?+Gd-gtILv7(S$tp}A1tc{U$NvIMnC1yPbtc^7aT zt0M3R{Onjrjc;$Tvnm6!H6Av25NrJ6tKNs<8FEZBmySZvkLOg7x^$~eAd;~u{8Isx7-B1PRD#Iii?AKj0U@%RdIxokeJBG z0clA(gB-L`8W=|BkpL?BqmmTDf&~}1C~Ye#E^$5a_~X(ipMS=Pc$ zx&G>ib0Oi-wjJ2NZ5Kp;u(42uq~Mms1SCknUS38SWXzjkjywO|5g=Ai>^_WY4{+Ef z!q4+CnsrD_&|yel4TkmxQlfZHRo0X97Eo?eQ0I_gYn0*fO7MCm=+qoMJ`rA@6kH_2 z=YpIVc@7qn1_4=tMlG;^?>=J3!?&d2jpa_%+5PBak4K zp6z|(qp#RkPN}rdK3ftQy6v_;0rWpA6RfMP#sB@^eKDhczrO2ND6g!dvh)JxT(JN~s|DYEyaY~r)6&U$Nx_AQR`ixmz1Dt} zf&>vL&lE6iM7ug=b|@mF0Y!Bp`X#kZNXnfGwtBO%?CWou^yP03(px<8$YZ$r>Z_ub zaYuq4OI2p%kl9dQ$AqwC6HV0|XjW+!u2wr8@p&7ifnjt!2_O;M;?I__kQkNxH7{k` zzi(gQu}2<}mMmMw1pb$@=Vsu3G4VEZ^QJV{f$#?*TVs_{lAG zK`r3rffFhP4SKCZ%<82YkY9j-g9r121`n1;j~%Nf(P3F>ssGMD{nFc*?=ovrRb1mv zC;W>KVQhXLMokNbHtQ>^uw~_HG}N9F1G;4($6~|`B{+aKa3XRA70UZ*yS)8xbxmxu z56byfxD-^mWRSjEr&mLxQ?XiKwN|AhTLdmGL4ZXsBhk!5rv~)e;G(ZlgEa^7_yKP~ zU{RS`y9}p~M^&AKMwbM?PlVqqVdqgh{&Y(wyndj#O2MTQIDD~5!RFnK7@Wc}p=41l z&-5N-XrgJQ^wHtt@zEAH=H+RSn!@9YnxT%@-hPj%H6MKVQC2c~%T3pZAKtA=08MmH zKKV3;4IduWF95+PAb#=lpRom%dJ42A1SHWP?V74%=~_%5b#@{F2t`h6Yeg0{DLn4Ss0=lX>e$4GtbdDy z{&@DS4`4L5YehYl67YBhVKgXuAsfR*E5(Fkz(B{=;Oi(a*cB0)x`q-t|s@o+Ly zc@9abDfs$>Pn*sQtak~}+oW$n00<_2d@(9ChvxcmzZrL4GgCL!;H8 zNADaQ+p`Z_3)h~?^Mz0)DY)8f3a+Oi^piMzsraFxZ2-EK1gO+tIeIFaI%tzHFhyv! z6Y-iVwg0}y|EV5WP(UZe6mb_FI3uQy|NZZlm!E&$w`TdrB4;nr3{Dl)`quh)bHKDr z9_!2exb=xD4CrY<&vZS;<*S%A@FPD-M|P-f!l(geJp5V%t{fcu^~Q(7$x9Ew|F^&0 z!cHhv)R3UIyIN$&0?h*(Hx=NGH{ZmAf4(24D3d8w0+2ZHcfb8DMh_p3VMB&61E5$n z?KBaF$^Y5p0NJS)+%_=@dF`~T?Pxf>KtrVuC;SpFRRsg9bb2gW0VWL(4|T8;Wq3uo zB_U8a)CZ&(DaKQ%k!2VJ4l1<<$B!IsK>$25kYzGp78iZ|)2Q?)Kw>vA1l8-Q{8vF= z30S<{jWVYRnWm<*262_$?lipf;S1=~ukD(~m8;gUYx4Z@oztz7cKWfj0(<|N=v=B-g=97)tYryo{wKEPasiBpWeNp5(L!N*|2%**7iL8w9%`F6?aZe zLY7tT4($|h_=pQuNx@X8!E3>+F2OlNDr29FE#}^hxeowr@6b@cDT3Gy@yUMU3ki{gPf+u88w)zt1^+ z=1k@x9nHBhjGy0mr?cBt!p zSp0>IqIwZG&phQhPbG|2D^1jG_})`W%hK1GlA4-yE=B<&ar2GWF~QVZ3m4&6zx;XB z$50=QS6+J!uf6gzo_Y2;Jo50vc>C?Qv1!YesE_ZcXy56AMF1uzpV?TC z;}Vs&1p%fhmm*ijPz|gnAhYN|fR)54C{vN3P==0=UAE^_0GBKxSuH>e_#xW;E$$a| zTFi+-0D_<H6LH8UA=7kf&Y)7kcXzw+_`jaV(BW;DgcCMw z*nm-^M|Y@UT)Y@mkk4+Bh;$Z3$S%J%v`PZVRgVY&q0Uyz>e9od4&;MR4RI)Mo|=}1 zOBc**dLWAzJMO;sH>j+tPKa0ZM~Yqw^Ha#5NXE{cySgmoM-$7WK^eGxybc;wx42rd z@i>QGA_oTtM3&dnpf;*C@QDgs0VyiWmpKlOfCNJuxjxcNOOU~XgP;HF0!2mlTA zEVB`#r0B1et`=ZaIP545pigqwmjERt3Cta^ri3OqRg1d+9W{!7(p7hRZoK(sMgVAG zegFLr*zUTueA%!OBXm!X_!s-Vwx-5gxN@a;^TrLbz52M~vQ?;TJg+op54ScIy$7Pxr!m+wXtPwso5}Z#gHU0AbL? zNbZ3zzW5SWuU?BulP1J`?Cn4MIdXD(;iZ>fVj<22^X8$^>BRnn2T)a6b=DVu0#y&( zIjdK=yj_$)_=W=FGSbh2|29T1}feC5}gT4W@@Q zU9@4JGI>&)b4RNrfN~QwS&~xm55NC^Ett-nSu;*L)J5L+5QiVu^@ zmUjNM_=oFz1wqBE80kKz%ZY}1d&m^0D*0Du={@0U&oA$~i`58F1c0t-0`+2`lCNv8 zzM@SQpOTuYyX?v3QYGJh%C+FKtN61Tz{T3{U0p@`u$JHaHnc z77i=7*pY7r-dN|u=P6}C=@wi;zTU@ zd@1q==C?X2&6+cZ-Clp?W&GiRKf`1;Vg5xIu_i!EzW9Q5<2;*WU|>!nCbZHsqAO8U z>W5QQFp4Y(;}8r-qDl?$vJ%r3lfYC^oDwy~M)N|IqTp_|7F-*9COI^xa$WBe6cf}h zq(`a>`XfO{1^SGi?DOER2Oecki}7OZ+I7tKzB`e0n#+DgwGNKXXp^f=2Cg?-&|1l}o}vI!02+xyeqU{iZdqLcl0GEqIczxQLtcuC zos$X|P@gi|_VmAAND3bU`D>pjD`(9;Xt!BgQ-iO*`UWK@N?Fv4`mo0fl}g3u4;-kO zGv^{@Uzq_@XDx7SEAt1o94ZYYXl0F2#i6&ELxatWs&Ws8CMZZTaL6+ASa!e*kE}*^ z$fJeqlD|B_$>#iv>K}gWaW70v^mb3V-F`o^vN-wNB10~w&t4XI-5q0$L%Hy;_*SU^FW2HAi*La zH}mY61w{kR84A>>bBH+PvEaJ9AHtQ_Tod>6j~qRQZ@yU(>`r-(M(SuDX?1l8ha%(6 zb^PIXzw^ERpZ_Ff^z6y&G#b_!Nv%<{5SoU5s#GjbMe0R9WYiJx2T)U6i{1P7!|8Bz z)CY?jbbMVui`m412WWYL^a%s0;4AOid`oun4D;V zC64!}vzNp3I9A6oKlHN!qmjdfgN@i*89=s$$8l|5?V`8eZ6QXTG;spL|a^DnuC?5?G!o_L~u)!Jq10<*)E-fZ|# zbahs5HICW?cyqlhCnhDk^{M@E>;L_NpE!9kJKk^<;Er=V3J?a(A=h4W6?0Wv{`CqL zMQjU{X8gZD-j5GIUCb(gY!RiZlyu3?$;OmPlbCHVr6nm{dF0qJG};?mZ83bhAT7YK zOx0Pf1PJ4pjYCkloahwv7(XA~M3@X554?Tp3AAh__hd|N_9Y21Y-G~ySBX&4F}{8cFRm1`4A!2VIIE%DloB?&fgFU>*c1?RZ;1 z2e8I>t<>b7El|YJ5#ZQ3Zx3jF+_`fnGY2}?5t9V$+_{@gl;PdbJ-@j}b)>jhpOTt_ za_W=(?RSykV?D=l`mVP7JyX!}kL_dX+&4K5?5pBg0}!KHIBkhS+d(%jxLZ=9e`mFl zjGh$NkMsSrR6Ob-!%p==IJ=BLH9?KB!=_Umdej4Cc7n}bL}m=FX9`EIOUvxeDT zlZ)5rQKK+!+}OB7e_E|p_}f!YC+^#Oo9D?#9;g>9cAJN%aw^&x?X87q?LW@>!i%rB zFm0#zCclkaLInJW1WIjCO5lSJKEkD!UJ{oR+Hdc>4{O)2W3Fw{p}^0ol`7b+GeCx8 zSB*(PD?<7%!|?_%Qy||N=$C@)ab%q(2d^TdT9#1f@gdi&MYOIPTD1ykkfCZv?vy$- zSV(xR>wb{{LVAJ@rN@t>Z@>86&ep75&$M!FxdWaHh)Duy@{43gfk1$#fmU8t&dd{r z4;{+FV|(}SXN>z?ghs_A{l*O&JfD5?iLBG>mHV%~R?f@MkJ`26i!YZm(h+Yu z;#|Y^*Ik2Wp7{r>7@&wC`^Um`Y9XrNk>MoZHJ9h12 z=0HRSsI-s7IR_3OhTG*jXFmM-1A9QH?tZC&Kz#u90TF4f*50;`3UU)L`S2+4J1TtD0y#{zP_%` zo0yc;;sa1mdlHz%3tFIpi_~Q-SHE%7X4V&D$&#RcV94M>tdgd!9qoDJ#_7)+*S4=X zr6PU^Va8Z+x!kNj2UuelFSc#p(IyF)w_pL@eC<^{_?N%1E`VK)p}8sDMmX$ml(9t; zF@=xO`Z5mWNF44-PDX7&gjxe=D6JqWC{-)a%FzYIm0l4ARvw(h!PkzE2raKwz5u2s zo?VA0r&nQpK~zzuC!IEa2m3ACb}*m+a4Fuoj;JJnc0IHci5!?uJ^hSv#rNMiE32wc z#?L)&H@0ry&VHxXpCgA2h2CIfyHE?p>jviMJ_=f{7gAkPP=i#hQ>J; zUDSdEP+f0Zk$_mB9DO)Xzisn#%)eI401yNHC`W*gwrPh1Wr<53{QWdBK}fN zh9$=Op3);3l|C=5oC0k-&OObw0X>H#!`Yo{DZ5%!qaZ)M>9N}!epsyWcamQba_~GG z=0Kf+s3c(DzI`nJ83}8GRkdj0e}vm^zui?^UvF%_<8N$i#F};MPyUs<*K{sKiB1qm^jJZ;c43dfr6n0OvG6_66AhDuFUAZLl`7xYlD zWRHZ42M5Vhl7WN0p&_c^gQ8s2eB(?(i#^@Kf$t>mu!~|)#Mcb{hYZ5t3FEM6(T9+HUZhhxAkOJC zzJ}Ic#Qvx~BHhjuG)d&<_C|um!j!($U%WV? zFLQfAgkxg==#3-JHXJq|6YQO@oOy32KGZ9{xtm~xBEuMi?I)RyIATpeLqj8LqS2rz zNQpBRkgDe4jv+Mc3aA7cg2K&Pwy>Nx?dl5( z3gW6T`1ZS1EJyxBjsk=cCQxBaoG_k6K`5d^y%J{4oDuUp6bv4We?0pvuDId~+nUGB}O z;{+zh?0a5*L zYL7J5Fi4|Tvq4CN;zU^~>tE0jNHXa$F}Hg#`$#x_91Nadn7*TY_b3hEZ8osoQgpUY zshM#zE9S?mzH1koD>?#{YRJyYLWbUpsr|Z#s@*4Y zkm~#}aw@dakveXWxtP<>1+Cc?4*LQDq^Yd5!sd8dBO+O@_(^>t!tSs7iQ zT7f*NIl$>~GXHPtH9!kGE%GXXhfb$wUBxVBb1*+kI)c!GUt3#?!$*#Ent9OlVX07e z#3<@u?5qF=^C}!s#17*pI<)|AGb>=XEW;S33(mli!;qStg(_PElEna=yn@EeURdIG zKyR-{9t7mH(pRac%)#zEdzB(atpY*dU~};ZZ452VA458ot^$pnVE4_-PF5I%1%*Ti zCQq7pvTf)aZ@tZa{!qk5rwck~x8HUPJ71rC`WcI+kYB=_*|VaO0i9ltKiq#myM6ZQ zr})P||A`HoH=mYXfEGHsT`+MX{_>Z<;Hxh`N1lCg{Gymn#=#m6lXwkN+jj&sLa-SY z^#Jg5)NN1<+USLlkdlNkGcEwn3pjG30?Fm2sMD!&*}NGny*q9wHLeLZuQ^d!gC*Pd zz+PI4L23?T71ha&jM=E_7NwO0e?Va=&v4+#p_p*hHP^=V+>bx`ltuP_NZuWYBmp$| zD33`wNeW4B-LeJi)~;nAIyW~L{rmOBfC2rZl7g$RyDstS>#n0;HRPwhe$86%-o1OI zva&Kkk|f3J^QnD4pDPgXo5(@BvZ|`V>-A_{E;oPpNRd!cQK{()k&i4ZN}^fkzc8OV zUUWYaP~p&aqyPXQ07*naR8{N8U=b*%E3|_SkD3iRe0)UvVQLtpv`7eM^vc1&u|XZk z!DA+|E#@p2rft^wC*2k&x#ce{}R9yu}xg94ZpKzEiU)bj@E z@a`< z&t2HJ{~(qvU(U!t{=k8_@`}r1ZcG+jdMV~#d@`8<9FMs09v&?ICdo(6ZM1K78B-w#r+ z(0%M^{HH%vR)6}uPOnzFkJL8;l2c+AsigzWc4$mnbpq-< zArcTC=n=3aCSv@Y8BBXYa8y^pL<@R)1|tAqB{Uig?cW=qTL=MB#EHsUtSzrZeMt!} zcY2^c%RT|ACK=v<%)HMVO15h34R)V3!Fuxh-nna6i!O;>jmUgBMcjyN6G&gvT8rcV zMHCFoV>c3Hz4Okyc;u1Cm@=Ev2GPg>rL-nanZj<7pb8|9$IArCR1T(6tD)7zR@cAw z^2;zwont(7y5QWwD4VGRLlXuy4~?LP-7BHLL5G7*50Xtf7)=RyXW{!8DFW#tP%3y? z#9&{&6G?}TA+L89DNvR;iE{ct6>Cyf)<8WtjW6?0$Hm2f+WE02NE*k z+tsi5b|oY0;mF+&30#CKzs=d324Yds5k?A*h4S-Z;S-U9{Jj3By^|>`Ee$;Smq$EH zmoHP*)YKZzROmyWOQX?vrcRzD-+bdu@?}?EX};&qJ3K?B)%wJ3@%OCPwcIA~UGWphj z{>vg=1Y-2vMSvG>ZQRaddN@|Ip0kWjWr2lj)?>vt->{w8`18kO!Th-$9z7{3F2T)L zU4%Qwli0NTL<|cHeHhl*%;>L!Q4x?(<`f{4%EU0yZjs)O!O+G+D7?MIn;w>?rW_tX#N?_n?sn zYIp6%;*w&h&1T3v4R{`$C_xDXAUhqZj=rn9Yk2RbfDaWX)|WF`J7dSYZ&Q_Rx$TOz!}wToCztQK^$um{@EROSblcL z9n_62@YS+q?v0x^@x{f*xQfb3+3WSHYHT*97)(u0HtTdcDK#ZU?$@usFml8Qan_tU z>cpg^7PG;#PySV&m(S_CqZWWrQWL;MB0ALog+xFmEsy@7n+nx|0Q{BV3}bM6;S%Vg z;NXOU(YzW-bzYz@mZc_73eb}TfMDf#)Hs!sEppEmrVrrp)d2~;gN9}7dZ2U{!Dh2z z^QKMcliR!75P=^FK>I-78--@6tdcwi=r&`<49uE2{fu6h(gr1a*I-ERI3C;~h+|u? za5zvdp+>D>11BO~5s)cyow8Y=lHwjFHT;qSzo>zudu4T-r1~nXh32wi{20MYup*@NW568hMwf;GD0*@p^)2WC^wnf|e^T<{3 z3=I+E!h~EEKKkgt{_AeMQA^Ey5J3b!U%HHi;7iNOLkj*MajteXx7G#qLTe}CeyNJdqco(Zvy799gtm-Ey;dFnM~glkc>zpoUF!af=XV2CrOPoQ};=~Qok5cQkX{qb(kc* zn9#~(w0X-`)KpjF#v88d(&Ow(v`Z6kCZIa#0|yUd%hqkImwO~gtxetpBuebpuP;(k zk~@@j{q*MR9TSW@%~o9(U5pABEJKOXgd-X}ZUyge@xmY~;Qb174D6%uNb16BJ4sO> z%4CkBphBgv#k_|<*gZ`S4SdoNP-=$6H98eV0a}jy@6{46nqh<{MlRLmChvX`Ivs}u zivU$8TMCeQ&}v(+syE;LB$-63k@tmj@x=1O5o20Oxt6D|rdEc}*K+RiOF*SdfsF*keg!JO z0>P&slOhEocXoOma6#+wV`>>?W?;?;J&Z9-PpD^4eZ2&OfrHiBHR8cFM-}MRWcKXIY+jR+5@RZ-iDV$OJBY-Z!b0b3kN!-(U@+gs6#*P$%CQD^7%rEB`i7>f zeXSQb)*z$aAwum}pcjFDynx;`u)F5q+YJgD>=9(tCi0jyNCT58`jOVx%W%1a6B?^r zum~|9+fgXD^Z33d)A`cti-eTa)Fx9BMXTO>?|l}b`uR_9JC{oIek1`gKxBa22T25) z)2kP=7pHlpEwXpty}vV ztioiC7O7pZnV=E9lb5hP;Dg4j#_$X+auYbDB&!gs2&1M}g3lNHOnN49d#NQC42gngbn+ za0{dfo60q)6C4o(lgWrLmwmI&t^a;Nh{XB-!=wQ!jD@3ksU7^mM(EQ5IFYG{1hjF@7Wg zZGbdN?|<+i>TI@?4~#XTyFDKK z$TV@#YB(75Jd6e&T#R{F5`oHUk&%|Ld5^^`z+&lK0#@zdu(K=xA7{j4uYHO1jM)95 z-(K`Cii(Qx@Iw!D>M@;Vw68j#Bhgy8KWwbhyc-)42eN%7@gbRiuu{cunpz3J{OTJg zLpO^SF04=V9<-jN7UT>hv~l@l_#Fxo6NDCnf-ZWsT7cUv!|z8_wol^Pyd(~JNvFOp z(&}xmm(bvlpz`#DqWR>{~eNXXCBVxpB}j$lTk0IQjAHS(cE=q2cD zDY47wWd*8h6eK3{EKSfENYKl;ykD*H_jg?9{q_A%x~I>ad2*$vYmIu^zx&wH&UKu-PWU*Wkwpofg9-;k2TQTLlT*B_Af` znlRYNu?U9AMB|r_NQ1_n2Z;29KXbiTMzWrV!zqEJJ6dh$5rI^b!hAxs>HS+J0F8|f z?Ao;pvuDrlv}5TmIBSuBj>Jcwe1^hRtJo`&uQLsPm(wX;ckxuuEyG=BrBe_wa=OrI zaSvD9P=g!Fed+bBT;xhHjldW`F?^W|GJInF5}2q(4zc~niGx`G(FPd}PKo6Pi5O&Z z0{TcCv>o-$a=;Ej#&L~|oE{qVOXQ%}@hqoDyZP1@|1csW0faKA0*6mQelpL}14bjy zv;y(G*6HHYeQNL2EA9&1di(87t_?~lJ^Qa0SOYC01>Kwo{1^#{1N5E#^@%50w^2$j zMoalnl<}h3)9`;YjWFxF>hL)@oid98#`3PB1vSCSw<;QJ6G|}BKz6x)zXG2>DCqN% z0gyw1oyrb9GSd=RWDdC!&y2#F!%yc)P|%=LAR0I%CkW^jn#(AZ--_>2ODG)5D*_1b zcPP@iw>p4P8EUqfXf1`%5qN8Z2RGdPS6q3`H8Edp>6gngIgy8ZYC2*`N5b8NFI!D!o z1c2rU8TQ6D(tutWRX!Oh$!D-T<^Z*ZZXAr^u3-voP!#AjGUP-K3OW0ccE%xNX=MNo zyAKvljU=9Dx&BI@AA&`NQQ2A)nEB9VMu7e_yPOdbiJTiL(ixCB)KR)Z$FWFAT52bj z4{;olQ&OVd6>bZ@cFkIh8a?`d!p8fYCIOTlc=OG-SkHs&uRZ1Iym7+@kE>*zDW!kc zmZp$^$4;TVKBLx&K(1<`!@W_&*I~0;(&@N6wy$;Cfp~l<J-dYHc z=Lanc`kz2$WhE;cjD?-sHeptNCvH6>p>Qlh=x9R+6b6l5+Bi_E&1B>{rnHs=%w`T| zv%pdZG~Vc^*~p`RnhLU_gD*hcMyV2rHWzxRRouz>=1j>sZCaXROoj?uD@Ba$sbcdN z35Y3u+8GhS5m{OsJa7QhX3S`DA8i1_`ZMY^@xOp`ngqP|`kPG5b<+)}x3dgT>Nbq@OaH0YY&oj$q9~aE=4@uJ?F%TfsD#x!D%ansYlnG4T^NN6#-}t8VfX6csv|ZlVd~>PZRW7dR267L1!Tl zWs^i?!xfN&GoS)Cmue!T7afQ}=^VBfABp;ckplE!DSLb!2G3=jvm}6aYT?{%WXR^> z`~Tpb)yr<_s@9cmx55UsB%R(OMY(+<5VSk6TH4SHoDMjJ?b;)E?vx`GSt4RQ_iyP~%g zr^>T%nL0(7{}<4G+B>xa4!OXsHnD11hBu66aNPXHo8 z1B1eTl~u&}J2y8;VLc)n6cQxi5vlS?iA*>8oy3b&qD0z-j&}BIi=0i)R>7d=@cH3W zp*Ye~)Y?SY?Q*B=R!Ge}JCD;W$U17>xAbq;E9cL&mlT&UuVhLEP+1vWZ!f*_`bj^E zA8~GgN{oB;>J{@?zgo5o1|iVtpM}c%s;lFhE9!i}0ha{_8`aFfD-x7TudNkXxSXnq z&NYx=IuL^d3`!C3{b2{3p|2yH7NBTmN5frL9`zMT6O^5kgOrpMTy@2z=iUiE4V+UX zfF|GfCrM=6ozq##FnMfrFF_#cjd~JNH=%>G+5UlF)|w$a>n8LrCV_4(;wmU@&q(AOHIq6 zaCuFwh`KtNEv)AXXiigNoCdv$dVZL(tupw5l+fpFPntDeNbYLX)wlG7pokGg|I8N4 z|AZ(I=Mo91wbkK?zdeQh`}R?%K+K;zr^)>f-uEl<$^uWP>HtV5LIxKU{y&|T^u+2{ zl&ZXYjpsWLCQNY`)~7aXtCEyxgM!Ear3uO_18fI;E)De75U-VKA1j9i`FgA_@nTEm zsSi)7f$CcN4mvMdqE{=pak$s;+S7j&c5L6l-aPCjKq)990whZP5#1YP0!1#Q1qJyr zk8}6ly+}569?>RKpIVz7H4ok#4SwK&*b~?O@{jDEfdvEc^{RD{`b@(KN6Y8ayg}(Q ziZC=Z1a&{%8yqW{ZVmmD1T?q<@J0|OBL7kc`=6v60$8;0&8YX8&1N*z*LU-C*>ULB zB!C7nHG28$-=4x>{_-em=Loit@Tjh-J^8qw-gIrvd5L9abtm=yrVEf5cdbo|na^qq zE)n>y+FJj_s~^cPz44am-!H#v`0(qs2{-)dCC?5n-?_U+j=IdVJfc%ZWtAALvG<(Y z(Si1KNu-uPm*nYS(eU{Cs2@cQ!E+xLRkGAcCr*6V4U-HH|Mph@&h6Vh2oV9Q8zwyY z{1M$3yZ7vaR--{~@960O(gPHi6eGD4H++qa5-atKwfhZ&B3_4e!}09n0%kgmw-3eHaz6%x}abw>pJN?g&9yi^T7-s3#^Z<=#!Y*L3h zQ1naT@L3EKslI*tqW(t`(6yk+^udQ8#jf3ZS-zh{fA{_Vk9g*pe`3qlt*jOCEw|kK z|J%C?@TSi1ed5|CO`Wz-+Tt$53k(}>!zal0pRgfg#+*4bHuf7+7>o@cz6~ihWVkz} zrG>WCCGPkCoG-m0aw9FJv?cHJx?t^^XxZb3L&S?; zjT0W03NSl>k^j9+i{C4F;RSj`V+9KaB=7-UO8?6;!L?$m&B+W)Cq)*>FR;M9=;lGL zz+OIjioNeEO*F<}z~<9o<0I4r7#Lau zna~(HN)kSt&bzZN!h2omAqc7r@SJN3GNI66G5Mh4zAFY|0I+D$4^BOq$jU@tfHrMf zd6gIb;O#d`4c9hHI>uJDwKfYh8dUSL-WBQZa*>rP;Kr0Z`P@r_VIxOMd~KhXn`?ag zmDy@*&dK1m;Vi#8H?SQL34EyBC-7n6TN8i-5(ZTj1(7^0u|mBdHeg48iGhL>xs)MS_G74m+k?#E36ok-p}t5*sSguwnb{n4(qd|B~}iyK()N%rX*Oi>2JTXWJ{MUfxW+f z4i828f0*%O(Z~#CeUT7c9X8%fa8(QSg$a6^lZQ<>S72~7L!NE3x3~}BFN0hG_GB} z%6#znL2JqNR91}GENQG@0~!ZoFu>v3js*^c7#QHn3fqMI?Y9nZwmX1qzuIwypdTA}!ev=3{(-@+=~bXQ;$irnLE)pn@QuhxY)R z+zWVL+{^I-xe*oZW=M_~!j{Wg=$Rk}^!~)(fn|a4u&PlL?c9J1S1w(03j%1>+02_+ z_P^^g?t3}lm&LyVo6RP&p8f;?moHt?K0RqT@{>vkLzPJQ}m8Pj$|jc)l0kDoJo?AYLqdyY!OxM0N~Qai$t&nOHH0IV$68>al>B7m3M#~b;Zh{IFVw>0C@_S=|n*; zazEsnA)DL}R#2kzppGKJ3{^!#EOL1Ku|jqPPW+==h~apa-u^6RW^uo(0uey~Wd+$) z@>#VR_caqh3;w+D!t;z#uS%&jefrTys_cv_VlHJ8=nVz~SD(ur+>|BebE_unNLKzz zYo!T)G_Dp~soVU9MPDrYF*qSH(PJdDWa)A^d*)2}ckAH!XP;)=P9PAl=P&q1k(rUH z`QYu>joOT3{H`%vzQ4C5MbUyl6sZdWofhQSvIHbu&a;^s;lq>VXH~BoL;d>pCKC>w5eSG1-G5;F@R4K3 zfzRW5Tv)JxRpnWLQl-_+GlU696gI|ohvB2{_!;1MKm)rPkRR2qZEKJFzwoD}@Y|AK z;QZc=5Nf&wVRz}kNs;@(>cp?XF~cSx#v-LhTh^dlNUS`|=BomP+$D7-MUwE-JI4v& z&@Cf04&y<%h(#=yl^hmPTD{xqlfEqPuHIi4z?nDCURJEu`XaLa4;?%Rk3aqxG5&Ey zUT&^++Js^H*1QX{P7y`i#PH*!!aC~4b54WeEBa^4mc7vnx zAhs}46sw#C&2no6#1#^*Ftj@F2m5@1751cQpiQ(8y2bK<@8MjdYa%zUz2@OQeS9WF z6A}}7j|>_t82i{1!Qe5Itrv3*`g3PbvFZh}?dQr;o<-pjjdah)Hz^EQ8+RRNCAI2e zii(c1hbQ&pVj;d^LrB?~0w+$Lh7Rr9GM>*W1lnfqyO&>3Nz@pgLT+vzWMySR_ikMo zUoXfnfVHcBhkng{(sleoQL&XwQ2rpx7zAIh*yh9oxSo~{IoYcork_bC?>I{8`jf2hqr9#?`bcmH!eYTHy{#$K}3iKJ!8*JkQjv4qbruy-| zVblOA_dcjhi?h-Qa3sq_0=0HgJW%48WwL^V$0j60U-zm%NGoE&z(*&$ZF&0aS>SPb z(4?_vx#>EBfA32s03+O#loUd!VsdFqOHH-ToiT+P*R)KEJ=;|Uyb!T{Mx(jAnm>Gh z(?tLPAOJ~3K~#cKr8*jhO z|D4UrJG}Gv(5{hI`#pfifS9$2*0M!F;9X*Or1sJe~N8P;;C6?SU#qKZaHS6rQW$Lx7(6m`I7&~S(Aw;7n)sB=s zaNxjx@@Uq)1q>ZB$c-#56p7%wAAW$nDJigf%StFoI|T`19q_Ax6J%rUqwIM(=Z0r9 z9m3;@JtY3bzFffnSym&DmD6 zBCNE-BJRI7A8y>Z0Sy~Abb6l8zXuxSH5ApJ2|)78;>Am#eTNP(eCS}e*Yk37&9nb+ zqGf!uGM=D{QvO&E$CQ6*sf9$b6)a7;fvo^ z3SYyEP;ESLPN~ys#1WDTG~ZVvhpsdxWYk_C7a>Q zV(8T}!Xo&3`3}vJ)F$|@$^w`PL-}xjJBV+TTA{~;*^t=i&MFhH-;*a#!Sl~Q zYoFxt-p9bQd-rZ2lZU{G6DMHj&J@TiC?tLeuGdw=#6;*bcqAmY>jleGPD62FKAAYH z3v}Bq-aq)jzkMAOla+!)Sr*uMrUcYxHnfT2LDNtU#00S+91Y7H&I^va2TQ|X{q}vJ z;)cRsyAMJH&jLIy8!DT0Lur}6PvpShAw!&=KXUXqgenvdL?)nGg8&QwaFx`$O&i9# zXI zHRufnm@s}Ej2<~0Bpz0Uc(Y?rkldpiQ3)$6Q^JuWM_~Joo#ciW2!w9Ye~VTv1(PN} zEdg15-QEkgbT@Je*kK|ThgS zR^nJ90}}&RJFyajVE!*#ibju{DCBVN3|Dxbu^e#r`~^6$|G>Qp1Oh>T4`?)6ICA7D z3>w62BaE4^@4x>62DA`CWlN0M@`~hAe@lwk5|}A!26JA23s?^M^TmsQC7HnPUEMPT z^!V`b!@VF_CW8y-FF;DlF1UR48p!1#Zev4OcsPukFbQOFEg-Gb3fVXxXYY#3r?b{bw_v zC@+t+4Pz#vq8$!0A;8Y#>Cmpr!{l>mT)K1xZs+Dg*Djsz>+z}v{9A200hrqQ?282; z6bc#Bq-X*BfBn25L_y$?Zb%cB^0C?Lrb2WJ(n|V)mn z!*jU&k5xp!e#D63Roy}u&T)|OF8)ECdi5oe1z z@Wq-#Pyryyk9Mu0KD0-Mv5|>FTXU?ZBRCuOxDN7 z3@hyybvsBVs`wa!tA6a2FD|i}P24Thp9ZG4&g5H6w>a^-c7vxXzyAK`&?ZfrG8T&* zQ~#)N`1I4$Nb0|`pre0`L_0pz5!4zj96WFU_UzdUnYXfl&E`0*bi=~J_=ATH=MNkA zB%~>MhEq3+b(#64TqzIO!u!|*f>Nia3s|}$agwHC@>uJ8Uwp@z@c5I$gv3N4m&@hs z-Lu;oVJ)ce9;OysG!y=Bwv$VTfrAH& zKpL&xy63PtR&3^3EjD5#1_Q3-ryv0Z+R}Xf&SU9ieftk^A}QiI?AW0_WZ%w(T`9ZB zDh|(S6(Nub@B{nzA0XK^rc4ZWCN28kLKxCI$S-3nSE&3{1f*fK0I=h`!_Y@3kY|u6 zykWxzC@3fZE|&wz$vr$~3tfT1Lf5XH$!{wuErs2?cEjPL$4CnuQyCEjlg3PZ96I$G z4pewED58QuNl742ZHD|ZEm+G9d@3_J8`0%c&w^|f1w|Gy=tR+ARx|>3OgrE;>;^6R zPK0734?^|n5Gko#0Dvl?J;qLlW-VH}y_Vd&FQk`HaPn#vaP%s`Br>1pfvCs;OyXq} zS)u>PN%j{nUb+lQrSgHu1XRoPu=dZ5jNbn=o}2naaqoznuyAPwY5Ek4r218A|Lbx7 zpimERHlNbT>bEvM`POsnzWw`S+TShpk5T-UtJfeRJe&|6F}i>L`RA%!U;=M=^tQ(b zekW!JmM!}YHg8ct#||B!OP5ae6+}>QF#p9_v-mJ;7Op_mi+}pbc>2^K;OTD5TSsu& z!S~pQ9Mhe6!`{~MR3WjAspMe;IUi1&CKLz+jOI>F+x0N#|9cT4Dro%_ER%a=?qJY~ z=iv2M=fKv#x07Ra?0Chp0;9){l{9P7Li@_}Y3lBZVrdXiM4LS#oJ-_{9nmJ*%07_# zPw@PY<+*d;dfP49z|t4i7m>4&m7PP*V*vLa^gW@|=^4Lk@18vn9U>rYZxsc0$e`|o zzrZWz&&vk*?c=%jcU(_Pv;S>2TL_22-%J1vBoR-W@+1@t9Rj=e?1LRUcEHY^J0Ll^ zC-m*p%jtS&l?Ma(02pU~{?^T#uygwk*s~`Ew8gogD#`^mWdtFYMFImP>EU}V6a{)S zrt~Q=TG${6QUG5X0%6f{(796=7&?3yL_|iBc0yMyS@JU^Nh+6pz{S{QLj?S9?0Cl4 z`FtL{FnbPIX3Tbq6T79LZAmoHt?Y~HY*edF>eiz+XZ zBQTc<>j$%0VRzAPh`!&sY6^1H7K2b0L5UU7#(D`Y_&s{|qWbpl@7J_mQe14FI;69u zZ+%vU=(ztCO7yFCpLS~xoH=WbTOj6GqM0yZJYz?SC!kivjQ@L58>zk|it_x2&AeXo3*~dqba?Vn_0#>ZBrMIz6isn>z+f9N2aLTSda0puq^#*9$ zzC8?M=qT_7ES0;ACgR*MxlTn+?DgFqG{CrYnOJ_k&&S}gEzr{oZ z2q*tPefkV6UAEj~DX9+O-pU5!phs(~GX(&PzWr9Iz4}k^Ly7(?7h+E;8f5s}kIMwq zozFsJAT6aAhs0RI>o;Nd82PYi{G>@9YyCLso;`b>MDJMRMKKQ=En2p!)&Ky?S2_5d z_&|dc%nslLgRO=gI(CFU5BGAq@tZYoE@{@hx&8lVPM=Yy?AXq_c=aN?EI-R)P!)2m z1~p$MV6(-1HrPs}6qqO$>CIIeDN1K%X@pWaOBAf&NEK16$e0GEejPjVdiH*plF5-` z1Ad5@IwM4(;0DFE&`@xxLexN^Sn9U{!QEs9f`Ws2%hqg?&3SQp@rBDfgIk6HSEXVx z4lMnXOzb~@pO1Oif+r?V4tIT>4$$x%{rK~e3iA~Ls5me?mywa_76knA!}lPk@*yd_ z;mOlg0N8U=^il;;CA*cdk|1jb;H_>dlm%X#DtR)_Qf-;PIsrI zXpjE=3wKlt@H-iYb=lGqclk+dGrL)4g?z2m$QK7w9KP5nl}JeX9upl#v(+pROU+!N zgc8ZZ+0n7}t&`fe<8uIEq6j$3AttTWV!hit-2tkj2 z{eCON;l+;+$Ls(S<1p>l>7kB{$G>g6wo+_mbA(2%)?ZIeWtS9}nDg>-QD~T@P$*at zk&)v14H}q36bjdpNQLfdT&4VX_!v>xbHu1|kHM~=-k>U?d%)ufDl}r^;D~AX)~#BS ztiZhYKdMkxV8M6aE52RuRoS6UKMQ-XECR8Z%g96?)0!pPwCYQYP%$ImRzkva5ff7{ zpe0aEz*ag7gG=|$-MgV1X)LMWU{A3?e9@vApE;I^L_eKpHo0 z9CYr~@tzzEyw>n>^5kjQxBmbkN!D-B0Qx-K8;Xld$btsPhl*szDub(6t`Pl?%HXz! zf~20wZtuhl0384z{@s7T{yVTdFz(sc;g7Fghb~c8Tj{3E3No4JwI+5Jl$Ms+|1TEV zriFq^lxS)j?nw}Ut$?dnuOVqqrWNw#OPAE&eDn%uS_g}Nm0*fR%rB6HLsu_QG45E7 zNiPg-XsX|&t$AS2p4FMeA3k)YxWaTF%CAeiX z@ZzXRrZJA$0lZ02h=|S(coQO`A(2XjZQ8ZN|KaH9Iy2qXwNhAAOeVg|r-h&@4FkP9ium($+3m8Js7&+l!k7+oj6*Bs=zx&E` z>tk)qzGdO;LL6o=mU~g7l;o)*zN$sH!R!TJJj+fifXKgJk7_zprO<53ic!l>r8RNWdyCE+(stqT*sGDJg-{ z(o&K|aRly%Ye3Sf+WZ%HIYV0XeeV_p^#y~b;aFe1qKZrDi4;)R14<4!XF`0 z3xb5q6T+ZoEPk%?VCW0ud+LK?TB%2jnZzDAc(DC|<8Q}c1cLy)Uoa>L1Oio$diCl- zT3Wi>=SsvPuu{qjf1j@aAmxS;w9W=f9-HVpsS(oo6wz;yNjp{q~bAD7fPJCA?U# zUr&eg7cP+Adnmu?v8C^k0sWzAlSXweHH*Io)9?83mL0(Iji=v_=M`-=5g0Uq1G5=} zCr5q+4j_0;aX_F4k)$NszZf9kceiQV)@`7e^3>D9**lg~J>$53E*V&NEs9#%tU&kV zc}J3gGwP zee%;UzmhxW<13YQqpDzTkMz(}mA3qO$`}gLQ*)G{zsLmgSSk;2}`hS{C$<*AsQ2z4+9j`iS9Ih95im+sLrQ)9O0N9Kp`U3 z*HUiRG>>z6dkL2BE+?i`XE(0c2XRt6}ZgOeR*7aTvYu%JLE=5hS)E-+h= zZtNZeU|@3H!=T^|mB|W(C=|RkTXu#&^Tb$-Ky!(skocHTSOCchz|K=$luw>GCV%yY zt#+MKx^#w_5-h8@k|zVG9FT;G8Xm5Y$z&i3!E97Ug+AAnG>+uI($IGB)2E;Ld@o+} z*u&qqU*G#a4u$&}w7Y^Sdm$2v?8kUS`FP=olAJU*f{`!$mqWp`&y4lB6=p6G-MVxp zL4fOcf@s>TDG0(F0Z`ZN&!LbUpP@8?hCwzu*&SFaV|`c)Q6)hDMz`Kt0goLyqFwXL z$K1!;QeMB$WCAEEGTZBJr}L~}uuw24kx%sIr3&}3o$kFxeEFrVrxVlMp2&OwI0Fqb zX3m^J($RR_?scaFM%AlV3rQxzamAaSmd2F}{qGLo@&taj2>Of4W(5}gx{^J9c>m(@ zjSCcfj_;LaWRMl2@~?+)*|Ls8IHO?>eaqz_OGl$YPp;#h6hc z-vHO7LY6rM)~;H^Shscs2AGKceIKq<#~8)RFbH_+sj1MWbt|XWu)L6W{WQdc`Tu!H z`W7d4gWi4ndfevn!9Sxm2b%Z}wtR&@1rU2!kf#cxcr5`}sa@TJQs>w2VfdHH9 zCUV4Pau2J6xNEx^oP@V**+K#UrZlF(V6e=8_hs{>Hde2}Af{>x3Ty!YM%zfRkqcSS zrJn6X_UCoJJq*QA5N2I{3d;zoM9 zM(i&gz_yZM99D49$Bf3^fX)h_-U4O?u%{na8lJ%8@mSMmzbQGH<$r3qS0an`*T&Uu zOAWk-@Ls~88nbKOzBd5hpi56@&z*PMwrh{x@Xw{n$C7Ago!GjI&j0{dt>1q418LhG zHgss6;~imH0pS_|?Af&oUVVBTv<#`(iaK2*giq#w?tPmZH%NvT%J~V!Ol1fFc>IYc zK+$#x+)~^AZ}zY&6ho(aXlUiKinR@Q6$BvBj#fbPRL7k5*tpVx@nykm58muh?!}Fg ztT_3j>?{TV9qaL-q3q72kgK*rio9bXEWv+_!JvCRqVG1<8bdj|>oGYvpRpX88+U-W9G!t!4}a zP+=2~5q1v*0)ff_IYH~!Zy@hovSg{-`5QE32%O2~d7K!S;CP9i%`-!X&fUCjk7FbT zlGn>gb_QbwE|6M+D_5?PRki^IDLfZ*Oai zii##3Nld^1?Tdx~1GB6ldDWAz42S1VMG^`sCvH^RQ0X9mwgvWxsR>&_fgT+Ut z89NHDo!ludEGS?cE$K210>2Xj-as(mKd@uPQ8yxjp#^N}Q%^yG$uk4p4GwciiPi+G zmi_E?b0pL{PLQ++O7~yrhtJkjCO94=> z3W*&T-XIMQA+LJ8CmA}9@pE0fqv|3m90bs|z!+hBk~~dMOVcm@W*+a6MkWu5AQqIA zSV66}1qf)~lU`zmha2!b+Dr<9Lm8t9?D@x*zW{0%uvgi&Bp{D%Im`SRusBjQK+X7FW@+4ybU@#a=Z_IvH zKe3g`L%WyF0EqVCzE(m(zQzhY<2*MG;LIQiaqkSk!52k;?xo~q0QUxTNdTpqX$&0p zsPRgXf5Wl5IERJTT6LcuyynrINg%NG8Rh2YI{k*os3^%F8@I`>3fdGOziqYT`v?T2 zdBjL=MGxlEl zx&XSQ7v{hzTDxWqgm@6p@&-wCQ)t?(nb&Rb{Go6WwtLeM2=D0pdjdyj6PCrfgz0`{ zv8%;Zzeb`$LBN`I8whI|tvNlV-(Py+c}=&lT)BY9JeHT1+C+d5SBoj@(JYI(b3O0O zks<>?Qsbs>;URqiqS9RewF`7f;N1BO_Wd3|X_CBv7o+qi3Wye2g_}2Qyf0xoPAo6H zFr7HSF#(+sm#kc;829Qz-JY`8(yix==5mR1MxgZY*k)y->_bbW-5zR1Ju_{JIy)=N z=^e2#^&mXLMwSW-3xkIq>QJ!;_XiwEwv397f(;utyB&zZLx;j;l>&68iq(KQ+&jZ( zyd@o;AJY$xAMu=}!B*4wh7Docw(WK1WE7Wcu{aWoZ)M#AK?Ubjm5G9ggjPPc!!jHS zf;oO)LcEQaQRpQI2nh{?GTQl(;WYCF?rT<2xrq={;8>U7cMyOnNNjPz_9ahpmx~rI zREmp_1l12=jsnY+6p?O3TrZ)h@J^#mQE~xe8q^U58Y@m1Zt)w60^vjwaAB*Rpi2Ud zc=f%{1lu#&8ejQ<1UU0Y(7i_wA_L2`3K=+fu=LNrcgv@~ z^Rs?!YPdQp-wH;@slEI+t~_aig?m--Uur2d2{-YJuBAxF4ee``Urzu4AOJ~3K~!b_ z{kPxjEHb>t2M>CL{EdXcuuw(a&r?=0MvWW^88>dYbqU}~^3^xrhqD#PVB?CC%jMW7 zlpr2JbIwip{-c+{>gD1cH1H9~$jI>8X;Pbzo0~`OH%BBiOaP_NQQaBGvrW*kYY(5> zUA%aajQNg`1_jy3f!UrTKtQ!ysYIV!Ka;^g`E_+*#M)R}kRK78r5#$Pw+~ zzZdhn$1__4V;i8VoCKJuq$q2{-UU5+_p?L@jEqJwjwsMtjSu(j>lUiv1mfx*6+rC) zru4Ap<~SgB>e5B{$q%cA+p~l!gBNl!F2b^}oo0+S{V@@$DhwMkkZ70xv0|0SPwd^d zkM!%Gma`AuFd2|ASXg8w(%9?HS>VsJ7AVn}Au5=HI0YL}uY|{8L7dDW{^83vc}TFO zKL=IK5MiN;D%<}D3AlK{cIHi+HoFll`=H*x%=cL0SguChd{-yprh)=^W<(G8@8AKq z_kznl)CJ_`4~p@0JRgC(!*7RV@Uwf zwq0A!vJE?hNA(Sg%Dkk}qXl|t%J%K|l`fAfbmYe%HwBe5J)uE-12I3?)+)2)m=ez1 zw2^xKAZ|D{wu`^&&CzTGX2mQC)$@Ab{2ZKwW05)k;ifJmKXRp3+Td zsSV;V88euGI052`#sU4Jr9r`hAATWAn)Gvr^*ldv5u)HQw+}vg>;z#<2fP4kHy-NP z(XAu^Hi8{*@gILmFBEe;hWAZF*|2Z#Ubkzbq9#Ij3rrhgmJF3Mz10uvx9VodQBz>1 zEU+iF404J!ppdYkag^s(NLhZm=;)Cnb>GUYnt>l92I zC~!C&&}iMqp|sRgO`3=wd!OCZK0|e zEEx_^sGs3!V4wH#r&3FkVJaS%Ma6`0AWG(&h!!5iX8pZo>w}?MQcV~>bTEh|5?H(L zPp2=8s~-o+gCB?Mg`N+$V>y5X0UkD$U`9ZY4L{F+lks)hKVajQzia4G@(r4Z$HWms z((K#w-Uk&YgfU~{2vlqo@O`WEWoBkVD+bkAL|j~~*ET_v6E~PyAYk$h@E{vZp7*2_yRL13S z+zf?W5u&~dv!9yiwwlB;(uffw;OODQUR&F0GH4SYPta(zU{sd?+s81EhOuGc?m|wE zzziFfP|y^P2{bRk)3HQ<8G%F!0s-@s$L2A2`ry86>!8Y`BRmEHM4!2!047eH=+q9l zVck0A2eZao9|^mt7}JX4dujz#t0}?-Kpe~Gm}k8Gxw%>M=A`g~z0m5Mt3rQ9wwq;E z==jh>P7Np5uBAH3Mg~y3K}rIbE;0UwPMy1$(@H$|91FxjZZB4aiBM&L0S#KIViyfl zFZi-0OUg=aot1MdsLoMZ;$>+XK&60V2e#{N+_cH*?I>+o1w0PPaC$?i z5FlQ85*B^-zS~wvvX+O0z@O_k*26&&$g*@mUt;(8~qcYA|-`*3IcwL%G?6KvzJW z0hR=C1}&$G|#~ENDmaFdz1^R%0#F#z=0iGn3gVG=2jM1vtltfO2*KObKdG^H<%k z26#Ry$4{JatGVy^=O0U}aD$KD50>2!NM{DP$xAH#;M4hG8`iDU4e8shbU<=@?KfY2 zVGzf)DY~uky@LW-lrbnc*s1FP$%>A$kpa|h&?SMRDKD1fQ~UU&Wa_k*1~cQ^%X=q{LMP5=Xgco3EY=z zbJwn3H_5t{<<=VL3SVu47B4{ni(;-hGU*4@H8B1{T#;eTy>ln$zRimlF7TTA@V_W4 zvw+cLyD5$rOAO!sv`p~TXP@OS_;4mC$b3_-VCRc7&uk6JxRnK&;+8j3OFUQAaCnbu z*vyGBf&?-olF_oW0n`m7BqVq&353QZay(7-7ORK5w~9@KDgx3rICTo?dT`YFXLJ1N zg>i?~H^Dl9N}^w%-sFuahfV{D(DT3j8P4asPhgl%=9G5`#L3u@Ja80Tghs>y-5c=C zw-e{XvV|YHZ8dt-2qI!sn>-;dT(sS*VII1(GSbt*s>}hg2R1&9EENSE`;8`L7*C*E z1AUG8zS;y?UYtEGCg$KFJWN5_f9o+ z%V1t!zMEXIBlhgs1)bu&kI@0ItZG+V6s8GF?WA=fw}n;7;Vs({z}=%q_pT%*@9HZM*|aNZUvq^>Xd46Z!qb-I zK~PW-v>h-3iu~XQM2jqNdE3vh?QiGSzzO3XB>{k|h*3>NZeG5dT(Bdqoj(N$A>I>T zXY(oeV&M-E9PE9fMeAUH#{5P}Nl;qCOb9-4>^OvSy^w`fH{rhn?kbNaPh5dI1h=z< z(J-#aTuT99tCg`%XE7fY>;y0vY|ESzd6enZw?E{4IPV=(Opw;&85Uxyh+~CqsVc_T zMWJyzN2NeqQQ^L|Y840q>LM`kWGV?nMnwr(LK$PI$Yrx?bkT0&gmI*_hU#!(;o+R0 zm#yJvsin$_1Oil`9B|J;H2qyt@`HwtfxoZ06WauWI|M;}SpoTX{Nt0MFzg{v`7vjp zO6vYUe{t$T$5k#7F{&j3Lg=39Di`dC(wua!(K%lKL2)ghW2c9_eh-fMr6nbPjrl5- zZ65h^gS#yB_6^J7(Ken*itSy{mjsx4eWjuG7$K4AM96Jm#g*7YUW^<% zba|Nj*Ci`xjchn^%XUza2SAGgVI99m-y!Vx-+fCH9c=L}1BI=J7Bl>LNeL;}mGy8~f}4X>0Cfj+NdSXAR|G}GF;>${OG=bX&sV33kT3M>+ncBfE?u^q z3|5Ja8VUZ{bs(gDpU0J%MU+p!H41>VNY-`HlKYK`7s-<$5& z60dwwsaF<~Ne`jZYN17+F>r>}3{u&Npbu{YIg|pJvOWR4+Px`Ts7qO; za3aeJEC0)dA`Kfd3oUkbY+8xg+M;DkR{EtQL0*yoPKZ-*AU>Q6GARen7FgkYffW)v z^u^Xd`!Wv)Ph@EZR08WZuq1H&ggY-FsYy$Ao|a)WwfJ_StO)^RE{=gCXK4-rw33Ifk7VD&|OzT-MV+PW|q0HsVJ+NkuZp=fz@du zP!6aj#FVGF z4<0xG3l@As0s)G}adQ{smuCVZ#4JeJww0_xZ(KbMK{i!%2o|xRzT5)Q{5;?btdLfw zgUzX0xKL+qO)1`o|n>MM>9oi@hlGB#=LL&$_IXc!IcRK9CTpiR4W z(oz%O%_u;xRx*+TP|3`d=087j$0iBn2uKYRXbx(K@8)PCD zgarXyFV#Yj*a{^^1FX5EgcDcN;P9bCPTS)>fMea-wd-o+F}jg{0~8A8*1&zccR{ZP zo;9&{-7rAkA&>g?J!j9IcUqMC0$sOn?Ls2N?gL&>7%{T5_(%ruImma&b$X7XD$&lr6W$@ugpTe4T8*0><*Jv~z<$?=tUI8he zxnT#1-@NFSFnp9}TY5)iW@Hc%F@J#pTvQZuh9WAljbH3qjhPQkUE1TZ0jVzYaqMRc zSK2uEIz(MotB(LkrFCwi#S6Q0=dN6XLFb27clp|0N|?y z3rSiiBEq}VQ~-4Yo=O6-i7i=Lm*l@muXOAC!9f$>Z?RZm;lgi8eHVjV$4M8rxgQE` zq%1Ig9O1WMy163zfcJv3hGjSE8gLbk61K=lfByOBph5lm9#@Ydu`M=>m$TvGsUt9Q z)HqQ4-|<0!1uhQTeYnJl7J(*iXh`MiZ*V_>L_1G%!DYG3qC1FGB@b4vSqCiN=CR~j z$2LS?Qo)We8Vw*4ix^+)HDoLtzGd=R4iJSUx|J%h6|ydy2$7Z5%vp1i(K2}C$dUFJ zqN1Yo1~W*c9xD5KBLzm2^WC8}vCL0SnIhP+WrYR6xrSme+m61##YGj{q^!~e<>u|{ zB#}{3P6Ij)+JCHAMQ#Md`VAZ1Qau6GJz&~=^=ey50GahPhL0L${c-M}*7lJcd+;ix z4E(ECuIQRHZEC;!vDNV8i4!EVg^FKkX=$X!O9Llj$8*enZ~$V+cs zf$6Wj1sj$xz){WbeM2!KBpY;esjBw1+gtVyFJhLz;CE4;Le;i zr!9H|84N}UFBe8Yv~Js$tu;QR1ZL9ZPdB?-U7970^t9 z!2}?VYO876{vm5d+Eq?zQ7)TBnFTDKNH<{kBk;n^nPT)>hw=#Uzr)8!Iv7`TCYI04K=l z)1M=n7pU4t1A|H=`0TjP@~qvucCugJy&vuk&6+kP>|;mrQBiTRQ=3y}#trhX*=#1i zD-Z=&z)ag4V0t^tlbm>$hj!k~PXSv{2D^@@l^FkaB-890uSwsE)o-0?bLepl= zDzt9~y#d4`4?BRm53voPB=<5Xf|w`u^=I|)`v;TUo<~t9$M(az2Ap{3VNi>|YwKVC zz{@YqBtJ1GHde$HNc8|Zw@K z%vpe|Wn36IWU$Y@)|R)?oY7I9+-jjSQPjYZ z*h@ZlIr|DNKZcT7<~BQ$Hd3|ehjjBO;;q{Wmpcb5g<2ML=;&SwK!k_aZ7v%3 z+l32CkrVL9BLhpeZQJHlZw!r$GpcT+xlQ022j5Jr^(VFNn)S_hi{kwGLL_@(t@pse zgG87JqsjpT29U0VzyKG3BQPjDf8nB25KvlDS_}ekGPLdx$>zQ?Yr55Bp)_o(PE4`5 z#`^J1OjD*!W4CG7j)p?9AVka~7Mh%I?epMj;()*X7nH$N_A{2iDL z#Q*>u1s%&^H3=ve+@-T~Y2v+i-hm)>8i0p6K1U5OZH9NJK>IOrNxP#kkw4e{<;z#e zzfAWs@+&MX9&Af9LRP5`X7rGQO3#MuG76eT+M*UQmx9!y!WwtHs{NRMbYSp^+?#vbkV3dXt?$-;yBdf2SJ9iRg zxH)rX!yB)^LN55g02hEOTD5FJvP6#cd$}Ss#C1Ddg(VoT^7C(;mT*r9#|XtL z4Xiv~1X{fb*kA>n!2m|R1~#l(216d523fjLpoO1Gbri@G9)?dBEMnXr3_ez^S`F>m zw})v{o~V&$jY%#Tf8DCpt03M3*$$zl1ki850KdKimHq_fN`hfwVSWebuI&Aj$4|h7 zabqF5b6e=rx&iFjz8ZogW{8q=;8ZRJOAjky#o+?bnOKlo$OeUkg3ISlGJbZ0`nJpm zW)15ioN^+lxP+}sblcL{@#A>6%S=uKs9Hyn8jl*JW$%3aweIWh76s9yJ}AGK=|LMK z+hxmFz`XZAA`vc<{H9Eq44;4Y38`xaQv3mUL%X)E$*VL|7?IerlR2%_IWSExrC?8n z3L+g+eAuoUD+jK%@QifYq!FxbfBogA`{}6_F@Ogb-g?amK}@HCR&fF-R_Z{=XG7y? z9t4XVhfxZmWPJE{&ki_v>?}O}%D2#C*mE#^;!OB?$x@;b;R+ky;|DZogQ=4rud&CT zNiG;m9@C~ygA2v(#SgTIKbLQT`eFlgY12lwxXH)lNo*4)DoBP@hw^>|QE&LR7X!gjYxH4rMb`Q0o#qJeGa zi{YP3W{Bn$!_Mv7-9Edn3)DGPxNuVL-lGR>-n@kjz+vIxyoi`41~A*0$;DbL6dT3r z0i#B7-gx^RG1mSa-;WpbqD4Oty=6Y14<#ifB)c$T#BlO?0kyvXDgwM-TuTBo=gbk! z&AzEUe0&?XOEksD8#`Xcg44MyC*dj@$RLQ9>jm$3Wa+f9W@+8y6YenuppeQd~NJ9K92=@gJWPy!MW4NL8DQ_@X_AY@#rxV zjk6{{;m%C2Nl3kZoiRX~`{tYQQua+y=In-GzAYHQw#mhZ@}Nzk6bkap5Gmt=1ax31 z%!kyxOE7WuAMo;P@4}4Pv)w)q@B5h87$Q&X?|oTXR%Yh|{QB#!AYm(k?Lm(~T29(* z6E6T3n8{i=w@d^1Wfu5*<7yZ%aA1u-4^<7fVRP6J+X2}6w=E(XGHi?`N3YOZ744K` zroE#)apAi3lh3~pxds5Znp(8zdm`$CLKi}j2;O<;Et2lX0Xz@@1W*}ZNgzGVnVdTR zt8c_l&iz{ZPbS~6@0JA)=UD6nEl+5uFiGB?`;wcr8Lqyrr`>sn3n~*({SU`il*Lu6RM59?pIWg9(Ov9#Ufjo2nX&BPXuhvA*I+5_APU`D4BRz<~p#weQ0Z=E3W)ze)mtz{)>> zssLRQKo-9%x_0YI`mOzm`oUHYWUc(|2cE%jN92O$9k>P=0X&!}D=UN8@?P=f%a+0F z#UBGpbq%DR=QF}T;kvXA;v5tn5y7~<>tv!*x$6(qL=@!bdz1^_zI83chjAfH%7S1q z1tAjKaq$L)lm$Ds{RJf@rEXi%yn|b}vV0zi(vmVlSfn)qu-i;)s9X>r;ag$ty0ta; zB-{gdJWZoU4tF96IszkEdh$}C() zDTV+0`fK|rjMpk=u2QcDN&*i$==MNb68G|jvk)2VpACwT@3I}62mSgpGvZPGkIS`f zkJlb5NY->*DsRDal?%35Eb#K|Sx|cWa)o~ZhVE!fpME`+@%8xlc)#w;y!?EpeH$=n zFmMFEH3^u&VX+uj{OBctDxP%_9)kc!k|0_-Lb;H|d+&b)TefVmPyWM(x+`7<5I|J| zH6)ly0;Kz>f&o9KbPw&@=Jc+XEn2{(OEuHGJ%H*1E!#|&G2`MBq0~(tof!h2b2cin zUM$4bXO>Pu&Xn0~AqGXrnW_!rCwf;f$ptT4vII`-T?-+i%B{RvK-pUY9f4(l+qZ9% zvdO2Ped$%^+h{bAf9ZYW^LY>#-=tFeqcu{{x>akp=c6K{2+_SR!fV0?NrG5!S+Zm) zyz%Bcq|L8k!-jIYvn42yEj;iEU4~otsO#D;&|bQ!mh)Djt}yM3M0Pi!Zo6 zUszZOrKP2Ha*XgA1YmzQMyyaCy}tP3beQtwV*$cn0M(9%I(Bp`3CQL0N(BKTo|&^} z*KWHhF=oB!R>1%sToe`*I=S41hK0iBzjs1fiIA)iy#akKgTx#no|98-fG4Ir<#Fqi zCyqn+?*8|{*90I_zn)7FFmb|o0Nt%h@14;?LDS}KNGF*qxUM7%FUYxl8ze3Z(#cOf z1DT~>h#U;aS6ZR(&`BVXNS$89^7qAy7s*25i!Z*qujXU-0$zgvtn;_;(1HAo#J|Rk zl4|MC44}4yE(tiQb~S0*G{S7DXhFbM_cIs`PA#sqYheHnE|C3>{1Zo@dCcE`{S9VO zJg9w;&16zGn5=Ag>bV)vu0wmqZLp+@B(~%pwJQifx!_m_bDmo_Z$aACvwo9unV{6O z;kjp@Wqj?z#fyYD(I7sal%+lSY+$M2+}U%Gm6b(^>NLa>3B3Ekm!LBG4kqxdswg=O znJ^1x&3W1Fb#(lsfn!kSP2Xh2+5{i`O&}|Ol=rfWs5JQ;U6|NXJ(X zc;peMjT$8}lMe%UaDWq?LgA)Ljlm}T{M%}{p^`&^%5(Aw=feh#Tf+MvedKYQBS()x z`}XZ?RVkH8E*SX^%l=po+^9})E!PAFOT|;~!HQUH^>urG*h_EIdcYawe{wj zdD}rQ7{!GgF>1`C(4eVz(xuKs!L?!ueE$7vc=`3$!O@=#SNuzsE_eD4%ox%#!gU2c zg8*RA(GDPh`vFoCa3SywA2XS)7sgx6;sh#BFVmzKTD3W)R#56Ga2=QMxooXW_5P>- zgUF~TCpJ=`RWJb8iOvzRadB|!)G6rN|8cmj3npFwjxbpOGF1|o@zPw6#}Ow})COqX zx>c<@)=YB2h_2nc!>Y9#VCTV$&}Y<45Jj|tG6R>%NYD-FNz=36Ft_J1Q*z|+VUl_F zXWnt-0_5J#v3GdTcy{I+pn?c+l?^~U{2UbzT67x&hmM|x7A;#ky}o1TE|~ZJhj8`E zWwI~$=-`cKMogN@wfBQWdz3r$L0-WZVUqX+VqX03IyZ zTuu-B{hKiu`0-hJ(9uK@s*69T(+?_R5p?b2n-2_X)#{Et=e z+%ujDkBH>ny>YfU2w*D61K`JU#@;6wtV3M;QOaG^h9BIlu|L zUQ9i&>3>(?n1G#X2z0{5QUZ3dp}qi?vZ1_X@Z$wbVEykuLUBnEL^WszUrl=k;_BZS zL-6}ou3k&{1q~A#!aL8t^}rC|S=1d~RplBz0|=lR@$0hR;rzL?Fl*M#Dlb#vN@T%; zZ=nBx0d^0K8jSY=U|tQ}y;?VppvC!(a!YAcN_}b8i&g#|#8GwGZ=}7tHcc)wXU%c? zKOP?>Pog?s(?3TZoRX2Ry!iZ9uN9Zzg1y6M#~7?gT}MJV1thKtKlo zQ2zHPpMDN9xeV&ni-899;|N*sZUasv1R}w(wyiF4(x;D4v*yhqI4B6tpFa;NDJkSR zbQQ!$lO~NxU{IOkgn@lYNij^H{(O}L6?or9M@PY`)oV#zA3v9AzcBT`apNY^{@Aa7 zKj_z|_XDYY69@tVs8vveh7qU=JaX5iGcnhpz@i;VI|O+&3LE7HV3CKW%N#E~J7b>@PC0wN!KGc(ivdSqlI z$V22185u<;AIIPTd)H5#JP8`L8a{ac-8#L}#B$orTUn5rnhJTjd3Gyc%oJe1KOU3D zjT?bPEP}OvZiM{2JksCaw_iUPG0eSHu&S_O;}+PrZ=e0sP=6pOD44kA21vm0zGDy& zKmY**aJP711{69tz|}Bj4lt#CBmIW`^^g#`lS3Y^M*H=BaMoH_zC&RhJU*C-qst07 z3DZini0W90yK)=mfBI2C_TGj7Cm?_T0tlcoz=;bVuGs@RI5>Lz1e7?a&DB*udQZSf z7LSeN@xg4tij}J&uRKct - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg deleted file mode 100644 index ba6a3da53786..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_notations.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg deleted file mode 100644 index f708a9e2dd1a..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_weight_signs.svg +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg deleted file mode 100644 index 012de1cf3e9c..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/overview.svg +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg deleted file mode 100644 index d02ec9b376ff..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg deleted file mode 100644 index 0a9010081d3e..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_example.svg +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg deleted file mode 100644 index 86c0fce6e7b6..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/seg_coord_projection.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg deleted file mode 100644 index bc771f38332c..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deformation.svg +++ /dev/null @@ -1,1100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg deleted file mode 100644 index 80aa95077676..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain.svg +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_interpolation.png deleted file mode 100644 index d879858822e91b03a2d2ced8d606b065fba395f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46508 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 U#zUMQ&;S4c07*qoM6N<$f@4=^A^-pY diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg deleted file mode 100644 index 41cb58d59188..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/terrain_triangulation.svg +++ /dev/null @@ -1,4633 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg deleted file mode 100644 index 7bb1b0acba4e..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg deleted file mode 100644 index 16b75982fb0b..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_coord_example.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg deleted file mode 100644 index 4a64b8a8c3f7..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/tri_notations.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg deleted file mode 100644 index 58b673435a07..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_coord_example.svg +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg deleted file mode 100644 index fc4637fdbfb0..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_notations.svg +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg deleted file mode 100644 index b497a3f490ae..000000000000 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/wp_zero_set.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp index b8edfd758254..dc24d16966c8 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp @@ -16,7 +16,7 @@ using WP = CGAL::Barycentric_coordinates::Wachspress_coordinates_3 gen(4.0); + CGAL::Random_points_in_sphere_3 gen(1.0); std::vector points; const std::size_t number_of_points = 250; @@ -25,7 +25,7 @@ int main() Surface_mesh sm; CGAL::convex_hull_3(points.begin(), points.end(), sm); PMP::triangulate_faces(faces(sm), sm); - const std::size_t number_of_vertices = CGAL::vertices(sm).size(); + const std::size_t number_of_vertices = num_vertices(sm); WP wp(sm, CP3::FAST_WITH_EDGE_CASES); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 34bfa23b439e..09ff506fa060 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -28,23 +28,23 @@ namespace Barycentric_coordinates { \brief 3D discrete harmonic coordinates. - This class implements 2D discrete harmonic coordinates ( \cite cgal:bc:fhk-gcbcocp-06, - \cite cgal:pp-cdmsc-93, \cite cgal:bc:eddhls-maam-95 ), which can be computed - at any point inside a strictly convex polygon. + This class implements 3D discrete harmonic coordinates, which can be computed + at any point inside a convex polyhedron with triangular faces. - Discrete harmonic coordinates are well-defined in the closure of a strictly - convex polygon but they are not necessarily positive. The coordinates are - computed analytically. See more details in the user manual \ref compute_dh_coord "here". + Discrete harmonic coordinates are well-defined in the closure of a convex polyhedron + with triangular faces but they are not necessarily positive. The coordinates are + computed analytically. - \tparam VertexRange - a model of `ConstRange` whose iterator type is `RandomAccessIterator` + \tparam PolygonMesh + must be a model of the concept `FaceListGraph`. \tparam GeomTraits a model of `BarycentricTraits_3` - \tparam PointMap - a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and - value type is `Point_2`. The default is `CGAL::Identity_property_map`. + \tparam VertexToPointMap + a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector::const_type`. */ template< typename PolygonMesh, @@ -87,26 +87,26 @@ namespace Barycentric_coordinates { \brief initializes all internal data structures. This class implements the behavior of discrete harmonic coordinates - for 2D query points. + for 3D query points. - \param polygon - an instance of `VertexRange` with the vertices of a strictly convex polygon + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy - one of the `Computation_policy_2`; - the default is `Computation_policy_2::PRECISE_WITH_EDGE_CASES` + one of the `Computation_policy_3`; + the default is `Computation_policy_3::FAST` \param traits a traits class with geometric objects, predicates, and constructions; the default initialization is provided - \param point_map - an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`; + \param vertex_to_point_map + an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; the default initialization is provided - \pre polygon.size() >= 3 - \pre polygon is simple - \pre polygon is strictly convex + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ Discrete_harmonic_coordinates_3( const PolygonMesh& polygon_mesh, @@ -145,16 +145,16 @@ namespace Barycentric_coordinates { /*! - \brief computes 2D discrete harmonic coordinates. + \brief computes 3D discrete harmonic coordinates. - This function fills `c_begin` with 2D discrete harmonic coordinates computed - at the `query` point with respect to the vertices of the input polygon. + This function fills `c_begin` with 3D discrete harmonic coordinates computed + at the `query` point with respect to the vertices of the input polyhedron. - The number of returned coordinates equals to the number of polygon vertices. + The number of returned coordinates equals to the number of vertices. After the coordinates \f$b_i\f$ with \f$i = 1\dots n\f$ are computed, where - \f$n\f$ is the number of polygon vertices, the query point \f$q\f$ can be obtained - as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polygon vertices. + \f$n\f$ is the number of vertices, the query point \f$q\f$ can be obtained + as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polyhedron vertices. \tparam OutIterator a model of `OutputIterator` that accepts values of type `FT` @@ -351,53 +351,48 @@ namespace Barycentric_coordinates { /*! \ingroup PkgBarycentricCoordinates3RefFunctions - \brief computes 2D discrete harmonic weights. + \brief computes 3D discrete harmonic coordinates. - This function computes 2D discrete harmonic weights at a given `query` point - with respect to the vertices of a strictly convex `polygon`, that is one - weight per vertex. The weights are stored in a destination range - beginning at `w_begin`. + This function computes 3D discrete harmonic coordinates at a given `query` point + with respect to the vertices of a convex `polyhedron` with triangular faces, that is one + coordinate per vertex. The coordinates are stored in a destination range + beginning at `c_begin`. - Internally, the class `Discrete_harmonic_coordinates_2` is used. If one wants to process + Internally, the class `Discrete_harmonic_coordinates_3` is used. If one wants to process multiple query points, it is better to use that class. When using the free function, internal memory is allocated for each query point, while when using the class, it is allocated only once, which is much more efficient. However, for a few query points, it is easier to use this function. It can also be used when the processing time is not a concern. - \tparam PointRange - a model of `ConstRange` whose iterator type is `RandomAccessIterator` - and value type is `GeomTraits::Point_2` + \tparam Point_3 + A model of `Kernel::Point_3`. + + \tparam Mesh + must be a model of the concept `FaceListGraph`. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \tparam GeomTraits - a model of `BarycentricTraits_2` - - \param polygon - an instance of `PointRange` with 2D points, which form a strictly convex polygon + \param surface_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param query a query point - \param w_begin - the beginning of the destination range with the computed weights - - \param traits - a traits class with geometric objects, predicates, and constructions; - this parameter can be omitted if the traits class can be deduced from the point type + \param c_begin + the beginning of the destination range with the computed coordinates \param policy - one of the `Computation_policy_2`; - the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + one of the `Computation_policy_3`; + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, - one past the last weight stored + one past the last coordinate stored - \pre polygon.size() >= 3 - \pre polygon is simple - \pre polygon is strictly convex + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ template< typename Point_3, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index d5ea7e1a2196..e9bdf5d5649c 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -29,11 +29,10 @@ namespace Barycentric_coordinates { \brief 3D mean value coordinates. This class implements 3D mean value coordinates, which can be computed - at any point in the plane. + at any point in the space. - Mean value coordinates are well-defined everywhere in the plane and are - non-negative in the kernel of a star-shaped polygon. The coordinates are - computed analytically. See more details in the user manual \ref compute_mv_coord "here". + Mean value coordinates are well-defined and non-negative in the closure + of a convex polyhedron with triangular faces. The coordinates are computed analytically. \tparam PolygonMesh must be a model of the concept `FaceListGraph`. @@ -41,7 +40,7 @@ namespace Barycentric_coordinates { \tparam GeomTraits a model of `BarycentricTraits_3` - \tparam PointMap + \tparam VertexToPointMap a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector::const_type`. @@ -91,7 +90,7 @@ namespace Barycentric_coordinates { for 3D query points. \param polygon_mesh - an instance of `PolygonMesh` with the vertices of a simple polyhedron + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy one of the `Computation_policy_3`; @@ -101,13 +100,13 @@ namespace Barycentric_coordinates { a traits class with geometric objects, predicates, and constructions; the default initialization is provided - \param point_map + \param vertex_to_point_map an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; the default initialization is provided - \pre vertices(polygon_mesh).size() >= 4 - \pre polygon_mesh is strongly convex - \pre polygon_mesh should only have triangular faces. + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ Mean_value_coordinates_3( const PolygonMesh& polygon_mesh, @@ -153,7 +152,7 @@ namespace Barycentric_coordinates { \brief computes 3D mean value coordinates. This function fills `c_begin` with 3D mean value coordinates computed - at the `query` point with respect to the vertices of the input surface mesh. + at the `query` point with respect to the vertices of the input polyhedron. The number of returned coordinates equals to the number of vertices. @@ -363,11 +362,11 @@ namespace Barycentric_coordinates { /*! \ingroup PkgBarycentricCoordinates3RefFunctions - \brief computes 3D mean value weights. + \brief computes 3D mean value coordinates. - This function computes 3D mean value weights at a given `query` point - with respect to the vertices of a simple `polyhedron`, that is one - weight per vertex. The weights are stored in a destination range + This function computes 3D mean value coordinates at a given `query` point + with respect to the vertices of a convex `polyhedron` with triangular faces, that is one + weight per vertex. The coordinates are stored in a destination range beginning at `c_begin`. Internally, the class `Mean_value_coordinates_3` is used. If one wants to process @@ -387,28 +386,24 @@ namespace Barycentric_coordinates { a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \param surface_mesh - an instance of `PolygonMesh` with the vertices of a simple polyhedron + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param query a query point \param c_begin - the beginning of the destination range with the computed weights - - \param traits - a traits class with geometric objects, predicates, and constructions; - this parameter can be omitted if the traits class can be deduced from the point type + the beginning of the destination range with the computed coordinates \param policy one of the `Computation_policy_3`; - the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, - one past the last weight stored + one past the last coordinates stored - \pre vertices(polygon_mesh).size() >= 4 - \pre polygon_mesh is strongly convex - \pre polygon_mesh should only have triangular faces. + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ template< typename Point_3, @@ -430,4 +425,4 @@ namespace Barycentric_coordinates { } // namespace Barycentric_coordinates } // namespace CGAL -#endif // CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H +#endif // CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H \ No newline at end of file diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h deleted file mode 100644 index 17da878872ea..000000000000 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Voronoi_coordinates_3.h +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov -// - -#ifndef CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H -#define CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H - -// #include - -// Internal includes. -#include -#include - -namespace CGAL { -namespace Barycentric_coordinates { - - template< - typename PolygonMesh, - typename GeomTraits, - typename VertexToPointMap = typename property_map_selector::const_type> - class Voronoi_coordinates_3 { - - public: - using Polygon_mesh = PolygonMesh; - using Geom_traits = GeomTraits; - using Vertex_to_point_map = VertexToPointMap; - - using Construct_vec_3 = typename GeomTraits::Construct_vector_3; - using Cross_3 = typename GeomTraits::Construct_cross_product_vector_3; - using Dot_3 = typename GeomTraits::Compute_scalar_product_3; - using Sqrt = typename internal::Get_sqrt::Sqrt; - using Approximate_angle_3 = typename GeomTraits::Compute_approximate_angle_3; - - typedef typename GeomTraits::FT FT; - typedef typename GeomTraits::Point_3 Point_3; - typedef typename GeomTraits::Vector_3 Vector_3; - - Voronoi_coordinates_3( - const PolygonMesh& polygon_mesh, - const Computation_policy_3 policy, - const VertexToPointMap vertex_to_point_map, - const GeomTraits traits = GeomTraits()) : - m_polygon_mesh(polygon_mesh), - m_computation_policy(policy), - m_vertex_to_point_map(vertex_to_point_map), - m_traits(traits), - m_construct_vector_3(m_traits.construct_vector_3_object()), - m_cross_3(m_traits.construct_cross_product_vector_3_object()), - m_dot_3(m_traits.compute_scalar_product_3_object()), - sqrt(internal::Get_sqrt::sqrt_object(m_traits)), - m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()) { - - // Check if polyhedron is strongly convex - CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); - m_weights.resize(vertices(m_polygon_mesh).size()); - } - - Voronoi_coordinates_3( - const PolygonMesh& polygon_mesh, - const Computation_policy_3 policy = - Computation_policy_3::FAST, - const GeomTraits traits = GeomTraits()) : - Voronoi_coordinates_3( - polygon_mesh, - policy, - get_const_property_map(CGAL::vertex_point, polygon_mesh), - traits) { } - - template - OutIterator operator()(const Point_3& query, OutIterator c_begin) { - return compute(query, c_begin); - } - - VertexToPointMap get_vertex_to_point_map(){ - return m_vertex_to_point_map; - } - - private: - const PolygonMesh& m_polygon_mesh; - const Computation_policy_3 m_computation_policy; - const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 - const GeomTraits m_traits; - - const Construct_vec_3 m_construct_vector_3; - const Cross_3 m_cross_3; - const Dot_3 m_dot_3; - const Sqrt sqrt; - const Approximate_angle_3 m_approximate_angle_3; - - std::vector m_weights; - - template - OutputIterator compute( - const Point_3& query, OutputIterator coordinates) { - - // Compute weights. - const FT sum = compute_weights(query); - CGAL_assertion(sum != FT(0)); - - // The coordinates must be saved in the same order as vertices in the vertex range. - const auto vd = vertices(m_polygon_mesh); - CGAL_assertion(m_weights.size() == vd.size()); - - for (std::size_t vi = 0; vi < vd.size(); vi++) { - - CGAL_assertion(vi < m_weights.size()); - const FT coordinate = m_weights[vi]/sum; - *(coordinates++) = coordinate; - } - - return coordinates; - } - - FT compute_weights(const Point_3& query) { - - // Sum of weights to normalize them later. - FT sum = FT(0); - - // Vertex index. - std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); - for (const auto& vertex : vd) { - - // Call function to calculate coordinates - const FT weight = compute_mv_vertex_query(vertex, query); - - CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; - sum += weight; - ++vi; // update vi - } - - CGAL_assertion(sum != FT(0)); - return sum; - } - - // Compute wp coordinates for a given vertex v and a query q - template - FT compute_mv_vertex_query(const Vertex& vertex, const Point_3& query){ - - // Map vertex descriptor to point_3 - // const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); - - // Circulator of faces around the vertex - CGAL::Face_around_target_circulator - face_circulator(m_polygon_mesh.halfedge(vertex), m_polygon_mesh); - - CGAL::Face_around_target_circulator - face_done(face_circulator); - - // Compute weight w_v - FT weight = FT(0); - - // Iterate using the circulator - do{ - - // Vertices around face iterator - const auto hedge = halfedge(*face_circulator, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - auto vertex_itr = vertices.begin(); - CGAL_precondition(vertices.size() == 3); - - // Weight of vertex for this particular face - // FT partial_weight = FT(0); - int vertex_idx = -1; - - // Store useful information - std::vector query_vertex_vectors; - std::vector m_vectors; - Vector_3 r_vector; - Vector_3 m_vector_vertex; - query_vertex_vectors.resize(3); - m_vectors.resize(3); - - for(std::size_t i = 0; i < 3; i++){ - - if(*vertex_itr == vertex) - vertex_idx = i; - - const Vector_3 p = m_construct_vector_3(query, get(m_vertex_to_point_map, *vertex_itr)); - query_vertex_vectors[i] = p; - vertex_itr++; - } - - // Current vertex should be present in face - assert(vertex_idx != -1); - - for(int i = 0; i < 3; i++){ - - m_vectors[i] = m_cross_3(query_vertex_vectors[i], query_vertex_vectors[(i+1)%3]); - assert(m_vectors[i].squared_length() > 0); - m_vectors[i] /= sqrt(m_vectors[i].squared_length()); - - Vector_3 edge_vector = query_vertex_vectors[i+1] - query_vertex_vectors[i]; - FT cot_1 = cot_dihedral_angle(-edge_vector, query_vertex_vectors[i]); - FT cot_2 = cot_dihedral_angle(edge_vector, query_vertex_vectors[i+1]); - - std::cout <<"Cotangents: "<< cot_1 << " " << cot_2 << "\n"; - - FT d_val = query_vertex_vectors[i].squared_length() * cot_2 + - query_vertex_vectors[i+1].squared_length() * cot_1; - - r_vector = r_vector + d_val * m_vectors[i]; - - if(i != vertex_idx && i+1 != vertex_idx) - m_vector_vertex = m_vectors[i]; - } - - weight += m_dot_3(r_vector, m_vector_vertex); - - std::cout << r_vector << "\n"; - - face_circulator++; - - }while(face_circulator!=face_done); - - std::cout << "Weight: "< - OutIterator voronoi_coordinates_3( - const CGAL::Surface_mesh& surface_mesh, - const Point_3& query, - OutIterator c_begin, - const Computation_policy_3 policy = - Computation_policy_3::FAST) { - - using Geom_Traits = typename Kernel_traits::Kernel; - using SM = CGAL::Surface_mesh; - - Voronoi_coordinates_3 voronoi(surface_mesh, policy); - return voronoi(query, c_begin); - } - -} // namespace Barycentric_coordinates -} // namespace CGAL - -#endif // CGAL_BARYCENTRIC_VORONOI_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 33db0e4a93b9..0563f76c0e89 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -27,25 +27,24 @@ namespace Barycentric_coordinates { /*! \ingroup PkgBarycentricCoordinates3RefAnalytic - \brief 2D Wachspress coordinates. + \brief 3D Wachspress coordinates. - This class implements 2D Wachspress coordinates ( \cite cgal:bc:fhk-gcbcocp-06, - \cite cgal:bc:mlbd-gbcip-02, \cite cgal:bc:w-rfeb-75 ), which can be computed - at any point inside a strictly convex polygon. + This class implements 3D Wachspress coordinates, which can be computed + at any point inside a convex polyhedron with triangular faces. - Wachspress coordinates are well-defined and non-negative in the closure - of a strictly convex polygon. The coordinates are computed analytically. - See more details in the user manual \ref compute_wp_coord "here". + Wachspress coordinates are well-defined and non-negative in the closure of a convex + polyhedron with triangular faces. The coordinates are computed analytically. - \tparam VertexRange - a model of `ConstRange` whose iterator type is `RandomAccessIterator` + \tparam PolygonMesh + must be a model of the concept `FaceListGraph`. \tparam GeomTraits - a model of `BarycentricTraits_2` + a model of `BarycentricTraits_3` - \tparam PointMap - a model of `ReadablePropertyMap` whose key type is `VertexRange::value_type` and - value type is `Point_2`. The default is `CGAL::Identity_property_map`. + \tparam VertexToPointMap + a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector::const_type`. */ template< typename PolygonMesh, @@ -88,26 +87,26 @@ namespace Barycentric_coordinates { \brief initializes all internal data structures. This class implements the behavior of Wachspress coordinates - for 2D query points. + for 3D query points. - \param polygon - an instance of `VertexRange` with the vertices of a strictly convex polygon + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy - one of the `Computation_policy_2`; - the default is `Computation_policy_2::PRECISE_WITH_EDGE_CASES` + one of the `Computation_policy_3`; + the default is `Computation_policy_3::FAST` \param traits a traits class with geometric objects, predicates, and constructions; the default initialization is provided - \param point_map - an instance of `PointMap` that maps a vertex from `polygon` to `Point_2`; + \param vertex_to_point_map + an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; the default initialization is provided - \pre polygon.size() >= 3 - \pre polygon is simple - \pre polygon is strictly convex + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, @@ -145,16 +144,16 @@ namespace Barycentric_coordinates { /// @{ /*! - \brief computes 2D Wachspress coordinates. + \brief computes 3D Wachspress coordinates. - This function fills `c_begin` with 2D Wachspress coordinates computed - at the `query` point with respect to the vertices of the input polygon. + This function fills `c_begin` with 3D Wachspress coordinates computed + at the `query` point with respect to the vertices of the input polyhedron. - The number of returned coordinates equals to the number of polygon vertices. + The number of returned coordinates equals to the number of vertices. After the coordinates \f$b_i\f$ with \f$i = 1\dots n\f$ are computed, where - \f$n\f$ is the number of polygon vertices, the query point \f$q\f$ can be obtained - as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polygon vertices. + \f$n\f$ is the number of vertices, the query point \f$q\f$ can be obtained + as \f$q = \sum_{i = 1}^{n}b_ip_i\f$, where \f$p_i\f$ are the polyhedron vertices. \tparam OutIterator a model of `OutputIterator` that accepts values of type `FT` @@ -340,53 +339,49 @@ namespace Barycentric_coordinates { /*! \ingroup PkgBarycentricCoordinates3RefFunctions - \brief computes 2D Wachspress weights. + \brief computes 3D Wachspress coordinates. - This function computes 2D Wachspress weights at a given `query` point - with respect to the vertices of a strictly convex `polygon`, that is one - weight per vertex. The weights are stored in a destination range - beginning at `w_begin`. + This function computes 3D Wachspress coordinates at a given `query` point + with respect to the vertices of a convex `polyhedron` with triangular faces, that is one + coordinate per vertex. The coordinates are stored in a destination range + beginning at `c_begin`. - Internally, the class `Wachspress_coordinates_2` is used. If one wants to process + Internally, the class `Wachspress_coordinates_3` is used. If one wants to process multiple query points, it is better to use that class. When using the free function, internal memory is allocated for each query point, while when using the class, it is allocated only once, which is much more efficient. However, for a few query points, it is easier to use this function. It can also be used when the processing time is not a concern. - \tparam PointRange - a model of `ConstRange` whose iterator type is `RandomAccessIterator` - and value type is `GeomTraits::Point_2` + \tparam Point_3 + A model of `Kernel::Point_3`. + + \tparam Mesh + must be a model of the concept `FaceListGraph`. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \tparam GeomTraits - a model of `BarycentricTraits_2` + \param surface_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron - \param polygon - an instance of `PointRange` with 2D points, which form a strictly convex polygon \param query a query point - \param w_begin - the beginning of the destination range with the computed weights - - \param traits - a traits class with geometric objects, predicates, and constructions; - this parameter can be omitted if the traits class can be deduced from the point type + \param c_begin + the beginning of the destination range with the computed coordinates \param policy - one of the `Computation_policy_2`; - the default is `Computation_policy_2::FAST_WITH_EDGE_CASES` + one of the `Computation_policy_3`; + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, - one past the last weight stored + one past the last coordinate stored - \pre polygon.size() >= 3 - \pre polygon is simple - \pre polygon is strictly convex + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is strongly convex. + \pre polygon_mesh is simplicial. */ template< typename Point_3, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index e77d5c96e515..089e9d41f69e 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -27,13 +27,12 @@ namespace Barycentric_coordinates{ \brief computes tetrahedron coordinates. This function computes barycentric coordinates at a given `query` point - with respect to the points `p0`, `p1`, `p2`, and `p3, which form a tetrahedron, that is one + with respect to the points `p0`, `p1`, `p2`, and `p3`, which form a tetrahedron, that is one coordinate per point. The coordinates are stored in a destination range beginning at `c_begin`. After the coordinates \f$b_0\f$, \f$b_1\f$, \f$b_2\f$, and \f$b_2\f$ are computed, the query - point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. See more details - in the user manual \ref compute_tetra_coord "here". + point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` @@ -108,12 +107,11 @@ namespace Barycentric_coordinates{ \brief computes tetrahedron coordinates. This function computes barycentric coordinates at a given `query` point - with respect to the points `p0`, `p1`, `p2`, and `p3`, which form a triangle, that is one + with respect to the points `p0`, `p1`, `p2`, and `p3`, which form a tetrahedron, that is one coordinate per point. The coordinates are returned in a tuple. After the coordinates \f$b_0\f$, \f$b_1\f$, \f$b_2\f$, and \f$b_3\f$ are computed, the query - point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. See more details - in the user manual \ref compute_tetra_coord "here". + point \f$q\f$ can be obtained as \f$q = b_0p_0 + b_1p_1 + b_2p_2 + b_3p_3\f$. \tparam GeomTraits a model of `BarycentricTraits_3` diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index b29b547f4d6a..86b2d5593dd6 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -18,7 +18,6 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_wp_weights.cpp") create_single_source_cgal_program("test_dh_weights.cpp") create_single_source_cgal_program("test_mv_weights.cpp") - create_single_source_cgal_program("test_voronoi_weights.cpp") create_single_source_cgal_program("test_edge_cases.cpp") create_single_source_cgal_program("test_containers.cpp") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp deleted file mode 100644 index f582ad337118..000000000000 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_voronoi_weights.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "include/utils.h" - -// Typedefs. -using SCKER = CGAL::Simple_cartesian; -using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; -using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; - -template -void test_overloads() { - - using FT = typename Kernel::FT; - using Point_3 = typename Kernel::Point_3; - using Mesh = typename CGAL::Surface_mesh; - - // tetrahedron - Mesh tetrahedron; - std::vector tetrahedron_coords; - - std::tie(tetrahedron, tetrahedron_coords) = tests::get_irregular_tetrahedron(); - CGAL::Barycentric_coordinates::Voronoi_coordinates_3 voronoi_tetrahedron(tetrahedron); - - const FT step = FT(1) / FT(10); - const FT scale = FT(10); - const FT limit = step*scale; - - std::vector voronoi_coordinates_tetrahedron; - voronoi_coordinates_tetrahedron.resize(4); - - //Check for barycenter - voronoi_tetrahedron(Point_3(FT(1)/FT(4), FT(1)/FT(4), FT(1)/FT(4)), - voronoi_coordinates_tetrahedron.begin()); - - for(std::size_t i = 0; i < 4; i++) - std::cout << voronoi_coordinates_tetrahedron[i] << std::endl; - - tests::test_barycenter(voronoi_coordinates_tetrahedron); - - // Sample interior points - for(FT x = step; x < limit; x += step){ - for(FT y = step; y < limit; y += step){ - for(FT z = step; z < FT(1) - x - y - step; z+= step){ // Excludes points inside faces - - voronoi_coordinates_tetrahedron.clear(); - voronoi_coordinates_tetrahedron.resize(4); - - const Point_3 query(x, y, z); - voronoi_tetrahedron(query, voronoi_coordinates_tetrahedron.begin()); - - for(std::size_t i = 0; i < 4; i++) - std::cout << voronoi_coordinates_tetrahedron[i] << std::endl; - - tests::test_linear_precision(voronoi_coordinates_tetrahedron, tetrahedron_coords, query); - tests::test_partition_of_unity(voronoi_coordinates_tetrahedron); - } - } - } - -} - -int main(){ - - // Set cout precision - std::cout.precision(20); - - std::cout << "SCKER test :" << std::endl; - test_overloads(); - std::cout << "SCKER PASSED" << std::endl; - - std::cout << "EPICK test :" << std::endl; - test_overloads(); - std::cout << "EPICK PASSED" << std::endl; - - std::cout << "EPECK test :" << std::endl; - test_overloads(); - std::cout << "EPECK PASSED" << std::endl; - - return EXIT_SUCCESS; -} From 2d535946bbfb9b00603496ecbf19d92309124ed9 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 29 Jul 2021 19:59:54 -0300 Subject: [PATCH 46/68] fixing warnings in docs build --- .../doc/Barycentric_coordinates_3/PackageDescription.txt | 6 +++--- .../Discrete_harmonic_coordinates_3.h | 2 +- .../Barycentric_coordinates_3/Mean_value_coordinates_3.h | 2 +- .../Barycentric_coordinates_3/Wachspress_coordinates_3.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 8b1e73a9d834..0aebce37669d 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -33,9 +33,9 @@ defined for convex simplicial polytopes.} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin -\cgalPkgSince{} -\cgalPkgBib{} -\cgalPkgLicense{\ref licensesGPL "GPL"} +\cgalPkgSince{....} +\cgalPkgBib{....} +\cgalPkgLicense{....} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 09ff506fa060..23891da34f04 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -44,7 +44,7 @@ namespace Barycentric_coordinates { \tparam VertexToPointMap a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector::const_type`. + CGAL::vertex_point_t>`. */ template< typename PolygonMesh, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index e9bdf5d5649c..f2abfc145386 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -43,7 +43,7 @@ namespace Barycentric_coordinates { \tparam VertexToPointMap a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector::const_type`. + CGAL::vertex_point_t>`. */ template< typename PolygonMesh, diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 0563f76c0e89..5652bb71f1b2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -44,7 +44,7 @@ namespace Barycentric_coordinates { \tparam VertexToPointMap a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector::const_type`. + CGAL::vertex_point_t>`. */ template< typename PolygonMesh, From 46ad2ae6045ca16cea5be95186797283f6e9e4c7 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 2 Aug 2021 09:56:32 -0300 Subject: [PATCH 47/68] fixing issues after first revision --- .../benchmark_tetrahedon_coordinates.cpp | 54 +++++++++++++++- Barycentric_coordinates_3/benchmark/bench.md | 5 +- .../Barycentric_coordinates_3.txt | 63 +++++++++++++++---- .../Concepts/BarycentricTraits_3.h | 27 +------- .../PackageDescription.txt | 6 +- .../Barycentric_coordinates_3/dependencies | 2 + .../Barycentric_coordinates_3/examples.txt | 1 + .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../discrete_harmonic_coordinates.cpp | 45 +++++++++++++ .../mean_value_coordinates.cpp | 25 +++++--- .../shape_deformation.cpp | 6 +- .../Discrete_harmonic_coordinates_3.h | 14 ++--- .../Mean_value_coordinates_3.h | 20 +++--- .../Wachspress_coordinates_3.h | 14 ++--- .../internal/utils_3.h | 3 - .../Barycentric_coordinates_3/include/utils.h | 5 +- .../test_tetrahedron_coordinates.cpp | 3 +- 17 files changed, 208 insertions(+), 86 deletions(-) create mode 100644 Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp index 6fa311d054b9..7999673f8845 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp @@ -1,6 +1,54 @@ +#include +#include +#include +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = typename Kernel::FT; +using Point_3 = typename Kernel::Point_3; +using Timer = CGAL::Real_timer; -int main(){ +int main() { - return 0; -} \ No newline at end of file + const std::size_t number_of_x_coordinates = 100; + const std::size_t number_of_y_coordinates = 100; + const std::size_t number_of_z_coordinates = 100; + + const FT zero = FT(0); + const FT one = FT(1); + const FT four = FT(4); + + const FT x_step = one / static_cast(number_of_x_coordinates); + const FT y_step = one / static_cast(number_of_y_coordinates); + const FT z_step = one / static_cast(number_of_z_coordinates); + + const Point_3 p0 = Point_3(zero - x_step, zero - y_step, zero - z_step); + const Point_3 p1 = Point_3(four + x_step, zero - y_step, zero - z_step); + const Point_3 p2 = Point_3(zero - x_step, four + y_step, zero - z_step); + const Point_3 p3 = Point_3(zero - x_step, zero - y_step, four + z_step); + + Timer timer; + std::vector coordinates(4); + + double time = 0.0; + timer.start(); + + for (FT x = zero; x <= one; x += x_step){ + for (FT y = zero; y <= one; y += y_step){ + for (FT z = zero; z <= one; z += z_step){ + + const Point_3 query(x, y, z); + CGAL::Barycentric_coordinates::tetrahedron_coordinates( + p0, p1, p2, p3, query, coordinates.begin()); + } + } + } + + timer.stop(); + time += timer.time(); + timer.reset(); + + std::cout.precision(10); + std::cout << "benchmark_tretrahedron_coordinates (CPU time): " << + time << " seconds" << std::endl; + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/benchmark/bench.md b/Barycentric_coordinates_3/benchmark/bench.md index fa001609b1f9..8cca1dfa7534 100644 --- a/Barycentric_coordinates_3/benchmark/bench.md +++ b/Barycentric_coordinates_3/benchmark/bench.md @@ -3,4 +3,7 @@ Benchmark July 7: Here are the results for a triangulated cube, with 1000000 points sampled regularly: 1) Wachspress : benchmark_polyhedron_8_vertices (CPU time): 59.1783669 seconds 2) Discrete Harmonic : benchmark_polyhedron_8_vertices (CPU time): 167.832063 seconds -3) Mean Value : benchmark_polyhedron_8_vertices (CPU time): 199.651994 seconds \ No newline at end of file +3) Mean Value : benchmark_polyhedron_8_vertices (CPU time): 199.651994 seconds + + Here is the result for a tetrahedron, with 1000000 points sampled regularly: + 1) Tetrahedron: benchmark_tretrahedron_coordinates (CPU time): 1.612018108 seconds \ No newline at end of file diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index 6093139a1049..4d82cc671279 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -70,33 +70,74 @@ for all of these 250 points. \subsection dh_example Discrete Harmonic Coordinates +This example is very similar to the one used with tetrahedron coordinates. +We started with a regular icosahedron and for points inside, outside and +at the boundary, we calculate discrete harmonic coordinates. In this example, +we use the fast with edge cases algorithm because it treats points very close +to the boundaries. +\anchor dh_coord_example +\cgalExample{Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp} + +\subsection mv_example Mean Value Coordinates + +\anchor mv_coord_example +\cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} + +\subsection shape_deform_example Shape deformation + This is an example that shows how to deform a simple smooth sphere into another shape, topologically equivalent. To achieve this, we enclose the sphere inside a cube cage, then we calculate the barycentric coordinate of each vertex with respect to the cube cage. After deforming the cage, we calculate the linear combination of the points to get the deformed sphere vertices. -\anchor dh_coord_example +\anchor shape_deform_example \cgalExample{Barycentric_coordinates_3/shape_deformation.cpp} -\subsection mv_example Mean Value Coordinates - -This example is very similar to the one used with tetrahedron coordinates. -We started with a regular icosahedron and for points inside, outside and -at the boundary, we calculate mean value coordinates. In this example, we -use the fast with edge cases algorithm because it treats points very close -to the boundaries. -\anchor mv_coord_example -\cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} - \section gbc_degeneracies Edge Cases +The precision of each coordinate depends on the used `Kernel`. If an inexact kernel +is used and the user is not sure if the points are near the boundaries, `FAST_WITH_EDGE_CASES` +algorithm should be used. \section gbc_performance Performance +Here are the results of the benchmark done for each coordinate. A total of 1000000 +points were sampled regularly inside each shape. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Coordinate type Shape Total time (in seconds)
WachspressTriangulated cube59.1783669
Discrete HarmonicTriangulated cube167.832063
Mean ValueTriangulated cube199.651994
TetrahedronTetrahedron1.612018108
+ \section gbc_history History +This package was introduced during GSoC 2021 and implemented by Antonio Gomes +under the supervision of Dmitry Anisimov. \section gbc_acknowledgments Acknowledgments diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h index ae40f236deb0..c5238dd3e94f 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h @@ -7,7 +7,9 @@ namespace Barycentric_coordinates { A concept that describes the set of requirements of the template parameter `GeomTraits` used to parameterize all classes and functions with 3D barycentric -coordinates from the namespace `CGAL::Barycentric_coordinates`. +coordinates from the namespace `CGAL::Barycentric_coordinates`. The concept `BarycentricTraits_3` +extends the concept `BarycentricTraits_2` and adds the requirements for 3D objects. +\cgalGeneralizes `BarycentricTraits_2` \cgalHasModel - All models of `Kernel` @@ -29,11 +31,6 @@ typedef unspecified_type FT; /// \name 3D Geometric Objects /// @{ -/*! - A model of `Kernel::Point_2`. -*/ -typedef unspecified_type Point_2; - /*! A model of `Kernel::Point_3`. */ @@ -49,15 +46,6 @@ typedef unspecified_type Vector_3; /// \name 3D Generalized Constructions /// @{ -/*! - A construction object that must provide the function operator: - - `FT operator(const Point_2& p, const Point_2& q, const Point_2& r)` - - that returns the signed area of the triangle defined by the points `p`, `q`, and `r`. -*/ -typedef unspecified_type Compute_area_2; - /*! A construction object that must provide the function operator: @@ -95,15 +83,6 @@ typedef unspecified_type Compute_scalar_product_3; */ typedef unspecified_type Compute_determinant_3; -/*! - A construction object that must provide the function operator: - - `FT operator(const FT& value)` - - that returns the square root of value. -*/ -typedef unspecified_type Sqrt; - /*! A construction object that must provide the function operator: diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 0aebce37669d..6df1b284fd67 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -33,9 +33,9 @@ defined for convex simplicial polytopes.} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin -\cgalPkgSince{....} -\cgalPkgBib{....} -\cgalPkgLicense{....} +\cgalPkgSince{5.1} +\cgalPkgBib{abha-gbc} +\cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies index b01ca3d8bcac..79d42fedd85a 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/dependencies @@ -1,6 +1,8 @@ Manual Mesh_2 Polygon +BGL +Barycentric_coordinates_2 Kernel_d Kernel_23 Generator diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt index d3b5fb955e34..cf204ddf092d 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/examples.txt @@ -1,6 +1,7 @@ /*! \example Barycentric_coordinates_3/tetrahedron_coordinates.cpp \example Barycentric_coordinates_3/wachspress_coordinates.cpp +\example Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp \example Barycentric_coordinates_3/mean_value_coordinates.cpp \example Barycentric_coordinates_3/shape_deformation.cpp */ diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index 067adcc8f90e..7e64a711cfc9 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -16,6 +16,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("tetrahedron_coordinates.cpp") create_single_source_cgal_program("shape_deformation.cpp") create_single_source_cgal_program("wachspress_coordinates.cpp") + create_single_source_cgal_program("discrete_harmonic_coordinates.cpp") create_single_source_cgal_program("mean_value_coordinates.cpp") else() diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp new file mode 100644 index 000000000000..52aa680f06f9 --- /dev/null +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp @@ -0,0 +1,45 @@ +#define PHI 1.6180339887498948482 + +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using FT = Kernel::FT; +using Point_3 = Kernel::Point_3; +using Surface_mesh = CGAL::Surface_mesh; +namespace PMP = CGAL::Polygon_mesh_processing; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; + +int main(){ + + Surface_mesh icosahedron; + CGAL::make_icosahedron(icosahedron, Point_3(0.0, 0.0, 0.0), 2.0); + PMP::triangulate_faces(faces(icosahedron), icosahedron); + + std::vector coords; + std::vector queries{ + Point_3(-1, 1 + PHI, PHI), Point_3(0.5, (1+3*PHI)/2, PHI/2), Point_3(1, 1+PHI, -PHI), //Boundary + Point_3(-1, 1, 1), Point_3(0, 0, 1), Point_3(0, 2, 1), //Interior + Point_3(0, 2*PHI, 4), Point_3(0, 3, 2*PHI), Point_3(4, 0, 0)}; //EXterior + + std::cout << std::endl << "Discrete harmonic coordinates : " << std::endl << std::endl; + + for (const auto& query : queries){ + + coords.clear(); + CGAL::Barycentric_coordinates::discrete_harmonic_coordinates_3( + icosahedron, query, std::back_inserter(coords), CP3::FAST_WITH_EDGE_CASES); + + // Output discrete harmonics coordinates. + for (std::size_t i = 0; i < coords.size() -1; ++i) { + std::cout << coords[i] << ", "; + } + std::cout << coords[coords.size() -1] << std::endl; + } + std::cout << std::endl; + + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp index ec51d080bd9c..c5cadb01b6e7 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp @@ -11,19 +11,28 @@ using Point_3 = Kernel::Point_3; using Surface_mesh = CGAL::Surface_mesh; namespace PMP = CGAL::Polygon_mesh_processing; using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; -using MV = CGAL::Barycentric_coordinates::Mean_value_coordinates_3; int main(){ - Surface_mesh icosahedron; - CGAL::make_icosahedron(icosahedron, Point_3(0.0, 0.0, 0.0), 2.0); - PMP::triangulate_faces(faces(icosahedron), icosahedron); + Surface_mesh concave; + + const Point_3 p0(0.0, 3.0, 0.0); + const Point_3 p1(1.0, 1.0, 0.0); + const Point_3 p2(3.0, 0.0, 0.0); + const Point_3 p3(0.0, 0.0, 0.0); + const Point_3 p4(0.0, 0.0, 3.0); + const Point_3 p5(0.0, 3.0, 3.0); + const Point_3 p6(1.0, 1.0, 3.0); + const Point_3 p7(3.0, 0.0, 3.0); + + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, concave); + PMP::triangulate_faces(faces(concave), concave); std::vector coords; std::vector queries{ - Point_3(-1, 1 + PHI, PHI), Point_3(0.5, (1+3*PHI)/2, PHI/2), Point_3(1, 1+PHI, -PHI), //Boundary - Point_3(-1, 1, 1), Point_3(0, 0, 1), Point_3(0, 2, 1), //Interior - Point_3(0, 2*PHI, 4), Point_3(0, 3, 2*PHI), Point_3(4, 0, 0)}; //EXterior + Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1.0)), Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(2.0)), // Only points in the kernel + Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(1.0)), Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(2.0)), + Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(1.0)), Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(2.0))}; std::cout << std::endl << "Mean value coordinates : " << std::endl << std::endl; @@ -31,7 +40,7 @@ int main(){ coords.clear(); CGAL::Barycentric_coordinates::mean_value_coordinates_3( - icosahedron, query, std::back_inserter(coords), CP3::FAST_WITH_EDGE_CASES); + concave, query, std::back_inserter(coords), CP3::FAST); // Output mean value coordinates. for (std::size_t i = 0; i < coords.size() -1; ++i) { diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp index 3c3be1231c2d..e3dd378acb97 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -60,7 +60,7 @@ int main() { CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, quad_cage); PMP::triangulate_faces(faces(quad_cage), quad_cage); - CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3 dh(quad_cage); + CGAL::Barycentric_coordinates::Mean_value_coordinates_3 mv(quad_cage); auto vertex_to_point_map = get_property_map(CGAL::vertex_point, deformed); std::vector coords; @@ -71,7 +71,7 @@ int main() { const Point_3 vertex_val = get(vertex_to_point_map, v); coords.clear(); - dh(vertex_val, std::back_inserter(coords)); + mv(vertex_val, std::back_inserter(coords)); FT x = FT(0), y = FT(0), z = FT(0); for(std::size_t i = 0; i < 8; i++){ diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 23891da34f04..4724dc713682 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -93,8 +93,8 @@ namespace Barycentric_coordinates { an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy - one of the `Computation_policy_3`; - the default is `Computation_policy_3::FAST` + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \param traits a traits class with geometric objects, predicates, and constructions; @@ -132,7 +132,7 @@ namespace Barycentric_coordinates { Discrete_harmonic_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::FAST, + Computation_policy_3::FAST_WITH_EDGE_CASES, const GeomTraits traits = GeomTraits()) : Discrete_harmonic_coordinates_3( polygon_mesh, @@ -384,7 +384,7 @@ namespace Barycentric_coordinates { the beginning of the destination range with the computed coordinates \param policy - one of the `Computation_policy_3`; + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, @@ -396,10 +396,10 @@ namespace Barycentric_coordinates { */ template< typename Point_3, - typename Mesh, + typename PolygonMesh, typename OutIterator> OutIterator discrete_harmonic_coordinates_3( - const Mesh& surface_mesh, + const PolygonMesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -407,7 +407,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); + Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); return discrete_harmonic(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index f2abfc145386..281e27cc83a2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -93,8 +93,8 @@ namespace Barycentric_coordinates { an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy - one of the `Computation_policy_3`; - the default is `Computation_policy_3::FAST` + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \param traits a traits class with geometric objects, predicates, and constructions; @@ -105,7 +105,6 @@ namespace Barycentric_coordinates { the default initialization is provided \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is strongly convex. \pre polygon_mesh is simplicial. */ Mean_value_coordinates_3( @@ -123,8 +122,6 @@ namespace Barycentric_coordinates { sqrt(internal::Get_sqrt::sqrt_object(m_traits)), m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()) { - // Check if polyhedron is strongly convex - CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); m_weights.resize(vertices(m_polygon_mesh).size()); query_vertex_vectors.resize(3); unit_vectors.resize(3); @@ -137,7 +134,7 @@ namespace Barycentric_coordinates { Mean_value_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::FAST, + Computation_policy_3::FAST_WITH_EDGE_CASES, const GeomTraits traits = GeomTraits()) : Mean_value_coordinates_3( polygon_mesh, @@ -395,30 +392,29 @@ namespace Barycentric_coordinates { the beginning of the destination range with the computed coordinates \param policy - one of the `Computation_policy_3`; + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, one past the last coordinates stored \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is strongly convex. \pre polygon_mesh is simplicial. */ template< typename Point_3, - typename Mesh, + typename PolygonMesh, typename OutIterator> OutIterator mean_value_coordinates_3( - const Mesh& surface_mesh, + const PolygonMesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = - Computation_policy_3::FAST) { + Computation_policy_3::FAST_WITH_EDGE_CASES) { using Geom_Traits = typename Kernel_traits::Kernel; - Mean_value_coordinates_3 mean_value(surface_mesh, policy); + Mean_value_coordinates_3 mean_value(surface_mesh, policy); return mean_value(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 5652bb71f1b2..bf31d41b03a2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -93,8 +93,8 @@ namespace Barycentric_coordinates { an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param policy - one of the `Computation_policy_3`; - the default is `Computation_policy_3::FAST` + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; + the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \param traits a traits class with geometric objects, predicates, and constructions; @@ -132,7 +132,7 @@ namespace Barycentric_coordinates { Wachspress_coordinates_3( const PolygonMesh& polygon_mesh, const Computation_policy_3 policy = - Computation_policy_3::FAST, + Computation_policy_3::FAST_WITH_EDGE_CASES, const GeomTraits traits = GeomTraits()) : Wachspress_coordinates_3( polygon_mesh, @@ -373,7 +373,7 @@ namespace Barycentric_coordinates { the beginning of the destination range with the computed coordinates \param policy - one of the `Computation_policy_3`; + one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; the default is `Computation_policy_3::FAST_WITH_EDGE_CASES` \return an output iterator to the element in the destination range, @@ -385,10 +385,10 @@ namespace Barycentric_coordinates { */ template< typename Point_3, - typename Mesh, + typename PolygonMesh, typename OutIterator> OutIterator wachspress_coordinates_3( - const Mesh& surface_mesh, + const PolygonMesh& surface_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -396,7 +396,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Wachspress_coordinates_3 wachspress(surface_mesh, policy); + Wachspress_coordinates_3 wachspress(surface_mesh, policy); return wachspress(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 6c1eb3ec1677..0b6ee7ba584b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -210,8 +210,6 @@ template OutIterator coordinates, const GeomTraits& traits){ - using Point_3 = typename GeomTraits::Point_3; - using Vector_3 = typename GeomTraits::Vector_3; using FT = typename GeomTraits::FT; using Plane_3 = typename GeomTraits::Plane_3; using Point_2 = typename GeomTraits::Point_2; @@ -267,7 +265,6 @@ template OutIterator coordinates, const GeomTraits& traits){ - using Point_3 = typename GeomTraits::Point_3; using Vector_3 = typename GeomTraits::Vector_3; using FT = typename GeomTraits::FT; const auto& dot_3 = traits.compute_scalar_product_3_object(); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h index 75965dbc582f..9afe3cc90021 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/include/utils.h @@ -150,12 +150,11 @@ namespace tests{ CGAL::Random_points_in_tetrahedron_3 gen_in(tetra_inside); CGAL::Random_points_in_triangle_mesh_3 gen_surf(tetra_surf); - std::copy_n(gen_in, n/2, out); - std::copy_n(gen_surf, n/2, out); + out = std::copy_n(gen_in, n/2, out); + out = std::copy_n(gen_surf, n/2, out); return out; } - } #endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp index bc2486d7fe04..5f016514daa3 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp @@ -1,3 +1,4 @@ int main(){ -} + +} \ No newline at end of file From 99f88a3cd1302844d53563724565b7aafe00cb76 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 3 Aug 2021 09:52:04 -0300 Subject: [PATCH 48/68] more fixes --- .../Barycentric_coordinates_3.txt | 34 ++-- .../PackageDescription.txt | 1 + .../mean_value_coordinates.cpp | 8 +- .../Discrete_harmonic_coordinates_3.h | 8 +- .../Mean_value_coordinates_3.h | 8 +- .../Wachspress_coordinates_3.h | 9 +- .../boundary_coordinates_3.h | 188 ++++++++++++++++++ .../Barycentric_coordinates_3/CMakeLists.txt | 1 + .../test_boundary_coordinates.cpp | 74 +++++++ .../test_tetrahedron_coordinates.cpp | 71 ++++++- Documentation/doc/biblio/cgal_manual.bib | 10 + 11 files changed, 379 insertions(+), 33 deletions(-) create mode 100644 Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h create mode 100644 Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_boundary_coordinates.cpp diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index 4d82cc671279..16cd800b58cc 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -9,7 +9,7 @@ namespace Barycentric_coordinates { \authors Antonio Gomes, Dmitry Anisimov -\section gbc_introduction Introduction +\section gbc_3_introduction Introduction Barycentric coordinates are an important tool in computer graphics and geometric modeling. Originally, these coordinates were used to represent a given point with respect to a simplex but have been later @@ -22,7 +22,7 @@ In particular, this package includes an implementation of Wachspress, discrete h one extra function to calculate barycentric coordinates with respect to tetrahedrons. -\section gbc_interface Software Design +\section gbc_3_interface Software Design Wachspress, discrete harmonic, and mean value coordinates are all generalized barycentric coordinates that can be computed analytically. In this implementation, we restrict our polyhedra to convex ones @@ -44,7 +44,7 @@ which are required for the computation. All models of Kernel can be used. A poly a model of the concept FaceListGraph with a property map that maps a vertex from the polyhedron to CGAL::Point_3. -\section gbc_examples Examples +\section gbc_3_examples Examples In order to facilitate the process of learning this package, we provide various examples with a basic usage of different barycentric components. @@ -59,28 +59,32 @@ at the boundary of the tetrahedron. \anchor tetra_coord_example \cgalExample{Barycentric_coordinates_3/tetrahedron_coordinates.cpp} -\subsection wp_example Wachspress Coordinates +\subsection wp_3_example Wachspress Coordinates In this example, we generate 250 random points inside a unitary sphere, centered at the origin, then we take the convex hull of this set of points and use this as our polyhedron. Finally, we calculate Wachspress coordinates for all of these 250 points. -\anchor wp_coord_example +\anchor wp_3_coord_example \cgalExample{Barycentric_coordinates_3/wachspress_coordinates.cpp} -\subsection dh_example Discrete Harmonic Coordinates +\subsection dh_3_example Discrete Harmonic Coordinates This example is very similar to the one used with tetrahedron coordinates. We started with a regular icosahedron and for points inside, outside and at the boundary, we calculate discrete harmonic coordinates. In this example, we use the fast with edge cases algorithm because it treats points very close to the boundaries. -\anchor dh_coord_example +\anchor dh_3_coord_example \cgalExample{Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp} -\subsection mv_example Mean Value Coordinates +\subsection mv_3_example Mean Value Coordinates -\anchor mv_coord_example +This example shows how to compute mean value coordinates for a set of points in a +star-shaped polyhedron. We note that this type of coordinate is well-defined for a +concave polyhedron and it may yield negative coordinate values for points outside +the polyhedron's kernel. +\anchor mv_3_coord_example \cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} \subsection shape_deform_example Shape deformation @@ -91,16 +95,18 @@ sphere inside a cube cage, then we calculate the barycentric coordinate of each vertex with respect to the cube cage. After deforming the cage, we calculate the linear combination of the points to get the deformed sphere vertices. -\anchor shape_deform_example +\anchor shape_deform_example_3 \cgalExample{Barycentric_coordinates_3/shape_deformation.cpp} -\section gbc_degeneracies Edge Cases +\section gbc_3_degeneracies Edge Cases The precision of each coordinate depends on the used `Kernel`. If an inexact kernel is used and the user is not sure if the points are near the boundaries, `FAST_WITH_EDGE_CASES` algorithm should be used. +Implementation details are describe in \cgalCite{cgal:bc:f-wmvc-14} for Wachspress and mean value +coordinates, and in \cgalCite{cgal:bc:jlw-ggcccsp-07} for discrete harmonic coordinates. -\section gbc_performance Performance +\section gbc_3_performance Performance Here are the results of the benchmark done for each coordinate. A total of 1000000 points were sampled regularly inside each shape. @@ -134,12 +140,12 @@ points were sampled regularly inside each shape. -\section gbc_history History +\section gbc_3_history History This package was introduced during GSoC 2021 and implemented by Antonio Gomes under the supervision of Dmitry Anisimov. -\section gbc_acknowledgments Acknowledgments +\section gbc_3_acknowledgments Acknowledgments */ diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 6df1b284fd67..9608d7a4eab0 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -54,6 +54,7 @@ defined for convex simplicial polytopes.} - `wachspress_coordinates_3()` - `mean_value_coordinates_3()` - `discrete_harmonic_coordinates_3()` +- `boundary_coordinates_3()` */ } /* namespace Barycentric_coordinates */ diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp index c5cadb01b6e7..860b0f5eb729 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp @@ -1,5 +1,3 @@ -#define PHI 1.6180339887498948482 - #include #include #include @@ -30,9 +28,9 @@ int main(){ std::vector coords; std::vector queries{ - Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1.0)), Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(2.0)), // Only points in the kernel - Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(1.0)), Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(2.0)), - Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(1.0)), Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(2.0))}; + Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1.0)), Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(2)), // Only points in the kernel + Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(1.0)), Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(2)), + Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(1.0)), Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(2))}; std::cout << std::endl << "Mean value coordinates : " << std::endl << std::endl; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 4724dc713682..9f25edac3103 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -374,8 +374,8 @@ namespace Barycentric_coordinates { \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \param surface_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -399,7 +399,7 @@ namespace Barycentric_coordinates { typename PolygonMesh, typename OutIterator> OutIterator discrete_harmonic_coordinates_3( - const PolygonMesh& surface_mesh, + const PolygonMesh& polygon_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -407,7 +407,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Discrete_harmonic_coordinates_3 discrete_harmonic(surface_mesh, policy); + Discrete_harmonic_coordinates_3 discrete_harmonic(polygon_mesh, policy); return discrete_harmonic(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 281e27cc83a2..5ef761a01940 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -382,8 +382,8 @@ namespace Barycentric_coordinates { \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \param surface_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -406,7 +406,7 @@ namespace Barycentric_coordinates { typename PolygonMesh, typename OutIterator> OutIterator mean_value_coordinates_3( - const PolygonMesh& surface_mesh, + const PolygonMesh& polygon_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -414,7 +414,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Mean_value_coordinates_3 mean_value(surface_mesh, policy); + Mean_value_coordinates_3 mean_value(polygon_mesh, policy); return mean_value(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index bf31d41b03a2..ee1db9e96d24 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -362,9 +362,8 @@ namespace Barycentric_coordinates { \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \param surface_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron - + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -388,7 +387,7 @@ namespace Barycentric_coordinates { typename PolygonMesh, typename OutIterator> OutIterator wachspress_coordinates_3( - const PolygonMesh& surface_mesh, + const PolygonMesh& polygon_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -396,7 +395,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Wachspress_coordinates_3 wachspress(surface_mesh, policy); + Wachspress_coordinates_3 wachspress(polygon_mesh, policy); return wachspress(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h new file mode 100644 index 000000000000..90ed7b91503d --- /dev/null +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h @@ -0,0 +1,188 @@ +// Copyright (c) 2021 GeometryFactory SARL (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) : Antonio Gomes, Dmitry Anisimov +// + +#ifndef CGAL_BARYCENTRIC_BOUNDARY_COORDINATES_3_H +#define CGAL_BARYCENTRIC_BOUNDARY_COORDINATES_3_H + +// #include + +#include +#include + +namespace CGAL{ +namespace Barycentric_coordinates{ + + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes boundary barycentric coordinates. + + This function computes boundary barycentric coordinates at a given `query` point + with respect to the vertices of a simple `polyhedron`, that is one + coordinate per vertex. The coordinates are stored in a destination range + beginning at `c_begin`. + + If `query` is at the vertex, the corresponding coordinate is set to one, while + all other coordinates are zero. If `query` is on the face, the three corresponding + coordinates are triangle coordinates, while all other coordinates are set to zero. + If `query` is not on the boundary, all the coordinates are set to zero. + + Internally, `triangle_coordinates_2()` are used. + + \tparam PolygonMesh + must be a model of the concept `FaceListGraph`. + + \tparam GeomTraits + a model of `BarycentricTraits_3` + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \tparam VertexToPointMap + a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. + + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \param traits + a traits class with geometric objects, predicates, and constructions; + the default initialization is provided + + \param vertex_to_point_map + an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + the default initialization is provided + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + the flag indicating whether the + query point belongs to the polyhedron boundary + + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is simplicial. + */ + template< + typename PolygonMesh, + typename OutIterator, + typename GeomTraits, + typename VertexToPointMap> + std::pair boundary_coordinates_3( + const PolygonMesh& polygon_mesh, + const typename GeomTraits::Point_3& query, + OutIterator c_begin, + const GeomTraits& traits, + const VertexToPointMap vertex_to_point_map) { + + const auto edge_case = internal::locate_wrt_polyhedron( + vertex_to_point_map, polygon_mesh, query, c_begin, traits); + + if(edge_case == internal::Edge_case::BOUNDARY) + return {c_begin, true}; + else{ + internal::get_default(num_vertices(polygon_mesh), c_begin); + return {c_begin, false}; + } + } + + /*! + \ingroup PkgBarycentricCoordinates3RefFunctions + + \brief computes boundary barycentric coordinates. + + This function computes boundary barycentric coordinates at a given `query` point + with respect to the vertices of a simple `polyhedron`, that is one + coordinate per vertex. The coordinates are stored in a destination range + beginning at `c_begin`. + + If `query` is at the vertex, the corresponding coordinate is set to one, while + all other coordinates are zero. If `query` is on the face, the three corresponding + coordinates are triangle coordinates, while all other coordinates are set to zero. + If `query` is not on the boundary, all the coordinates are set to zero. + + Internally, `triangle_coordinates_2()` are used. + + \tparam PolygonMesh + must be a model of the concept `FaceListGraph`. + + \tparam Point_3 + a model of `Kernel::Point_3` + + \tparam OutIterator + a model of `OutputIterator` that accepts values of type `GeomTraits::FT` + + \tparam VertexToPointMap + a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. + + \param polygon_mesh + an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + + \param query + a query point + + \param c_begin + the beginning of the destination range with the computed coordinates + + \param vertex_to_point_map + an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + the default initialization is provided + + \return an output iterator to the element in the destination range, + one past the last coordinate stored + the flag indicating whether the + query point belongs to the polyhedron boundary + + \pre num_vertices(polygon_mesh) >= 4. + \pre polygon_mesh is simplicial. + */ + template< + typename PolygonMesh, + typename Point_3, + typename OutIterator, + typename VertexToPointMap = typename property_map_selector::const_type> + std::pair boundary_coordinates_3( + const PolygonMesh& polygon_mesh, + const Point_3& query, + OutIterator c_begin, + const VertexToPointMap vertex_to_point_map){ + + using GeomTraits = typename Kernel_traits::Kernel; + const GeomTraits traits; + + return boundary_coordinates_3(polygon_mesh, query, c_begin, traits, vertex_to_point_map); + } + + template< + typename PolygonMesh, + typename Point_3, + typename OutIterator> + std::pair boundary_coordinates_3( + const PolygonMesh& polygon_mesh, + const Point_3& query, + OutIterator c_begin){ + + return boundary_coordinates_3(polygon_mesh, query, c_begin, + get_const_property_map(CGAL::vertex_point, polygon_mesh)); + } + +} +} + +#endif diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 86b2d5593dd6..3ed9b317fde8 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -20,6 +20,7 @@ if(CGAL_FOUND) create_single_source_cgal_program("test_mv_weights.cpp") create_single_source_cgal_program("test_edge_cases.cpp") create_single_source_cgal_program("test_containers.cpp") + create_single_source_cgal_program("test_boundary_coordinates.cpp") else() message(WARNING "This program requires the CGAL library, and will not be compiled.") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_boundary_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_boundary_coordinates.cpp new file mode 100644 index 000000000000..cba2074292cb --- /dev/null +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_boundary_coordinates.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_overloads(){ + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // tetrahedron + Mesh tetrahedron; + std::vector vertices; + std::vector sample_points; + std::vector tetra_coords(4); + + // 500 interior points + 500 surface points + std::tie(tetrahedron, vertices) = tests::get_irregular_tetrahedron(); + tests::random_points_tetrahedron(vertices, std::back_inserter(sample_points), 1000); + + std::size_t num_samples = 0; + for(auto& point : sample_points){ + + const FT x = point.x(), y = point.y(), z = point.z(); + const Point_3 query(x, y, z); + CGAL::Barycentric_coordinates::boundary_coordinates_3(tetrahedron, query, tetra_coords.begin()); + + if(num_samples < 500){ + assert(tetra_coords[0] == FT(0) && + tetra_coords[1] == FT(0) && + tetra_coords[2] == FT(0) && + tetra_coords[3] == FT(0)); + } + else{ + assert(CGAL::abs(1-x-y-z - tetra_coords[0]) == FT(0) && + CGAL::abs(x - tetra_coords[1]) == FT(0) && + CGAL::abs(y - tetra_coords[2]) == FT(0) && + CGAL::abs(z - tetra_coords[3]) == FT(0)); + } + + num_samples++; + } +} + +int main(){ + + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp index 5f016514daa3..41d944ce135d 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp @@ -1,4 +1,73 @@ +#include +#include +#include + +#include +#include + +#include "include/utils.h" + +// Typedefs. +using SCKER = CGAL::Simple_cartesian; +using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; +using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; + +template +void test_overloads(){ + + using FT = typename Kernel::FT; + using Point_3 = typename Kernel::Point_3; + using Mesh = typename CGAL::Surface_mesh; + + // tetrahedron + Mesh tetrahedron; + std::vector vertices; + std::vector sample_points; + std::vector tetra_coords(4); + + //Interior and surface points + std::tie(tetrahedron, vertices) = tests::get_irregular_tetrahedron(); + tests::random_points_tetrahedron(vertices, std::back_inserter(sample_points), 1000); + + //Add vertices + for(auto& v : vertices) + sample_points.push_back(v); + + //Exterior + interior points + std::vector ext_vertices = {Point_3(0.0, 0.0, 0.0), Point_3(5.0, 0.0, 0.0), + Point_3(0.0, 5.0, 0.0), Point_3(0.0, 0.0, 5.0)}; + tests::random_points_tetrahedron(ext_vertices, std::back_inserter(sample_points), 1000); + + for(auto& point : sample_points){ + + const FT x = point.x(), y = point.y(), z = point.z(); + const Point_3 query(x, y, z); + CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], + vertices[2], vertices[3], query, tetra_coords.begin()); + + assert(CGAL::abs(1-x-y-z - tetra_coords[0]) == FT(0) && + CGAL::abs(x - tetra_coords[1]) == FT(0) && + CGAL::abs(y - tetra_coords[2]) == FT(0) && + CGAL::abs(z - tetra_coords[3]) == FT(0)); + } +} + int main(){ + // Set cout precision + std::cout.precision(20); + + std::cout << "SCKER test :" << std::endl; + test_overloads(); + std::cout << "SCKER PASSED" << std::endl; + + std::cout << "EPICK test :" << std::endl; + test_overloads(); + std::cout << "EPICK PASSED" << std::endl; + + std::cout << "EPECK test :" << std::endl; + test_overloads(); + std::cout << "EPECK PASSED" << std::endl; -} \ No newline at end of file + return EXIT_SUCCESS; +} diff --git a/Documentation/doc/biblio/cgal_manual.bib b/Documentation/doc/biblio/cgal_manual.bib index 51dfb1090046..eacb0d699aaf 100644 --- a/Documentation/doc/biblio/cgal_manual.bib +++ b/Documentation/doc/biblio/cgal_manual.bib @@ -3321,6 +3321,16 @@ @techreport{cgal:hssz-gmcabonbc-97 year={1997} } +@article{cgal:bc:jlw-ggcccsp-07, + title={A general geometric construction of coordinates in a convex simplicial polytope}, + volume={24}, + DOI={10.1016/j.cagd.2006.12.001}, + number={3}, + journal={Computer Aided Geometric Design}, + author={Ju, Tao and Liepa, Peter and Warren, Joe}, + year={2007}, + pages={161–178} +} % ---------------------------------------------------------------------------- % END OF BIBFILE % ---------------------------------------------------------------------------- From 4d0c310139f010041706ba77ceb4d9603f312645 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Wed, 4 Aug 2021 09:25:31 -0300 Subject: [PATCH 49/68] minor fixes --- .../discrete_harmonic_coordinates.cpp | 1 + .../mean_value_coordinates.cpp | 23 ++++++++++--------- .../shape_deformation.cpp | 2 ++ .../tetrahedron_coordinates.cpp | 1 - .../wachspress_coordinates.cpp | 1 + .../include/CGAL/Barycentric_coordinates_3.h | 1 + .../Discrete_harmonic_coordinates_3.h | 8 +++---- .../Mean_value_coordinates_3.h | 21 +++++++++-------- .../Wachspress_coordinates_3.h | 11 +++++---- .../boundary_coordinates_3.h | 8 ++----- 10 files changed, 40 insertions(+), 37 deletions(-) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp index 52aa680f06f9..9a5da54b76c6 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp @@ -3,6 +3,7 @@ #include #include #include + #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp index 860b0f5eb729..b565ad391deb 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp @@ -1,6 +1,7 @@ #include #include #include + #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -14,23 +15,23 @@ int main(){ Surface_mesh concave; - const Point_3 p0(0.0, 3.0, 0.0); - const Point_3 p1(1.0, 1.0, 0.0); - const Point_3 p2(3.0, 0.0, 0.0); - const Point_3 p3(0.0, 0.0, 0.0); - const Point_3 p4(0.0, 0.0, 3.0); - const Point_3 p5(0.0, 3.0, 3.0); - const Point_3 p6(1.0, 1.0, 3.0); - const Point_3 p7(3.0, 0.0, 3.0); + const Point_3 p0(0, 3, 0); + const Point_3 p1(1, 1, 0); + const Point_3 p2(3, 0, 0); + const Point_3 p3(0, 0, 0); + const Point_3 p4(0, 0, 3); + const Point_3 p5(0, 3, 3); + const Point_3 p6(1, 1, 3); + const Point_3 p7(3, 0, 3); CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, concave); PMP::triangulate_faces(faces(concave), concave); std::vector coords; std::vector queries{ - Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1.0)), Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(2)), // Only points in the kernel - Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(1.0)), Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(2)), - Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(1.0)), Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(2))}; + Point_3(FT(1)/FT(2), FT(1)/FT(2), FT(1)), Point_3(FT(1)/FT(3), FT(1)/FT(3), FT(2)), // Only points in the kernel + Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(1)), Point_3(FT(4)/FT(3), FT(1)/FT(3), FT(2)), + Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(1)), Point_3(FT(1)/FT(3), FT(4)/FT(3), FT(2))}; std::cout << std::endl << "Mean value coordinates : " << std::endl << std::endl; diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp index e3dd378acb97..00961d811d3f 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp @@ -89,4 +89,6 @@ int main() { std::ofstream out_deformed("deformed_sphere.off"); out_deformed << deformed << std::endl; + + return EXIT_SUCCESS; } \ No newline at end of file diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp index eef1b51b296f..40409671a96c 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp @@ -51,5 +51,4 @@ int main(){ } return EXIT_SUCCESS; - } diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp index dc24d16966c8..fcca4b8c1530 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp @@ -3,6 +3,7 @@ #include #include #include + #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index baf822287125..bfb647e99556 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -24,6 +24,7 @@ */ #include +#include #include #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 9f25edac3103..c74d51a3ffcf 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -28,7 +28,7 @@ namespace Barycentric_coordinates { \brief 3D discrete harmonic coordinates. - This class implements 3D discrete harmonic coordinates, which can be computed + This class implements 3D discrete harmonic coordinates \cite cgal:bc:jlw-ggcccsp-07, which can be computed at any point inside a convex polyhedron with triangular faces. Discrete harmonic coordinates are well-defined in the closure of a convex polyhedron @@ -42,7 +42,7 @@ namespace Barycentric_coordinates { a model of `BarycentricTraits_3` \tparam VertexToPointMap - a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + a property map with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector`. */ @@ -75,7 +75,7 @@ namespace Barycentric_coordinates { /// Point type. typedef typename GeomTraits::Point_3 Point_3; - /// Vector type. + /// %Vector type. typedef typename GeomTraits::Vector_3 Vector_3; /// @} @@ -368,7 +368,7 @@ namespace Barycentric_coordinates { \tparam Point_3 A model of `Kernel::Point_3`. - \tparam Mesh + \tparam PolygonMesh must be a model of the concept `FaceListGraph`. \tparam OutIterator diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 5ef761a01940..766242c8b936 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -28,11 +28,12 @@ namespace Barycentric_coordinates { \brief 3D mean value coordinates. - This class implements 3D mean value coordinates, which can be computed - at any point in the space. + This class implements 3D mean value coordinates ( \cite cgal:bc:f-wmvc-14, + \cite cgal:bc:jlw-ggcccsp-07 ), which can be computed at any point in the space. - Mean value coordinates are well-defined and non-negative in the closure - of a convex polyhedron with triangular faces. The coordinates are computed analytically. + Mean value coordinates are well-defined everywhere in the space and are + non-negative in the kernel of a star-shaped polyhedron. The coordinates are + computed analytically. \tparam PolygonMesh must be a model of the concept `FaceListGraph`. @@ -41,7 +42,7 @@ namespace Barycentric_coordinates { a model of `BarycentricTraits_3` \tparam VertexToPointMap - a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + a property map with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector`. */ @@ -75,7 +76,7 @@ namespace Barycentric_coordinates { /// Point type. typedef typename GeomTraits::Point_3 Point_3; - /// Vector type. + /// %Vector type. typedef typename GeomTraits::Vector_3 Vector_3; /// @} @@ -90,7 +91,7 @@ namespace Barycentric_coordinates { for 3D query points. \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + an instance of `PolygonMesh`, which must be a simplicial polyhedron \param policy one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; @@ -362,7 +363,7 @@ namespace Barycentric_coordinates { \brief computes 3D mean value coordinates. This function computes 3D mean value coordinates at a given `query` point - with respect to the vertices of a convex `polyhedron` with triangular faces, that is one + with respect to the vertices of a `polyhedron` with triangular faces, that is one weight per vertex. The coordinates are stored in a destination range beginning at `c_begin`. @@ -376,14 +377,14 @@ namespace Barycentric_coordinates { \tparam Point_3 A model of `Kernel::Point_3`. - \tparam Mesh + \tparam PolygonMesh must be a model of the concept `FaceListGraph`. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + an instance of `PolygonMesh`, which must be a simplicial polyhedron \param query a query point diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index ee1db9e96d24..f7ef8521e07d 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -29,8 +29,9 @@ namespace Barycentric_coordinates { \brief 3D Wachspress coordinates. - This class implements 3D Wachspress coordinates, which can be computed - at any point inside a convex polyhedron with triangular faces. + This class implements 3D Wachspress coordinates ( \cite cgal:bc:f-wmvc-14, + \cite cgal:bc:jlw-ggcccsp-07 ), which can be computed at any point inside a convex + polyhedron with triangular faces. Wachspress coordinates are well-defined and non-negative in the closure of a convex polyhedron with triangular faces. The coordinates are computed analytically. @@ -42,7 +43,7 @@ namespace Barycentric_coordinates { a model of `BarycentricTraits_3` \tparam VertexToPointMap - a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + a property map with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector`. */ @@ -75,7 +76,7 @@ namespace Barycentric_coordinates { /// Point type. typedef typename GeomTraits::Point_3 Point_3; - /// Vector type. + /// %Vector type. typedef typename GeomTraits::Vector_3 Vector_3; /// @} @@ -356,7 +357,7 @@ namespace Barycentric_coordinates { \tparam Point_3 A model of `Kernel::Point_3`. - \tparam Mesh + \tparam PolygonMesh must be a model of the concept `FaceListGraph`. \tparam OutIterator diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h index 90ed7b91503d..163553b8f729 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h @@ -37,8 +37,6 @@ namespace Barycentric_coordinates{ coordinates are triangle coordinates, while all other coordinates are set to zero. If `query` is not on the boundary, all the coordinates are set to zero. - Internally, `triangle_coordinates_2()` are used. - \tparam PolygonMesh must be a model of the concept `FaceListGraph`. @@ -49,7 +47,7 @@ namespace Barycentric_coordinates{ a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \tparam VertexToPointMap - a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + a property map with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector`. @@ -115,8 +113,6 @@ namespace Barycentric_coordinates{ coordinates are triangle coordinates, while all other coordinates are set to zero. If `query` is not on the boundary, all the coordinates are set to zero. - Internally, `triangle_coordinates_2()` are used. - \tparam PolygonMesh must be a model of the concept `FaceListGraph`. @@ -127,7 +123,7 @@ namespace Barycentric_coordinates{ a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \tparam VertexToPointMap - a model of ReadablePropertyMap with boost::graph_traits::vertex_descriptor as + a property map with boost::graph_traits::vertex_descriptor as key type and Point_3 as value type. The default is `property_map_selector`. From a1d2071286d12c8d9fc7626ad97bdef82fd40452 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 5 Aug 2021 10:01:30 -0300 Subject: [PATCH 50/68] adding thumb --- .../PackageDescription.txt | 2 +- .../fig/barcoord3_thumb.png | Bin 0 -> 3847 bytes .../fig/barcoord_thumb.png | Bin 14797 -> 0 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord3_thumb.png delete mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 9608d7a4eab0..533223a6a8d6 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -22,7 +22,7 @@ Free functions to compute barycentric coordinates. \addtogroup PkgBarycentricCoordinates3Ref \cgalPkgDescriptionBegin{3D Generalized Barycentric Coordinates, PkgBarycentricCoordinates3} -\cgalPkgPicture{barcoord_thumb.png} +\cgalPkgPicture{barcoord3_thumb.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Antonio Gomes, Dmitry Anisimov} diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord3_thumb.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord3_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..3f266461ffb4024125c2f37a649d942086922978 GIT binary patch literal 3847 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 JPDHLkV1kVfY`_2j literal 0 HcmV?d00001 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb.png deleted file mode 100644 index 77690dd84965d5f1efea25e2f7d837c05286157d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14797 zcmV;;IWoqHP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z001udNkle^M!I^snKpGQm9gIr3)o}=4M2_mohARX zgl~S~Yy9zvKQUqA#LA`Ge@9l9dbM|;54CBKpI`a|-d?`6a+&tyQ4-*Y6=D8~N7C|v z$sM1&iMQUVfc&8XSZEkMYXaSo6jS<|xZ|@o@z%1pD_85o0R^^I)7y!;@vvhQHP(QwvWIBzJh%%wCfuCP+1J6GDY~`x$?*J>MXsU0d zdFB{;l6`2ciNqt!bVhRDmu~0T#TAhEYk;-ZSf<5@sbkri>BSHdr4=G(Fv}Rt16TZS z7B600xqAC6z&$-Z)Kpb7>!1VKY7QWE0eBseOp_zI|4aW~vE=>Pf^FMo^5n^gwgj1M z1~j1E_lO$IGDq`=f4QASk3C+wiu)VDIX@gTB8)j;BK^rh1R^j2bVM>E(#|8_xPxaa zbiMxpoEy^?%w|nCOTUQIOO(FcLtFGk;*2)Nap#wQ#^MTH@1FpVojRT^^Z*790^56J zkz}+W6S0_Rc5wFd7&!R8Wj}+w% zUTX~z>00vDck<+uPgbt({y@8Op+<&V)JOTPPx|KORYpQ&8o{Q=<7Gq&2>#QjBO&7P7Mie?l-M9!V+}I(fsC1zu^Ag z-&eWP`!c`)^Nu-$?sRY759>1E27R(%TGd4Ua_xiM{mZ*6S9@OucI!`$_scUG?W9v)`+1*Kd+YZyi_AD%pcOu|7s8UzT+M_l1#$`KAz zn`-%?+IQqCFQDzE>3`v&SXT|P^`XBydm9QDx*ukG}dxpCzf!DqT@Px@7 zWa5KfKCFuZEIrK6Q9?1MVLT7q{5W^~;#UcG(}HghP8> z2mkN3r@8ZvyDF&cEs6$cX>KOkWRT9Jclyiyf{2!`Pk!*GC%EJPRsyfR2C%?jT88Czv5DDA{BcjTn$v!>7)O=A%OVf)06f_(~~Yzcn(vLI{9fIVx37VddM(caX- zEBCD6Cg)bJx#23LFe_;73V?0fF4CdHW7Noz#2N$x12(25ifC7FGj=X!>YU=4FJfr0 zMoi={_rH%3`**(ioy!qaK;CJJ?(XiUuC8v#lj5;zCeLVNaIgm{cATceg$ciCqJgZo zX=xe5pMUcX*Ij!nO4}90b{1f*hbLVeyx;%^Qd@T*9~Q%VU*!HCn-MKzSajceTzAcn z&^Q&;c6X2kFn_^3Vl|-Eu1S;1zb{bQrmb}hPd>Pg>#n_}V##{~;0Y5Z&^$6qW{2`& zvEA=0Z8M^|okjPpu2}M(0N65PG>wdrN)K)muJRy?q0bdTs0)_7mhXJ?W{NA%m9fhJ z2AFg3G@l>d(b9t$#sm=okXxHa@zjIs`0h17LQ|pZ?E*NP&0?CvOGgep>>!lUS0WDH z6{IafR_tp6(hjXH?JTM_`UPC6cvuaa0x)vp=Iv042 zXlduEKd$H6Z`?%IPFK*})fAnG*D!f{JITx-S`_OUBLC3aco2A$Cjk-oN#W4iG>WGl zSj%srKQ&ZbeL3d{<8erbs*^C+yC!4X0sJ1i> z7t^x~n6DfmKnsDE0xc!V5ICm5Hl_QpB(`OcwIo@~AR94An~J)&(JXpykS~4ZW(NAZ zE9mYDfUBzOX{d3?q?1Dk7~7L63yMQ3;cC|!x8kHd%{wre1X|g&wzl!=GRePu`DXfh zyD9*OU+C)UqPe+wyFO(4)X@wMc5i%>YpX_8{N_fzl534GsLyGQk(Wd=q_rT@`?LW=xwl zg@!r<+p&j7&!QNfEbBpp`>oV>7QUH@N)DAATf3mb>bM#*MBeInX(@9~OS1 z-F8JO2Yo4!g$RP2x9ZUAS~)Z{)br|6&E;2q^yqq*0erx$iDc7T5z-j8>m9DM7Nr8L zBE$7jb&j^Z1C(-TY;5B7RUfDmRzJ{Wipvz7aRPexuuN}V`_QFaY#fhZ*jQr zfGr3eQWXFfo+6d|9j~hrm3zOSX1VH?KnU#>kAyc11VZcld??77xz|Nr_0r0rsiA?_ z-rCBSuDFrgfATGA>KZ;;0Bfzc*;QGNN{>ElF0Vehfk<_;+sh+FD7o=RSfV_2Yv1kx zIf*i-f~lZZozt&fHB9CH5k5InqdhrOd%wrVwDR2APCk3}P5kr+U!}2mM1`VPZO?*@{&#trh{I$2y*V9!2csOIq#10aw52d#iWtH%iBKHVq zL|%~PpCE`56R1G@GBkN){(O=DSvw9*O^v+pL4-@LzKJax*H-}E*65ftiJ22ekxr*e z=dESIEf%y&rokd8c;r4$N)KXN@z0 z^RK#<)qh{{;RM)moE=V+^7-(=b4fVes3E&0gFm6Rb=&aQ@Y|vTn`l3PqP16DLh%biGL?oh}k3irwx|zIlkrAwmjQIsYpt z0$d1D^0Qn^*3?!fSnG`7tSfF}<-6}x050;vR*ZvYP9&2YL?BD-F}R9)V-R z)*QB6;j`8-+Cg8O`%!C6byb}8x{c3#^T+$WCClCMp@$qWnN)fcP6Ql7<*%*K0;vU3 z38a!pE!}4&5K1Dn^rb|C@PCATZy?AL{xzt<3Y7@@WB;)txvuq zYfIiO?T6Dl#xYoRFY7w$G13kq2V@>l>4L2!hGSwl62p-QC6QWq$BVqwDU=J2)VZyU zbwRPp|E|3*OP!Z7-PYRBe9pCS>Vni+XzD8*ryWI2Ji&&+k(_tskGSV2U#Da8RX)!?buen<8QwARPBXN>3*i{)Tqfx?D9&`b3PJCA(ltM`G9#>Es>)p}b?2Ksv&0 z%T=xhjVfr}!7b@UfB>}CVB`c%g+jT{LZO6%tcI1T22TCg>-ocV*Dz`7wEY)g+qQ|v z<2#im0Ys{+IcU^q-t6fk+R%_g)hi;1e8entE`%*ms}`O;=AE}wSS##DD&JzO^O`l@ z4vRt+e$Rufa{9FfRnVtF$(6Lg4%9G)V5LCm$uN5TC65r$f z&01+{6A6~pB>2bge{a9AS|3}KxV%ZcGiQ#sIm#|c^L9G^B_ z^cj?34N-KS){2@~oK4FGICkzloc^32rdZ5?lG<=F4HN9C9m=wO zK#7pT6$HGD08{EaLXCY|YpUaM{@zf>X+QZ9FFgPJM-$-L{^Nbpl8x)GyY3r693UEv(%ak1bI(0TI-TaBhaO_fmMz3$G3x5-LPGtvN4%zn zm!5lurMj0`%qplv(g0xs28i54f{-Pwv=ki&!3)TgMbh>Vf3qH{#G%U^AIilwM5LrQ z9^(&>|AoUwj%M7rar+9s$s#4|th3Ids;Y|i_I6G>=_D2}Ud*G9K8j^on5M~s1q+xo zY0|Lw@tE5&jwe_AnueMYC}p5T1WgnrqG%C8;XOnQhZsu4(85CF(R#j-zOWuxSW{6ghFfIDqMsnIuZsP$Ajym%2 zeF9;jwcZLe`d`dsGTd>;9h`jf$$8-;nN0G=8*i{=$r4hj6cZ*)V8n~B8DQ05*7*zMFfqB)}jDT zPM6lo``uNcgr{8Ly{_|Jm^oFe%YMAzQXleyYA+&?gS)|UAa*{NxU(fwl z-^7B$_BzO&B>@K5x^*k}-FF}7o_j8}wY7owve_&vSFYrxmtMkg9J1LgQ>RX4-n@B5 z$98S(tC3_@iQI*ZUYQfCk3`pU|9xJ1=_OXISV4PxJEKO8 zV)Eq4d0+IY&!5HXqt;LrjTZp*2UMXTNC?WLP_6|PwsP=4lLyh=g-7PWEr2XNEJgk_ zowsn;B875+B>=Jl<%C$k`#z<8lc(U9Q%;)52b#OC+N&TB4KToxB}-Vod^zWyd+xA4 zam$u1y!hgatY5z#!!Q`pI)X*dJi+fXPtr7@742xW(!K_|oG1|loj`U|Jh`+F)Ew~I zzjsFt2<4R#i|EyNP97v(0A6X|9CGPdmpe!4LiJL)pDz%k>=cnraMxG&8pzuQ7~t{8 zA4f{bamO9EU7z05)5Ef5%eeo(`}xJQcT+pP8LKWztgRZO$|_u_Fu3`ARFn^My&%WG z7Yjasaw{KlKm{g1;Aig`g^#`qVCMiU_gN)B-}@{9B<(cO4Vt^I{@LCDd3XQ=+;h)8 z%$_})88c>V_qX(J?cw9!K8;OPy=31Tz!}J3)yIjAt|8JG!>o%Uq(C_Zx)lNOq0CNt zzC~(U2Y25oy6|{lg{-w;=>)8MDBxUR2~7qaqU;=d2$D{k$OgsTSO1)aM=so>01sQq zI^&Epc<#C9SiO4nc7IE4QyrttR%By@#-qm2^s#Zo$JdhCI!Nd9Yw3DE?j28z1#O0$P3RWKJMt8<@gS&CpDmL zMb``K*!0LM`rh7(-IYcNfsC05%P3Tf`~?LOwDd5|7YA~sfDacTC@H2CgB+R+W#@vM z#>LGjnHIR!v^YG71Z2r2SScBL1B2VXC>U{wS~vfw+K^1$iP&Y7B`&l4q-G*cRgI*W%E zKSI0F!t4VM+^ql)JID%;haY|z%d$BB_~VCt&KJIY0qbg)5RF!$guoUCDQS`x?jt1( zl#r;L@0DO!5@86Oz6_bp6v;Kcq&E&=RGB2k)e{?4jghe2z{YmmKrIw7`GX>@iX&w= ziuMOn{wTTlwL&nZ1E~@I$U2{wm;QUQD3WXY`0)kTbJ4ly@3tUoyDd2HQzuX6<(FS( zaBz^(qelv<$M{Y5L#UO8@dsa9rurthSI58IUiFm!ONF#fF|sTWOg(grCmK&~q-G zQ@*=PnOYF7HF)y5KQltMaKHhx_e`;2d&XI3oyDDZ-brg~D;*sj+dO{K*y%j{cLy;> zpqyM2q(BORl!B;K#HB)rEQmDPv+^Vc{n}(uTMT9mvO*FY-#~0)13II~ZcUQeJV5u7 z56}uCO)=u*>xk6HkcLFr3T-R*3nd7p(OTr!Bo#`IZd#MeVYLthHc9onV}IQuXRyA8 z0PQDm4VF+?I7Ea)R6CejA+<)gTYT@=U%_@9El7NMNl+3gCBhU)3Bqt`*=@pf=~)Qj2G71G_ACZzElF#G0c9|#O)^5_ zNCQU;lA8uet?$R~PLbI%NUSwMY*aN?eH63GLfZ&p@LDM?_8mLfSER|c> zkXm7AS1pey2Lpwna}QSzb>;8eL~!@L?poW=*U!C~&s=cH-Vr*61a8$To_gvjF1qNV zA@}!iGMe+gb~4$CYlxaLgdvcIYrVqoz?Gtq?;2&cF4To>g_RcA3T!P%D@j@yq@_tl zNs@h81~#YYd#|5#X9`(u5gAoOtgRX|ZlE2FlT~O(m+;dC-%D<1T;&0%6b9M_O*vRv zVQPhe^7ar>?glWPuU0uQbx7{iz6pav=ej<=b>1zz(vmxO$;yU~jt;uJySe9{dpPgB z^UCLB5kXtcc-|dYL98KG&;=y<{4*C^NxwcFD)8nD2A+R2BpBKuiUT?WwrgQ3AyDHb z&V*Vr0|^G!^fA!2mCY}1BB=z8W2&fXjuNXi5eV#z<7(x?Q=xLIng^J>iMevCT?#;< zrEA6cqLgqaU0}-*AFl^W&c5G-HfR`C&5ggg8b>QG-bI$Y!$DSfj2tPzCt)B>*V~=+uj)mn($vPFItB?#6UDX62o5F+!|g@M_Zj3Ly6|1O zNv8w1XDl=zh;8g$A`rxCO%^@-XQG)p4w`rHp3j4a8WSc=V9AmtLq^tyw)oT2_ftPE z3R)t~0+`Y$P_>ZWv+#fxg{JnhIj!(O)y2fF0p)0v?OyLll|{{n7)>2D#OfoY`fWPb zr}$uLFUcMoEd-G&6KP4;B0c}B1HyM99D)`GJ4pAi0B*?*jt#A_}7`qE#hQccXA zJ#XIu7~F+tk3IGn&CSi!*Vh-l-%#7glP^4qUDtzYm`Ev*o)t?27v#M4O4qV|+Oxk2 zPXx4IQDhCJM2h^6$^~32uwn+aBjVJLic`}XBh_cq^FfmC^+~$c4k8VK6*Dm+hO4AG z#lQ(2wA_*@6~$?oE|I!J$CN<-i@zJtF+pBJ!0eaByRQQ)jBGm?qo`0Cg zj??w72N)n8kJH-P$`emK!OWR6iz4fHmcGlXjZe_fQjH@Fw3K;p10d&ou*7GH=_Er1Z1A(5jH2^9jk9Fdc8p=nb)9KCRAb3zKOhki)LAwoj5H}DLa#{B zD`IE~M$w(4^LdC{KFmFPEja>#7XEf1osV>MXu}?@b7_lPvlXI{(&W2i!)wQtc2zMA ziG~Q(O;MDiN%dvvUY}&ks(w$T5!AKCh}Bvcmb+Vk6JCg3&NUY{C@8R#HfbIi;}1{W zge1+kuKqr9n1#IEX2E%Ra?+%H_vOfuBi-6^)a2FI{+qP*Chhc(3>1lp<*|1)$OVG5RJ9Wt7br~AZ9i9);G=?duZjMqnGC`u=LMcu6 z+9W;eT^i3M9jusv8FAwYJp|awn~4&L2t{0EsgW6KgiTYO#oyjo#MTWq3y(Nv?^3jX z%$zxs#~yo(cs!oJqszt*w(#x;f1$A@juZmJ6c`bSG$jdB(Hu!pWjQz)q_hPh2Xt{s z&@XP`!%JvUg06$lIuE8OF(HbN&(p0hWpX=i-9Swkf<(PV{ip=hEitsx^lwemza>r2 z2T8JNg&8$4Es2oQ*JUIy(8Ps9mB>v!G22E%==QKV=v(rmS3 zY_b#dITl(dFL(~pZaxndg(f~N3j0j!;(O!eyDf605J+>%1>^x9FY&$tmGwF+1=O_0 zsBMd5ryT}+vh;3Bv-XW1q6vd&wM9*xL46IxO$X^X;5d1uPKDXV1G%NOmjCzE%}6Qv z=2h1Z39@7ljoR8;PB`HN9(dpZlv2!`F^jk{2FJ;|MFm4(7y`o}uT5(yK?+)|6w_jz zOp10>Z)8CT99LH=GU!6zRxoPut6+s|rib2xO^8rbJG^_B``O%aAd^vOEvT-u7(Xq+ z0rRVwGSQ%34Uk^b#g=7VtXtMcUso0_1cvEm73Kakr353}8+qujH}TyYzLwu6d$0K5 zaAd8N;_0WKX8wYMcGD3vO%0|#!O=uMZLzCKm8EI-rVH<+9 zV=&+t^k+>{7-X~{D+G4#9KV4FiUzK~E_7Y!0{*%na!TP=mLv*8C``jG+#AwCdKIT! zYmQRFt!QbVM5=X32~%ruZ1+6F6g0M2G__i!(~7}9Mfa908{T(FR2f)NK}~~6qT298 z8E6~Tz@MMFg>*K}zkU0bfKT3QH`@L9$3M>7Z?0hVD~j2NDNpAWC3pPQWqh3yg)l-Dz$e5K4%Og~I*BcC%HQc&AqP}gYS*qXt9hpsI)Ygf8AAggP%sH!uFCnRm{ zbv*v!Pl(R3TU@SV2kp|cZ?U2O7){AGtY9pr8MftWbjfyIJ;c9D zE3mz8lXyass4{43v&g0uz1K6$OEDtL#Sq7|b+pcMuWta8BnY5~PLua7SDHYQEr|9S{!(PiG(p>uVK zLv_fULrR+CXdFPs4Vp(<%xDr!Sv^SWJMdq3-ple8@9rH7?z0{K_>HeIeSR7{-bBO~ z%_N3sirPH3WjG#+hc!oCDQfQR`TT_CU*@k-(pe%t<5bMdJ z-tHy&cbk+^#iV0T<#U%@$owN0($d_#cL2Wk#vjq}`kyhsJOi>C%!1q}d^ga@TeNS@ zt_p$ff4?Ya^{qVAf$EMfX-y^RF<@!BEa*CvABVm2P(D~QG^gkHay8r_)Erb8%X-Pa zk)nG58V{JusW)BBiD#e5m{B7G2+ARsU8TL7;UY|1C1q5{#$IIMgCW2x;BLYm{}d5tm?@Qb-0SfcAa1AN4km6{C9n4$1-Qp?cf+nhr}ikocVB-!(_VT3=a6cWqas*YjW%3G zM&#CwX zHs-QE`rl5oHES{I;6pj}o1bODiN`acy?wi``5ps!(c;By{J{^H1FW1~g)#*?qflPq zJ`b$WMVe~Z@|2&VJpfg}Dzp#ukoBTVgB#{mfp1?9zt1lbC>817LG}n98)6H~Rnp9) zCh|@nnWY2tY=oN8<2mf|f8waK&tU!`2eYGFwC@psw*=K%y)-Vv&!A16GvZ!z#BSPO%SGA-}9t8F~)WX5O-N>HO9AzAi`2MO#Z0W~w#> z$^av!iLFk;tG#ru)AUu>G3S)eaO{QWanRw1(okK!)7O5F0Q}4Au4DX?B}9O9wL!YY zf~Y^yUcmf4KJ0IW$0yAAQxY81o zA<>Q|+G!JeyN}eHNjj4d9XE-OediJmKlPIwIAaEe+&!ApZUuPtrcFG2*<~DK+hhO( z$Tpa$s6g6o5nL(n#?O+#?dpUmEi~kTg(5%f_rFDfm~Syy*gAD7P_+&UNxDQes7Q@+ z&v7ib*bwP;h`&34^JX8N8)2}niRovb#~Ejw!m%g(1J#kpZfDQk3h?i4xPh4~Rv^i5 z$>GwXUAWk88Bi3`BT=H$LYEQ}3ajVydy$n0zVe`|@D`Q2D0K=1QliUPtNjxIU6iX9qIGXl;mU=M z5XeDA?aCz9(q2-_vut#re%b-d`?s?=_N+6RJ$?FKE9mSNfY)r^%%ZP;l_P)*g~l%h z$Q~P`%O+hLMWj`sYa_SN+kew4%F%SOCE<&lbCGrKXXT@ARgSLFB~&t9zUoO3Lf{qw~fv2Y<(dw_!XIKU3@tFM2Z1C}kr@IdvK!OTvZ z*t;o`6JsdhE$_@JSHgE|8(mtP)ggJXDEHN25R3KflKxf&{j&0{Cd`3KN>Q~cP2!ya zjJF5a(gPc<7#$~`%86&6!HK7xMq^bK`_kCu0N;1_-DH0CE1JBhS@`^~0EDfnS<+9> zp*1)$iIH?y3J6gMQi5W1aE25$y7~ODCKPCefL1s|OqsD^gUgNRfD}pVU@| z_W|8LmdO{N$!TYv$)SfGMudHT>@t9}z`fu8Hj~$^@!j#SG}D^B4vmj?lWvR9e_#Sb zgUl#&xt57g1YIm&>LT%8`72H6A}iHeqlCbTy85rtX%k(Sp=x=Onm7B=oi3>BYwC!7 z;s}nv=t4gJiBHfR+VK1%1Mpox`x*AHe@(L&Scx*V@Eq{*6-iqDuoV>a%}%(>Mw5=a z_r(z182D3_aL}dv!BV)y=&4DD!~sYvj2?&hsx;Ma3{dk<68aoA11pg9Po2iROD^W5 zPk)-pyI5V?lVg|cvh&*V<^1}nqd0WaChQP33tx&f3t=V6yP0UdT&RRV83H9iXEbKFgS8<;Y-Ni0(m~?uGMFhvFYtH389R=a ze>|C!FTR*rbLLQGn(XIe=Xcq0fV;1{is_p+1#ZTyOTS3K|>gD8pYr|}zsM9{)C{F>SGcVW7Mz4ZYHXC` zgcySpW28q%uxkudT;f=T1wz`B#DoK66e6uLdK`>y8*6ix=zD3RYtxvUvd94ksa+Pa zf%kx|u^7ys&mrfX$C01>B;!Vo#Nb0Yc5WlIC2zgO-A5h8p`D#%OMvDFPnLOImjPZ` z0w-Z$*BaP0?z8eVUL25Vg&b7Keg`?IFnVo7Ru^Sg0Gojgz~(VynDpsSbJ9f@F=xgM z1Rwgb;}o3&etY@l%;@aIF3-S(Gapd`)RN6;S&h+SWAxZW%N!T(uTwssp9Izc?K5E_;IOT>K#(6m^ zH@)xM0IZY}arogJ`MJ+=*(X2A$O@>thN7Q)`DGqB>L?bZ(`JSZK*XcqHNfgG{O+E*T25q;F_Mr_5*JNy(335_KY+5`01x})X_&1ucS8n z21Va{!wrmIv0~U1YypF~h4a4ZYV?8y%)anK{^68UXsfHMT_vc=z5hN>U3n#kmjO3N!9AX}W+qIa z?&Om>|Kf|8KW7f6s3bT0EWjGL^Qxpq#oih+AX*psEUS^wAu7>7^WZ!U@#J z<^^RVB* Date: Mon, 9 Aug 2021 10:30:06 -0300 Subject: [PATCH 51/68] new line at the end of each file --- .../benchmark_polyhedron_8_vertices.cpp | 2 +- .../discrete_harmonic_coordinates.cpp | 2 +- .../Barycentric_coordinates_3/mean_value_coordinates.cpp | 2 +- .../examples/Barycentric_coordinates_3/shape_deformation.cpp | 2 +- .../CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h | 2 +- .../test/Barycentric_coordinates_3/test_wp_weights.cpp | 4 +++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp index 3e953e1ac376..7e1cc5155db0 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp @@ -82,4 +82,4 @@ int main(){ benchmark_overload() << " seconds" << std::endl; return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp index 9a5da54b76c6..3badf511ba2d 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp @@ -43,4 +43,4 @@ int main(){ return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp index b565ad391deb..85aacff5efa3 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp @@ -51,4 +51,4 @@ int main(){ return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp index 00961d811d3f..bf9baf87fdb4 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp @@ -91,4 +91,4 @@ int main() { out_deformed << deformed << std::endl; return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 766242c8b936..d021dba12ea1 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -422,4 +422,4 @@ namespace Barycentric_coordinates { } // namespace Barycentric_coordinates } // namespace CGAL -#endif // CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H \ No newline at end of file +#endif // CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 6dea4c0c7643..1697e7380221 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -21,6 +21,8 @@ void test_overloads() { using Mesh = CGAL::Surface_mesh; namespace PMP = CGAL::Polygon_mesh_processing; + + // Cube Mesh cube; std::vector cube_coords; @@ -81,4 +83,4 @@ int main(){ std::cout << "EPECK PASSED" << std::endl; return EXIT_SUCCESS; -} \ No newline at end of file +} From 41078527befaa47ceda1aaeebac9878e4303ce7f Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 10 Aug 2021 09:47:00 -0300 Subject: [PATCH 52/68] WP boundary coordinates can now be any polygon --- .../Barycentric_coordinates_3.txt | 2 +- .../wachspress_coordinates.cpp | 1 - .../Wachspress_coordinates_3.h | 7 +- .../internal/utils_3.h | 110 +++++++++++++----- .../test_wp_weights.cpp | 5 +- 5 files changed, 81 insertions(+), 44 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index 16cd800b58cc..4be2aebd7524 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -82,7 +82,7 @@ to the boundaries. This example shows how to compute mean value coordinates for a set of points in a star-shaped polyhedron. We note that this type of coordinate is well-defined for a -concave polyhedron and it may yield negative coordinate values for points outside +concave polyhedron but it may yield negative coordinate values for points outside the polyhedron's kernel. \anchor mv_3_coord_example \cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp index fcca4b8c1530..7292a984263d 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp @@ -25,7 +25,6 @@ int main() Surface_mesh sm; CGAL::convex_hull_3(points.begin(), points.end(), sm); - PMP::triangulate_faces(faces(sm), sm); const std::size_t number_of_vertices = num_vertices(sm); WP wp(sm, CP3::FAST_WITH_EDGE_CASES); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index f7ef8521e07d..43bee7d59f15 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -201,7 +201,7 @@ namespace Barycentric_coordinates { case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( - m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits, true); if(edge_case == internal::Edge_case::BOUNDARY) { return coordinates; @@ -305,11 +305,6 @@ namespace Barycentric_coordinates { // Iterate using the circulator do{ - // Check if it is a triangular mesh - const auto hedge = halfedge(*face_circulator, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); - CGAL_precondition(vertices.size() == 3); - // Calculate normals of faces const Vector_3 face_normal_i = internal::get_face_normal( *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 0b6ee7ba584b..9823b6ae382f 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -22,7 +22,10 @@ // Internal includes #include #include +#include #include +#include +#include namespace CGAL{ namespace Barycentric_coordinates{ @@ -203,52 +206,93 @@ template typename OutIterator, typename GeomTraits> OutIterator boundary_coordinates_3( - VertexRange vertex, + VertexRange& vertices_face, const VertexToPointMap& vertex_to_point_map, const PolygonMesh& polygon_mesh, const typename GeomTraits::Point_3& query, OutIterator coordinates, - const GeomTraits& traits){ + const GeomTraits& traits, + bool use_wp_flag){ using FT = typename GeomTraits::FT; using Plane_3 = typename GeomTraits::Plane_3; using Point_2 = typename GeomTraits::Point_2; - const auto v0 = *vertex; vertex++; - const auto v1 = *vertex; vertex++; - const auto v2 = *vertex; - - // Check if the three vertices are not identical - CGAL_assertion(v0 != v1); - CGAL_assertion(v0 != v2); - CGAL_assertion(v1 != v2); + using Wachspress = CGAL::Barycentric_coordinates::Wachspress_2; + using Wachspress_coordinates = CGAL::Barycentric_coordinates::Generalized_barycentric_coordinates_2; + using Triangle_coordinates = CGAL::Barycentric_coordinates::Triangle_coordinates_2; const FT tol = get_tolerance(); - - const Plane_3 plane_face(get(vertex_to_point_map, v0), + const std::size_t num_sides_face = vertices_face.size(); + const std::size_t num_vertices_polyhedron = num_vertices(polygon_mesh); + + // Check if the vertices are distinct + for(auto itr1 = vertices_face.begin(); itr1 != vertices_face.end(); itr1++) + for(auto itr2 = std::next(itr1, 1); itr2 != vertices_face.end(); itr2++) + CGAL_assertion(*itr1 != *itr2); + + // Create plane + auto vertex_itr = vertices_face.begin(); + const auto v0 = *vertex_itr; vertex_itr++; + const auto v1 = *vertex_itr; vertex_itr++; + const auto v2 = *vertex_itr; + const Plane_3 face_plane(get(vertex_to_point_map, v0), get(vertex_to_point_map, v1), get(vertex_to_point_map, v2)); - const Point_2 query_2d = plane_face.to_2d(query); - const Point_2 v0_2d = plane_face.to_2d(get(vertex_to_point_map, v0)); - const Point_2 v1_2d = plane_face.to_2d(get(vertex_to_point_map, v1)); - const Point_2 v2_2d = plane_face.to_2d(get(vertex_to_point_map, v2)); - - const std::array coordinates_2d = compute_triangle_coordinates_2( - v0_2d, v1_2d, v2_2d, query_2d, traits); - - const auto vd = vertices(polygon_mesh); - for(const auto& v : vd){ - - if(v == v0) - *coordinates = CGAL::abs(coordinates_2d[0]) < tol? FT(0): coordinates_2d[0]; - else if(v == v1) - *coordinates = CGAL::abs(coordinates_2d[1]) < tol? FT(0): coordinates_2d[1]; - else if(v == v2) - *coordinates = CGAL::abs(coordinates_2d[2]) < tol? FT(0): coordinates_2d[2]; - else + + // Store 2d vertices + std::vector polygon; + polygon.reserve(num_sides_face); + auto polygon_itr = std::back_inserter(polygon); + Point_2 query_2 = face_plane.to_2d(query); + for(auto v : vertices_face){ + + *polygon_itr = face_plane.to_2d(get(vertex_to_point_map, v)); + polygon_itr++; + } + + // Check convexity + CGAL_assertion(is_convex_2(polygon.begin(), polygon.end(), traits)); + + // Store 2d barycentric coordinates + std::vector bar_coords_2; + bar_coords_2.reserve(num_sides_face); + + // Use wp_2 or triangle coordinates + if(use_wp_flag){ + + Wachspress_coordinates wachspress_coordinates(polygon.begin(), polygon.end()); + wachspress_coordinates(query_2, std::back_inserter(bar_coords_2)); + } + else{ + + CGAL_assertion(polygon.size() == 3); + Triangle_coordinates triangle_coordinates(polygon[0], polygon[1], polygon[2]); + triangle_coordinates(query_2, std::back_inserter(bar_coords_2)); + } + + // Fill coordinates + CGAL_assertion(bar_coords_2.size() == num_sides_face); + for(auto& vertex_polyhedron : vertices(polygon_mesh)){ + + bool found_vertex = false; + auto bar_coords_itr = bar_coords_2.begin(); + for(auto vertex_face : vertices_face){ + + if(vertex_polyhedron == vertex_face){ + + *coordinates = CGAL::abs(*bar_coords_itr) < tol? FT(0): *bar_coords_itr; + found_vertex = true; + break; + } + bar_coords_itr++; + } + + if(!found_vertex) *coordinates = FT(0); coordinates++; } + return coordinates; } @@ -263,7 +307,8 @@ template const PolygonMesh& polygon_mesh, const typename GeomTraits::Point_3& query, OutIterator coordinates, - const GeomTraits& traits){ + const GeomTraits& traits, + const bool use_wp_flag = false){ using Vector_3 = typename GeomTraits::Vector_3; using FT = typename GeomTraits::FT; @@ -302,7 +347,8 @@ template if(CGAL::abs(perp_dist_i) < tol){ if(!boundary_flag) - boundary_coordinates_3(vertex, vertex_to_point_map, polygon_mesh, query, coordinates, traits); + boundary_coordinates_3(vertices_face, vertex_to_point_map, polygon_mesh, + query, coordinates, traits, use_wp_flag); boundary_flag = true; } else if(perp_dist_i < 0) diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp index 1697e7380221..aa00f8666e9a 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp @@ -12,6 +12,7 @@ using SCKER = CGAL::Simple_cartesian; using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel; using EPECK = CGAL::Exact_predicates_exact_constructions_kernel; +using CP3 = CGAL::Barycentric_coordinates::Computation_policy_3; template void test_overloads() { @@ -21,14 +22,10 @@ void test_overloads() { using Mesh = CGAL::Surface_mesh; namespace PMP = CGAL::Polygon_mesh_processing; - - // Cube Mesh cube; std::vector cube_coords; - std::tie(cube, cube_coords) = tests::get_hexahedron(); - PMP::triangulate_faces(faces(cube), cube); CGAL::Barycentric_coordinates::Wachspress_coordinates_3 wp_cube(cube); From c7a25d85ef4684d3d075ea75fdbe27980acd301b Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 16 Aug 2021 10:41:30 -0300 Subject: [PATCH 53/68] updated doc after first revision --- .../benchmark_polyhedron_8_vertices.cpp | 63 +++-- .../benchmark_tetrahedon_coordinates.cpp | 31 ++- .../Barycentric_coordinates_3.txt | 224 +++++++++++++----- .../PackageDescription.txt | 2 +- ...rcoord3_thumb.png => barcoord_thumb_3.png} | Bin .../fig/bc_coords_bench_3.png | Bin 0 -> 21343 bytes .../fig/bc_coords_bench_structure_3.png | Bin 0 -> 102554 bytes .../fig/mv_coords_example_3.png | Bin 0 -> 25526 bytes .../fig/shape_deform_example_3.png | Bin 0 -> 16987 bytes .../Discrete_harmonic_coordinates_3.h | 79 +++--- .../Mean_value_coordinates_3.h | 70 +++--- .../boundary_coordinates_3.h | 56 ++--- .../internal/utils_3.h | 31 ++- .../tetrahedron_coordinates.h | 28 +-- .../test_tetrahedron_coordinates.cpp | 11 +- 15 files changed, 370 insertions(+), 225 deletions(-) rename Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/{barcoord3_thumb.png => barcoord_thumb_3.png} (100%) create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_3.png create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_structure_3.png create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/mv_coords_example_3.png create mode 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/shape_deform_example_3.png diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp index 7e1cc5155db0..abc1995c9dd5 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_polyhedron_8_vertices.cpp @@ -7,6 +7,8 @@ #include #include +#include + using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; @@ -19,7 +21,9 @@ using MVC3 = CGAL::Barycentric_coordinates::Mean_value_coordinates_3 using DHC3 = CGAL::Barycentric_coordinates::Discrete_harmonic_coordinates_3; template -double benchmark_overload(){ +double benchmark_overload(const FT scale){ + + const std::size_t number_of_runs = 10; // Cube SM cube; @@ -37,8 +41,7 @@ double benchmark_overload(){ COORD bar_cube(cube); - const FT step = FT(1) / FT(100); - const FT scale = FT(100); + const FT step = FT(1) / scale; const FT limit = step*scale; std::vector bar_coordinates_cube; @@ -48,38 +51,52 @@ double benchmark_overload(){ double time = 0.0; // Sample interior points - timer.start(); - for(FT x = step; x < limit; x += step){ - for(FT y = step; y < limit; y += step){ - for(FT z = step; z < limit; z += step){ + for(std::size_t i = 0; i < number_of_runs; i++){ - const Point_3 query(x, y, z); - bar_cube(query, bar_coordinates_cube.begin()); + timer.start(); + for(FT x = step; x < limit - step; x += step){ + for(FT y = step; y < limit - step; y += step){ + for(FT z = step; z < limit - step; z += step){ + + const Point_3 query(x, y, z); + bar_cube(query, bar_coordinates_cube.begin()); + } } } + timer.stop(); + time += timer.time(); + timer.reset(); } - timer.stop(); - time += timer.time(); - timer.reset(); - return time; + const double mean_time = + time / static_cast(number_of_runs); + + return mean_time; } int main(){ - std::cout.precision(10); + std::ofstream wp_file("wp_bench.txt"); + std::ofstream dh_file("dh_bench.txt"); + std::ofstream mv_file("mv_bench.txt"); - std::cout << "Wachspress : " << std::endl; - std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << - benchmark_overload() << " seconds" << std::endl; + std::ofstream num_file("num_bench.txt"); - std::cout << "Discrete Harmonic : " << std::endl; - std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << - benchmark_overload() << " seconds" << std::endl; + for(std::size_t i = 5; i <= 100; i += 5){ + + wp_file << benchmark_overload(i) << std::endl; + dh_file << benchmark_overload(i) << std::endl; + mv_file << benchmark_overload(i) << std::endl; + + num_file << i << std::endl; + + std::cout << i << "\% complete" << std::endl; + } - std::cout << "Mean Value : " << std::endl; - std::cout << "benchmark_polyhedron_8_vertices (CPU time): " << - benchmark_overload() << " seconds" << std::endl; + wp_file.close(); + dh_file.close(); + mv_file.close(); + num_file.close(); return EXIT_SUCCESS; } diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp index 7999673f8845..797a1890681d 100644 --- a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/benchmark_tetrahedon_coordinates.cpp @@ -13,6 +13,8 @@ int main() { const std::size_t number_of_y_coordinates = 100; const std::size_t number_of_z_coordinates = 100; + const std::size_t number_of_runs = 10; + const FT zero = FT(0); const FT one = FT(1); const FT four = FT(4); @@ -28,27 +30,32 @@ int main() { Timer timer; std::vector coordinates(4); - double time = 0.0; - timer.start(); - for (FT x = zero; x <= one; x += x_step){ - for (FT y = zero; y <= one; y += y_step){ - for (FT z = zero; z <= one; z += z_step){ + for(std::size_t i = 0; i < number_of_runs; i++){ - const Point_3 query(x, y, z); - CGAL::Barycentric_coordinates::tetrahedron_coordinates( - p0, p1, p2, p3, query, coordinates.begin()); + timer.start(); + for (FT x = zero; x <= one; x += x_step){ + for (FT y = zero; y <= one; y += y_step){ + for (FT z = zero; z <= one; z += z_step){ + + const Point_3 query(x, y, z); + CGAL::Barycentric_coordinates::tetrahedron_coordinates( + p0, p1, p2, p3, query, coordinates.begin()); + } } } + + timer.stop(); + time += timer.time(); + timer.reset(); } - timer.stop(); - time += timer.time(); - timer.reset(); + const double mean_time = + time / static_cast(number_of_runs); std::cout.precision(10); std::cout << "benchmark_tretrahedron_coordinates (CPU time): " << - time << " seconds" << std::endl; + mean_time << " seconds" << std::endl; return EXIT_SUCCESS; } diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index 4be2aebd7524..0c4a4ea90bda 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -11,37 +11,37 @@ namespace Barycentric_coordinates { \section gbc_3_introduction Introduction -Barycentric coordinates are an important tool in computer graphics and geometric modeling. +Barycentric coordinates are an important tool in computer graphics and geometric modeling. Originally, these coordinates were used to represent a given point with respect to a simplex but have been later generalized to more complex shapes. -The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation +The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation of three-dimensional closed-form generalized barycentric coordinates defined for convex simplicial polytopes. In particular, this package includes an implementation of Wachspress, discrete harmonic, mean value, and - one extra function to calculate barycentric coordinates with respect to tetrahedrons. + one extra function to calculate barycentric coordinates with respect to tetrahedra. In this implementation, + we restrict our polyhedra to convex ones with triangular faces, although some of the coordinates may + accept more general shapes. \section gbc_3_interface Software Design -Wachspress, discrete harmonic, and mean value coordinates are all generalized barycentric coordinates -that can be computed analytically. In this implementation, we restrict our polyhedra to convex ones -with triangular faces, although some of the coordinates may accept more general shapes. +Wachspress, discrete harmonic, and mean value coordinates are all generalized barycentric coordinates +that can be computed analytically. +All of the three analytic coordinates can be computed either by instantiating a class or through a +free function. Tetrahedron coordinates can be computed only through the free function. -All of the three barycentric coordinates can be computed either by instantiating a class or through a -free function. Tetrahedron coordinates can be computed only through the free function. - -We can specify a computation policy Barycentric_coordinates::Computation_policy_3 that can be FAST or +We can specify a computation policy Barycentric_coordinates::Computation_policy_3 that can be FAST or FAST_WITH_EDGE_CASES for each of the three coordinates. The difference between them is that FAST_WITH_EDGE_CASES treats points near the boundaries by projecting them into the face of the polyhedron and then calculating triangle coordinates. The output of a query point is the coordinate value with respect to each vertex, the number of coordinate - values is the same as the number of vertices, and the ordering is also the same. + values being the same as the number of vertices, and the ordering is also the same. -All class and function templates are parameterized by a traits class, which is a model of the concept -BarycentricTraits_3. It provides all necessary geometric primitives, predicates, and constructions, -which are required for the computation. All models of Kernel can be used. A polyhedron is provided as -a model of the concept FaceListGraph with a property map that maps a vertex from the polyhedron to CGAL::Point_3. +All class and function templates are parameterized by a traits class, which is a model of the concept +BarycentricTraits_3. It provides all necessary geometric primitives, predicates, and constructions, +which are required for the computation. All models of Kernel can be used. A polyhedron is provided as +a model of the concept FaceListGraph with a property map that maps a vertex from the polyhedron to BarycentricTraits_3::Point_3. \section gbc_3_examples Examples @@ -52,97 +52,209 @@ with a basic usage of different barycentric components. \subsection tetra_example Tetrahedron Coordinates -In this example, we use the global function tetrahedron_coordinates_in_tuple(). -We compute coordinates for the tetrahedron whose vertices are the points in the -set {(0,0,0), (1,0,0), (0,1,0), (0,0,1)}. Were used points inside, outside and +In this example, we use the global function tetrahedron_coordinates_in_tuple(). +We compute coordinates for the tetrahedron whose vertices are the points in the +set {(0,0,0), (1,0,0), (0,1,0), (0,0,1)}. We use points inside, outside and at the boundary of the tetrahedron. \anchor tetra_coord_example \cgalExample{Barycentric_coordinates_3/tetrahedron_coordinates.cpp} \subsection wp_3_example Wachspress Coordinates -In this example, we generate 250 random points inside a unitary sphere, -centered at the origin, then we take the convex hull of this set of points -and use this as our polyhedron. Finally, we calculate Wachspress coordinates +In this example, we generate 250 random points inside a unit sphere, +centered at the origin, then we take the convex hull of this set of points +and use this as our polyhedron. Finally, we calculate Wachspress coordinates for all of these 250 points. \anchor wp_3_coord_example \cgalExample{Barycentric_coordinates_3/wachspress_coordinates.cpp} \subsection dh_3_example Discrete Harmonic Coordinates -This example is very similar to the one used with tetrahedron coordinates. -We started with a regular icosahedron and for points inside, outside and -at the boundary, we calculate discrete harmonic coordinates. In this example, -we use the fast with edge cases algorithm because it treats points very close +This example is very similar to the one used with tetrahedron coordinates. +We start with a regular icosahedron and for points inside, outside and +at the boundary, we calculate discrete harmonic coordinates. In this example, +we use the fast with edge cases algorithm because it treats points very close to the boundaries. \anchor dh_3_coord_example \cgalExample{Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp} \subsection mv_3_example Mean Value Coordinates -This example shows how to compute mean value coordinates for a set of points in a -star-shaped polyhedron. We note that this type of coordinate is well-defined for a -concave polyhedron but it may yield negative coordinate values for points outside -the polyhedron's kernel. +This example shows how to compute mean value coordinates for a set of points in a +star-shaped polyhedron. We note that this type of coordinate is well-defined for a +concave polyhedron but it may yield negative coordinate values for points outside +the polyhedron's kernel (shown in blue). + \anchor mv_3_coord_example +\cgalFigureBegin{mv_3_example, mv_coords_example_3.png} +Example's point pattern. +\cgalFigureEnd + \cgalExample{Barycentric_coordinates_3/mean_value_coordinates.cpp} -\subsection shape_deform_example Shape deformation +\subsection shape_deform_example_3 Shape deformation -This is an example that shows how to deform a simple smooth sphere into -another shape, topologically equivalent. To achieve this, we enclose the -sphere inside a cube cage, then we calculate the barycentric -coordinate of each vertex with respect to the cube cage. After deforming -the cage, we calculate the linear combination of the points to get the +This is an example that shows how to deform a simple smooth sphere into +another shape, topologically equivalent. To achieve this, we enclose the +sphere inside a cube cage, then we calculate the barycentric +coordinate of each vertex with respect to the cube cage. After deforming +the cage, we calculate the linear combination of the points to get the deformed sphere vertices. -\anchor shape_deform_example_3 + +\anchor shape_deform_example +\cgalFigureBegin{shape_deform_example_3, shape_deform_example_3.png} +The shape on the left is deformed into the shape on the right. +\cgalFigureEnd + \cgalExample{Barycentric_coordinates_3/shape_deformation.cpp} \section gbc_3_degeneracies Edge Cases -The precision of each coordinate depends on the used `Kernel`. If an inexact kernel -is used and the user is not sure if the points are near the boundaries, `FAST_WITH_EDGE_CASES` +The precision of each coordinate depends on the used `Kernel`. If an inexact kernel +is used and the user is not sure if the points are near the boundaries, `FAST_WITH_EDGE_CASES` algorithm should be used. -Implementation details are describe in \cgalCite{cgal:bc:f-wmvc-14} for Wachspress and mean value +Implementation details are described in \cgalCite{cgal:bc:f-wmvc-14} for Wachspress and mean value coordinates, and in \cgalCite{cgal:bc:jlw-ggcccsp-07} for discrete harmonic coordinates. +The main purpose of the FAST_WITH_EDGE_CASES algorithm is to extend the region where the analytical +coordinates are well-defined. It adds the guarantee to calculate points near the boundaries. The +way it works is very simple: before calculating any coordinate, the algorithm checks, for each face, +if the distance between the query point and the plane is less than one predetermined tolerance. If so, +instead of calculating the analytical form of the coordinates, it decomposes the query point with +respect to this particular face and then calculates triangle coordinates. However, for Wachspress +coordinates, the 2D version is used because the faces are not necessarily triangular. Note that for +every vertice that does not belong to this face, the coordinate value will be zero. + +\subsection gbc_3_degeneracies_tetrahedron Tetrahedron Coordinates + +To compute tetrahedron coordinates of the query point `q`, we adopt the simple formula: +

+\f$w_i = \frac{V_i}{V}\f$ +
+ +where \f$V_i\f$ is the signed volume of the sub-tetrahedron opposite to the vertex \f$i\f$ and \f$V\f$ +is the total volume of the tetrahedron, that is \f$V = V_0 + V_1 + V_2 + V_3\f$. + +These coordinates can be computed exactly if an exact number type is chosen, for any query point +in the space and with respect to any non-degenerate tetrahedron. + +\subsection gbc_3_degeneracies_wachspress Wachspress Coordinates + +To compute Wachspress coordinates of the query point `q`, we adopt the simple formula: +
+\f$w_v({q}) = \sum_{i=2}^{k-1}det({p_{f_1}}({q}), {p_{f_i}}({q}), {p_{f_{i+1}}}({q}))\f$ +
+ +where \f${p_f}({q}) = \frac{{n_f}}{h_f({q})}\f$, \f$h_f({q}) = ({v} - {q})\cdot {n_f}\f$ denote the +perpendicular distance of \f$q\f$ to \f$f\f$, and for the face \f$f\f$, let \f${n_f}\f$ denote its unit outward normal. + +In this implementation, Wachspress coordinates are well defined in the closure of any convex +polyhedra. If an exact number type is chosen, they are computed exactly. + +\subsection gbc_3_degeneracies_discrete_harmonic Discrete Harmonic Coordinates + +To compute discrete harmonic coordinates of the query point `q`, we adopt the simple formula: +
+\f$w_i = \sum_{v_i \in T} \frac{cot[\theta_i^T]h_i^T}{2}\f$ +
+ +where, within a triangle face T = {v1, v2, v3}, \f$\theta_i^T\f$ is the dihedral angle between T and +triangle {x,\f$v_{i+1}\f$, \f$v_{i−1}\f$}, and \f$h_i^T\f$ is the edge length \f$|v_{i+1} − v_{i−1}|\f$. + +Discrete harmonic coordinates cannot be computed exactly due to a square root operation. +Although, if an exact number type is used, the default precision of the computation depends only on +two CGAL functions: CGAL::to_double() and CGAL::sqrt(). In this implementation, discrete harmonic +coordinates are well defined in the closure of any convex polyhedra with triangular faces. Unlike +Wachspress coordinates, they are not necessarily positive. + +\subsection gbc_3_degeneracies_mean_value Mean Value Coordinates + +To compute mean value coordinates of the query point `q`, we adopt the simple formula: +
+\f$w_i = \frac{1}{2}\sum_{j=1}^3 \beta_j\frac{{m_j}\cdot {m_{i+1}}}{{e_i}\cdot {m_{i+1}}}\f$ +
+ +where a vertex v is projected to the point (unit vector) \f$e_v = \frac{({v} - {q})}{|{v - q}|}\f$, and +\f${m_i} = \frac{{e_i} \times {e_{i+1}}}{|{e_i} \times {e_{i+1}}|}\f$. \f$\beta_j \in (0, \pi)\f$ is the angle +between \f$e_i\f$ and \f$e_{i+1}\f$. + +Like discrete harmonic, mean value coordinates cannot be computed exactly due to a square root +operation. In this implementation, mean value coordinates are well defined everywhere in the space, +but just for polyhedra with triangular faces. Also, they are non-negative in the kernel of a +star-shaped polyhedron. + \section gbc_3_performance Performance -Here are the results of the benchmark done for each coordinate. A total of 1000000 -points were sampled regularly inside each shape. +Efficiency is really important in this implementation. +These coordinates are used in applications that require +calculations for millions of points, thus developing metrics +to evaluate performance is absolutely necessary. In this section, +we present benchmark results for each algorithm. + +To make the benchmark and evaluate runtimes, for each analytic coordinate, +we regularly sample approximately $n^3$ ($n$ varying from 1 to 100) strictly +interior points with respect to a cube with unit length, and then calculate +its coordinate values (see figure bellow). The results are represented in a log-log +scale plot and are the mean value of 10 loop iterations (see plot bellow). + +\cgalFigureBegin{bc_coords_bench_structure_3, bc_coords_bench_structure_3.png} +Points shown in red are the sample points used to make the benchmark. +\cgalFigureEnd + +The performance strongly depends on the chosen kernel, +for this test we choose to use SCKER because is much faster than others. +Also, we can see that time (wp) << time (dh) < time (mv). This happens because wp +implementation has fewer instructions per loop than other two, so naturally +the computation time tends to be faster. + +\cgalFigureBegin{bc_coords_bench_3, bc_coords_bench_3.png} +Time in seconds to compute $n^3$ coordinate values for a cube. Are represented in the graph: +Wachspress (blue), discrete harmonic (orange) and mean value (green). +\cgalFigureEnd + +Tetrahedron coordinates are not shown in the same plot because the test is +slightly different. For this one, we simply show in a table (see table bellow) +the results for some pre-defined quantity of points. The test is done by regularly +sampling strictly interior points with respect to a tetrahedron with +unit sides that lies on the coordinate axis. - - + - - - + + + + + + + + + + - - - + + - - - + + - - - + +
Coordinate type Shape Number of points Total time (in seconds)
WachspressTriangulated cube59.178366910000.002662682533
500000.09459688663
1000000.1865084648
Discrete HarmonicTriangulated cube167.8320635000000.8238497972
Mean ValueTriangulated cube199.65199410000001.665773582
TetrahedronTetrahedron1.61201810850000008.359417105
+To benchmark each coordinate, we used a 2.6 GHz Intel Core i7 processor (6 cores) +and 16 GB DDR4 2933MHz memory. The installed operating system was Ubuntu 20.04 LTS. \section gbc_3_history History -This package was introduced during GSoC 2021 and implemented by Antonio Gomes +This package was introduced during GSoC 2021 and implemented by Antonio Gomes under the supervision of Dmitry Anisimov. \section gbc_3_acknowledgments Acknowledgments diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt index 533223a6a8d6..66cc3e9fe199 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/PackageDescription.txt @@ -22,7 +22,7 @@ Free functions to compute barycentric coordinates. \addtogroup PkgBarycentricCoordinates3Ref \cgalPkgDescriptionBegin{3D Generalized Barycentric Coordinates, PkgBarycentricCoordinates3} -\cgalPkgPicture{barcoord3_thumb.png} +\cgalPkgPicture{barcoord_thumb_3.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Antonio Gomes, Dmitry Anisimov} diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord3_thumb.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb_3.png similarity index 100% rename from Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord3_thumb.png rename to Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/barcoord_thumb_3.png diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_3.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_3.png new file mode 100644 index 0000000000000000000000000000000000000000..3643b8800f82d6af5ef71fc7f6a10ee656440acb GIT binary patch literal 21343 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 Z>x+l9mn+Dx4d4ez`vT@X>Fmv*{{j^a3i1E| literal 0 HcmV?d00001 diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_structure_3.png b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/fig/bc_coords_bench_structure_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7a528454bc9046378eeb261f10cd3445ce9c4dac GIT binary patch literal 102554 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::vertex_descriptor as - key type and Point_3 as value type. The default is `property_map_selector::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. */ template< - typename PolygonMesh, + typename TriangleMesh, typename GeomTraits, - typename VertexToPointMap = typename property_map_selector::const_type> class Discrete_harmonic_coordinates_3 { @@ -59,7 +59,7 @@ namespace Barycentric_coordinates { /// @{ /// \cond SKIP_IN_MANUAL - using Polygon_mesh = PolygonMesh; + using Triangle_mesh = TriangleMesh; using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; @@ -89,8 +89,8 @@ namespace Barycentric_coordinates { This class implements the behavior of discrete harmonic coordinates for 3D query points. - \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a convex simplicial polyhedron \param policy one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; @@ -101,19 +101,19 @@ namespace Barycentric_coordinates { the default initialization is provided \param vertex_to_point_map - an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + an instance of `VertexToPointMap` that maps a vertex from `triangle_mesh` to `Point_3`; the default initialization is provided - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is strongly convex. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is strongly convex. + \pre triangle_mesh is simplicial. */ Discrete_harmonic_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Computation_policy_3 policy, const VertexToPointMap vertex_to_point_map, const GeomTraits traits = GeomTraits()) : - m_polygon_mesh(polygon_mesh), + m_triangle_mesh(triangle_mesh), m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), m_traits(traits), @@ -123,21 +123,21 @@ namespace Barycentric_coordinates { sqrt(internal::Get_sqrt::sqrt_object(m_traits)){ // Check if polyhedron is strongly convex - CGAL_assertion(is_strongly_convex_3(m_polygon_mesh, m_traits)); - m_weights.resize(vertices(m_polygon_mesh).size()); + CGAL_assertion(is_strongly_convex_3(m_triangle_mesh, m_traits)); + m_weights.resize(vertices(m_triangle_mesh).size()); } /// @} Discrete_harmonic_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Computation_policy_3 policy = Computation_policy_3::FAST_WITH_EDGE_CASES, const GeomTraits traits = GeomTraits()) : Discrete_harmonic_coordinates_3( - polygon_mesh, + triangle_mesh, policy, - get_const_property_map(CGAL::vertex_point, polygon_mesh), + get_const_property_map(CGAL::vertex_point, triangle_mesh), traits) { } /// \name Access @@ -176,7 +176,7 @@ namespace Barycentric_coordinates { /// @} private: - const PolygonMesh& m_polygon_mesh; + const TriangleMesh& m_triangle_mesh; const Computation_policy_3 m_computation_policy; const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; @@ -201,7 +201,7 @@ namespace Barycentric_coordinates { case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( - m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + m_vertex_to_point_map, m_triangle_mesh, query, coordinates, m_traits); if(edge_case == internal::Edge_case::BOUNDARY) { return coordinates; @@ -220,7 +220,7 @@ namespace Barycentric_coordinates { } default:{ - internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + internal::get_default(vertices(m_triangle_mesh).size(), coordinates); return coordinates; } } @@ -236,7 +236,7 @@ namespace Barycentric_coordinates { CGAL_assertion(sum != FT(0)); // The coordinates must be saved in the same order as vertices in the vertex range. - const auto vd = vertices(m_polygon_mesh); + const auto vd = vertices(m_triangle_mesh); CGAL_assertion(m_weights.size() == vd.size()); for (std::size_t vi = 0; vi < vd.size(); vi++) { @@ -256,7 +256,7 @@ namespace Barycentric_coordinates { // Vertex index. std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); + const auto vd = vertices(m_triangle_mesh); for (const auto& vertex : vd) { @@ -280,10 +280,10 @@ namespace Barycentric_coordinates { const Point_3 vertex_val = get(m_vertex_to_point_map, vertex); // Circulator of faces around the vertex - CGAL::Face_around_target_circulator - face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); + CGAL::Face_around_target_circulator + face_circulator(halfedge(vertex, m_triangle_mesh), m_triangle_mesh); - CGAL::Face_around_target_circulator + CGAL::Face_around_target_circulator face_done(face_circulator); // Compute weight w_v @@ -293,8 +293,8 @@ namespace Barycentric_coordinates { do{ //Vertices around face iterator - const auto hedge = halfedge(*face_circulator, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + const auto hedge = halfedge(*face_circulator, m_triangle_mesh); + const auto vertices = vertices_around_face(hedge, m_triangle_mesh); auto vertex_itr = vertices.begin(); CGAL_precondition(vertices.size() == 3); @@ -326,7 +326,7 @@ namespace Barycentric_coordinates { m_construct_vector_3(query, point1)); const Vector_3 face_normal = internal::get_face_normal( - *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); + *face_circulator, m_vertex_to_point_map, m_triangle_mesh, m_traits); FT cot_dihedral = internal::cot_dihedral_angle( face_normal, normal_query, m_traits); @@ -347,7 +347,6 @@ namespace Barycentric_coordinates { }; - /*! \ingroup PkgBarycentricCoordinates3RefFunctions @@ -368,14 +367,14 @@ namespace Barycentric_coordinates { \tparam Point_3 A model of `Kernel::Point_3`. - \tparam PolygonMesh + \tparam TriangleMesh must be a model of the concept `FaceListGraph`. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -390,16 +389,16 @@ namespace Barycentric_coordinates { \return an output iterator to the element in the destination range, one past the last coordinate stored - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is strongly convex. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is strongly convex. + \pre triangle_mesh is simplicial. */ template< typename Point_3, - typename PolygonMesh, + typename TriangleMesh, typename OutIterator> OutIterator discrete_harmonic_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -407,7 +406,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Discrete_harmonic_coordinates_3 discrete_harmonic(polygon_mesh, policy); + Discrete_harmonic_coordinates_3 discrete_harmonic(triangle_mesh, policy); return discrete_harmonic(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index d021dba12ea1..56f1ba0eea40 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -35,21 +35,21 @@ namespace Barycentric_coordinates { non-negative in the kernel of a star-shaped polyhedron. The coordinates are computed analytically. - \tparam PolygonMesh + \tparam TriangleMesh must be a model of the concept `FaceListGraph`. \tparam GeomTraits a model of `BarycentricTraits_3` \tparam VertexToPointMap - a property map with boost::graph_traits::vertex_descriptor as - key type and Point_3 as value type. The default is `property_map_selector::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. */ template< - typename PolygonMesh, + typename TriangleMesh, typename GeomTraits, - typename VertexToPointMap = typename property_map_selector::const_type> class Mean_value_coordinates_3 { @@ -59,7 +59,7 @@ namespace Barycentric_coordinates { /// @{ /// \cond SKIP_IN_MANUAL - using Polygon_mesh = PolygonMesh; + using Triangle_mesh = TriangleMesh; using Geom_Traits = GeomTraits; using Vertex_to_point_map = VertexToPointMap; @@ -90,8 +90,8 @@ namespace Barycentric_coordinates { This class implements the behavior of mean value coordinates for 3D query points. - \param polygon_mesh - an instance of `PolygonMesh`, which must be a simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a simplicial polyhedron \param policy one of the `CGAL::Barycentric_coordinates::Computation_policy_3`; @@ -102,18 +102,18 @@ namespace Barycentric_coordinates { the default initialization is provided \param vertex_to_point_map - an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + an instance of `VertexToPointMap` that maps a vertex from `triangle_mesh` to `Point_3`; the default initialization is provided - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is simplicial. */ Mean_value_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Computation_policy_3 policy, const VertexToPointMap vertex_to_point_map, const GeomTraits traits = GeomTraits()) : - m_polygon_mesh(polygon_mesh), + m_triangle_mesh(triangle_mesh), m_computation_policy(policy), m_vertex_to_point_map(vertex_to_point_map), m_traits(traits), @@ -123,7 +123,7 @@ namespace Barycentric_coordinates { sqrt(internal::Get_sqrt::sqrt_object(m_traits)), m_approximate_angle_3(m_traits.compute_approximate_angle_3_object()) { - m_weights.resize(vertices(m_polygon_mesh).size()); + m_weights.resize(vertices(m_triangle_mesh).size()); query_vertex_vectors.resize(3); unit_vectors.resize(3); m_vectors.resize(3); @@ -133,14 +133,14 @@ namespace Barycentric_coordinates { /// @} Mean_value_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Computation_policy_3 policy = Computation_policy_3::FAST_WITH_EDGE_CASES, const GeomTraits traits = GeomTraits()) : Mean_value_coordinates_3( - polygon_mesh, + triangle_mesh, policy, - get_const_property_map(CGAL::vertex_point, polygon_mesh), + get_const_property_map(CGAL::vertex_point, triangle_mesh), traits) { } /// \name Access @@ -178,7 +178,7 @@ namespace Barycentric_coordinates { /// @} private: - const PolygonMesh& m_polygon_mesh; + const TriangleMesh& m_triangle_mesh; const Computation_policy_3 m_computation_policy; const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 const GeomTraits m_traits; @@ -209,7 +209,7 @@ namespace Barycentric_coordinates { case Computation_policy_3::FAST_WITH_EDGE_CASES:{ // Calculate query position relative to the polyhedron const auto edge_case = internal::locate_wrt_polyhedron( - m_vertex_to_point_map, m_polygon_mesh, query, coordinates, m_traits); + m_vertex_to_point_map, m_triangle_mesh, query, coordinates, m_traits); if(edge_case == internal::Edge_case::BOUNDARY) { return coordinates; @@ -228,7 +228,7 @@ namespace Barycentric_coordinates { } default:{ - internal::get_default(vertices(m_polygon_mesh).size(), coordinates); + internal::get_default(vertices(m_triangle_mesh).size(), coordinates); return coordinates; } } @@ -244,7 +244,7 @@ namespace Barycentric_coordinates { CGAL_assertion(sum != FT(0)); // The coordinates must be saved in the same order as vertices in the vertex range. - const auto vd = vertices(m_polygon_mesh); + const auto vd = vertices(m_triangle_mesh); CGAL_assertion(m_weights.size() == vd.size()); for (std::size_t vi = 0; vi < vd.size(); vi++) { @@ -264,7 +264,7 @@ namespace Barycentric_coordinates { // Vertex index. std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); + const auto vd = vertices(m_triangle_mesh); for (const auto& vertex : vd) { @@ -289,10 +289,10 @@ namespace Barycentric_coordinates { const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); // Circulator of faces around the vertex - CGAL::Face_around_target_circulator - face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); + CGAL::Face_around_target_circulator + face_circulator(halfedge(vertex, m_triangle_mesh), m_triangle_mesh); - CGAL::Face_around_target_circulator + CGAL::Face_around_target_circulator face_done(face_circulator); // Compute weight w_v @@ -302,8 +302,8 @@ namespace Barycentric_coordinates { do{ // Vertices around face iterator - const auto hedge = halfedge(*face_circulator, m_polygon_mesh); - const auto vertices = vertices_around_face(hedge, m_polygon_mesh); + const auto hedge = halfedge(*face_circulator, m_triangle_mesh); + const auto vertices = vertices_around_face(hedge, m_triangle_mesh); auto vertex_itr = vertices.begin(); CGAL_precondition(vertices.size() == 3); @@ -377,14 +377,14 @@ namespace Barycentric_coordinates { \tparam Point_3 A model of `Kernel::Point_3`. - \tparam PolygonMesh + \tparam TriangleMesh must be a model of the concept `FaceListGraph`. \tparam OutIterator a model of `OutputIterator` that accepts values of type `GeomTraits::FT` - \param polygon_mesh - an instance of `PolygonMesh`, which must be a simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a simplicial polyhedron \param query a query point @@ -399,15 +399,15 @@ namespace Barycentric_coordinates { \return an output iterator to the element in the destination range, one past the last coordinates stored - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is simplicial. */ template< typename Point_3, - typename PolygonMesh, + typename TriangleMesh, typename OutIterator> OutIterator mean_value_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Point_3& query, OutIterator c_begin, const Computation_policy_3 policy = @@ -415,7 +415,7 @@ namespace Barycentric_coordinates { using Geom_Traits = typename Kernel_traits::Kernel; - Mean_value_coordinates_3 mean_value(polygon_mesh, policy); + Mean_value_coordinates_3 mean_value(triangle_mesh, policy); return mean_value(query, c_begin); } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h index 163553b8f729..314d8be11d2c 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h @@ -37,7 +37,7 @@ namespace Barycentric_coordinates{ coordinates are triangle coordinates, while all other coordinates are set to zero. If `query` is not on the boundary, all the coordinates are set to zero. - \tparam PolygonMesh + \tparam TriangleMesh must be a model of the concept `FaceListGraph`. \tparam GeomTraits @@ -47,12 +47,12 @@ namespace Barycentric_coordinates{ a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \tparam VertexToPointMap - a property map with boost::graph_traits::vertex_descriptor as - key type and Point_3 as value type. The default is `property_map_selector::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. - \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -65,35 +65,35 @@ namespace Barycentric_coordinates{ the default initialization is provided \param vertex_to_point_map - an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + an instance of `VertexToPointMap` that maps a vertex from `triangle_mesh` to `Point_3`; the default initialization is provided \return an output iterator to the element in the destination range, one past the last coordinate stored + the flag indicating whether the query point belongs to the polyhedron boundary - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is simplicial. */ template< - typename PolygonMesh, + typename TriangleMesh, typename OutIterator, typename GeomTraits, typename VertexToPointMap> std::pair boundary_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const typename GeomTraits::Point_3& query, OutIterator c_begin, const GeomTraits& traits, const VertexToPointMap vertex_to_point_map) { const auto edge_case = internal::locate_wrt_polyhedron( - vertex_to_point_map, polygon_mesh, query, c_begin, traits); + vertex_to_point_map, triangle_mesh, query, c_begin, traits); if(edge_case == internal::Edge_case::BOUNDARY) return {c_begin, true}; else{ - internal::get_default(num_vertices(polygon_mesh), c_begin); + internal::get_default(num_vertices(triangle_mesh), c_begin); return {c_begin, false}; } } @@ -113,7 +113,7 @@ namespace Barycentric_coordinates{ coordinates are triangle coordinates, while all other coordinates are set to zero. If `query` is not on the boundary, all the coordinates are set to zero. - \tparam PolygonMesh + \tparam TriangleMesh must be a model of the concept `FaceListGraph`. \tparam Point_3 @@ -123,12 +123,12 @@ namespace Barycentric_coordinates{ a model of `OutputIterator` that accepts values of type `GeomTraits::FT` \tparam VertexToPointMap - a property map with boost::graph_traits::vertex_descriptor as - key type and Point_3 as value type. The default is `property_map_selector::vertex_descriptor as + key type and Point_3 as value type. The default is `property_map_selector`. - \param polygon_mesh - an instance of `PolygonMesh`, which must be a convex simplicial polyhedron + \param triangle_mesh + an instance of `TriangleMesh`, which must be a convex simplicial polyhedron \param query a query point @@ -137,24 +137,24 @@ namespace Barycentric_coordinates{ the beginning of the destination range with the computed coordinates \param vertex_to_point_map - an instance of `VertexToPointMap` that maps a vertex from `polygon_mesh` to `Point_3`; + an instance of `VertexToPointMap` that maps a vertex from `triangle_mesh` to `Point_3`; the default initialization is provided \return an output iterator to the element in the destination range, one past the last coordinate stored + the flag indicating whether the query point belongs to the polyhedron boundary - \pre num_vertices(polygon_mesh) >= 4. - \pre polygon_mesh is simplicial. + \pre num_vertices(triangle_mesh) >= 4. + \pre triangle_mesh is simplicial. */ template< - typename PolygonMesh, + typename TriangleMesh, typename Point_3, typename OutIterator, - typename VertexToPointMap = typename property_map_selector::const_type> std::pair boundary_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Point_3& query, OutIterator c_begin, const VertexToPointMap vertex_to_point_map){ @@ -162,20 +162,20 @@ namespace Barycentric_coordinates{ using GeomTraits = typename Kernel_traits::Kernel; const GeomTraits traits; - return boundary_coordinates_3(polygon_mesh, query, c_begin, traits, vertex_to_point_map); + return boundary_coordinates_3(triangle_mesh, query, c_begin, traits, vertex_to_point_map); } template< - typename PolygonMesh, + typename TriangleMesh, typename Point_3, typename OutIterator> std::pair boundary_coordinates_3( - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const Point_3& query, OutIterator c_begin){ - return boundary_coordinates_3(polygon_mesh, query, c_begin, - get_const_property_map(CGAL::vertex_point, polygon_mesh)); + return boundary_coordinates_3(triangle_mesh, query, c_begin, + get_const_property_map(CGAL::vertex_point, triangle_mesh)); } } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 9823b6ae382f..6afd0b7d2d63 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -147,20 +147,20 @@ template template< typename Face, typename VertexToPointMap, - typename PolygonMesh, + typename TriangleMesh, typename GeomTraits> typename GeomTraits::Vector_3 get_face_normal( const Face& face, const VertexToPointMap& vertex_to_point_map, - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const GeomTraits& traits){ using Point_3 = typename GeomTraits::Point_3; using Vector_3 = typename GeomTraits::Vector_3; const auto& cross_3 = traits.construct_cross_product_vector_3_object(); - const auto hedge = halfedge(face, polygon_mesh); - const auto vertices = vertices_around_face(hedge, polygon_mesh); + const auto hedge = halfedge(face, triangle_mesh); + const auto vertices = vertices_around_face(hedge, triangle_mesh); CGAL_precondition(vertices.size() >= 3); auto vertex = vertices.begin(); @@ -199,16 +199,27 @@ template return approximate_dot_3/approximate_cross_3_length; } + template + inline bool are_vertices_distinct(VertexRange& vertices_face){ + + // Check if the vertices are distinct + for(auto itr1 = vertices_face.begin(); itr1 != vertices_face.end(); itr1++) + for(auto itr2 = std::next(itr1, 1); itr2 != vertices_face.end(); itr2++) + if(*itr1 == *itr2) return false; + + return true; + } + template< typename VertexRange, typename VertexToPointMap, - typename PolygonMesh, + typename TriangleMesh, typename OutIterator, typename GeomTraits> OutIterator boundary_coordinates_3( VertexRange& vertices_face, const VertexToPointMap& vertex_to_point_map, - const PolygonMesh& polygon_mesh, + const TriangleMesh& triangle_mesh, const typename GeomTraits::Point_3& query, OutIterator coordinates, const GeomTraits& traits, @@ -224,12 +235,10 @@ template const FT tol = get_tolerance(); const std::size_t num_sides_face = vertices_face.size(); - const std::size_t num_vertices_polyhedron = num_vertices(polygon_mesh); + const std::size_t num_vertices_polyhedron = num_vertices(triangle_mesh); // Check if the vertices are distinct - for(auto itr1 = vertices_face.begin(); itr1 != vertices_face.end(); itr1++) - for(auto itr2 = std::next(itr1, 1); itr2 != vertices_face.end(); itr2++) - CGAL_assertion(*itr1 != *itr2); + CGAL_assertion(are_vertices_distinct(vertices_face)); // Create plane auto vertex_itr = vertices_face.begin(); @@ -272,7 +281,7 @@ template // Fill coordinates CGAL_assertion(bar_coords_2.size() == num_sides_face); - for(auto& vertex_polyhedron : vertices(polygon_mesh)){ + for(auto& vertex_polyhedron : vertices(triangle_mesh)){ bool found_vertex = false; auto bar_coords_itr = bar_coords_2.begin(); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index 089e9d41f69e..ebbf29aeb912 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -65,7 +65,7 @@ namespace Barycentric_coordinates{ \return an output iterator to the element in the destination range, one past the last coordinate stored - \pre volume_3(p0, p1, p2, p3) != 0 + \pre Compute_volume_3(p0, p1, p2, p3) != 0 */ template< typename OutIterator, @@ -135,18 +135,14 @@ namespace Barycentric_coordinates{ a traits class with geometric objects, predicates, and constructions; this parameter can be omitted if the traits class can be deduced from the point type - \return a tuple `std::tuple` + \return a array `std::array` with the computed coordinates - \pre volume_3(p0, p1, p2, p3) != 0 + \pre Compute_volume_3(p0, p1, p2, p3) != 0 */ template - std::tuple< - typename GeomTraits::FT, - typename GeomTraits::FT, - typename GeomTraits::FT, - typename GeomTraits::FT> - tetrahedron_coordinates_in_tuple( + std::array + tetrahedron_coordinates_in_array( const typename GeomTraits::Point_3& p0, const typename GeomTraits::Point_3& p1, const typename GeomTraits::Point_3& p2, @@ -160,17 +156,13 @@ namespace Barycentric_coordinates{ internal::tetrahedron_coordinates_impl( p0, p1, p2, p3, query, std::back_inserter(coordinates), traits); CGAL_assertion(coordinates.size() == 4); - return std::make_tuple(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); + return {coordinates[0], coordinates[1], coordinates[2], coordinates[3]}; } - //return tuple (infer from point_3) + //return array (infer from point_3) template - std::tuple< - typename Kernel_traits::Kernel::FT, - typename Kernel_traits::Kernel::FT, - typename Kernel_traits::Kernel::FT, - typename Kernel_traits::Kernel::FT> - tetrahedron_coordinates_in_tuple( + std::array::Kernel::FT, 4> + tetrahedron_coordinates_in_array( const Point_3& p0, const Point_3& p1, const Point_3& p2, @@ -179,7 +171,7 @@ namespace Barycentric_coordinates{ using GeomTraits = typename Kernel_traits::Kernel; const GeomTraits traits; - return tetrahedron_coordinates_in_tuple( + return tetrahedron_coordinates_in_array( p0, p1, p2, p3, query, traits); } diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp index 41d944ce135d..20300f8c7afe 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_tetrahedron_coordinates.cpp @@ -43,12 +43,21 @@ void test_overloads(){ const FT x = point.x(), y = point.y(), z = point.z(); const Point_3 query(x, y, z); CGAL::Barycentric_coordinates::tetrahedron_coordinates(vertices[0], vertices[1], - vertices[2], vertices[3], query, tetra_coords.begin()); + vertices[2], vertices[3], query, tetra_coords.begin()); + + const std::array tetra_coords_array = + CGAL::Barycentric_coordinates::tetrahedron_coordinates_in_array(vertices[0], vertices[1], + vertices[2], vertices[3], query); assert(CGAL::abs(1-x-y-z - tetra_coords[0]) == FT(0) && CGAL::abs(x - tetra_coords[1]) == FT(0) && CGAL::abs(y - tetra_coords[2]) == FT(0) && CGAL::abs(z - tetra_coords[3]) == FT(0)); + + assert(tetra_coords_array[0] == tetra_coords[0] && + tetra_coords_array[1] == tetra_coords[1] && + tetra_coords_array[2] == tetra_coords[2] && + tetra_coords_array[3] == tetra_coords[3]); } } From da7905a800ae78f8c9befe32e8ff36b8193569d6 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Tue, 17 Aug 2021 09:01:21 -0300 Subject: [PATCH 54/68] final tweaks --- .../Barycentric_coordinates_3.txt | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index 0c4a4ea90bda..a17e08031ba9 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -136,7 +136,9 @@ where \f$V_i\f$ is the signed volume of the sub-tetrahedron opposite to the vert is the total volume of the tetrahedron, that is \f$V = V_0 + V_1 + V_2 + V_3\f$. These coordinates can be computed exactly if an exact number type is chosen, for any query point -in the space and with respect to any non-degenerate tetrahedron. +in the space and with respect to any non-degenerate tetrahedron. No special cases are handled. +The computation always yields the correct result. The notion of correctness depends on the precision +of the used number type. Note that for exterior points some coordinate values will be negative. \subsection gbc_3_degeneracies_wachspress Wachspress Coordinates @@ -145,8 +147,12 @@ To compute Wachspress coordinates of the query point `q`, we adopt the simple fo \f$w_v({q}) = \sum_{i=2}^{k-1}det({p_{f_1}}({q}), {p_{f_i}}({q}), {p_{f_{i+1}}}({q}))\f$
-where \f${p_f}({q}) = \frac{{n_f}}{h_f({q})}\f$, \f$h_f({q}) = ({v} - {q})\cdot {n_f}\f$ denote the -perpendicular distance of \f$q\f$ to \f$f\f$, and for the face \f$f\f$, let \f${n_f}\f$ denote its unit outward normal. +for each vertex \f$v\f$, let \f$f_1, f_2, ..., f_k\f$ be the \f$k\f$ faces incident to \f$v\f$. We +are assuming that the faces are taken in anticlockwise order. + +We can define \f${p_f}({q}) = \frac{{n_f}}{h_f({q})}\f$, +and \f$h_f({q}) = ({v} - {q})\cdot {n_f}\f$ as the perpendicular distance of \f$q\f$ to \f$f\f$. +For the face \f$f\f$, let \f${n_f}\f$ denote its unit outward normal. In this implementation, Wachspress coordinates are well defined in the closure of any convex polyhedra. If an exact number type is chosen, they are computed exactly. @@ -155,7 +161,7 @@ polyhedra. If an exact number type is chosen, they are computed exactly. To compute discrete harmonic coordinates of the query point `q`, we adopt the simple formula:
-\f$w_i = \sum_{v_i \in T} \frac{cot[\theta_i^T]h_i^T}{2}\f$ +\f$w_i = \sum_{T : v_i \in T} \frac{cot[\theta_i^T]h_i^T}{2}\f$
where, within a triangle face T = {v1, v2, v3}, \f$\theta_i^T\f$ is the dihedral angle between T and @@ -192,10 +198,10 @@ to evaluate performance is absolutely necessary. In this section, we present benchmark results for each algorithm. To make the benchmark and evaluate runtimes, for each analytic coordinate, -we regularly sample approximately $n^3$ ($n$ varying from 1 to 100) strictly +we regularly sample approximately \f$n^3\f$ (\f$n\f$ varying from 1 to 100) strictly interior points with respect to a cube with unit length, and then calculate -its coordinate values (see figure bellow). The results are represented in a log-log -scale plot and are the mean value of 10 loop iterations (see plot bellow). +its coordinate values (see figure below). The results are represented in a log-log +scale plot and are the mean value of 10 loop iterations (see plot below). \cgalFigureBegin{bc_coords_bench_structure_3, bc_coords_bench_structure_3.png} Points shown in red are the sample points used to make the benchmark. @@ -203,17 +209,17 @@ Points shown in red are the sample points used to make the benchmark. The performance strongly depends on the chosen kernel, for this test we choose to use SCKER because is much faster than others. -Also, we can see that time (wp) << time (dh) < time (mv). This happens because wp -implementation has fewer instructions per loop than other two, so naturally +Also, we can see that time (WP) << time (DH) < time (MV). This happens because wp +implementation has fewer instructions per loop than the other two, so naturally, the computation time tends to be faster. \cgalFigureBegin{bc_coords_bench_3, bc_coords_bench_3.png} -Time in seconds to compute $n^3$ coordinate values for a cube. Are represented in the graph: -Wachspress (blue), discrete harmonic (orange) and mean value (green). +Time in seconds to compute \f$n^3\f$ coordinate values for a cube. Are represented in the graph: +Wachspress (blue), discrete harmonic (orange), and mean value (green). \cgalFigureEnd Tetrahedron coordinates are not shown in the same plot because the test is -slightly different. For this one, we simply show in a table (see table bellow) +slightly different. For this one, we simply show in a table (see table below) the results for some pre-defined quantity of points. The test is done by regularly sampling strictly interior points with respect to a tetrahedron with unit sides that lies on the coordinate axis. From f5f0f9293e3323e4380c847fd09d558532da256d Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Thu, 19 Aug 2021 19:00:56 -0300 Subject: [PATCH 55/68] Final fixes in the docs after revision --- .../Barycentric_coordinates_3.txt | 59 +++++++++-------- .../tetrahedron_coordinates.cpp | 65 ++++++++----------- .../internal/utils_3.h | 1 - 3 files changed, 58 insertions(+), 67 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index a17e08031ba9..c4568d03629e 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -13,15 +13,15 @@ namespace Barycentric_coordinates { Barycentric coordinates are an important tool in computer graphics and geometric modeling. Originally, these coordinates were used to represent a given point with respect to a simplex but have been later - generalized to more complex shapes. +generalized to more complex shapes. The package 3D Generalized Barycentric Coordinates offers an efficient and robust implementation -of three-dimensional closed-form generalized barycentric coordinates defined for convex simplicial polytopes. +of three-dimensional closed-form, generalized barycentric coordinates defined for convex simplicial polytopes. In particular, this package includes an implementation of Wachspress, discrete harmonic, mean value, and - one extra function to calculate barycentric coordinates with respect to tetrahedra. In this implementation, - we restrict our polyhedra to convex ones with triangular faces, although some of the coordinates may - accept more general shapes. +one extra function to calculate barycentric coordinates with respect to tetrahedra. In this implementation, +we restrict our polyhedra to convex ones with triangular faces, although some of the coordinates may +accept more general shapes. \section gbc_3_interface Software Design @@ -31,11 +31,13 @@ that can be computed analytically. All of the three analytic coordinates can be computed either by instantiating a class or through a free function. Tetrahedron coordinates can be computed only through the free function. -We can specify a computation policy Barycentric_coordinates::Computation_policy_3 that can be FAST or -FAST_WITH_EDGE_CASES for each of the three coordinates. The difference between them is that FAST_WITH_EDGE_CASES - treats points near the boundaries by projecting them into the face of the polyhedron and then calculating triangle coordinates. +Similarly to `Barycentric_coordinates::Computation_policy_2` in the 2D package, we can specify a computation +policy Barycentric_coordinates::Computation_policy_3 that can be FAST or +FAST_WITH_EDGE_CASES for each of the three analytical coordinates. The difference between them is that FAST_WITH_EDGE_CASES +treats points near the boundaries by projecting them into the face of the polyhedron and then calculating triangle coordinates. +Note that, different from the 2D package, there is not yet an implementation for a PRECISE algorithm. -The output of a query point is the coordinate value with respect to each vertex, the number of coordinate +The output of a query point is the coordinate value with respect to each vertex, the number of coordinates values being the same as the number of vertices, and the ordering is also the same. All class and function templates are parameterized by a traits class, which is a model of the concept @@ -46,13 +48,13 @@ a model of the concept FaceListGraph with a property map that maps a vertex from \section gbc_3_examples Examples -In order to facilitate the process of learning this package, we provide various examples -with a basic usage of different barycentric components. +To facilitate the process of learning this package, we provide various examples +with basic usage of different barycentric components. \subsection tetra_example Tetrahedron Coordinates -In this example, we use the global function tetrahedron_coordinates_in_tuple(). +In this example, we use the global function `tetrahedron_coordinates_in_array()`. We compute coordinates for the tetrahedron whose vertices are the points in the set {(0,0,0), (1,0,0), (0,1,0), (0,0,1)}. We use points inside, outside and at the boundary of the tetrahedron. @@ -116,14 +118,17 @@ algorithm should be used. Implementation details are described in \cgalCite{cgal:bc:f-wmvc-14} for Wachspress and mean value coordinates, and in \cgalCite{cgal:bc:jlw-ggcccsp-07} for discrete harmonic coordinates. -The main purpose of the FAST_WITH_EDGE_CASES algorithm is to extend the region where the analytical +For each coordinate, it is necessary to make divisions by the signed distance between the query +point and each face. So, if one of these distances is zero or close to zero (query point at the boundary), +it will cause a division by zero error or numerical instability, respectively. +To avoid this, the main purpose of the FAST_WITH_EDGE_CASES algorithm is to extend the region where the analytical coordinates are well-defined. It adds the guarantee to calculate points near the boundaries. The way it works is very simple: before calculating any coordinate, the algorithm checks, for each face, if the distance between the query point and the plane is less than one predetermined tolerance. If so, instead of calculating the analytical form of the coordinates, it decomposes the query point with respect to this particular face and then calculates triangle coordinates. However, for Wachspress coordinates, the 2D version is used because the faces are not necessarily triangular. Note that for -every vertice that does not belong to this face, the coordinate value will be zero. +every vertex that does not belong to this face, the coordinate value will be zero. \subsection gbc_3_degeneracies_tetrahedron Tetrahedron Coordinates @@ -136,24 +141,24 @@ where \f$V_i\f$ is the signed volume of the sub-tetrahedron opposite to the vert is the total volume of the tetrahedron, that is \f$V = V_0 + V_1 + V_2 + V_3\f$. These coordinates can be computed exactly if an exact number type is chosen, for any query point -in the space and with respect to any non-degenerate tetrahedron. No special cases are handled. -The computation always yields the correct result. The notion of correctness depends on the precision +in the space and with respect to any non-degenerate tetrahedron. No special cases are handled. +The computation always yields the correct result. The notion of correctness depends on the precision of the used number type. Note that for exterior points some coordinate values will be negative. \subsection gbc_3_degeneracies_wachspress Wachspress Coordinates -To compute Wachspress coordinates of the query point `q`, we adopt the simple formula: +For each vertex \f$v\f$, let \f$f_1, f_2, ..., f_k\f$ be the \f$k\f$ faces incident to \f$v\f$. We +are assuming that the faces are taken in counterclockwise order. + +We can define \f${p_f}({q}) = \frac{{n_f}}{h_f({q})}\f$, +and \f$h_f({q}) = ({v} - {q})\cdot {n_f}\f$ as the perpendicular distance of \f$q\f$ to \f$f\f$. +For the face \f$f\f$, let \f${n_f}\f$ denote its unit outward normal. + +So, to compute Wachspress coordinates of the query point `q`, we adopt the simple formula:
\f$w_v({q}) = \sum_{i=2}^{k-1}det({p_{f_1}}({q}), {p_{f_i}}({q}), {p_{f_{i+1}}}({q}))\f$
-for each vertex \f$v\f$, let \f$f_1, f_2, ..., f_k\f$ be the \f$k\f$ faces incident to \f$v\f$. We -are assuming that the faces are taken in anticlockwise order. - -We can define \f${p_f}({q}) = \frac{{n_f}}{h_f({q})}\f$, -and \f$h_f({q}) = ({v} - {q})\cdot {n_f}\f$ as the perpendicular distance of \f$q\f$ to \f$f\f$. -For the face \f$f\f$, let \f${n_f}\f$ denote its unit outward normal. - In this implementation, Wachspress coordinates are well defined in the closure of any convex polyhedra. If an exact number type is chosen, they are computed exactly. @@ -204,11 +209,11 @@ its coordinate values (see figure below). The results are represented in a log-l scale plot and are the mean value of 10 loop iterations (see plot below). \cgalFigureBegin{bc_coords_bench_structure_3, bc_coords_bench_structure_3.png} -Points shown in red are the sample points used to make the benchmark. +The points shown in red are the sample points used to make the benchmark. \cgalFigureEnd The performance strongly depends on the chosen kernel, -for this test we choose to use SCKER because is much faster than others. +for this test, we choose to use `Simple_cartesian` because is much faster than others. Also, we can see that time (WP) << time (DH) < time (MV). This happens because wp implementation has fewer instructions per loop than the other two, so naturally, the computation time tends to be faster. @@ -219,7 +224,7 @@ Wachspress (blue), discrete harmonic (orange), and mean value (green). \cgalFigureEnd Tetrahedron coordinates are not shown in the same plot because the test is -slightly different. For this one, we simply show in a table (see table below) +slightly different. For this one, we simply show in the table below the results for some pre-defined quantity of points. The test is done by regularly sampling strictly interior points with respect to a tetrahedron with unit sides that lies on the coordinate axis. diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp index 40409671a96c..813ea56fb000 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/tetrahedron_coordinates.cpp @@ -9,46 +9,33 @@ using Point_3 = Kernel::Point_3; int main(){ - // Construct tetrahedron - const Point_3 p0(0.0, 0.0, 0.0); - const Point_3 p1(1.0, 0.0, 0.0); - const Point_3 p2(0.0, 1.0, 0.0); - const Point_3 p3(0.0, 0.0, 1.0); - - // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. - const std::vector queries = { - Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), // interior query points - Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points - Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), // boundary query points - Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), // boundary query points - Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), // exterior query points - Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f)}; // exterior query point - - // Compute tetrahedra coordinates; - std::vector coordinates; - coordinates.reserve(queries.size()*4); - - // Output all tetrahedra coordinates. - std::cout << std::endl << "tetrahedra coordinates (all queries): " << std::endl + // Construct tetrahedron + const Point_3 p0(0.0, 0.0, 0.0); + const Point_3 p1(1.0, 0.0, 0.0); + const Point_3 p2(0.0, 1.0, 0.0); + const Point_3 p3(0.0, 0.0, 1.0); + + // Instantiate some interior, boundary, and exterior query points for which we compute coordinates. + const std::vector queries = { + Point_3(0.25f , 0.25f, 0.25f), Point_3(0.3f, 0.2f, 0.3f), // interior query points + Point_3(0.1f, 0.1f, 0.1f), Point_3(0.2f, 0.5f, 0.3f), // interior query points + Point_3(0.0f , 0.0f, 0.5f), Point_3(0.4f, 0.4f, 0.0f), // boundary query points + Point_3(0.0f, 0.4f, 0.4f), Point_3(0.4f, 0.0f, 0.4f), // boundary query points + Point_3(0.5f, 0.5f, 0.5f), Point_3(2.0f, 0.0f, 0.0f), // exterior query points + Point_3(-1.0f, -1.0f, 1.0f), Point_3(0.5f, 0.5f, -2.0f)}; // exterior query point + + std::cout << std::endl << "tetrahedra coordinates (all queries): " << std::endl << std::endl; - for (std::size_t i = 0; i < coordinates.size(); i += 3){ - std::cout << - coordinates[i + 0] << ", " << - coordinates[i + 1] << ", " << - coordinates[i + 2] << ", " << - coordinates[i + 3] << std::endl; - } - std::cout << std::endl; - - // Get a tuple of triangle coordinates for all query points - for(std::size_t i = 0; i < queries.size(); i++){ - const auto tuple = - CGAL::Barycentric_coordinates::tetrahedron_coordinates_in_tuple(p0, p1, p2, p3, queries[i]); - - std::cout << "tetrahedra coordinates (query " << i << "): " << - std::get<0>(tuple) << " " << std::get<1>(tuple) << " " << - std::get<2>(tuple) << " " << std::get<3>(tuple) << std::endl; - } + + // Get an array of triangle coordinates for all query points + for(std::size_t i = 0; i < queries.size(); i++){ + const auto coords_array = + CGAL::Barycentric_coordinates::tetrahedron_coordinates_in_array(p0, p1, p2, p3, queries[i]); + + std::cout << "tetrahedra coordinates (query " << i << "): " << + coords_array[0] << " " << coords_array[1] << " " << + coords_array[2] << " " << coords_array[3] << std::endl; + } return EXIT_SUCCESS; } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 6afd0b7d2d63..ec3a96d94257 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -235,7 +235,6 @@ template const FT tol = get_tolerance(); const std::size_t num_sides_face = vertices_face.size(); - const std::size_t num_vertices_polyhedron = num_vertices(triangle_mesh); // Check if the vertices are distinct CGAL_assertion(are_vertices_distinct(vertices_face)); From 4ffd27d8df82baccf4454d11a3983e6df34e8ef3 Mon Sep 17 00:00:00 2001 From: Antonio Gomes Date: Mon, 23 Aug 2021 10:10:15 -0300 Subject: [PATCH 56/68] adding benchmark scripts --- .../scripts/bench_pluto_notebook.jl | 858 ++++++++++++++++++ .../scripts/bench_raw.jl | 13 + 2 files changed, 871 insertions(+) create mode 100644 Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_pluto_notebook.jl create mode 100644 Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_raw.jl diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_pluto_notebook.jl b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_pluto_notebook.jl new file mode 100644 index 000000000000..37d42d5ce442 --- /dev/null +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_pluto_notebook.jl @@ -0,0 +1,858 @@ +### A Pluto.jl notebook ### +# v0.15.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 5352becc-fc1c-11eb-22d7-c352547689d4 +using Plots + +# ╔═╡ 0034844b-5e15-4db9-ac8b-fdb77df19125 +using DelimitedFiles + +# ╔═╡ d206a52d-b2ab-49ae-96d8-3305888031c5 +samples = readdlm("/home/antonio/Documentos/points.txt", ' ', Float64, '\n') + +# ╔═╡ c27a3346-13b8-4130-87a4-a923652124e4 +interior = scatter(samples[:, 1], samples[:, 2], samples[:, 3], markeralpha = 0.1, ms=1.1, color=RGB(1, 0, 0), title="Bench", label="samples") + +# ╔═╡ 58e01dd7-c7f4-4315-b673-0ec232b02cef +wp_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/wp_bench.txt", ' ', Float64, '\n') + + +# ╔═╡ b128fd0d-14e7-45a3-80e3-a76db50dfc7c +dh_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/dh_bench.txt", ' ', Float64, '\n') + +# ╔═╡ 832b2e15-7767-4c80-9444-bbc5dfe59ee6 +mv_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/mv_bench.txt", ' ', Float64, '\n') + +# ╔═╡ 87f2ae26-3511-47fc-bb35-4c961a57d9ae +num_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/num_bench.txt", ' ', Float64, '\n') + +# ╔═╡ 7b3f610a-c4f9-40ff-a904-a3ad7ae81ce1 +plot(num_bench, [wp_bench, dh_bench, mv_bench], label=["WP" "DH" "MV"], xaxis=("n", :log), yaxis=("time (sec)", :log), title="Log-log scale plot") + +# ╔═╡ 00000000-0000-0000-0000-000000000001 +PLUTO_PROJECT_TOML_CONTENTS = """ +[deps] +DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" + +[compat] +Plots = "~1.20.0" +""" + +# ╔═╡ 00000000-0000-0000-0000-000000000002 +PLUTO_MANIFEST_TOML_CONTENTS = """ +# This file is machine-generated - editing it directly is not advised + +[[Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "3.3.1" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.6+5" + +[[Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "e2f47f6d8337369411569fd45ae5753ca10394c6" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.16.0+6" + +[[ColorSchemes]] +deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"] +git-tree-sha1 = "9995eb3977fbf67b86d0a0a0508e83017ded03f2" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.14.0" + +[[ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.0" + +[[Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.12.8" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "344f143fa0ec67e47917848795ab19c6a455f32c" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.32.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[Contour]] +deps = ["StaticArrays"] +git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.5.7" + +[[DataAPI]] +git-tree-sha1 = "ee400abb2298bd13bfc3df1c412ed228061a2385" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.7.0" + +[[DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.10" + +[[DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.1.5+1" + +[[Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b3bfd02e98aedfa5cf885665493c5598c350cd2f" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.2.10+0" + +[[FFMPEG]] +deps = ["FFMPEG_jll"] +git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.4.1" + +[[FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "LibVPX_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "3cc57ad0a213808473eafef4845a74766242e05f" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "4.3.1+4" + +[[FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.1+14" + +[[Formatting]] +deps = ["Printf"] +git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" +uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" +version = "0.4.2" + +[[FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.10.1+5" + +[[FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.10+0" + +[[GLFW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] +git-tree-sha1 = "dba1e8614e98949abfa60480b13653813d8f0157" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.3.5+0" + +[[GR]] +deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"] +git-tree-sha1 = "182da592436e287758ded5be6e32c406de3a2e47" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.58.1" + +[[GR_jll]] +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "d59e8320c2747553788e4fc42231489cc602fa50" +uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" +version = "0.58.1+0" + +[[GeometryBasics]] +deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.1" + +[[Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.68.3+0" + +[[Grisu]] +git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" +uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" +version = "1.0.2" + +[[HTTP]] +deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] +git-tree-sha1 = "44e3b40da000eab4ccb1aecdc4801c040026aeb5" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "0.9.13" + +[[IniFile]] +deps = ["Test"] +git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8" +uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" +version = "0.5.0" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[IterTools]] +git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.3.0" + +[[IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.2" + +[[JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "2.1.0+0" + +[[LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.1+0" + +[[LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[LaTeXStrings]] +git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.2.1" + +[[Latexify]] +deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"] +git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92" +uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +version = "0.15.6" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[LibVPX_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "12ee7e23fa4d18361e7c2cde8f8337d4c3101bc7" +uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a" +version = "1.10.0+0" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+0" + +[[Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.3.0+3" + +[[Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+1" + +[[Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.35.0+0" + +[[Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.3.0+0" + +[[Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.36.0+0" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.7" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS]] +deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"] +git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "1.0.3" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Measures]] +git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.1" + +[[Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.0.0" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NaNMath]] +git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "0.3.5" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.5+0" + +[[OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "1.1.10+0" + +[[Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.3.2+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[PCRE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" +uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" +version = "8.44.0+0" + +[[Parsers]] +deps = ["Dates"] +git-tree-sha1 = "477bf42b4d1496b454c10cce46645bb5b8a0cf2c" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.0.2" + +[[Pixman_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.40.1+0" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[PlotThemes]] +deps = ["PlotUtils", "Requires", "Statistics"] +git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "2.0.1" + +[[PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"] +git-tree-sha1 = "501c20a63a34ac1d015d5304da0e645f42d91c9f" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.0.11" + +[[Plots]] +deps = ["Base64", "Contour", "Dates", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"] +git-tree-sha1 = "e39bea10478c6aff5495ab522517fae5134b40e3" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "1.20.0" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[Qt5Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] +git-tree-sha1 = "ad368663a5e20dbb8d6dc2fddeefe4dae0781ae8" +uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" +version = "5.15.3+0" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[RecipesBase]] +git-tree-sha1 = "b3fb709f3c97bfc6e948be68beeecb55a0b340ae" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.1.1" + +[[RecipesPipeline]] +deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"] +git-tree-sha1 = "2a7a2469ed5d94a98dea0e85c46fa653d76be0cd" +uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" +version = "0.3.4" + +[[Reexport]] +git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.1.0" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Scratch]] +deps = ["Dates"] +git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.1.0" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Showoff]] +deps = ["Dates", "Grisu"] +git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.0.3" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.0.1" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.2.12" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[StatsAPI]] +git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.0.0" + +[[StatsBase]] +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "fed1ec1e65749c4d96fc20dd13bea72b55457e62" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.33.9" + +[[StructArrays]] +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "000e168f5cc9aded17b6999a560b7c11dda69095" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.0" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] +git-tree-sha1 = "d0c690d37c73aeb5ca063056283fde5585a41710" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.5.0" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[URIs]] +git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.3.0" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[Wayland_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.19.0+0" + +[[Wayland_protocols_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"] +git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670" +uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" +version = "1.18.0+4" + +[[XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.9.12+0" + +[[XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.0+4" + +[[Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.7.10+4" + +[[Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.2+4" + +[[Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.1.0+4" + +[[Xorg_xcb_util_image_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" +uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" +version = "0.4.0+1" + +[[Xorg_xcb_util_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] +git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" +uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" +version = "0.4.0+1" + +[[Xorg_xcb_util_keysyms_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" +uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" +version = "0.4.0+1" + +[[Xorg_xcb_util_renderutil_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" +uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" +version = "0.3.9+1" + +[[Xorg_xcb_util_wm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] +git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" +uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" +version = "0.4.1+1" + +[[Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.4.2+4" + +[[Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.27.0+4" + +[[Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.0+0" + +[[libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "acc685bcf777b2202a904cdcb49ad34c2fa1880c" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.14.0+4" + +[[libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7a5780a0d9c6864184b3a2eeeb833a0c871f00ab" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "0.1.6+4" + +[[libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.38+0" + +[[libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] +git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.7+0" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" + +[[x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "2020.7.14+2" + +[[x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "3.0.0+3" + +[[xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "0.9.1+5" +""" + +# ╔═╡ Cell order: +# ╠═5352becc-fc1c-11eb-22d7-c352547689d4 +# ╠═0034844b-5e15-4db9-ac8b-fdb77df19125 +# ╠═d206a52d-b2ab-49ae-96d8-3305888031c5 +# ╠═c27a3346-13b8-4130-87a4-a923652124e4 +# ╠═58e01dd7-c7f4-4315-b673-0ec232b02cef +# ╠═b128fd0d-14e7-45a3-80e3-a76db50dfc7c +# ╠═832b2e15-7767-4c80-9444-bbc5dfe59ee6 +# ╠═87f2ae26-3511-47fc-bb35-4c961a57d9ae +# ╠═7b3f610a-c4f9-40ff-a904-a3ad7ae81ce1 +# ╟─00000000-0000-0000-0000-000000000001 +# ╟─00000000-0000-0000-0000-000000000002 diff --git a/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_raw.jl b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_raw.jl new file mode 100644 index 000000000000..21ee29982680 --- /dev/null +++ b/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/scripts/bench_raw.jl @@ -0,0 +1,13 @@ +using Plots +using DelimitedFiles + +samples = readdlm("/home/antonio/Documentos/points.txt", ' ', Float64, '\n') + +interior = scatter(samples[:, 1], samples[:, 2], samples[:, 3], markeralpha = 0.1, ms=1.1, color=RGB(1, 0, 0), title="Bench", label="samples") + +wp_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/wp_bench.txt", ' ', Float64, '\n') +dh_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/dh_bench.txt", ' ', Float64, '\n') +mv_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/mv_bench.txt", ' ', Float64, '\n') +num_bench = readdlm("/home/antonio/Documentos/gsoc/cgal-public-dev/Barycentric_coordinates_3/benchmark/Barycentric_coordinates_3/build/num_bench.txt", ' ', Float64, '\n') + +plot(num_bench, [wp_bench, dh_bench, mv_bench], label=["WP" "DH" "MV"], xaxis=("n", :log), yaxis=("time (sec)", :log), title="Log-log scale plot") From ef44ef27eb2f409f184f5eb12bb9eb5c33d37787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 13:50:37 +0200 Subject: [PATCH 57/68] use new macro --- .../Concepts/BarycentricTraits_3.h | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h index c5238dd3e94f..83666e14a7f7 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Concepts/BarycentricTraits_3.h @@ -7,12 +7,14 @@ namespace Barycentric_coordinates { A concept that describes the set of requirements of the template parameter `GeomTraits` used to parameterize all classes and functions with 3D barycentric -coordinates from the namespace `CGAL::Barycentric_coordinates`. The concept `BarycentricTraits_3` +coordinates from the namespace `CGAL::Barycentric_coordinates`. The concept `BarycentricTraits_3` extends the concept `BarycentricTraits_2` and adds the requirements for 3D objects. \cgalGeneralizes `BarycentricTraits_2` -\cgalHasModel -- All models of `Kernel` +\cgalHasModelsBegin +\cgalHasModelsBare{All models of the \cgal concept `Kernel`} +\cgalHasModelsEnd + */ class BarycentricTraits_3 { @@ -50,7 +52,7 @@ typedef unspecified_type Vector_3; A construction object that must provide the function operator: `FT operator(const Point_3& p0, const Point_3& p1, const Point_3& p2, const Point_3& p3)` - + that returns the signed volume of the tetrahedron defined by the four points `p0`, `p1`, `p2`, and `p3`. */ typedef unspecified_type Compute_volume_3; @@ -59,7 +61,7 @@ typedef unspecified_type Compute_volume_3; A construction object that must provide the function operator: `FT operator(const Vector_3& u, const Vector_3& v)` - + that returns an approximation of the angle between `u` and `v`. The angle is given in degrees. */ @@ -69,7 +71,7 @@ typedef unspecified_type Compute_approximate_angle_3; A construction object that must provide the function operator: `FT operator(const Vector_3& v, const Vector_3& w)` - + that returns the scalar (inner) product of the two vectors `v` and `w`. */ typedef unspecified_type Compute_scalar_product_3; @@ -78,7 +80,7 @@ typedef unspecified_type Compute_scalar_product_3; A construction object that must provide the function operator: `FT operator(const Vector_3& u, const Vector_3& v, const Vector_3& w)` - + that returns the determinant of the three vectors `u`, `v` and `w`. */ typedef unspecified_type Compute_determinant_3; @@ -87,7 +89,7 @@ typedef unspecified_type Compute_determinant_3; A construction object that must provide the function operator: `Vector_3 operator(const Point_3& p, const Point_3& q)` - + that returns the vector `q` - `p`. */ typedef unspecified_type Construct_vector_3; @@ -96,7 +98,7 @@ typedef unspecified_type Construct_vector_3; A construction object that must provide the function operator: `Vector_3 operator(const Vector_3& u, const Vector_3& v)` - + that returns the cross product between `u` and `v`. */ typedef unspecified_type Construct_cross_product_vector_3; From e1793d0ec9942c948c53ae0b4755bd6b551cc4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 14:30:33 +0200 Subject: [PATCH 58/68] fix permission --- .../doc/Barycentric_coordinates_3/Doxyfile.in | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Doxyfile.in old mode 100755 new mode 100644 From a2a6319aa0738eedc2bc9298cd0668939e88967d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 14:33:12 +0200 Subject: [PATCH 59/68] update cmake scripts --- .../Barycentric_coordinates_3/CMakeLists.txt | 25 +++++---------- .../Barycentric_coordinates_3/CMakeLists.txt | 31 +++++++------------ 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index 7e64a711cfc9..46edae13c6c2 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -1,24 +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.23) project(Barycentric_coordinates_3_Examples) -set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wextra -Werror") -cmake_minimum_required(VERSION 3.1...3.15) -set(CMAKE_CXX_STANDARD 14) +find_package(CGAL REQUIRED) -find_package(CGAL QUIET COMPONENTS Core) -if(CGAL_FOUND) +include(CGAL_CreateSingleSourceCGALProgram) - include(${CGAL_USE_FILE}) - include(CGAL_CreateSingleSourceCGALProgram) - - create_single_source_cgal_program("tetrahedron_coordinates.cpp") - create_single_source_cgal_program("shape_deformation.cpp") - create_single_source_cgal_program("wachspress_coordinates.cpp") - create_single_source_cgal_program("discrete_harmonic_coordinates.cpp") - create_single_source_cgal_program("mean_value_coordinates.cpp") - -else() - message(WARNING "This program requires the CGAL library, and will not be compiled.") -endif() +create_single_source_cgal_program("tetrahedron_coordinates.cpp") +create_single_source_cgal_program("shape_deformation.cpp") +create_single_source_cgal_program("wachspress_coordinates.cpp") +create_single_source_cgal_program("discrete_harmonic_coordinates.cpp") +create_single_source_cgal_program("mean_value_coordinates.cpp") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index 3ed9b317fde8..a2ff99f655a2 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -1,27 +1,18 @@ # 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.23) project(Barycentric_coordinates_3_Tests) -set(CMAKE_CXX_FLAGS "${CMAXE_CXX_FLAGS} -Wall -Wextra -Werror") -cmake_minimum_required(VERSION 3.1...3.15) -set(CMAKE_CXX_STANDARD 14) +find_package(CGAL REQUIRED) -find_package(CGAL QUIET COMPONENTS Core) -if(CGAL_FOUND) +include(CGAL_CreateSingleSourceCGALProgram) - include(${CGAL_USE_FILE}) - include(CGAL_CreateSingleSourceCGALProgram) - - create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") - create_single_source_cgal_program("test_wp_dh_mv_tetrahedron.cpp") - create_single_source_cgal_program("test_wp_weights.cpp") - create_single_source_cgal_program("test_dh_weights.cpp") - create_single_source_cgal_program("test_mv_weights.cpp") - create_single_source_cgal_program("test_edge_cases.cpp") - create_single_source_cgal_program("test_containers.cpp") - create_single_source_cgal_program("test_boundary_coordinates.cpp") - -else() - message(WARNING "This program requires the CGAL library, and will not be compiled.") -endif() +create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") +create_single_source_cgal_program("test_wp_dh_mv_tetrahedron.cpp") +create_single_source_cgal_program("test_wp_weights.cpp") +create_single_source_cgal_program("test_dh_weights.cpp") +create_single_source_cgal_program("test_mv_weights.cpp") +create_single_source_cgal_program("test_edge_cases.cpp") +create_single_source_cgal_program("test_containers.cpp") +create_single_source_cgal_program("test_boundary_coordinates.cpp") From fd7f8a1fe72faaea5213494c6d34e0db1ecff20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 15:00:13 +0200 Subject: [PATCH 60/68] add in package list --- .../Barycentric_coordinates_3.txt | 18 ++++++++---------- Documentation/doc/Documentation/packages.txt | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt index c4568d03629e..a12bb42aede2 100644 --- a/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt +++ b/Barycentric_coordinates_3/doc/Barycentric_coordinates_3/Barycentric_coordinates_3.txt @@ -31,11 +31,11 @@ that can be computed analytically. All of the three analytic coordinates can be computed either by instantiating a class or through a free function. Tetrahedron coordinates can be computed only through the free function. -Similarly to `Barycentric_coordinates::Computation_policy_2` in the 2D package, we can specify a computation +Similarly to `Barycentric_coordinates::Computation_policy_2` in the 2D package, we can specify a computation policy Barycentric_coordinates::Computation_policy_3 that can be FAST or -FAST_WITH_EDGE_CASES for each of the three analytical coordinates. The difference between them is that FAST_WITH_EDGE_CASES +FAST_WITH_EDGE_CASES for each of the three analytical coordinates. The difference between them is that FAST_WITH_EDGE_CASES treats points near the boundaries by projecting them into the face of the polyhedron and then calculating triangle coordinates. -Note that, different from the 2D package, there is not yet an implementation for a PRECISE algorithm. +Note that, different from the 2D package, there is not yet an implementation for a PRECISE algorithm. The output of a query point is the coordinate value with respect to each vertex, the number of coordinates values being the same as the number of vertices, and the ordering is also the same. @@ -58,7 +58,7 @@ In this example, we use the global function `tetrahedron_coordinates_in_array()` We compute coordinates for the tetrahedron whose vertices are the points in the set {(0,0,0), (1,0,0), (0,1,0), (0,0,1)}. We use points inside, outside and at the boundary of the tetrahedron. -\anchor tetra_coord_example + \cgalExample{Barycentric_coordinates_3/tetrahedron_coordinates.cpp} \subsection wp_3_example Wachspress Coordinates @@ -67,7 +67,7 @@ In this example, we generate 250 random points inside a unit sphere, centered at the origin, then we take the convex hull of this set of points and use this as our polyhedron. Finally, we calculate Wachspress coordinates for all of these 250 points. -\anchor wp_3_coord_example + \cgalExample{Barycentric_coordinates_3/wachspress_coordinates.cpp} \subsection dh_3_example Discrete Harmonic Coordinates @@ -77,7 +77,7 @@ We start with a regular icosahedron and for points inside, outside and at the boundary, we calculate discrete harmonic coordinates. In this example, we use the fast with edge cases algorithm because it treats points very close to the boundaries. -\anchor dh_3_coord_example + \cgalExample{Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp} \subsection mv_3_example Mean Value Coordinates @@ -87,7 +87,6 @@ star-shaped polyhedron. We note that this type of coordinate is well-defined for concave polyhedron but it may yield negative coordinate values for points outside the polyhedron's kernel (shown in blue). -\anchor mv_3_coord_example \cgalFigureBegin{mv_3_example, mv_coords_example_3.png} Example's point pattern. \cgalFigureEnd @@ -103,7 +102,6 @@ coordinate of each vertex with respect to the cube cage. After deforming the cage, we calculate the linear combination of the points to get the deformed sphere vertices. -\anchor shape_deform_example \cgalFigureBegin{shape_deform_example_3, shape_deform_example_3.png} The shape on the left is deformed into the shape on the right. \cgalFigureEnd @@ -118,8 +116,8 @@ algorithm should be used. Implementation details are described in \cgalCite{cgal:bc:f-wmvc-14} for Wachspress and mean value coordinates, and in \cgalCite{cgal:bc:jlw-ggcccsp-07} for discrete harmonic coordinates. -For each coordinate, it is necessary to make divisions by the signed distance between the query -point and each face. So, if one of these distances is zero or close to zero (query point at the boundary), +For each coordinate, it is necessary to make divisions by the signed distance between the query +point and each face. So, if one of these distances is zero or close to zero (query point at the boundary), it will cause a division by zero error or numerical instability, respectively. To avoid this, the main purpose of the FAST_WITH_EDGE_CASES algorithm is to extend the region where the analytical coordinates are well-defined. It adds the guarantee to calculate points near the boundaries. The diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index 9b1fa73107e4..30f92bba3abb 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -152,6 +152,7 @@ \package_listing{Interpolation} \package_listing{Barycentric_coordinates_2} +\package_listing{Barycentric_coordinates_3} \cgalPackageSection{PartSupportLibrary,Support Library} From 9ef332cf0a7d86bbe6ba8f7ccbdb681f71d71f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 15:07:15 +0200 Subject: [PATCH 61/68] add license include header --- .../include/CGAL/Barycentric_coordinates_3.h | 2 +- .../Discrete_harmonic_coordinates_3.h | 2 +- .../Mean_value_coordinates_3.h | 2 +- .../Wachspress_coordinates_3.h | 2 +- .../barycentric_enum_3.h | 2 +- .../boundary_coordinates_3.h | 2 +- .../internal/utils_3.h | 2 +- .../tetrahedron_coordinates.h | 2 +- .../CGAL/license/Barycentric_coordinates_3.h | 54 +++++++++++++++++++ .../include/CGAL/license/gpl_package_list.txt | 1 + 10 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 Installation/include/CGAL/license/Barycentric_coordinates_3.h diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h index bfb647e99556..b414f5503d99 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_COORDINATES_3_H -// #include +#include /** * \ingroup PkgBarycentricCoordinates3Ref diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index a4fe5f3aab8c..655e4df78ec6 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_DISCRETE_HARMONIC_COORDINATES_3_H #define CGAL_BARYCENTRIC_DISCRETE_HARMONIC_COORDINATES_3_H -// #include +#include // Internal includes. #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 56f1ba0eea40..19533dd371b2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H #define CGAL_BARYCENTRIC_MEAN_VALUE_COORDINATES_3_H -// #include +#include // Internal includes. #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 43bee7d59f15..03bccc1bcf5d 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H #define CGAL_BARYCENTRIC_WACHSPRESS_COORDINATES_3_H -// #include +#include // Internal includes. #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h index 0b6166790c8c..52803d58e211 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/barycentric_enum_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_ENUM_3_H #define CGAL_BARYCENTRIC_ENUM_3_H -// #include +#include namespace CGAL { diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h index 314d8be11d2c..e5e52acc6d89 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/boundary_coordinates_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_BOUNDARY_COORDINATES_3_H #define CGAL_BARYCENTRIC_BOUNDARY_COORDINATES_3_H -// #include +#include #include #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index ec3a96d94257..051acb4bcda1 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H #define CGAL_BARYCENTRIC_INTERNAL_UTILS_3_H -// #include +#include // STL includes #include diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h index ebbf29aeb912..4d4be8a297aa 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/tetrahedron_coordinates.h @@ -14,7 +14,7 @@ #ifndef CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H #define CGAL_BARYCENTRIC_TRIANGLE_COORDINATES_3_H -// #include +#include #include diff --git a/Installation/include/CGAL/license/Barycentric_coordinates_3.h b/Installation/include/CGAL/license/Barycentric_coordinates_3.h new file mode 100644 index 000000000000..dc5a648feedb --- /dev/null +++ b/Installation/include/CGAL/license/Barycentric_coordinates_3.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Andreas Fabri +// +// Warning: this file is generated, see include/CGAL/license/README.md + +#ifndef CGAL_LICENSE_BARYCENTRIC_COORDINATES_3_H +#define CGAL_LICENSE_BARYCENTRIC_COORDINATES_3_H + +#include +#include + +#ifdef CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE + +# if CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +# if defined(CGAL_LICENSE_WARNING) + + CGAL_pragma_warning("Your commercial license for CGAL does not cover " + "this release of the 3D Generalized Barycentric Coordinates package.") +# endif + +# ifdef CGAL_LICENSE_ERROR +# error "Your commercial license for CGAL does not cover this release \ + of the 3D Generalized Barycentric Coordinates package. \ + You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +# endif // CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +#else // no CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE + +# if defined(CGAL_LICENSE_WARNING) + CGAL_pragma_warning("\nThe macro CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE is not defined." + "\nYou use the CGAL 3D Generalized Barycentric Coordinates package under " + "the terms of the GPLv3+.") +# endif // CGAL_LICENSE_WARNING + +# ifdef CGAL_LICENSE_ERROR +# error "The macro CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE is not defined.\ + You use the CGAL 3D Generalized Barycentric Coordinates package under the terms of \ + the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +#endif // no CGAL_BARYCENTRIC_COORDINATES_3_COMMERCIAL_LICENSE + +#endif // CGAL_LICENSE_BARYCENTRIC_COORDINATES_3_H diff --git a/Installation/include/CGAL/license/gpl_package_list.txt b/Installation/include/CGAL/license/gpl_package_list.txt index 75ff9d5c9ed9..1bf86c8c2819 100644 --- a/Installation/include/CGAL/license/gpl_package_list.txt +++ b/Installation/include/CGAL/license/gpl_package_list.txt @@ -6,6 +6,7 @@ Alpha_wrap_3 3D Alpha Wrapping Apollonius_graph_2 2D Apollonius Graphs (Delaunay Graphs of Disks) Arrangement_on_surface_2 2D Arrangements Barycentric_coordinates_2 2D Generalized Barycentric Coordinates +Barycentric_coordinates_3 3D Generalized Barycentric Coordinates Boolean_set_operations_2 2D Regularized Boolean Set-Operations Bounding_volumes Bounding Volumes Box_intersection_d Intersecting Sequences of dD Iso-oriented Boxes From ee08aeb115994de7bdf28c7cda304104674d6414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 15:15:03 +0200 Subject: [PATCH 62/68] remove tabs --- .../Discrete_harmonic_coordinates_3.h | 22 +++++++++---------- .../Mean_value_coordinates_3.h | 20 ++++++++--------- .../Wachspress_coordinates_3.h | 22 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h index 655e4df78ec6..c0c4907d450b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Discrete_harmonic_coordinates_3.h @@ -70,7 +70,7 @@ namespace Barycentric_coordinates { /// \endcond /// Number type. - typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::FT FT; /// Point type. typedef typename GeomTraits::Point_3 Point_3; @@ -177,18 +177,18 @@ namespace Barycentric_coordinates { private: const TriangleMesh& m_triangle_mesh; - const Computation_policy_3 m_computation_policy; - const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 - const GeomTraits m_traits; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; const Construct_vec_3 m_construct_vector_3; const Cross_3 m_cross_3; const Dot_3 m_dot_3; const Sqrt sqrt; - std::vector m_weights; + std::vector m_weights; - template + template OutputIterator compute( const Point_3& query, OutputIterator coordinates) { @@ -254,7 +254,7 @@ namespace Barycentric_coordinates { // Sum of weights to normalize them later. FT sum = FT(0); - // Vertex index. + // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_triangle_mesh); @@ -263,10 +263,10 @@ namespace Barycentric_coordinates { // Call function to calculate wp coordinates const FT weight = compute_dh_vertex_query(vertex, query); - CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; - sum += weight; - ++vi; // update vi + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi } CGAL_assertion(sum != FT(0)); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index 19533dd371b2..f4f48332b8c7 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -71,7 +71,7 @@ namespace Barycentric_coordinates { /// \endcond /// Number type. - typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::FT FT; /// Point type. typedef typename GeomTraits::Point_3 Point_3; @@ -179,9 +179,9 @@ namespace Barycentric_coordinates { private: const TriangleMesh& m_triangle_mesh; - const Computation_policy_3 m_computation_policy; - const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 - const GeomTraits m_traits; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; const Construct_vec_3 m_construct_vector_3; const Cross_3 m_cross_3; @@ -196,7 +196,7 @@ namespace Barycentric_coordinates { std::vector m_vectors; std::vector angles; - template + template OutputIterator compute( const Point_3& query, OutputIterator coordinates) { @@ -262,7 +262,7 @@ namespace Barycentric_coordinates { // Sum of weights to normalize them later. FT sum = FT(0); - // Vertex index. + // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_triangle_mesh); @@ -271,10 +271,10 @@ namespace Barycentric_coordinates { // Call function to calculate wp coordinates const FT weight = compute_mv_vertex_query(vertex, query); - CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; - sum += weight; - ++vi; // update vi + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi } CGAL_assertion(sum != FT(0)); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 03bccc1bcf5d..67bad3233198 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -71,7 +71,7 @@ namespace Barycentric_coordinates { /// \endcond /// Number type. - typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::FT FT; /// Point type. typedef typename GeomTraits::Point_3 Point_3; @@ -177,18 +177,18 @@ namespace Barycentric_coordinates { private: const PolygonMesh& m_polygon_mesh; - const Computation_policy_3 m_computation_policy; - const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 - const GeomTraits m_traits; + const Computation_policy_3 m_computation_policy; + const VertexToPointMap m_vertex_to_point_map; // use it to map vertex to Point_3 + const GeomTraits m_traits; const Dot_3 m_dot_3; const Det_3 m_det_3; const Cross_3 m_cross_3; const Construct_vec_3 m_construct_vector_3; - std::vector m_weights; + std::vector m_weights; - template + template OutputIterator compute( const Point_3& query, OutputIterator coordinates) { @@ -254,7 +254,7 @@ namespace Barycentric_coordinates { // Sum of weights to normalize them later. FT sum = FT(0); - // Vertex index. + // Vertex index. std::size_t vi = 0; const auto vd = vertices(m_polygon_mesh); @@ -263,10 +263,10 @@ namespace Barycentric_coordinates { // Call function to calculate wp coordinates const FT weight = compute_wp_vertex_query(vertex, query); - CGAL_assertion(vi < m_weights.size()); - m_weights[vi] = weight; - sum += weight; - ++vi; // update vi + CGAL_assertion(vi < m_weights.size()); + m_weights[vi] = weight; + sum += weight; + ++vi; // update vi } CGAL_assertion(sum != FT(0)); From 88e7c72a9e491f35fbe0cd95968e5a95d6376cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 15:17:15 +0200 Subject: [PATCH 63/68] avoid duplicated targets --- .../examples/Barycentric_coordinates_3/CMakeLists.txt | 8 ++++---- ...oordinates.cpp => discrete_harmonic_coordinates_3.cpp} | 0 ...value_coordinates.cpp => mean_value_coordinates_3.cpp} | 0 .../{shape_deformation.cpp => shape_deformation_3.cpp} | 0 ...press_coordinates.cpp => wachspress_coordinates_3.cpp} | 0 .../test/Barycentric_coordinates_3/CMakeLists.txt | 6 +++--- .../{test_dh_weights.cpp => test_dh_weights_3.cpp} | 0 .../{test_mv_weights.cpp => test_mv_weights_3.cpp} | 0 .../{test_wp_weights.cpp => test_wp_weights_3.cpp} | 0 9 files changed, 7 insertions(+), 7 deletions(-) rename Barycentric_coordinates_3/examples/Barycentric_coordinates_3/{discrete_harmonic_coordinates.cpp => discrete_harmonic_coordinates_3.cpp} (100%) rename Barycentric_coordinates_3/examples/Barycentric_coordinates_3/{mean_value_coordinates.cpp => mean_value_coordinates_3.cpp} (100%) rename Barycentric_coordinates_3/examples/Barycentric_coordinates_3/{shape_deformation.cpp => shape_deformation_3.cpp} (100%) rename Barycentric_coordinates_3/examples/Barycentric_coordinates_3/{wachspress_coordinates.cpp => wachspress_coordinates_3.cpp} (100%) rename Barycentric_coordinates_3/test/Barycentric_coordinates_3/{test_dh_weights.cpp => test_dh_weights_3.cpp} (100%) rename Barycentric_coordinates_3/test/Barycentric_coordinates_3/{test_mv_weights.cpp => test_mv_weights_3.cpp} (100%) rename Barycentric_coordinates_3/test/Barycentric_coordinates_3/{test_wp_weights.cpp => test_wp_weights_3.cpp} (100%) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt index 46edae13c6c2..2d942fc6ff04 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(CGAL REQUIRED) include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("tetrahedron_coordinates.cpp") -create_single_source_cgal_program("shape_deformation.cpp") -create_single_source_cgal_program("wachspress_coordinates.cpp") -create_single_source_cgal_program("discrete_harmonic_coordinates.cpp") -create_single_source_cgal_program("mean_value_coordinates.cpp") +create_single_source_cgal_program("shape_deformation_3.cpp") +create_single_source_cgal_program("wachspress_coordinates_3.cpp") +create_single_source_cgal_program("discrete_harmonic_coordinates_3.cpp") +create_single_source_cgal_program("mean_value_coordinates_3.cpp") diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp similarity index 100% rename from Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates.cpp rename to Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp similarity index 100% rename from Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates.cpp rename to Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp similarity index 100% rename from Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation.cpp rename to Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates_3.cpp similarity index 100% rename from Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates.cpp rename to Barycentric_coordinates_3/examples/Barycentric_coordinates_3/wachspress_coordinates_3.cpp diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt index a2ff99f655a2..22759b826989 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/CMakeLists.txt @@ -10,9 +10,9 @@ include(CGAL_CreateSingleSourceCGALProgram) create_single_source_cgal_program("test_tetrahedron_coordinates.cpp") create_single_source_cgal_program("test_wp_dh_mv_tetrahedron.cpp") -create_single_source_cgal_program("test_wp_weights.cpp") -create_single_source_cgal_program("test_dh_weights.cpp") -create_single_source_cgal_program("test_mv_weights.cpp") +create_single_source_cgal_program("test_wp_weights_3.cpp") +create_single_source_cgal_program("test_dh_weights_3.cpp") +create_single_source_cgal_program("test_mv_weights_3.cpp") create_single_source_cgal_program("test_edge_cases.cpp") create_single_source_cgal_program("test_containers.cpp") create_single_source_cgal_program("test_boundary_coordinates.cpp") diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights_3.cpp similarity index 100% rename from Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights.cpp rename to Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_dh_weights_3.cpp diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights_3.cpp similarity index 100% rename from Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights.cpp rename to Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_mv_weights_3.cpp diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights_3.cpp similarity index 100% rename from Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights.cpp rename to Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_wp_weights_3.cpp From d7ab2dd719ac7f497918ea67f221e870028484f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 15:52:56 +0200 Subject: [PATCH 64/68] fix compilation errors and do not use Surface_mesher --- .../discrete_harmonic_coordinates_3.cpp | 1 + .../mean_value_coordinates_3.cpp | 6 +- .../shape_deformation_3.cpp | 42 +++++--------- .../internal/utils_3.h | 55 +------------------ .../test_containers.cpp | 8 +-- 5 files changed, 23 insertions(+), 89 deletions(-) diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp index 3badf511ba2d..929a28009ec7 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/discrete_harmonic_coordinates_3.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp index 85aacff5efa3..c5117d4ea562 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/mean_value_coordinates_3.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include @@ -24,8 +24,8 @@ int main(){ const Point_3 p6(1, 1, 3); const Point_3 p7(3, 0, 3); - CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, concave); - PMP::triangulate_faces(faces(concave), concave); + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, concave, + CGAL::parameters::do_not_triangulate_faces(false)); std::vector coords; std::vector queries{ diff --git a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp index bf9baf87fdb4..9d4f15db0055 100644 --- a/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp +++ b/Barycentric_coordinates_3/examples/Barycentric_coordinates_3/shape_deformation_3.cpp @@ -1,48 +1,32 @@ -#include #include -#include -#include -#include -#include #include -#include +#include +#include #include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; -// default triangulation for Surface_mesher -using Tr = CGAL::Surface_mesh_default_triangulation_3; - -using C2t3 = CGAL::Complex_2_in_triangulation_3; -using Sphere_3 = Kernel::Sphere_3; using Point_3 = Kernel::Point_3; using FT = Kernel::FT; -typedef FT (*Function)(Point_3); -using Surface_3 = CGAL::Implicit_surface_3; using Surface_mesh = CGAL::Surface_mesh; namespace PMP = CGAL::Polygon_mesh_processing; -FT sphere_function (Point_3 p){ - const FT x2=p.x()*p.x(), y2=p.y()*p.y(), z2=p.z()*p.z(); - return x2+y2+z2-1; -} - -int main() { +int main(int argc, char** argv) { - Tr tr; - C2t3 c2t3(tr); + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/sphere.off"); + Surface_mesh sm; - Surface_3 surface(sphere_function, Sphere_3(CGAL::ORIGIN, 2.)); - CGAL::Surface_mesh_default_criteria_3 criteria(30., 0.1, 0.1); - CGAL::make_surface_mesh(c2t3, surface, criteria, CGAL::Non_manifold_tag()); + if(!CGAL::IO::read_polygon_mesh(filename, sm)) + { + std::cerr << "Invalid input: " << filename << std::endl; + return 1; + } - Surface_mesh sm; Surface_mesh deformed; - CGAL::facets_in_complex_2_to_triangle_mesh(c2t3, sm); deformed = sm; Surface_mesh quad_cage; @@ -57,8 +41,8 @@ int main() { const Point_3 p6(2, 2, 2), p6_new(2, 2, 3); const Point_3 p7(-2, 2, 2), p7_new(-3, 3, 3); - CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, quad_cage); - PMP::triangulate_faces(faces(quad_cage), quad_cage); + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, quad_cage, + CGAL::parameters::do_not_triangulate_faces(false)); CGAL::Barycentric_coordinates::Mean_value_coordinates_3 mv(quad_cage); auto vertex_to_point_map = get_property_map(CGAL::vertex_point, deformed); @@ -67,7 +51,7 @@ int main() { std::vector target_cube{p0_new, p1_new, p2_new, p3_new, p4_new, p5_new, p6_new, p7_new}; - for(auto& v : vertices(deformed)){ + for(Surface_mesh::Vertex_index v : vertices(deformed)){ const Point_3 vertex_val = get(vertex_to_point_map, v); coords.clear(); diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 051acb4bcda1..a9a4c92cd9f0 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -39,55 +39,6 @@ enum class Edge_case { EXTERIOR_BOUNDARY = 3, // extension of the boundary }; -//Default sqrt -template -class Default_sqrt{ - typedef typename Traits::FT FT; - -public: - FT operator()(const FT &value) const{ - return FT(CGAL::sqrt(CGAL::to_double(value))); - } -}; - -BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(Has_nested_type_Sqrt, Sqrt, false) - -// Case: do_not_use_default = false. -template::value> - class Get_sqrt -{ -public: - typedef Default_sqrt Sqrt; - - static Sqrt sqrt_object(const Traits&) - { - return Sqrt(); - } -}; - -// Case: do_not_use_default = true. -template - class Get_sqrt -{ -public: - typedef typename Traits::Sqrt Sqrt; - - static Sqrt sqrt_object(const Traits &traits) - { - return traits.sqrt_object(); - } -}; - -// Get default values. - template - void get_default( - const std::size_t n, OutputIterator output) { - - for (std::size_t i = 0; i < n; ++i) { - *(output++) = 0; - } - } - template FT get_tolerance() { return FT(1) / FT(10000000000); @@ -280,7 +231,7 @@ template // Fill coordinates CGAL_assertion(bar_coords_2.size() == num_sides_face); - for(auto& vertex_polyhedron : vertices(triangle_mesh)){ + for(auto vertex_polyhedron : vertices(triangle_mesh)){ bool found_vertex = false; auto bar_coords_itr = bar_coords_2.begin(); @@ -331,7 +282,7 @@ template const FT tol = get_tolerance(); auto face_range = faces(polygon_mesh); - for(auto& face : face_range){ + for(auto face : face_range){ const auto hedge = halfedge(face, polygon_mesh); const auto vertices_face = vertices_around_face(hedge, polygon_mesh); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp index 225ee5c373f1..efc79440c6b4 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include // Typedefs. using SCKER = CGAL::Simple_cartesian; @@ -67,11 +67,9 @@ void test_containers() { const Point_3 p6(1, 0, 1); const Point_3 p7(1, 1, 1); - CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, polyhedron); - CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, surface_mesh); + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, polyhedron, CGAL::parameters::do_not_triangulate_faces(false)); + CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, surface_mesh, CGAL::parameters::do_not_triangulate_faces(false)); - PMP::triangulate_faces(faces(polyhedron), polyhedron); - PMP::triangulate_faces(faces(surface_mesh), surface_mesh); LCoords lcoords; VCoords vcoords; From 58897b24ae135594aa92374c3ea92ac7e48206f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 7 Jun 2024 16:05:57 +0200 Subject: [PATCH 65/68] simplify --- .../Barycentric_coordinates_3/Wachspress_coordinates_3.h | 5 +---- .../test/Barycentric_coordinates_3/test_containers.cpp | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 67bad3233198..2ce1606b8e57 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -250,15 +250,12 @@ namespace Barycentric_coordinates { } FT compute_weights(const Point_3& query) { - // Sum of weights to normalize them later. FT sum = FT(0); // Vertex index. std::size_t vi = 0; - const auto vd = vertices(m_polygon_mesh); - - for (const auto& vertex : vd) { + for (auto vertex : vertices(m_polygon_mesh)) { // Call function to calculate wp coordinates const FT weight = compute_wp_vertex_query(vertex, query); diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp index efc79440c6b4..f78d554cfaf5 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp @@ -70,7 +70,6 @@ void test_containers() { CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, polyhedron, CGAL::parameters::do_not_triangulate_faces(false)); CGAL::make_hexahedron(p0, p1, p2, p3, p4, p5, p6, p7, surface_mesh, CGAL::parameters::do_not_triangulate_faces(false)); - LCoords lcoords; VCoords vcoords; From 146c5b9df200df4a53f2f1691c4e2db86290f1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 12 Feb 2025 16:37:01 +0100 Subject: [PATCH 66/68] add missing include --- .../CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h index f4f48332b8c7..d3beb1926d49 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Mean_value_coordinates_3.h @@ -20,6 +20,8 @@ #include #include +#include + namespace CGAL { namespace Barycentric_coordinates { From 4b7c3576a48615cca4fbc6c5590cfdfd5bc73ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 21 Feb 2025 22:59:23 +0100 Subject: [PATCH 67/68] fix weight computation --- .../Wachspress_coordinates_3.h | 65 ++++++++----------- .../internal/utils_3.h | 6 +- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index 2ce1606b8e57..e700b05afed2 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -271,58 +271,47 @@ namespace Barycentric_coordinates { } // Compute wp coordinates for a given vertex v and a query q + // cf. Wachspress and mean value coordinates by Michael S. Floater Pages 17~18 template FT compute_wp_vertex_query(const Vertex& vertex, const Point_3& query){ // Map vertex descriptor to point_3 const Point_3& vertex_val = get(m_vertex_to_point_map, vertex); - - // Circulator of faces around the vertex - CGAL::Face_around_target_circulator - face_circulator(halfedge(vertex, m_polygon_mesh), m_polygon_mesh); - - CGAL::Face_around_target_circulator - done(face_circulator); - done--; done --; - // Vector connecting query point to vertex; const Vector_3 query_vertex = m_construct_vector_3(query, vertex_val); - // First face. p_1 is negated because the order of the circulator is reversed - const Vector_3 face_normal_1 = internal::get_face_normal( - *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); - const FT dist_perp_1 = m_dot_3(query_vertex, face_normal_1); - CGAL_assertion(dist_perp_1 != FT(0)); - const Vector_3 p_1 = -face_normal_1/dist_perp_1; - face_circulator++; - + // Loop on the faces around the vertex + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + halfedge_descriptor first_h = halfedge(vertex, m_polygon_mesh); + + auto compute_pf_i = [&](halfedge_descriptor h) + { + const Vector_3 nf = internal::get_face_normal( + face(h, m_polygon_mesh), m_vertex_to_point_map, m_polygon_mesh, m_traits); + const FT hfx = m_dot_3(query_vertex, nf); + CGAL_assertion(hfx != FT(0)); + return nf/hfx; + }; + + // First face. + const Vector_3 pf_1 = compute_pf_i(first_h); + halfedge_descriptor h_i=prev(opposite(first_h, m_polygon_mesh), m_polygon_mesh); + Vector_3 pf_i=compute_pf_i(h_i); // Compute weight w_v FT weight = FT(0); - - // Iterate using the circulator do{ - - // Calculate normals of faces - const Vector_3 face_normal_i = internal::get_face_normal( - *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); - face_circulator++; - const Vector_3 face_normal_i_1 = internal::get_face_normal( - *face_circulator, m_vertex_to_point_map, m_polygon_mesh, m_traits); - - // Distance of query to face - const FT perp_dist_i = m_dot_3(query_vertex, face_normal_i); - CGAL_assertion(perp_dist_i != 0); - const FT perp_dist_i_1 = m_dot_3(query_vertex, face_normal_i_1); - CGAL_assertion(perp_dist_i_1 != 0); - - // pf vector - const Vector_3 p_i = face_normal_i/perp_dist_i; - const Vector_3 p_i_1 = face_normal_i_1/perp_dist_i_1; + halfedge_descriptor h_i_p_1=prev(opposite(h_i, m_polygon_mesh), m_polygon_mesh); + if (h_i_p_1==first_h) + break; + const Vector_3 pf_i_p_1=compute_pf_i(h_i_p_1); // Sum partial result to weight - weight += m_det_3(p_1, p_i, p_i_1); + weight += m_det_3(pf_1, pf_i, pf_i_p_1); + h_i=h_i_p_1; + pf_i = pf_i_p_1; + }while(true); - }while(face_circulator!=done); + CGAL_assertion(weight > 0); return weight; } diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index a9a4c92cd9f0..87b4b154d03b 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -108,7 +108,8 @@ enum class Edge_case { using Point_3 = typename GeomTraits::Point_3; using Vector_3 = typename GeomTraits::Vector_3; - const auto& cross_3 = traits.construct_cross_product_vector_3_object(); + const auto cross_3 = traits.construct_cross_product_vector_3_object(); + const auto dot_3 = traits.compute_scalar_product_3_object(); const auto hedge = halfedge(face, triangle_mesh); const auto vertices = vertices_around_face(hedge, triangle_mesh); @@ -121,7 +122,8 @@ enum class Edge_case { const Vector_3 u = point2 - point1; const Vector_3 v = point3 - point1; - const Vector_3 face_normal = cross_3(u, v); + Vector_3 face_normal = cross_3(u, v); + face_normal /= approximate_sqrt(dot_3(face_normal, face_normal)); return face_normal; } From b8a75bb0213edb6178cd78a088d757ce0c4ec3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 21 Feb 2025 23:34:17 +0100 Subject: [PATCH 68/68] do not use deprecated functions --- .../Generalized_barycentric_coordinates_2.h | 42 ++++++------------- .../Wachspress_coordinates_3.h | 4 +- .../internal/utils_3.h | 14 ++----- .../test_containers.cpp | 1 - 4 files changed, 19 insertions(+), 42 deletions(-) diff --git a/Barycentric_coordinates_2/include/CGAL/Barycentric_coordinates_2/Generalized_barycentric_coordinates_2.h b/Barycentric_coordinates_2/include/CGAL/Barycentric_coordinates_2/Generalized_barycentric_coordinates_2.h index 94db271c736c..63c6088ee926 100644 --- a/Barycentric_coordinates_2/include/CGAL/Barycentric_coordinates_2/Generalized_barycentric_coordinates_2.h +++ b/Barycentric_coordinates_2/include/CGAL/Barycentric_coordinates_2/Generalized_barycentric_coordinates_2.h @@ -387,23 +387,18 @@ Generalized_barycentric_coordinates_2 else { // Otherwise, all the coordinates are zero apart from those for the chosen edge with the index = `index`. for(int i = 0; i < index; ++i) { - *output = FT(0); - ++output; + *output++ = FT(0); } // Compute segment coordinates along the chosen edge with the index = `index`. - Segment_coordinates_2 segment_coordinates(vertex[index], vertex[index+1]); - std::optional success = segment_coordinates(query_point, output); - ++output; + output = segment_coordinates_2(vertex[index], vertex[index+1], query_point, output); for(int i = index + 1; i < last; ++i) { - *output = FT(0); - ++output; + *output++ = FT(0); } // Return coordinates. - if(success) return std::optional(output); - else return std::optional(); + return output; } // Pointer cannot be here. Something went wrong. @@ -429,31 +424,24 @@ Generalized_barycentric_coordinates_2 else { // Otherwise, all the coordinates are zeros apart from those for the edge with the query point. int index; - bool status = false; for(index = 0; index < last; ++index) { if(collinear_2(vertex[index], vertex[index+1], query_point) && collinear_are_ordered_along_line_2(vertex[index], query_point, vertex[index+1])) { // Compute segment coordinates along the edge with the query point. - Segment_coordinates_2 segment_coordinates(vertex[index], vertex[index+1]); - std::optional success = segment_coordinates(query_point, output); - if(success) status = true; - ++output; + output = segment_coordinates_2(vertex[index], vertex[index+1], query_point, output); break; } else { - *output = FT(0); - ++output; + *output++ = FT(0); } } for(int i = index + 1; i < last; ++i) { - *output = FT(0); - ++output; + *output++ = FT(0); } // Return coordinates. - if(status == true) return std::optional(output); - else return std::optional(); + return output; } // Pointer cannot be here. Something went wrong. @@ -478,22 +466,18 @@ Generalized_barycentric_coordinates_2 // Compute segment coordinates along the last edge of the polygon. Segment_coordinates_2 segment_coordinates(vertex[last], vertex[0]); - std::optional success = segment_coordinates(query_point, std::back_inserter(coordinate)); + segment_coordinates_2(vertex[last], vertex[0], query_point, std::back_inserter(coordinate)); // Store all the coordinate values. // All the values are zeros apart from those corresponding to the first and the last vertices of the polygon. - *output = coordinate[1]; - ++output; + *output++ = coordinate[1]; for(int i = 1; i < last; ++i) { - *output = FT(0); - ++output; + *output++ = FT(0); } - *output = coordinate[0]; - ++output; + *output++ = coordinate[0]; // Return computed coordinates. - if(success) return std::optional(output); - else return std::optional(); + return output; } // COORDINATES AT VERTEX. diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h index e700b05afed2..51adcd143ae5 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/Wachspress_coordinates_3.h @@ -280,7 +280,7 @@ namespace Barycentric_coordinates { // Vector connecting query point to vertex; const Vector_3 query_vertex = m_construct_vector_3(query, vertex_val); - // Loop on the faces around the vertex + // Loop on the faces the vertex using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; halfedge_descriptor first_h = halfedge(vertex, m_polygon_mesh); @@ -299,6 +299,8 @@ namespace Barycentric_coordinates { Vector_3 pf_i=compute_pf_i(h_i); // Compute weight w_v FT weight = FT(0); + + // Iterate using the circulator do{ halfedge_descriptor h_i_p_1=prev(opposite(h_i, m_polygon_mesh), m_polygon_mesh); if (h_i_p_1==first_h) diff --git a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h index 87b4b154d03b..3fb88cdd3536 100644 --- a/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h +++ b/Barycentric_coordinates_3/include/CGAL/Barycentric_coordinates_3/internal/utils_3.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include namespace CGAL{ @@ -182,10 +182,6 @@ enum class Edge_case { using Plane_3 = typename GeomTraits::Plane_3; using Point_2 = typename GeomTraits::Point_2; - using Wachspress = CGAL::Barycentric_coordinates::Wachspress_2; - using Wachspress_coordinates = CGAL::Barycentric_coordinates::Generalized_barycentric_coordinates_2; - using Triangle_coordinates = CGAL::Barycentric_coordinates::Triangle_coordinates_2; - const FT tol = get_tolerance(); const std::size_t num_sides_face = vertices_face.size(); @@ -220,15 +216,11 @@ enum class Edge_case { // Use wp_2 or triangle coordinates if(use_wp_flag){ - - Wachspress_coordinates wachspress_coordinates(polygon.begin(), polygon.end()); - wachspress_coordinates(query_2, std::back_inserter(bar_coords_2)); + wachspress_coordinates_2(polygon, query_2, std::back_inserter(bar_coords_2)); } else{ - CGAL_assertion(polygon.size() == 3); - Triangle_coordinates triangle_coordinates(polygon[0], polygon[1], polygon[2]); - triangle_coordinates(query_2, std::back_inserter(bar_coords_2)); + triangle_coordinates_2(polygon[0], polygon[1], polygon[2], query_2, std::back_inserter(bar_coords_2)); } // Fill coordinates diff --git a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp index f78d554cfaf5..1727abbb0f50 100644 --- a/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp +++ b/Barycentric_coordinates_3/test/Barycentric_coordinates_3/test_containers.cpp @@ -46,7 +46,6 @@ void test_containers() { using FT = typename Kernel::FT; using Point_3 = typename Kernel::Point_3; - namespace PMP = CGAL::Polygon_mesh_processing; using Polyhedron = CGAL::Polyhedron_3; using Surface_mesh = CGAL::Surface_mesh;